scipy curve_fit引发"OptimizeWarning:无法估计参数的协方差". [英] scipy curve_fit raises "OptimizeWarning: Covariance of the parameters could not be estimated"

查看:511
本文介绍了scipy curve_fit引发"OptimizeWarning:无法估计参数的协方差".的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将此功能适合某些数据:

但是当我使用我的代码

 将numpy导入为np从scipy.optimize导入curve_fit导入matplotlib.pyplot作为pltdef f(x,开始,结束):res = np.empty_like(x)res [x<开始] = -1解析度[x>结束] = 1线性= np.all([[[开始< = x],[x< =结束]],轴= 0)[0]res [linear] = np.linspace(-1.,1.,num = np.sum(linear))返回资源如果__name__ =='__main__':xdata = np.linspace(0.,1000.,1000)ydata = -np.ones(1000)ydata [500:1000] = 1.ydata = ydata + np.random.normal(0.,0.25,len(ydata))popt,pcov = curve_fit(f,xdata,ydata,p0 = [495.,505.])打印(popt,pcov)plt.figure()plt.plot(xdata,f(xdata,* popt),'r-',label ='fit')plt.plot(xdata,ydata,'b-',label ='data')plt.show() 

我收到错误

OptimizeWarning:无法估计参数的协方差

输出:

在此示例中,起点和终点应接近500,但与我最初的猜测相比,它们根本没有变化.

解决方案

的警告(不是错误)

  OptimizeWarning:无法估计参数的协方差 

意味着拟合不能确定拟合参数的不确定性(方差).

主要问题是您的模型函数 f 将参数 start end 视为离散值-它们用作整数位置功能形式的变化.scipy的 curve_fit (以及 scipy.optimize 中的所有其他优化例程)都假定参数是连续变量,而不是离散变量.

拟合过程将尝试在参数中采取较小的步骤(通常在机器精度附近)以获取相对于变量(雅可比行列式)的残差的数值导数.将值用作离散变量,这些导数将为零,并且拟合过程将不知道如何更改值以改善拟合.

您似乎正在尝试将阶跃函数适合某些数据.请允许我推荐尝试 lmfit (

Lmfit具有许多额外功能.例如,如果要设置某些参数值的边界或修复某些参数值的变化,则可以执行以下操作:

 #生成命名参数,并提供初始值:pars = model.make_params(line_intercept = ydata.min(),line_slope = 0,step_center = xdata.mean(),step_amplitude = ydata.std(),step_sigma = 2.0)#现在设置步进幅度的最大值和最小值"pars ['step_amplitude'].min = 0pars ['step_amplitude'].max = 100#将行的偏移量固定为-1.0pars ['line_offset'].value = -1.0pars ['line_offset'].vary = False#然后使用这些参数运行out = model.fit(ydata,pars,x = xdata) 

如果您知道模型应该是 Step + Constant 并且常量应该是固定的,则还可以将模型修改为

从lmfit.models中的

 导入ConstantModel#将模型数据作为步进+常数step_mod = StepModel(形式='线性',前缀='step_')const_mod = ConstantModel(前缀='const_')模型= step_mod + const_modpars = model.make_params(const_c = -1,step_center = xdata.mean(),step_amplitude = ydata.std(),step_sigma = 2.0)pars ['const_c'].vary = False 

I am trying to fit this function to some data:

But when I use my code

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

def f(x, start, end):
    res = np.empty_like(x)
    res[x < start] =-1
    res[x > end] = 1
    linear = np.all([[start <= x], [x <= end]], axis=0)[0]
    res[linear] = np.linspace(-1., 1., num=np.sum(linear))
    return res

if __name__ == '__main__':

    xdata = np.linspace(0., 1000., 1000)
    ydata = -np.ones(1000)
    ydata[500:1000] = 1.
    ydata = ydata + np.random.normal(0., 0.25, len(ydata))

    popt, pcov = curve_fit(f, xdata, ydata, p0=[495., 505.])
    print(popt, pcov)
    plt.figure()
    plt.plot(xdata, f(xdata, *popt), 'r-', label='fit')
    plt.plot(xdata, ydata, 'b-', label='data')
    plt.show()

I get the error

OptimizeWarning: Covariance of the parameters could not be estimated

Output:

In this example start and end should be closer to 500, but they dont change at all from my initial guess.

解决方案

The warning (not error) of

OptimizeWarning: Covariance of the parameters could not be estimated

means that the fit could not determine the uncertainties (variance) of the fitting parameters.

The main problem is that your model function f treats the parameters start and end as discrete values -- they are used as integer locations for the change in functional form. scipy's curve_fit (and all other optimization routines in scipy.optimize) assume that parameters are continuous variables, not discrete.

The fitting procedure will try to take small steps (typically around machine precision) in the parameters to get a numerical derivative of the residual with respect to the variables (the Jacobian). With values used as discrete variables, these derivatives will be zero and the fitting procedure will not know how to change the values to improve the fit.

It looks like you're trying to fit a step function to some data. Allow me to recommend trying lmfit (https://lmfit.github.io/lmfit-py) which provides a higher-level interface to curve fitting, and has many built-in models. For example, it includes a StepModel that should be able to model your data.

For a slight modification of your data (so that it has a finite step), the following script with lmfit can fit such data:

#!/usr/bin/python
import numpy as np
from lmfit.models import StepModel, LinearModel
import matplotlib.pyplot as plt

np.random.seed(0)
xdata = np.linspace(0., 1000., 1000)
ydata = -np.ones(1000)
ydata[500:1000] = 1.
# note that a linear step is added here:
ydata[490:510] = -1 + np.arange(20)/10.0
ydata = ydata + np.random.normal(size=len(xdata), scale=0.1)

# model data as Step + Line
step_mod = StepModel(form='linear', prefix='step_')
line_mod = LinearModel(prefix='line_')

model = step_mod + line_mod

# make named parameters, giving initial values:
pars = model.make_params(line_intercept=ydata.min(),
                         line_slope=0,
                         step_center=xdata.mean(),
                         step_amplitude=ydata.std(),
                         step_sigma=2.0)

# fit data to this model with these parameters
out = model.fit(ydata, pars, x=xdata)

# print results
print(out.fit_report())

# plot data and best-fit
plt.plot(xdata, ydata, 'b')
plt.plot(xdata, out.best_fit, 'r-')
plt.show()

which prints out a report of

[[Model]]
    (Model(step, prefix='step_', form='linear') + Model(linear, prefix='line_'))
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 49
    # data points      = 1000
    # variables        = 5
    chi-square         = 9.72660131
    reduced chi-square = 0.00977548
    Akaike info crit   = -4622.89074
    Bayesian info crit = -4598.35197
[[Variables]]
    step_sigma:      20.6227793 +/- 0.77214167 (3.74%) (init = 2)
    step_center:     490.167878 +/- 0.44804412 (0.09%) (init = 500)
    step_amplitude:  1.98946656 +/- 0.01304854 (0.66%) (init = 0.996283)
    line_intercept: -1.00628058 +/- 0.00706005 (0.70%) (init = -1.277259)
    line_slope:      1.3947e-05 +/- 2.2340e-05 (160.18%) (init = 0)
[[Correlations]] (unreported correlations are < 0.100)
    C(step_amplitude, line_slope)     = -0.875
    C(step_sigma, step_center)        = -0.863
    C(line_intercept, line_slope)     = -0.774
    C(step_amplitude, line_intercept) =  0.461
    C(step_sigma, step_amplitude)     =  0.170
    C(step_sigma, line_slope)         = -0.147
    C(step_center, step_amplitude)    = -0.146
    C(step_center, line_slope)        =  0.127

and produces a plot of

Lmfit has lots of extra features. For example, if you want to set bounds on some of the parameter values or fix some from varying, you can do the following:

# make named parameters, giving initial values:
pars = model.make_params(line_intercept=ydata.min(),
                         line_slope=0,
                         step_center=xdata.mean(),
                         step_amplitude=ydata.std(),
                         step_sigma=2.0)

# now set max and min values for step amplitude"
pars['step_amplitude'].min = 0
pars['step_amplitude'].max = 100

# fix the offset of the line to be -1.0
pars['line_offset'].value = -1.0
pars['line_offset'].vary = False

# then run fit with these parameters
out = model.fit(ydata, pars, x=xdata)

If you know the model should be Step+Constant and that the constant should be fixed, you could also modify the model to be

from lmfit.models import ConstantModel
# model data as Step + Constant
step_mod = StepModel(form='linear', prefix='step_')
const_mod = ConstantModel(prefix='const_')

model = step_mod + const_mod

pars = model.make_params(const_c=-1,
                         step_center=xdata.mean(),
                         step_amplitude=ydata.std(),
                         step_sigma=2.0)
pars['const_c'].vary = False

这篇关于scipy curve_fit引发"OptimizeWarning:无法估计参数的协方差".的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆