SciPy curve_fit拟合洛伦兹分布无法偏移峰值问题排查
异常根本原因
问题来自接口使用错误叠加数值尺度隐患:
curve_fit参数顺序传反:scipy.optimize.curve_fit的固定调用格式是curve_fit(拟合函数, 自变量x数据, 因变量y数据, ...),你的代码里写的是curve_fit(lorentzian, test_data, frequencies, ...),把洛伦兹输出的幅值数据(本应是因变量y)当成了自变量x,把频率数组(本应是自变量x)当成了要拟合的因变量y,拟合目标和你定义的洛伦兹函数完全不匹配——相当于要让模型学会「输入洛伦兹幅值返回对应频率」,而这个映射本身是一对多的非单值函数,优化器根本找不到有效的梯度下降方向,所以参数从始至终停在初始猜测值不动,自然无法估计协方差,返回全inf的协方差矩阵。- 数值尺度风险:你的频率参数在1e9量级,线宽gamma在1.5e7量级,不同参数的数值差了2个数量级,雅可比矩阵计算时很容易出现浮点精度截断问题,哪怕参数顺序正确,在有噪声的实测场景下也可能出现收敛异常。
修复方案
- 首先修正
curve_fit的参数传入顺序,把自变量frequencies放在第二个参数位置,因变量test_data放在第三个参数位置,修正后的调用语句为:
popt, pcov = curve_fit(lorentzian, frequencies, test_data, maxfev = 100000, p0 = guess)
- 对于你当前生成的无噪声理想数据,只修正参数顺序即可得到完全准确的拟合结果,拟合参数会和你设定的
fake参数完全一致,不会再报协方差估计失败的警告。 - 如果后续要拟合带噪声的实测数据,建议先对频率轴做尺度缩放,比如将所有频率值减去标称中心频率5e9,把自变量范围压缩到±5e7区间,拟合完成后再给中心频率参数加回5e9即可,能大幅提升数值稳定性,避免大数值带来的精度问题。
修正后的完整测试代码
import matplotlib.pyplot as plt import numpy as np from scipy.optimize import curve_fit def lorentzian(freq, gamma, center_freq, amplitude): return amplitude * gamma**2 / ( gamma**2 + ( freq - center_freq )**2) + 1 frequencies = np.arange(4.95e9, 5.05e9, 0.1e9/1000) guess = [15000000, 4.997e9, 28] fake = [15000000, 5.02e9, 28] test_data = lorentzian(frequencies, *fake) # 修正参数顺序 popt, pcov = curve_fit(lorentzian, frequencies, test_data, maxfev = 100000, p0 = guess) fit_lorentzian = lorentzian(frequencies, *popt) plt.plot(frequencies, test_data, label='Ideal data') plt.plot(frequencies, fit_lorentzian, linestyle='--', label='Fit result') plt.legend() plt.show()
内容的提问来源于stack exchange,提问作者ttshaw1
相关产品推荐
相关产品推荐

