lmfit中SineModel+ConstantModel拟合不准及参数不确定性问题
问题描述
我尝试用lmfit把简单正弦函数拟合到实验数据里,发现SineModel结合ConstantModel的拟合精度很差。我最关心峰值频率,虽然能用scipy.find_peaks()拿到,但还是想展示拟合曲线。
第一个拟合函数(效果不佳)
我使用的Sine函数代码如下:
def Sine(self, x_axis, y_axis): sine = SineModel() const = ConstantModel() x_fit = np.linspace(min(x_axis), max(x_axis), x_axis.size) guess_sine = sine.guess(y_axis, x=x_fit) pars = sine.guess(y_axis, x=x_fit) sine_offset = SineModel() + ConstantModel() pars.add('c', value=1, vary=True) result = sine_offset.fit(y_axis, pars, x=x_fit) return result
该函数的拟合结果(图表及报告)显示效果不理想。
自定义拟合函数(效果好但无参数不确定性)
后来我自行定义拟合函数,并提供基于FFT的合理初始猜测值,拟合效果更贴近数据,但报告提示“Uncertainties could not be estimated”,无法计算参数不确定性:
def Sine_User2(self, x_axis, y_axis): def sine_func(x, amplitude, freq, shift, c): return amplitude * np.sin(freq * x + shift) + c sinemodel = Model(sine_func) # 对数据做FFT以提供曲线拟合的初始猜测值 x = np.array(x_axis) y = np.array(y_axis) ff = np.fft.fftfreq(len(x), (x[1] - x[0])) # 假设均匀间隔 Fyy = abs(np.fft.fft(y)) guess_freq = abs(ff[np.argmax(Fyy[1:]) + 1]) * 2. * np.pi guess_amp = np.std(y) * 2.**0.5 guess_offset = np.mean(y) x_fit = np.linspace(min(x_axis), max(x_axis), x_axis.size) params = sinemodel.make_params(amplitude = guess_amp, freq = guess_freq, shift = 0, c = guess_offset ) result = sinemodel.fit(y_axis, params, x = x_fit) return result
我曾尝试为参数添加最值限制,将sinmodel.make_params替换为以下代码,但拟合效果又回到第一个模型的糟糕状态,说明问题出在初始值设置上:
params = Parameters() params.add('amplitude', value=guess_amp, min = 0) params.add('freq', value=guess_freq, min=0) params.add('shift', value=0, min=-2*np.pi, max=2*np.pi) params.add('c', value=guess_offset)
核心问题
- 如何改进第一个
Sine函数的拟合效果? - 第二个
Sine_User2函数无法计算参数不确定性的原因是什么?
实验数据(CSV)
Wavelength (nm),Power (dBm),,,,, 1549.9,-13.76008731,,,,, 1549.905,-13.69423162,,,,, 1549.91,-12.59004339,,,,, 1549.915,-11.31061848,,,,, 1549.92,-10.58731809,,,,, 1549.925,-10.19024329,,,,, 1549.93,-10.07301418,,,,, 1549.935,-10.19513172,,,,, 1549.94,-10.45582159,,,,, 1549.945,-11.15984161,,,,, 1549.95,-12.15876596,,,,, 1549.955,-13.44674933,,,,, 1549.96,-13.56388277,,,,, 1549.965,-12.2513065,,,,, 1549.97,-11.08699015,,,,, 1549.975,-10.43829185,,,,, 1549.98,-10.12861158,,,,, 1549.985,-10.0962929,,,,, 1549.99,-10.1852173,,,,, 1549.995,-10.55438183,,,,, 1550,-11.19555345,,,,, 1550.005,-12.28715299,,,,, 1550.01,-13.5153863,,,,, 1550.015,-13.47019261,,,,, 1550.02,-12.12394732,,,,, 1550.025,-11.01946751,,,,, 1550.03,-10.42138778,,,,, 1550.035,-10.14438079,,,,, 1550.04,-10.05681218,,,,, 1550.045,-10.17148605,,,,, 1550.05,-10.56046759,,,,, 1550.055,-11.11621478,,,,, 1550.06,-12.19930263,,,,, 1550.065,-13.48428349,,,,, 1550.07,-13.43424913,,,,, 1550.075,-12.08019952,,,,, 1550.08,-11.08731704,,,,, 1550.085,-10.45730899,,,,, 1550.09,-10.11278169,,,,, 1550.095,-10.00651194,,,,, ,,,,,,
问题解答
一、改进第一个Sine函数的拟合效果
第一个函数效果差的核心原因是初始猜测值严重偏离数据特征:你用sine.guess()生成的默认参数,再手动添加c=1的常数项,完全没贴合你的实验数据。
改进方案是复用自定义函数里的FFT初始猜测逻辑,给SineModel+ConstantModel设置贴合数据的初始参数:
def Sine(self, x_axis, y_axis): x = np.array(x_axis) y = np.array(y_axis) # 用FFT计算贴合数据的初始猜测值 ff = np.fft.fftfreq(len(x), (x[1] - x[0])) Fyy = abs(np.fft.fft(y)) guess_freq = abs(ff[np.argmax(Fyy[1:]) + 1]) * 2. * np.pi guess_amp = np.std(y) * 2.**0.5 guess_offset = np.mean(y) # 组合模型 sine_offset = SineModel() + ConstantModel() # 生成参数并设置初始值(注意SineModel的参数名是amplitude、frequency、phase) pars = sine_offset.make_params( amplitude=guess_amp, frequency=guess_freq, phase=0, c=guess_offset ) x_fit = np.linspace(min(x_axis), max(x_axis), x_axis.size) result = sine_offset.fit(y_axis, pars, x=x_fit) return result
关键注意点:
SineModel的内置参数名是amplitude(振幅)、frequency(频率)、phase(相位),和自定义函数的参数名不要混淆- 放弃使用
sine.guess()的默认初始值,用FFT得到的贴合数据的猜测值,这是拟合收敛到最优解的核心
二、Sine_User2无法计算参数不确定性的原因
提示“Uncertainties could not be estimated”通常是因为拟合的协方差矩阵无法求解,常见原因有两个:
- 参数强相关性:自定义函数中
freq(频率)和shift(相位)存在极强的冗余相关性——微调相位可以抵消频率变化带来的拟合差异,导致协方差矩阵奇异,无法计算不确定度。 - 未收敛到全局最优:虽然拟合曲线看起来贴合数据,但可能只是陷入了局部极小值,而非全局最优解,此时协方差矩阵的计算也会失败。
解决办法:
- 限制相位范围:给
shift参数设置min=-np.pi, max=np.pi(正弦函数周期为2π,该范围已覆盖所有相位情况,可大幅降低和频率的相关性) - 检查收敛状态:打印
result.fit_report()查看message字段,确认拟合是否真的收敛。若为局部极小值,可尝试微调shift的初始值(比如设为np.pi/2)或频率初始值后重新拟合。 - 改用
SineModel组合方式:SineModel内部已经优化了参数相关性问题,只要初始值合理,通常能正常计算参数不确定度。
内容的提问来源于stack exchange,提问作者Euan Tough
相关产品推荐
相关产品推荐

