如何使用scipy.optimize.least_squares计算标准偏差

时间:2017-02-22 09:53:36

标签: python-2.7 scipy least-squares

我将拟合与optimize.curve_fit和optimize.least_squares进行比较。使用curve_fit,我得到协方差矩阵pcov作为输出,我可以通过以下方法计算拟合变量的标准偏差:

perr = np.sqrt(np.diag(pcov))

如果我使用least_squares进行拟合,我没有得到任何协方差矩阵输出,我无法计算变量的标准偏差。

这是我的例子:

#import modules
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import least_squares

noise = 0.5
N = 100
t = np.linspace(0, 4*np.pi, N)

# generate data
def generate_data(t, freq, amplitude, phase, offset, noise=0, n_outliers=0, random_state=0):
    #formula for data generation with noise and outliers
    y = np.sin(t * freq + phase) * amplitude + offset
    rnd = np.random.RandomState(random_state)
    error = noise * rnd.randn(t.size)
    outliers = rnd.randint(0, t.size, n_outliers)
    error[outliers] *= 10
    return y + error

#generate data
data = generate_data(t, 1, 3, 0.001, 0.5, noise, n_outliers=10)

#initial guesses
p0=np.ones(4)
x0=np.ones(4)

# create the function we want to fit
def my_sin(x, freq, amplitude, phase, offset):
    return np.sin(x * freq + phase) * amplitude + offset

# create the function we want to fit for least-square
def my_sin_lsq(x, t, y):
    # freq=x[0]
    # phase=x[1]
    # amplitude=x[2]
    # offset=x[3]
    return (np.sin(t*x[0]+x[2])*x[1]+ x[3]) - y

# now do the fit for curve_fit
fit = curve_fit(my_sin, t, data, p0=p0)
print 'Curve fit output:'+str(fit[0])

#now do the fit for least_square
res_lsq = least_squares(my_sin_lsq, x0, args=(t, data))
print 'Least_squares output:'+str(res_lsq.x)


# we'll use this to plot our first estimate. This might already be good enough for you
data_first_guess = my_sin(t, *p0)

#data_first_guess_lsq = x0[2]*np.sin(t*x0[0]+x0[1])+x0[3]
data_first_guess_lsq = my_sin(t, *x0)

# recreate the fitted curve using the optimized parameters
data_fit = my_sin(t, *fit[0])
data_fit_lsq = my_sin(t, *res_lsq.x)

#calculation of residuals
residuals = data - data_fit
residuals_lsq = data - data_fit_lsq
ss_res = np.sum(residuals**2)
ss_tot = np.sum((data-np.mean(data))**2)
ss_res_lsq = np.sum(residuals_lsq**2)
ss_tot_lsq = np.sum((data-np.mean(data))**2)

#R squared
r_squared = 1 - (ss_res/ss_tot)
r_squared_lsq = 1 - (ss_res_lsq/ss_tot_lsq)
print 'R squared curve_fit is:'+str(r_squared)
print 'R squared least_squares is:'+str(r_squared_lsq)

plt.figure()
plt.plot(t, data)
plt.title('curve_fit')
plt.plot(t, data_first_guess)
plt.plot(t, data_fit)
plt.plot(t, residuals)

plt.figure()
plt.plot(t, data)
plt.title('lsq')
plt.plot(t, data_first_guess_lsq)
plt.plot(t, data_fit_lsq)
plt.plot(t, residuals_lsq)

#error
perr = np.sqrt(np.diag(fit[1]))
print 'The standard deviation errors for curve_fit are:' +str(perr)

我非常感谢任何帮助,祝福

ps:我从这个来源获得了很多输入,并使用了部分代码Robust regression

2 个答案:

答案 0 :(得分:2)

optimize.least_squares的结果在其中有一个名为jac的参数。来自documentation

  

jac:ndarray,稀疏矩阵或LinearOperator,形状(m,n)

     

在解决方案中修改雅可比矩阵,在这个意义上,J ^ T J是成本函数的Hessian的Gauss-Newton近似。类型与算法使用的类型相同。

这可以用于使用以下公式估计参数的协方差矩阵:Sigma =(J'J)^ - 1。

J = res_lsq.jac
cov = np.linalg.inv(J.T.dot(J))

要查找参数的方差,可以使用:

var = np.sqrt(np.diagonal(cov))

答案 1 :(得分:0)

SciPy 程序 optimize.least_squares 要求用户在输入中提供函数 fun(...),该函数返回残差向量。这通常定义为

residuals = (data - model)/sigma

其中 datamodel 是向量,其中包含要拟合的数据以及每个数据点的相应模型预测,而 sigma 是每个 data 值中的 1σ 不确定性.

在这种情况下,假设可以信任输入 sigma 的不确定性,可以使用 jac 返回的输出雅可比矩阵 least_squares 来估计协方差矩阵。此外,假设协方差矩阵是对角的,或者简单地忽略非对角项,还可以得到模型参数中的 1σ 不确定性 perr(通常称为“形式误差”)如下(见第 15.4.2 节) Numerical Recipes 3rd ed.)

import numpy as np
from scipy import linalg, optimize

res = optimize.least_squares(...)

U, s, Vh = linalg.svd(res.jac, full_matrices=False)
tol = np.finfo(float).eps*s[0]*max(res.jac.shape)
w = s > tol
cov = (Vh[w].T/s[w]**2) @ Vh[w]  # robust covariance matrix
perr = np.sqrt(np.diag(cov))     # 1sigma uncertainty on fitted parameters

上面获取协方差矩阵的代码与下面的简单代码形式相同(如 Alex 所建议的),但上面的主要优点是即使雅可比矩阵接近退化时也能工作,这是一种常见的出现在现实世界的最小二乘拟合中

cov = linalg.inv(res.jac.T @ res.jac)  # covariance matrix when jac not degenerate

如果不相信输入的不确定性 sigma,仍然可以假设拟合良好,从拟合本身估计数据的不确定性。这对应于假设 chi**2/DOF=1,其中 DOF 是自由度数。在这种情况下,可以使用以下几行在计算不确定性之前重新调整协方差矩阵

chi2dof = np.sum(res.fun**2)/(res.fun.size - res.x.size)
cov *= chi2dof
perr = np.sqrt(np.diag(cov))    # 1sigma uncertainty on fitted parameters