scipy.optimize.

curve_fit#

scipy.optimize.curve_fit(f, xdata, ydata, p0=None, sigma=None, absolute_sigma=False, check_finite=None, bounds=(-inf, inf), method=None, jac=None, *, full_output=False, nan_policy=None, **kwargs)[原始碼]#

使用非線性最小平方來擬合函數 f 到資料。

假設 ydata = f(xdata, *params) + eps

參數:
fcallable

模型函數 f(x, …)。它必須將自變數作為第一個引數,並將要擬合的參數作為後續的個別引數。

xdataarray_like

資料被測量的自變數。通常應該是長度為 M 的序列,或是 (k,M) 形狀的陣列,用於具有 k 個預測變數的函數,並且如果它是類陣列物件,則每個元素都應該是可轉換為浮點數的。

ydataarray_like

應變數資料,長度為 M 的陣列 - 名義上是 f(xdata, ...)

p0array_like,選用

參數的初始猜測值(長度為 N)。如果為 None,則初始值將全部為 1(如果可以使用內省來確定函數的參數數量,否則會引發 ValueError)。

sigmaNone 或 純量 或 長度為 M 的序列 或 MxM 陣列,選用

決定 ydata 中的不確定性。如果我們將殘差定義為 r = ydata - f(xdata, *popt),那麼 sigma 的解釋取決於其維度數量

  • 純量或 1-D 的 sigma 應包含 ydata 中誤差的標準差值。在這種情況下,最佳化函數為 chisq = sum((r / sigma) ** 2)

  • 2-D 的 sigma 應包含 ydata 中誤差的共變異數矩陣。在這種情況下,最佳化函數為 chisq = r.T @ inv(sigma) @ r

    在 0.19 版本中新增。

None(預設)等同於用 1 填充的 1-D sigma

absolute_sigmabool,選用

如果為 True,則 sigma 以絕對意義使用,且估計的參數共變異數 pcov 反映這些絕對值。

如果為 False(預設),則只有 sigma 值的相對大小重要。傳回的參數共變異數矩陣 pcov 是基於按常數因子縮放 sigma。此常數是透過要求使用縮放 sigma 時,最佳參數 popt 的縮減 chisq 等於 1 來設定。換句話說,sigma 會縮放以符合擬合後殘差的樣本變異數。預設為 False。在數學上,pcov(absolute_sigma=False) = pcov(absolute_sigma=True) * chisq(popt)/(M-N)

check_finitebool,選用

如果為 True,檢查輸入陣列是否不包含 nan 或 inf,如果包含則引發 ValueError。如果輸入陣列確實包含 nan,將此參數設定為 False 可能會靜默地產生無意義的結果。如果未明確指定 nan_policy,則預設為 True,否則為 False。

boundsarray_like 的 2 元組或 Bounds,選用

參數的下限和上限。預設為無界限。有兩種方法可以指定界限

  • Bounds 類別的實例。

  • array_like 的 2 元組:元組的每個元素都必須是長度等於參數數量的陣列,或是純量(在這種情況下,界限被認為對所有參數都相同)。使用 np.inf 搭配適當的符號來停用所有或某些參數的界限。

method{‘lm’,‘trf’,‘dogbox’},選用

用於最佳化的方法。有關更多詳細資訊,請參閱 least_squares。對於無約束問題,預設為 ‘lm’,如果提供 bounds,則預設為 ‘trf’。當觀測值數量少於變數數量時,方法 ‘lm’ 將無法運作,在這種情況下請使用 ‘trf’ 或 ‘dogbox’。

在 0.17 版本中新增。

jaccallable,字串或 None,選用

具有簽名 jac(x, ...) 的函數,它將模型函數相對於參數的 Jacobian 矩陣計算為密集 array_like 結構。它將根據提供的 sigma 進行縮放。如果為 None(預設),則將以數值方式估計 Jacobian。字串關鍵字可用於 ‘trf’ 和 ‘dogbox’ 方法來選擇有限差分方案,請參閱 least_squares

在 0.18 版本中新增。

full_outputboolean,選用

如果為 True,此函數會傳回其他資訊:infodictmesgier

在 1.9 版本中新增。

nan_policy{‘raise’,‘omit’,None},選用

定義當輸入包含 nan 時如何處理。以下選項可用(預設為 None)

  • ‘raise’:拋出錯誤

  • ‘omit’:執行計算時忽略 nan 值

  • None:不執行 NaN 的特殊處理(除非 check_finite 完成);當存在 NaN 時的行為取決於實作,並且可能會變更。

請注意,如果明確指定此值(非 None),則 check_finite 將設定為 False。

在 1.11 版本中新增。

**kwargs

傳遞給 leastsq(對於 method='lm')或 least_squares(否則)的關鍵字引數。

傳回值:
poptarray

參數的最佳值,使得 f(xdata, *popt) - ydata 的平方殘差總和最小化。

pcov2-D 陣列

popt 的估計近似共變異數。對角線提供參數估計的變異數。若要計算參數的一個標準差誤差,請使用 perr = np.sqrt(np.diag(pcov))。請注意,cov 與參數誤差估計之間的關係是根據模型函數在最佳值附近的線性近似推導而來的 [1]。當此近似變得不準確時,cov 可能無法提供不確定性的準確度量。

sigma 參數如何影響估計的共變異數取決於 absolute_sigma 引數,如上所述。

如果解處的 Jacobian 矩陣沒有完整秩,則 ‘lm’ 方法會傳回一個填充 np.inf 的矩陣,另一方面,‘trf’ 和 ‘dogbox’ 方法使用 Moore-Penrose 偽逆來計算共變異數矩陣。具有大條件數的共變異數矩陣(例如,使用 numpy.linalg.cond 計算)可能表示結果不可靠。

infodictdict (僅當 full_output 為 True 時傳回)

包含可選輸出的字典,具有以下鍵

nfev

函數呼叫的次數。方法 ‘trf’ 和 ‘dogbox’ 不會計算數值 Jacobian 近似的函數呼叫次數,與 ‘lm’ 方法相反。

fvec

在解處評估的殘差值,對於 1-D sigma,這是 (f(x, *popt) - ydata)/sigma

fjac

最終近似 Jacobian 矩陣的 QR 分解的 R 矩陣的排列,以欄方式儲存。與 ipvt 一起,可以近似估計的共變異數。方法 ‘lm’ 僅提供此資訊。

ipvt

長度為 N 的整數陣列,它定義一個排列矩陣 p,使得 fjac*p = q*r,其中 r 是上三角矩陣,對角線元素的量值不增加。p 的第 j 欄是單位矩陣的第 ipvt(j) 欄。方法 ‘lm’ 僅提供此資訊。

qtf

向量 (transpose(q) * fvec)。方法 ‘lm’ 僅提供此資訊。

在 1.9 版本中新增。

mesgstr (僅當 full_output 為 True 時傳回)

提供有關解決方案資訊的字串訊息。

在 1.9 版本中新增。

ierint (僅當 full_output 為 True 時傳回)

整數旗標。如果它等於 1、2、3 或 4,則表示已找到解決方案。否則,未找到解決方案。在任何一種情況下,可選輸出變數 mesg 都會提供更多資訊。

在 1.9 版本中新增。

引發:
ValueError

如果 ydataxdata 包含 NaN,或如果使用了不相容的選項。

RuntimeError

如果最小平方最小化失敗。

OptimizeWarning

如果無法估計參數的共變異數。

另請參閱

least_squares

最小化非線性函數的平方和。

scipy.stats.linregress

計算兩組量測值的線性最小平方迴歸。

註解

使用者應確保輸入 xdataydataf 的輸出為 float64,否則最佳化可能會傳回不正確的結果。

使用 method='lm',演算法透過 leastsq 使用 Levenberg-Marquardt 演算法。請注意,此演算法只能處理無約束問題。

方法 ‘trf’ 和 ‘dogbox’ 可以處理盒約束。有關更多資訊,請參閱 least_squares 的文件字串。

要擬合的參數必須具有相似的尺度。多個數量級的差異可能會導致不正確的結果。對於 ‘trf’ 和 ‘dogbox’ 方法,可以使用 x_scale 關鍵字引數來縮放參數。

參考文獻

[1]

K. Vugrin et al. Confidence region estimation techniques for nonlinear regression in groundwater flow: Three case studies. Water Resources Research, Vol. 43, W03423, DOI:10.1029/2005WR004804

範例

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from scipy.optimize import curve_fit
>>> def func(x, a, b, c):
...     return a * np.exp(-b * x) + c

定義要擬合且帶有一些雜訊的資料

>>> xdata = np.linspace(0, 4, 50)
>>> y = func(xdata, 2.5, 1.3, 0.5)
>>> rng = np.random.default_rng()
>>> y_noise = 0.2 * rng.normal(size=xdata.size)
>>> ydata = y + y_noise
>>> plt.plot(xdata, ydata, 'b-', label='data')

擬合函數 func 的參數 a、b、c

>>> popt, pcov = curve_fit(func, xdata, ydata)
>>> popt
array([2.56274217, 1.37268521, 0.47427475])
>>> plt.plot(xdata, func(xdata, *popt), 'r-',
...          label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt))

將最佳化限制在 0 <= a <= 30 <= b <= 10 <= c <= 0.5 的區域

>>> popt, pcov = curve_fit(func, xdata, ydata, bounds=(0, [3., 1., 0.5]))
>>> popt
array([2.43736712, 1.        , 0.34463856])
>>> plt.plot(xdata, func(xdata, *popt), 'g--',
...          label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt))
>>> plt.xlabel('x')
>>> plt.ylabel('y')
>>> plt.legend()
>>> plt.show()
../../_images/scipy-optimize-curve_fit-1_00_00.png

為了獲得可靠的結果,模型 func 不應過度參數化;多餘的參數可能會導致不可靠的共變異數矩陣,並且在某些情況下,擬合品質較差。作為模型是否可能過度參數化的快速檢查,請計算共變異數矩陣的條件數

>>> np.linalg.cond(pcov)
34.571092161547405  # may vary

該值很小,因此不會引起太多關注。但是,如果我們要向 func 新增第四個參數 d,其效果與 a 相同

>>> def func2(x, a, b, c, d):
...     return a * d * np.exp(-b * x) + c  # a and d are redundant
>>> popt, pcov = curve_fit(func2, xdata, ydata)
>>> np.linalg.cond(pcov)
1.13250718925596e+32  # may vary

如此大的值引起了關注。共變異數矩陣的對角線元素(與擬合的不確定性相關)提供了更多資訊

>>> np.diag(pcov)
array([1.48814742e+29, 3.78596560e-02, 5.39253738e-03, 2.76417220e+28])  # may vary

請注意,第一項和最後一項比其他元素大得多,這表示這些參數的最佳值是模稜兩可的,並且模型中只需要其中一個參數。

如果 f 的最佳參數相差多個數量級,則產生的擬合可能不準確。有時,curve_fit 可能無法找到任何結果

>>> ydata = func(xdata, 500000, 0.01, 15)
>>> try:
...     popt, pcov = curve_fit(func, xdata, ydata, method = 'trf')
... except RuntimeError as e:
...     print(e)
Optimal parameters not found: The maximum number of function evaluations is
exceeded.

如果參數尺度大致事先已知,則可以在 x_scale 引數中定義它

>>> popt, pcov = curve_fit(func, xdata, ydata, method = 'trf',
...                        x_scale = [1000, 1, 1])
>>> popt
array([5.00000000e+05, 1.00000000e-02, 1.49999999e+01])