从 scipy optimize.least_squares 方法获取拟合参数的协方差矩阵

Getting covariance matrix of fitted parameters from scipy optimize.least_squares method

我正在使用 scipy.optimize 的 least_squares method 来执行约束非线性最小二乘优化。我想知道如何获取拟合参数的协方差矩阵以获得拟合参数的误差条?

这对于 curve_fit and leastsq 来说似乎很清楚,但对于 least_squares 方法则不太清楚(至少对我而言)。

我一直在做的一种方法是,因为我知道 least_squares return 是雅可比矩阵 J(即 "jac" return 值),那么我所做的就是用 2*J^T J 近似 Hessian H。最后,协方差矩阵为 H^{-1},因此近似为 (2*J^T J) ^{-1},但我担心协方差的近似值可能过于粗糙?

curve_fit 本身使用来自 least_squareshttps://github.com/scipy/scipy/blob/2526df72e5d4ca8bad6e2f4b3cbdfbc33e805865/scipy/optimize/minpack.py#L739

的雅可比矩阵的伪逆

需要注意的一件事是,如果结果接近界限,则整个方法都是可疑的。

实际上在 optimize.least_squares 中,我从 optimize.leastsqoptimize.curve_fit 中恢复了 相同的错误 使用:

hess_inv = (J.T J)^{-1}

他们解释了这个近似:为什么 Hessian=JT J 的近似 合理吗?

另一方面,我从 optimize.minimize 最小化并使用您提到的表达式恢复了 相同的错误

hess_inv = (2 J.T J)^{-1}


代码

使用this example and calculating the errors of the fit as the square root of the diagonal elements of the covariance, which is given by hess_inv * residual variance (as in this post):

import numpy as np
from scipy import optimize
import random

np.random.seed(0)

#True parameters
def f( x, p):
    return p[0]*x + 0.4*np.sin(p[1]*x) + p[2]
p = np.array([1, 40, 2])

print('True p: ', p)

#Generate random data
xdata = np.linspace(0., 1, 120)
ydata = f(xdata, p) +  np.random.normal(0., 0.2, len(xdata))

#Fits
pstart = [1,42,1]
errFunc = lambda p, x, y: f(x,p) - y

#Error of parameters
def errFit(hess_inv, resVariance):
    return np.sqrt( np.diag( hess_inv * resVariance))

#optimize.leastsq
fit, hess_inv, infodict, errmsg, success = optimize.leastsq(errFunc, pstart, args=(xdata, ydata), full_output=1)
dFit = errFit(hess_inv, (errFunc(fit, xdata, ydata)**2).sum()/(len(ydata)-len(pstart) ) )
print('leastsq:\n\tp: ' , fit, '\n\tdp: ', dFit)

#optimize.curve_fit
fit, cov = optimize.curve_fit(lambda x, p0, p1, p2: f(x, [p0, p1, p2]), xdata, ydata, p0=pstart)
dFit = np.sqrt(np.diag(cov))
print('curve_fit:\n\tp: ' , fit, '\n\tdp: ', dFit)

#optimize.minimize
result = optimize.minimize(lambda p,x,y: np.sum( errFunc(p,x,y)**2 ), pstart,  args=(xdata, ydata))
dFit = errFit( result.hess_inv,  result.fun/(len(ydata)-len(pstart)) )
print('minimize:\n\tp: ' , result.x, '\n\tdp: ', dFit)

#optimize.least_squares
result = optimize.least_squares(errFunc, pstart, args=(xdata, ydata))
dFit = errFit( np.linalg.inv(np.dot(result.jac.T, result.jac)), (errFunc(result.x, xdata, ydata)**2).sum()/(len(ydata)-len(pstart) ) ) 
dFit2 = errFit( np.linalg.inv(2 * np.dot(result.jac.T, result.jac)), (errFunc(result.x, xdata, ydata)**2).sum()/(len(ydata)-len(pstart) ) ) 
print('least_squares:\n\tp: ' , result.x, '\n\tdp: ', dFit, '\n\tdp2: ', dFit2)

输出:

True p:  [ 1 40  2]
leastsq:
    p:  [ 1.0309677  40.11550517  2.01042538] 
    dp:  [0.06615346 0.11975949 0.03823861]
curve_fit:
    p:  [ 1.0309677  40.11550517  2.01042538] 
    dp:  [0.06615346 0.11975949 0.03823861]
minimize:
    p:  [ 1.03096783 40.1155004   2.01042534] 
    dp:  [0.04706757 0.08684545 0.02719714]
least_squares:
    p:  [ 1.03096771 40.11550517  2.01042538] 
    dp:  [0.06615349 0.11975979 0.03823862] 
    dp2:  [0.04677758 0.08468296 0.02703878]