optimize.curve_fit 不覆盖参数 space

optimize.curve_fit does not cover parameter space

我正在尝试使用 curve_fit 将温度和降水数据拟合到周期函数中。出于某种原因,curve_fit 似乎没有测试 bounds 参数定义的整个参数 space。 我拼凑了一个小模型来演示这一点。


############################################################
# First generate some data
import numpy as np

# Seed the random number generator for reproducibility
np.random.seed(0)
def test_func(x, b, c, d, e=0.0):
    return e*x + b * np.sin(2*np.pi * x/c + d)

def func_err(x,y):
    sumxy=0.0
    pos=0;
    for xtst in x:
        sumxy+=np.square(xtst-y[pos])
        pos+=1
    sumxy/=len(x)
    return sumxy

x_data = np.linspace(0, 10, num=50)*2*np.pi
y_data = test_func(x_data,5.0,20,-0.20, 0.1) + 1.0*np.random.normal(size=50)

# And plot it
import matplotlib.pyplot as plt

############################################################
# Now fit a simple sine function to the data
from scipy import optimize


params, params_covariance = optimize.curve_fit(test_func, x_data, y_data,
                                               p0=[1.0, 18, 0.0, 0.0],
                                               bounds=([0.1,5,-5.0, -5.0],[100,100,5.0, 5.0]))

print([params,func_err(y_data,test_func(x_data,params[0],params[1], params[2], params[3]))])

############################################################
# And plot the resulting curve on the data

plt.figure(figsize=(6, 4))
plt.scatter(x_data, y_data, label='Data')
plt.plot(x_data, test_func(x_data, params[0], params[1],params[2], params[3]),
         label='Fitted function')

plt.legend(loc='best')

plt.show()

根据给定的p0=[1.0, 18, 0.0, 0.0],rotuine 找到了一个很好的解决方案, 但是对于像 p0=[1.0, 10, 0.0, 0.0] 这样的初始值,它会非常失败。 为什么例程没有覆盖边界给出的范围来找到它的解决方案?

我认为这是由于周期函数的性质。您的参数 c 决定了函数的周期性。当您对周期性的初始猜测与正确的周期性相去甚远时,拟合将停留在局部最小值。

你可以认为当 p0=[1.0, 10, 0.0, 0.0] 时,拟合算法会找到局部最佳拟合,如第二张图所示,即 [ 0.65476428, 11.14188385, -1.09652992, 0.08971854] 对应 [b,c,d,e],它会尝试将参数稍微移动一点,但它周围的梯度表明这是最合适的,就好像它在参数的“谷”中 space,所以它在那里停止迭代。

curve_fit 不会探索您的整个参数 space:它仅从您的初始猜测开始,在本例中为 p0,并使用启发式方法找到局部最优值.

如果您想探索参数 c 的整个参数 space,您可以实施简单的网格搜索。例如,您可以尝试 c 边界之间的所有值,并对每个 c 值执行 curve_fit,然后选择拟合误差最小的值。

这是一个示例代码:


def MSE(params,x_data,y_data):
    "to calclate mean square error, this is the same as your func_error"
    return ((test_func(x_data,*params)-y_data)**2).mean()

besterror = 10000
bestParam = None

for c_ in np.arange(5,100,1):
    # grid search for parameter c between 5 and 100, step size is 1.
    params, params_covariance = optimize.curve_fit(test_func, x_data, y_data,
                                                   p0=[1.0, c_+0.5, 0.0, 0.0],
                                                   bounds=([0.1,c_,-5.0, -5.0],[100,c_+1,5.0, 5.0]))
    error = MSE(params,x_data,y_data)
    if error<besterror:
        besterror = error 
        bestParam = params

params = bestParam

print([params,func_err(y_data,test_func(x_data,params[0],params[1], params[2], params[3]))])

############################################################
# And plot the resulting curve on the data

params_covariance

plt.figure(figsize=(6, 4))
plt.scatter(x_data, y_data, label='Data')
plt.plot(x_data, test_func(x_data, params[0], params[1],params[2], params[3]),
         label='Fitted function')

plt.legend(loc='best')

plt.show()

在这种情况下不需要对其他参数进行网格搜索,因为 curve_fit 足以找到其他参数的最佳值。

这是一种蛮力方法,可能有库可以帮助您以更有效的方式执行此操作。