在Scipy中,curve_fit如何及为何计算参数估计的协方差

37 投票
3 回答
33495 浏览
提问于 2025-04-17 15:44

我一直在使用 scipy.optimize.leastsq 来拟合一些数据。我想要得到这些估计值的置信区间,所以我查看了 cov_x 的输出,但文档上对这个内容的解释非常不清楚,我不知道该如何从中得到参数的协方差矩阵。

首先,它说这是一个雅可比矩阵,但在说明中也提到“cov_x 是对海森矩阵的雅可比近似”,所以这实际上不是一个雅可比矩阵,而是用雅可比的一些近似值得到的海森矩阵。这两种说法哪个是正确的呢?

其次,这句话让我感到困惑:

这个矩阵必须乘以残差方差,才能得到参数估计的协方差——请参见 curve_fit

我确实去查看了 curve_fit 的源代码,他们做了:

s_sq = (func(popt, *args)**2).sum()/(len(ydata)-len(p0))
pcov = pcov * s_sq

这对应于将 cov_x 乘以 s_sq,但我在任何参考资料中都找不到这个公式。有人能解释一下这个公式为什么是正确的吗?我的直觉告诉我应该是反过来的,因为 cov_x 应该是一个导数(雅可比或海森),所以我在想:cov_x * covariance(parameters) = sum of errors(residuals),其中 sigma(parameters) 是我想要的东西。

我该如何将 curve_fit 所做的与我在维基百科上看到的内容联系起来呢?http://en.wikipedia.org/wiki/Propagation_of_uncertainty#Non-linear_combinations

3 个回答

7

数学基础

我们先从线性回归开始。在很多统计问题中,我们假设变量有一些潜在的分布,里面有一些未知的参数,我们需要估计这些参数。在线性回归中,我们假设因变量 yi 和自变量 xij 之间有线性关系:

yi = xi1β1 + ... + xipβp + σεi, i = 1, ..., n。

这里,εi 是独立的标准正态分布,βj 是 p 个未知参数,σ 也是未知的。我们可以把这个写成矩阵的形式:

Y = X β + σε。

在这里,Y、β 和 ε 都是列向量。为了找到最好的 β,我们需要最小化平方和:

S = (Y - X β)T (Y - X β)。

我直接写出解决方案:

β^ = (XT X)-1 XT Y。

如果我们把 Y 看作具体的观测数据,那么 β^ 就是根据这个观测得到的 β 的估计。另一方面,如果我们把 Y 看作随机变量,那么估计值 β^ 也会变成随机变量。这样,我们就可以看到 β^ 的协方差。

因为 Y 服从多元正态分布,而 β^ 是 Y 的线性变换,所以 β^ 也服从多元正态分布。β^ 的协方差矩阵是:

Cov(β^) = (XT X)-1 XT Cov(Y) ((XT X)-1 XT)T = (X T X)-1 σ2

但这里的 σ 是未知的,所以我们也需要估计它。如果我们设:

Q = (Y - X β^)T (Y - X β^),

可以证明 Q / σ2 服从自由度为 n - p 的卡方分布(而且,Q 和 β^ 是独立的)。这使得:

σ^2 = Q / (n - p)

成为 σ2 的无偏估计。因此,β^ 的最终协方差估计是:

(XT X)-1 Q / (n - p)。

SciPy API

curve_fit 是最方便的,第二个返回值 pcov 就是 β^ 的协方差估计,也就是上面提到的最终结果 (XT X)-1 Q / (n - p)。

leastsq 中,第二个返回值 cov_x 是 (XT X)-1。从 S 的表达式中,我们看到 XT X 是 S 的海森矩阵(准确来说是海森矩阵的一半),这就是文档中说 cov_x 是海森矩阵的逆的原因。要得到协方差,你需要把 cov_x 乘以 Q / (n - p)。

非线性回归

在非线性回归中,yi 与参数之间的关系是非线性的:

yi = f(xi, β1, ... , βp) + σεi

我们可以计算 f 对 βj 的偏导数,这样就可以把它近似为线性。然后计算基本上和线性回归是一样的,只不过我们需要迭代地近似最小值。在实际中,算法可以是一些更复杂的,比如 Levenberg–Marquardt 算法,这是 curve_fit 的默认算法。

关于提供 Sigma 的更多信息

这一部分讲的是 curve_fit 中的 sigmaabsolute_sigma 参数。如果你在使用 curve_fit 时对 Y 的协方差没有任何先验知识,可以忽略这一部分。

绝对 Sigma

在上面的线性回归中,yi 的方差是 σ,且是未知的。如果你知道方差,可以通过 sigma 参数提供给 curve_fit,并设置 absolute_sigma=True

假设你提供的 sigma 矩阵是 Σ。即:

Y ~ N(X β, Σ)。

Y 服从均值为 X β 和协方差为 Σ 的多元正态分布。我们想要最大化 Y 的似然性。从 Y 的概率密度函数来看,这等价于最小化:

S = (Y - X β)T Σ-1 (Y - X β)。

解决方案是:

β^ = (XT Σ-1 X)-1 XT Σ-1 Y。

而:

Cov(β^) = (XT Σ-1 X)-1

上面的 β^ 和 Cov(β^) 是 curve_fitabsolute_sigma=True 时的返回值。

相对 Sigma

在某些情况下,你不知道 yi 的确切方差,但你知道不同 yi 之间的相对关系,比如 y2 的方差是 y1 方差的 4 倍。这时你可以传递 sigma 并设置 absolute_sigma=False

这时:

Y ~ N(X β, Σσ)

提供一个已知的矩阵 Σ 和一个未知的数 σ。要最小化的目标函数和绝对 sigma 的情况是一样的,因为 σ 是一个常数,因此估计值 β^ 也是一样的。但协方差:

Cov(β^) = (XT Σ-1 X)-1 σ2

里面有未知的 σ。为了估计 σ,我们设:

Q = (Y - X β^)T Σ-1 (Y - X β^)。

同样,Q / σ2 服从自由度为 n - p 的卡方分布。

Cov(β^) 的估计是:

(XT Σ-1 X)-1 Q / (n - p)。

这就是 curve_fitabsolute_sigma=False 时的第二个返回值。

7

我在寻找类似问题的过程中发现了这个解决方案,对HansHarhoff的回答有一点小改进。使用leastsq函数的完整输出会返回一个叫做infodict的值,这里面包含了infodict['fvec'] = f(x) - y。这样,我们就可以计算简化的卡方值,公式是:

s_sq = (infodict['fvec']**2).sum()/ (N-n)

顺便说一下,感谢HansHarhoff为解决这个问题做了大部分的工作。

32

好的,我想我找到了答案。首先,解决方案是:cov_x*s_sq实际上就是你想要的参数的协方差。对对角线上的元素取平方根会得到标准差(但要小心协方差哦!)。

残差方差 = 减少的卡方 = s_sq = sum[(f(x)-y)^2]/(N-n),这里的N是数据点的数量,n是拟合参数的数量。减少的卡方

我困惑的原因是,leastsq给出的cov_x实际上在其他地方并不叫做cov(x),而是减少的cov(x)或分数cov(x)。它在其他参考资料中没有出现的原因是,这只是一个简单的重新缩放,这在数值计算中很有用,但在教科书中并不相关。

关于Hessian和Jacobian,文档的表述不太清楚。实际上在这两种情况下计算的是Hessian,因为在最小值处Jacobian是零。他们的意思是,他们使用了一种近似的Jacobian来找到Hessian。

还有一点。似乎curve_fit的结果并没有真正考虑错误的绝对大小,而只是考虑了提供的sigma的相对大小。这意味着即使误差条的大小变化了一百万倍,返回的pcov也不会改变。当然,这样是不对的,但似乎是标准做法,比如Matlab在使用他们的曲线拟合工具箱时也是这样。正确的程序在这里描述:https://en.wikipedia.org/wiki/Linear_least_squares_(mathematics)#Parameter_errors_and_correlation

一旦找到最佳值,这个过程似乎相当简单,至少对于线性最小二乘法来说是这样。

撰写回答