Python 如何设置曲线拟合的初始值以找到最佳优化,而不仅仅是局部优化?

Python 如何设置曲线拟合的初始值以找到最佳优化,而不仅仅是局部优化?,python,scikit-learn,scipy,least-squares,best-fit-curve,Python,Scikit Learn,Scipy,Least Squares,Best Fit Curve,我试图拟合一个幂律函数,并且为了找到最佳拟合参数。然而,我发现,如果参数的初始猜测不同,“最佳拟合”输出也不同。除非我找到正确的初始猜测,否则我可以得到最佳优化,而不是局部优化。有没有办法找到**合适的初始猜测**????。下面列出了我的代码。请随时提出意见。谢谢 import numpy as np import pandas as pd from scipy.optimize import curve_fit import matplotlib.pyplot as plt %matplotl

我试图拟合一个幂律函数,并且为了找到最佳拟合参数。然而,我发现,如果参数的初始猜测不同,“最佳拟合”输出也不同。除非我找到正确的初始猜测,否则我可以得到最佳优化,而不是局部优化。有没有办法找到**合适的初始猜测**????。下面列出了我的代码。请随时提出意见。谢谢

import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
%matplotlib inline

# power law function
def func_powerlaw(x,a,b,c):
    return a*(x**b)+c

test_X = [1.0,2,3,4,5,6,7,8,9,10]
test_Y =[3.0,1.5,1.2222222222222223,1.125,1.08,1.0555555555555556,1.0408163265306123,1.03125, 1.0246913580246915,1.02]

predict_Y = []
for x in test_X:
    predict_Y.append(2*x**-2+1)
如果我与默认的初始猜测一致,那么p0=[1,1,1] 下面是适合的,这不是最好的适合。

如果我将初始猜测更改为p0=[0.5,0.5,0.5] 我能找到最合适的

---------------------于2018年7月10日更新-------------------------------------------------------------------------------------------------------------------------


由于我需要运行数千到数百万的幂律函数,使用@James Phillips的方法太昂贵了。那么,除了曲线拟合外,还有什么方法是合适的呢?例如sklearn、np.linalg.lstsq等。

以下是使用scipy.optimize.differential_进化遗传算法的示例代码,以及您的数据和方程。此scipy模块使用拉丁超立方体算法来确保对参数空间进行彻底搜索,因此需要在其中进行搜索的边界-在本例中,这些边界基于数据的最大值和最小值。对于其他问题,如果您知道预期的参数值范围,则可能需要提供不同的搜索边界

import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
import warnings

# power law function
def func_power_law(x,a,b,c):
    return a*(x**b)+c

test_X = [1.0,2,3,4,5,6,7,8,9,10]
test_Y =[3.0,1.5,1.2222222222222223,1.125,1.08,1.0555555555555556,1.0408163265306123,1.03125, 1.0246913580246915,1.02]


# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
    warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
    val = func_power_law(test_X, *parameterTuple)
    return numpy.sum((test_Y - val) ** 2.0)


def generate_Initial_Parameters():
    # min and max used for bounds
    maxX = max(test_X)
    minX = min(test_X)
    maxY = max(test_Y)
    minY = min(test_Y)
    maxXY = max(maxX, maxY)

    parameterBounds = []
    parameterBounds.append([-maxXY, maxXY]) # seach bounds for a
    parameterBounds.append([-maxXY, maxXY]) # seach bounds for b
    parameterBounds.append([-maxXY, maxXY]) # seach bounds for c

    # "seed" the numpy random number generator for repeatable results
    result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
    return result.x

# generate initial parameter values
geneticParameters = generate_Initial_Parameters()

# curve fit the test data
fittedParameters, pcov = curve_fit(func_power_law, test_X, test_Y, geneticParameters)

print('Parameters', fittedParameters)

modelPredictions = func_power_law(test_X, *fittedParameters) 

absError = modelPredictions - test_Y

SE = numpy.square(absError) # squared errors
MSE = numpy.mean(SE) # mean squared errors
RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
Rsquared = 1.0 - (numpy.var(absError) / numpy.var(test_Y))
print('RMSE:', RMSE)
print('R-squared:', Rsquared)

print()


##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
    axes = f.add_subplot(111)

    # first the raw data as a scatter plot
    axes.plot(test_X, test_Y,  'D')

    # create data for the fitted equation plot
    xModel = numpy.linspace(min(test_X), max(test_X))
    yModel = func_power_law(xModel, *fittedParameters)

    # now the model as a line plot
    axes.plot(xModel, yModel)

    axes.set_xlabel('X Data') # X axis data label
    axes.set_ylabel('Y Data') # Y axis data label

    plt.show()
    plt.close('all') # clean up after using pyplot

graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)

下面是使用scipy.optimize.differential_进化遗传算法的示例代码,以及您的数据和方程。此scipy模块使用拉丁超立方体算法来确保对参数空间进行彻底搜索,因此需要在其中进行搜索的边界-在本例中,这些边界基于数据的最大值和最小值。对于其他问题,如果您知道预期的参数值范围,则可能需要提供不同的搜索边界

import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
import warnings

# power law function
def func_power_law(x,a,b,c):
    return a*(x**b)+c

test_X = [1.0,2,3,4,5,6,7,8,9,10]
test_Y =[3.0,1.5,1.2222222222222223,1.125,1.08,1.0555555555555556,1.0408163265306123,1.03125, 1.0246913580246915,1.02]


# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
    warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
    val = func_power_law(test_X, *parameterTuple)
    return numpy.sum((test_Y - val) ** 2.0)


def generate_Initial_Parameters():
    # min and max used for bounds
    maxX = max(test_X)
    minX = min(test_X)
    maxY = max(test_Y)
    minY = min(test_Y)
    maxXY = max(maxX, maxY)

    parameterBounds = []
    parameterBounds.append([-maxXY, maxXY]) # seach bounds for a
    parameterBounds.append([-maxXY, maxXY]) # seach bounds for b
    parameterBounds.append([-maxXY, maxXY]) # seach bounds for c

    # "seed" the numpy random number generator for repeatable results
    result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
    return result.x

# generate initial parameter values
geneticParameters = generate_Initial_Parameters()

# curve fit the test data
fittedParameters, pcov = curve_fit(func_power_law, test_X, test_Y, geneticParameters)

print('Parameters', fittedParameters)

modelPredictions = func_power_law(test_X, *fittedParameters) 

absError = modelPredictions - test_Y

SE = numpy.square(absError) # squared errors
MSE = numpy.mean(SE) # mean squared errors
RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
Rsquared = 1.0 - (numpy.var(absError) / numpy.var(test_Y))
print('RMSE:', RMSE)
print('R-squared:', Rsquared)

print()


##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
    axes = f.add_subplot(111)

    # first the raw data as a scatter plot
    axes.plot(test_X, test_Y,  'D')

    # create data for the fitted equation plot
    xModel = numpy.linspace(min(test_X), max(test_X))
    yModel = func_power_law(xModel, *fittedParameters)

    # now the model as a line plot
    axes.plot(xModel, yModel)

    axes.set_xlabel('X Data') # X axis data label
    axes.set_ylabel('Y Data') # Y axis data label

    plt.show()
    plt.close('all') # clean up after using pyplot

graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)

没有简单的答案:如果有,它将在
curve\u fit
中实现,然后它就不必询问您起点。一种合理的方法是首先拟合齐次模型
y=a*x**b
。假设y为正(这通常是使用幂律时的情况),这可以通过一种粗略而快速的方式完成:在对数刻度上,
log(y)=log(a)+b*log(x)
这是一种线性回归,可以用
np.linalg.lstsq
来解决。这为
log(a)
b
提供了候选项;使用这种方法的
c
候选值是
0

test_X = np.array([1.0,2,3,4,5,6,7,8,9,10])
test_Y = np.array([3.0,1.5,1.2222222222222223,1.125,1.08,1.0555555555555556,1.0408163265306123,1.03125, 1.0246913580246915,1.02])

rough_fit = np.linalg.lstsq(np.stack((np.ones_like(test_X), np.log(test_X)), axis=1), np.log(test_Y))[0]
p0 = [np.exp(rough_fit[0]), rough_fit[1], 0]
结果就是你在第二张图中看到的良好匹配


顺便说一句,最好立即制作一个NumPy数组。否则,您将首先切片
X[1:://code>,这将被NumPy化为一个整数数组,然后抛出一个带有负指数的错误。(我想
1.0
的目的是使它成为一个浮点数组?这就是
dtype=np.float
参数应该用来做的。)

没有简单的答案:如果有,它将在
曲线拟合中实现,然后它就不必问你起点了。一种合理的方法是首先拟合齐次模型
y=a*x**b
。假设y为正(这通常是使用幂律时的情况),这可以通过一种粗略而快速的方式完成:在对数刻度上,
log(y)=log(a)+b*log(x)
这是一种线性回归,可以用
np.linalg.lstsq
来解决。这为
log(a)
b
提供了候选项;使用这种方法的
c
候选值是
0

test_X = np.array([1.0,2,3,4,5,6,7,8,9,10])
test_Y = np.array([3.0,1.5,1.2222222222222223,1.125,1.08,1.0555555555555556,1.0408163265306123,1.03125, 1.0246913580246915,1.02])

rough_fit = np.linalg.lstsq(np.stack((np.ones_like(test_X), np.log(test_X)), axis=1), np.log(test_Y))[0]
p0 = [np.exp(rough_fit[0]), rough_fit[1], 0]
结果就是你在第二张图中看到的良好匹配


顺便说一句,最好立即制作一个NumPy数组。否则,您将首先切片
X[1:://code>,这将被NumPy化为一个整数数组,然后抛出一个带有负指数的错误。(我想
1.0
的目的是使它成为一个浮点数组?这就是
dtype=np.float
参数应该用来做的。)

除了《欢迎使用堆栈溢出》中非常好的回答“没有简单、通用的方法,James Phillips经常使用差异进化” 有助于找到好的起点(甚至是好的解决方案!)如果比
curve\u fit()
”稍慢一些,请允许我单独给出一个您可能觉得有用的答案

首先,
curve\u fit()
默认为任何参数值的事实是一个非常糟糕的想法。这种行为没有可能的理由,您和其他人应该将参数存在默认值的事实视为
curve\u fit()实现中的一个严重错误
并假装此错误不存在。永远不要相信这些默认设置是合理的

从一个简单的数据图可以看出,
a=1,b=1,c=1
是非常非常糟糕的起始值。函数会衰减,因此
b<0
。事实上,如果从
a=1,b=-1,c=1
开始,你就会找到正确的解决方案

它也可能有助于对参数设置合理的界限。甚至设置
c
of(-100100)的界限可能有帮助。关于
b
的符号,我想你可以从一个简单的数据图中看到边界。当我为你的问题尝试这一点时,
c
的边界在初始值为
b=1
时没有帮助,但它对
b=0
b=-5
有帮助

更重要的是,尽管您在绘图中打印最佳拟合参数
popt
,但您不会打印
pcov
中包含的变量与t之间的不确定性或相关性
from lmfit import Model
mod = Model(func_powerlaw)
params = mod.make_params(a=1, b=1, c=1)
ret = mod.fit(test_Y[1:], params, x=test_X[1:])
print(ret.fit_report())
[[Model]]
    Model(func_powerlaw)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 1318
    # data points      = 9
    # variables        = 3
    chi-square         = 0.03300395
    reduced chi-square = 0.00550066
    Akaike info crit   = -44.4751740
    Bayesian info crit = -43.8835003
[[Variables]]
    a: -1319.16780 +/- 6892109.87 (522458.92%) (init = 1)
    b:  2.0034e-04 +/- 1.04592341 (522076.12%) (init = 1)
    c:  1320.73359 +/- 6892110.20 (521839.55%) (init = 1)
[[Correlations]] (unreported correlations are < 0.100)
    C(a, c) = -1.000
    C(b, c) = -1.000
    C(a, b) =  1.000
params = mod.make_params(a=1, b=-0.5, c=1) ## Note !
ret = mod.fit(test_Y[1:], params, x=test_X[1:])
print(ret.fit_report())
[[Model]]
    Model(func_powerlaw)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 31
    # data points      = 9
    # variables        = 3
    chi-square         = 4.9304e-32
    reduced chi-square = 8.2173e-33
    Akaike info crit   = -662.560782
    Bayesian info crit = -661.969108
[[Variables]]
    a:  2.00000000 +/- 1.5579e-15 (0.00%) (init = 1)
    b: -2.00000000 +/- 1.1989e-15 (0.00%) (init = -0.5)
    c:  1.00000000 +/- 8.2926e-17 (0.00%) (init = 1)
[[Correlations]] (unreported correlations are < 0.100)
    C(a, b) = -0.964
    C(b, c) = -0.880
    C(a, c) =  0.769