【问题标题】:scipy.optimize curve_fit() won't converge even with proper parametersscipy.optimize curve_fit() 即使使用适当的参数也不会收敛
【发布时间】:2022-11-14 04:03:26
【问题描述】:

我在尝试找到高斯曲线拟合的参数时遇到了麻烦。

网站https://mycurvefit.com/ 很快就提供了一个很好的答案。但是,我使用来自 scipy.optimize 库的 python 的 curve_fit() 实现并没有提供好的结果(即使在输入答案时)。

例如,我试图拟合的方程如下:

def gauss_func(x, a, b, c):
    return a * np.exp(-(x-b)**2/(2*c**2))

带输入点:

x_main = np.array([19.748, 39.611, 59.465])
y_main = np.array([0.438160379, 0.008706677, 0.000160106])

我想在哪里找到参数一个,bC. 从 mycurvefit 网站,我得到了答案:

一个 = 4821416

b = -154.0293

c = 30.51661

这很适合给定的点。但是当我尝试使用 curve_fit() 运行时:

poptMain, pcovMain = curve_fit(gauss_func, x_main, y_main, p0=(1, -1, 1),sigma=np.array([1,1,1]))

我得到“RuntimeError:未找到最佳参数:函数调用次数已达到 maxfev = 800。”错误。

我尝试了什么:

  • 将 maxfev 更改为其他值,例如 5000、10000、100000(无效)。
  • 将初始猜测 p0 替换为更接近 mycurvefit 答案的值(无效)和常见值,例如 [1, 1, 1]、[1, 0, 1] 等(无效)。

即使输入答案,它仍然找不到参数!我之前曾在其他类似案例中使用过相同的代码,并且效果很好。但这一次它根本没有收敛。我能做些什么来解决这个问题?

【问题讨论】:

  • 考虑使用不同的优化器(例如 Nelder Meade vs Levenberg Marquardt)

标签: python scipy


【解决方案1】:

您的问题是尝试用三个未知数(a、b 和 c)和三个点来拟合方程,这有时会出现收敛问题。您需要在用于拟合的数组中提供更多值,用于拟合的点数应至少比未知数多一个,在您的情况下,最小值为 4 个值,但最好提供更多.

如果不是,您可能会遇到问题。例如,使用您为 a b 和 c a 提供的值创建了一些数据,只需绘制这些值,您就可以看到数组的点实际上位于曲线的边缘,这表明一个 bC你给出的肯定是不正确的。

import numpy as np


def gauss_func(x, a, b, c):
    return a * np.exp(-(x-b)**2/(2*c**2))

# create data using the values of a b and c you have give
x = np.linspace(-360, 60, 100)
# create y values and add random noise
y = gauss_func(x, 4821416, -154.0293, 30.51661) + np.random.normal(0, 100000, x.shape)

# fit the created data
poptMain, pcovMain = curve_fit(gauss_func, x, y, p0=(2000, -1, 1))

#plot data
plt.figure()
plt.plot(x, y, label='created data')
plt.plot(x, gauss_func(x, *poptMain), label='fit created data')
plt.scatter(x_main, y_main, label='points given', color='r', zorder=3)
plt.legend()
plt.show()

笔记已使用您认为是真实值的值创建了已创建的数据:
a = 4821416; b = -154.0293 和 c = 30.51661

【讨论】:

  • “用三个点拟合一个具有三个未知数(a、b 和 c)的方程,这将永远行不通”,不是真的。一个二次方程用三个点完美定义。高斯函数只是二次方程的指数版本。我知道更多的价值观会是理想的,但这不是我手头的东西(我只有这 3 点)。非线性高斯曲线是最适合我需要分析的物理现象的那些点(我对其他“3点”进行了同样的分析,他们做得很好)。例如,多项式拟合对此没有好处。
  • 我同意在数学上适合一组使用非线性模型进行观测n未知参数米≥n,但是如果您的数据点有很多噪音,那么您可能会有很多不确定性。请注意mycurvefit.com 给出的值与现实相差甚远
【解决方案2】:

只有 3 个数据点,很难可靠地拟合具有 3 个参数的函数。您肯定不会得到对不确定性的可靠估计,因为这需要比可变参数更多的数据点。此外,指数衰减确实是非线性拟合能够可靠求解的最困难的形式之一。

当然,好的初始值是总是对于非线性最小二乘很重要。而且你的a=1, b=-1, c=1 的初始值并不好——事实上它们有点糟糕。如果您绘制数据并使用这些值评估您的函数,您将能够看到这一点。如果其他一些工具给了你更好的价值,你为什么不尝试这些价值呢?

您还遇到了scipy.optimize.curve_fit 的非常不幸的行为,它将达到其最大函数评估次数视为 RuntimeError,必须将其作为异常捕获然后处理。不幸的是:经过 8000 次迭代,解决方案可能不是最优的,但仍然可以报告(好吧,除了 curve_fit 返回值的格式非常糟糕,无法为您提供额外的信息)。

我可能会建议使用 lmfit(披露,我是主要作者),它不会将达到最大函数调用次数视为异常,并且能够提供更完整的拟合报告。那看起来像这样:

import numpy as np
import lmfit as lmf
import matplotlib.pyplot as plt

def gauss_func(x, a, b, c):
    return a * np.exp(-(x-b)**2/(2*c**2))

x_main = np.array([19.748, 39.611, 59.465])
y_main = np.array([0.438160379, 0.008706677, 0.000160106])

model = lmf.Model(gauss_func)
params = model.make_params(a=5000, b=-25, c=10)

result = model.fit(y_main, params, x=x_main)
print(result.fit_report())

plt.plot(x_main, y_main, label='data')
plt.plot(x_main, result.init_fit, label='inital')
plt.plot(x_main, result.best_fit, label='best fit')
plt.legend()
plt.show()

这将打印出来


[[Model]]
    Model(gauss_func)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 8000
    # data points      = 3
    # variables        = 3
    chi-square         = 7.9694e-09
    reduced chi-square = 7.9694e-09
    Akaike info crit   = -53.2387966
    Bayesian info crit = -55.9429598
    R-squared          = 0.99999994
##  Warning: uncertainties could not be estimated:
[[Variables]]
    a:  368.976235 (init = 5000)
    b: -57.3427447 (init = -25)
    c:  21.0033737 (init = 10)

和这样的情节:

这可能与curve_fit 的发现没有太大区别。但是,当然,无法找到参数中的不确定性,而且不确定性会非常高:ab 的大量值将给出合适的拟合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多