使用带权重的scipy.optimize.curve_fit

时间:2014-12-29 21:31:12

标签: python scipy curve-fitting

根据documentation,参数sigma可用于设置拟合中数据点的权重。这些"描述"参数absolute_sigma=True时的1-sigma错误。

我有一些人为的正常分布噪声数据,这些数据各不相同:

n = 200
x = np.linspace(1, 20, n)
x0, A, alpha = 12, 3, 3

def f(x, x0, A, alpha):
    return A * np.exp(-((x-x0)/alpha)**2)

noise_sigma = x/20
noise = np.random.randn(n) * noise_sigma
yexact = f(x, x0, A, alpha)
y = yexact + noise

如果我想使用y将嘈杂的fcurve_fit匹配到我应该设置sigma的内容?这里的文档并不具体,但我通常会使用1/noise_sigma**2作为权重:

p0 = 10, 4, 2
popt, pcov = curve_fit(f, x, y, p0)
popt2, pcov2 = curve_fit(f, x, y, p0, sigma=1/noise_sigma**2, absolute_sigma=True)
但是,它似乎并没有提高适应性。

enter image description here

此选项仅用于通过协方差矩阵更好地解释拟合不确定性吗?这两个告诉我的区别是什么?

In [249]: pcov
Out[249]: 
array([[  1.10205238e-02,  -3.91494024e-08,   8.81822412e-08],
       [ -3.91494024e-08,   1.52660426e-02,  -1.05907265e-02],
       [  8.81822412e-08,  -1.05907265e-02,   2.20414887e-02]])

In [250]: pcov2
Out[250]: 
array([[ 0.26584674, -0.01836064, -0.17867193],
       [-0.01836064,  0.27833   , -0.1459469 ],
       [-0.17867193, -0.1459469 ,  0.38659059]])

1 个答案:

答案 0 :(得分:1)

至少对于scipy版本1.1.0,参数260应该等于每个参数上的错误。 documentation具体说:

  

1-d sigma应包含误差的标准偏差值   ydata。在这种情况下,优化函数为chisq = sum((r / sigma)   ** 2)。

在您的情况下,将是:

sigma

我查看了source代码,并验证了以这种方式指定sigma时,它将使curve_fit(f, x, y, p0, sigma=noise_sigma, absolute_sigma=True) 最小化。

作为旁注,此 通常是您希望在知道错误时将其最小化的内容。在给定模型((f-data)/sigma)**2的情况下,观察点data的可能性如下:

f

如果您采用负对数,则将变为(不取决于参数的恒定因素):

L(data|x0,A,alpha) = product over i Gaus(data_i, mean=f(x_i,x0,A,alpha), sigma=sigma_i)

那只是卡方。

我编写了一个测试程序来验证-log(L) = sum over i (f(x_i,x0,A,alpha)-data_i)**2/(sigma_i**2) 确实返回了正确指定了sigma的正确值:

curve_fit

输出:

from __future__ import print_function
import numpy as np
from scipy.optimize import curve_fit, fmin

np.random.seed(0)

def make_chi2(x, data, sigma):
    def chi2(args):
        x0, A, alpha = args
        return np.sum(((f(x,x0,A,alpha)-data)/sigma)**2)
    return chi2

n = 200
x = np.linspace(1, 20, n)
x0, A, alpha = 12, 3, 3

def f(x, x0, A, alpha):
    return A * np.exp(-((x-x0)/alpha)**2)

noise_sigma = x/20
noise = np.random.randn(n) * noise_sigma
yexact = f(x, x0, A, alpha)
y = yexact + noise

p0 = 10, 4, 2

# curve_fit without parameters (sigma is implicitly equal to one)
popt, pcov = curve_fit(f, x, y, p0)
# curve_fit with wrong sigma specified
popt2, pcov2 = curve_fit(f, x, y, p0, sigma=1/noise_sigma**2, absolute_sigma=True)
# curve_fit with correct sigma
popt3, pcov3 = curve_fit(f, x, y, p0, sigma=noise_sigma, absolute_sigma=True)

chi2 = make_chi2(x,y,noise_sigma)

# double checking that we get the correct answer
xopt = fmin(chi2,p0,xtol=1e-10,ftol=1e-10)

print("popt  = %s, chi2 = %.2f" % (popt,chi2(popt)))
print("popt2 = %s, chi2 = %.2f" % (popt2, chi2(popt2)))
print("popt3 = %s, chi2 = %.2f" % (popt3, chi2(popt3)))
print("xopt  = %s, chi2 = %.2f" % (xopt, chi2(xopt)))

如您所见,当将popt = [ 11.93617403 3.30528488 2.86314641], chi2 = 200.66 popt2 = [ 11.94169083 3.30372955 2.86207253], chi2 = 200.64 popt3 = [ 11.93128545 3.333727 2.81403324], chi2 = 200.44 xopt = [ 11.93128603 3.33373094 2.81402741], chi2 = 200.44 指定为curve_fit的参数时,chi2确实已正确最小化。

关于改进为什么没有“更好”的原因,我不确定。我唯一的猜测是,如果没有指定sigma值,您将隐式假定它们相等,并且在适合的数据部分(峰值)上,误差“近似”相等。

要回答第二个问题, sigma选项不仅用于更改协方差矩阵的输出,它实际上还可以更改最小化的内容。