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
足以找到其他参数的最佳值。
这是一种蛮力方法,可能有库可以帮助您以更有效的方式执行此操作。
我正在尝试使用 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
足以找到其他参数的最佳值。
这是一种蛮力方法,可能有库可以帮助您以更有效的方式执行此操作。