You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Split-Pseudo-Voigt拟合求助:自定义函数curve_fit不收敛

Split-Pseudo-Voigt 峰拟合不收敛问题的解决思路

一、有没有现成的内置实现?

  • SciPy本身没有封装好的Split-Pseudo-Voigt函数,但lmfit库的models模块里直接有SplitPseudoVoigtModel,这个库专门做曲线拟合,自带参数初始化、边界约束,比自己写函数用curve_fit省心太多,强烈推荐试试。
  • 你嫌弃PyMca文档烂,但它源码里其实有现成的实现——去PyMca5/PyMca/FitPlugins/SplitPseudoVoigtFitPlugin.py里扒代码就行,直接复用它的拟合逻辑,省得自己踩坑。

二、自定义函数不收敛的常见坑

如果非要自己写函数用curve_fit,大概率是这几个问题:

  • 初始值给得太离谱:curve_fit对初始值极度敏感,Split-Pseudo-Voigt有峰位、半高宽、高斯占比(η)、峰高、背景好几个参数,初始值和真实值差太远的话,直接就发散了。先手动用scipy.signal.find_peaks找峰,把峰位、峰高、半高宽的初始值设得贴近实际,背景用基线估计的结果。
  • 没加参数约束:有些参数有物理意义,比如η必须在0到1之间,半高宽得是正数。curve_fit默认不管这些,参数可能跑到负数或者大于1的范围,直接导致拟合崩了。用bounds参数给每个参数加范围,比如bounds=([-np.inf, 0, 0, 0], [np.inf, np.inf, 1, np.inf])(对应峰位、半高宽、η、峰高的约束)。
  • 函数写错了:Split-Pseudo-Voigt是左右半峰用不同的高斯/洛伦兹权重,很多人写成普通Pseudo-Voigt(整个峰用同一个η),这肯定不对。正确的实现大概是这样:
    def split_pseudo_voigt(x, x0, dx, eta, height, bg):
        # x0: 峰位, dx: 半高宽, eta: 左半峰高斯占比(0-1)
        left_mask = x <= x0
        right_mask = x > x0
    
        # 左半峰:高斯权重eta,洛伦兹权重1-eta
        gauss_left = np.exp(-np.log(2) * ((x[left_mask] - x0)/dx)**2)
        lorentz_left = 1 / (1 + ((x[left_mask] - x0)/dx)**2)
        peak_left = height * (eta * gauss_left + (1 - eta) * lorentz_left)
    
        # 右半峰:高斯权重1-eta,洛伦兹权重eta
        gauss_right = np.exp(-np.log(2) * ((x[right_mask] - x0)/dx)**2)
        lorentz_right = 1 / (1 + ((x[right_mask] - x0)/dx)**2)
        peak_right = height * ((1 - eta) * gauss_right + eta * lorentz_right)
    
        return np.concatenate([peak_left, peak_right]) + bg
    
  • 背景没处理好:如果谱线有倾斜或弯曲的基线,只加常数背景会拖垮拟合效果。要么把背景改成线性(加个斜率参数),要么先做基线扣除再拟合峰型。

三、快速调试方法

  1. 先单独画自定义函数的曲线,用你觉得合理的参数值,确认形状是对的,排除函数写错的问题。
  2. 用lmfit替代curve_fit,它会输出参数迭代的详细过程,能直观看到哪个参数在乱跳,方便针对性调整。
  3. 给参数加约束,别让它们跑到不合理的范围里。

内容的提问来源于stack exchange,提问作者Philipp J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:07:38