【问题标题】:Using scipy.optimize.curve_fit with weights将 scipy.optimize.curve_fit 与权重一起使用
【发布时间】:2014-12-29 21:31:11
【问题描述】:

根据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

如果我想使用curve_fit 将嘈杂的y 调整为f,我应该将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)

不过,它似乎并没有提高合身性。

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

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]])

【问题讨论】:

  • 当你说它似乎并没有提高合身性时,你期待看到什么?
  • 成群的角马威风凛凛地扫过平原。或者失败了,我认为 rms 拟合残差在“with-sigma”情况下会更好,但更糟(0.64 vs 1.07)。
  • 哈哈,牛羚。不过,未加权算法是否不会最小化 rms(回想一下我模糊地记住的日子,当时我做了很多曲线拟合)?在这种情况下,权重肯定只会增加它吗?您是在告诉它“不要太担心这里的这些点,即使以整体 rms 为代价也能更好地适应这些其他点”。
  • 请注意:R 的 nls 采用权重,看起来 Python 的 sigma 对应于 nls 权重的平方根。

标签: python scipy curve-fitting


【解决方案1】:

至少对于 scipy 1.1.0 版,参数sigma 应该等于每个参数的错误。特别是documentation 说:

一维 sigma 应该包含误差的标准差值 数据。在这种情况下,优化的函数是 chisq = sum((r / sigma) ** 2).

你的情况是:

curve_fit(f, x, y, p0, sigma=noise_sigma, absolute_sigma=True)

我查看了 source 代码并验证了当您以这种方式指定 sigma 时,它会最小化 ((f-data)/sigma)**2

作为旁注,这 通常,当您知道错误时,您希望将其最小化。给定模型f 观察点data 的可能性由下式给出:

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)

这只是卡方。

我编写了一个测试程序来验证curve_fit 确实返回了正确的值以及正确指定的 sigma:

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

正如您所见,当您将 sigma=sigma 指定为 curve_fit 的参数时,chi2 确实正确地最小化了。

至于为什么改进不是“更好”,我不太确定。我唯一的猜测是,在不指定 sigma 值的情况下,您隐含地假设它们是相等的,并且在拟合重要的数据部分(峰值)上,误差“大约”相等。

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

【讨论】:

  • 为什么不 sigma = 噪声?
  • @KornpobBhirombhakdi 如果你知道噪声项,那么你可以从数据中减去它,然后你就有一个 完美 信号,你甚至不需要拟合任何事物。对于真实数据,您通常知道误差的标准差,但您不知道每个数据点的实际误差,这就是您适合的原因。
猜你喜欢
  • 1970-01-01
  • 2018-04-20
  • 1970-01-01
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
  • 1970-01-01
  • 2020-06-07
  • 2016-02-20
相关资源
最近更新 更多