scipy.optimize.curve_fit在一个函数上可以正常工作,但在等效函数上会阻塞。示例:
def func(x, a, b, c, d):
# return np.exp(a + b * (c -d * x ) ) # works fine
return a * np.exp( b * (c -d * x ) ) # gives error
误差在协方差矩阵中:
params= [ 1.16507769 13.26573913 5.90351144 6.24181411]
cov=
[[-2.16168732e+13 2.55685110e+12 2.64410274e+11
-1.20320851e+12]
[ 6.54220223e+12 7.78321447e+11 -7.70863674e+11
-3.66264006e+11]
[-1.50943415e+12 -5.12305287e+11 3.25950385e+11
2.41081648e+11]
[-3.07864319e+12 -3.66264061e+11 3.62754606e+11 1.72357248e+11]]
C:\Python34\hsf\pandas\sample.py:119: RuntimeWarning:
invalid value encountered in sqrt
perr = np.sqrt(np.diag(pcov))
perr= [ nan 882225.28145113 570920.64728287 415159.3038493 ]
协方差矩阵中的第一个数字应该是方差,即平方和。怎么会是负面的?
运行无误的版本给出的图形和
params= [ 4.32069414 39.26093245 1.8885566 2.10902333]
cov=
[[ 3.82867157e+14 -3.12417211e+14 5.27625493e+12 1.67824840e+13]
[-2.81245333e+14 2.17844627e+14 -3.31542721e+12 -1.17022169e+13]
[ 3.77680355e+12 -2.52146086e+12 2.50916226e+10 1.35448290e+11]
[ 1.51079875e+13 -1.17022170e+13 1.78098719e+11 6.28621803e+11]]
perr= [19566991.51220365 14759560.52776919 158403.35405783 792856.73527642]
答案 0 :(得分:1)
您有两个冗余参数。您说可以找到的功能的版本为exp(a + b*(c - d*x))
。表达式
a + b*(c - d*x)
可以写
a + b*c - b*d*x = A + B*x
其中A = a + b*c
和B = -b*d
。因此,您可以将函数简化为exp(A + B*x)
。参数过多的问题在于解决方案不是孤立的-解决方案空间的维数与冗余参数的数量相同。这导致Hessian矩阵是奇异的。由于正常的数值不精确性,冗余情况下的Hessian矩阵不会精确地是奇异的,但条件差且接近奇异。协方差矩阵是从Hessian矩阵的逆得到的,因此,如果Hessian矩阵接近奇异,则协方差的计算在数值上是不稳定的,因此不值得信赖。
以下是演示此问题的完整脚本:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
def func(x, a, b, c, d):
return np.exp(a + b*(c - d*x))
def func2(x, a, b):
return np.exp(a - b*x)
np.random.seed(8675309)
x = np.linspace(0, 10, 16)
y = (10*np.exp(-0.5*x)*np.random.lognormal(sigma=0.25, size=len(x))
+ 0.5*np.random.rand(len(x)))
p, pcov = curve_fit(func, x, y)
print("p:", p)
print("pcov:")
print(pcov)
print()
p2, pcov2 = curve_fit(func2, x, y)
print("p2:", p2)
print("pcov2:")
print(pcov2)
plt.plot(x, y, 'bo', label="data")
xx = np.linspace(0, 10, 101)
yy = func(xx, *p)
plt.plot(xx, yy, 'k--', label="4 parameter fit")
yy2 = func2(xx, *p2)
plt.plot(xx, yy2, 'g', linewidth=4, alpha=0.3, label="2 parameter fit")
plt.legend(shadow=True)
plt.grid(True)
plt.xlabel('x')
plt.show()
该脚本生成以下图:
无论我们使用两个还是四个参数,curve_fit
都找到了相同的解决方案。
这是脚本的打印输出:
p: [1.15386327 1.18656718 1.09383746 0.47713239]
pcov:
[[-2.43958994e+12 -7.92454940e+12 9.37347302e+12 3.19227931e+12]
[-3.98689093e+13 -1.05598664e+13 4.33789687e+13 4.25387501e+12]
[ 3.88631613e+13 1.64330041e+13 -4.79524254e+13 -6.61977557e+12]
[ 1.60605592e+13 4.25387505e+12 -1.74745310e+13 -1.71360622e+12]]
p2: [2.45177495 0.56614914]
pcov2:
[[0.00143014 0.00077371]
[0.00077371 0.00130843]]
pcov
是四个参数拟合的结果,基本上是垃圾。在此示例中,所有对角元素均为负,矩阵不对称。
pcov2
(两个参数拟合的协方差矩阵)很好。
故事的寓意:如果您需要使用curve_fit
返回的协方差矩阵,请不要在模型函数中使用冗余参数。
顺便说一句,正如您已经提到的,您可以将exp(A + B*x)
重写为exp(A)*exp(B*x)
,然后定义C = exp(A)
来将您的函数表示为C*exp(B*x)
。然后,您可以使用参数B
和C
代替A
中的B
和curve_fit
。请注意,此版本允许C
小于或等于0,因此可以使用诸如-2*exp(-3*x)
之类的答案。当您使用exp(A + B*x)
时,不能使用像这样的负函数。为了确保两个参数化具有相同的可能解决方案,您将必须使用bounds
参数来约束C
。