当我设置bounds参数时,scipy curve_fit会将初始猜测值作为最优结果

lf5gs5x2  于 2022-11-09  发布在  其他
关注(0)|答案(1)|浏览(312)

我尝试使用scipy中的curve_fit函数,用高斯函数和洛伦兹函数拟合一条曲线。

def gaussian(x, a, x0, sig):
    return a * np.exp(-1/2 * (x - x0)**2 / sig**2)

def lorentzian(x, a, b, c):
    return a*c**2/((x-b)**2+c**2)

def decompose(x, z, n, b, *par):
    hb_n = gaussian(x, par[0], 4861.3*(1+z), n)
    hb_b = lorentzian(x, par[1], 4861.3*(1+z), b)
    return hb_b + hb_n

而当我设置p0参数时,我可以得到一个合理的结果,它很好地拟合了曲线。

guess = [0.0001, 2, 10, 3e-16, 3e-16]
p, c = curve_fit(decompose, wave, residual, guess)

fitting parametersthe fitting model and data figure when I set the p0 parameter显示器
但是如果我同时设置p0和bounds参数,curve_fit函数会将初始猜测作为最终拟合结果,这与数据有很大的偏差。

guess = [0.0001, 2, 10, 3e-16, 3e-16]
p, c = curve_fit(decompose, wave, residual, guess, bounds=([-0.001, 0, 0, 0, 0], [0.001, 10, 100, 1e-15, 1e-15]))

fitting parametersthe fitting model and data figure when I set the p0 and bounds parameters simultaneously打印机
“我尝试了很多种不同的参数边界组合,但是拟合的结果总是返回初始的猜测值。我已经在这个问题上困了很长时间了。如果有人能给予我一些解决这个问题的建议,我将非常感激。”

aij0ehis

aij0ehis1#

这是由于优化算法及其参数的组合而发生的。
official documentation
方法('lm','trf','dogbox'},可选
用于优化的方法。有关详细信息,请参阅least_squares。对于无约束的问题,默认值为'lm';如果提供了界限,则默认值为'trf'。当观察值的数量小于变量的数量时,方法'lm'将不起作用,在这种情况下,请使用'trf'或'dogbox'。
所以当你添加边界约束时curve_fit将使用不同的优化算法(信赖域而不是Levenberg-Marquardt)。
要调试该问题,您可以尝试将full_output=True设置为注解中提到的Warren Weckesser
在边界拟合的情况下,您将看到类似于以下内容的内容:

'nfev': 1
`gtol` termination condition is satisfied.

所以优化在第一次迭代后就停止了,这就是为什么找到了与最初猜测相似的参数。
要解决此问题,您可以指定较低的gtol参数。您可以在此处找到可用参数的完整列表:https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares
示例:x1c 0d1x
编码:

import numpy as np
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit

def gaussian(x, a, x0, sig):
    return a * np.exp(-1 / 2 * (x - x0)**2 / sig**2)

def lorentzian(x, a, b, c):
    return a * c**2 / ((x - b)**2 + c**2)

def decompose(x, z, n, b, *par):
    hb_n = gaussian(x, par[0], 4861.3 * (1 + z), n)
    hb_b = lorentzian(x, par[1], 4861.3 * (1 + z), b)
    return hb_b + hb_n

gt_parameters = [-2.42688295e-4, 2.3477827, 1.56977708e1, 4.47455820e-16, 2.2193466e-16]
wave = np.linspace(4750, 5000, num=400)

gt_curve = decompose(wave, *gt_parameters)
noisy_curve = gt_curve + np.random.normal(0, 2e-17, size=len(wave))

guess = [0.0001, 2, 10, 3e-16, 3e-16]
bounds = ([-0.001, 0, 0, 0, 0], [0.001, 10, 100, 1e-15, 1e-15])
options = [
    ("Levenberg-Marquardt without bounds", dict(method="lm")),
    ("Trust Region without bounds", dict(method="trf")),
    ("Trust Region with bounds", dict(method="trf", bounds=bounds)),
    (
        "Trust Region with bounds + fixed tolerance",
        dict(method="trf", bounds=bounds, gtol=1e-36),
    ),
]
fig, axs = plt.subplots(len(options))
for (title, fit_params), ax in zip(options, axs):
    ax.set_title(title)

    p, c = curve_fit(decompose, wave, noisy_curve, guess,**fit_params)

    fitted_curve = decompose(wave, *p)

    ax.plot(wave, gt_curve, label="gt_curve")
    ax.plot(wave, noisy_curve, label="noisy")
    ax.plot(wave, fitted_curve, label="fitted_curve")

handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels)
plt.show()

相关问题