首页 > 解决方案 > 高斯数据拟合根据 x 数据的位置而变化

问题描述

如果我将与该数据相对应的 x 值的间隔移动(到),我很难理解为什么我的高斯拟合到一组数据(ydata)不能很好地工作。高斯写为:xdata1xdata2

其中A只是一个幅度因子。更改数据的某些值,很容易使其适用于两种情况,但也可以很容易地找到它不适用于xdata1以及未估计参数协方差的情况。我scipy.optimize.curve_fit在 Windows 7 上使用带有 Python 3.7.1 的 Spyder。

代码的结果

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

xdata1 = np.linspace(-9,4,20, endpoint=True) # works fine
xdata2 = xdata1+2
ydata = np.array([8,9,15,12,14,20,24,40,54,94,160,290,400,420,300,130,40,10,8,4])

def gaussian(x, amp, mean, sigma):
    return amp*np.exp(-(((x-mean)**2)/(2*sigma**2)))/(sigma*np.sqrt(2*np.pi))

popt1, pcov1 = curve_fit(gaussian, xdata1, ydata)
popt2, pcov2 = curve_fit(gaussian, xdata2, ydata)

fig, ([ax1, ax2]) = plt.subplots(nrows=1, ncols=2,figsize=(9, 4))

ax1.plot(xdata1, ydata, 'b+:', label='xdata1')
ax1.plot(xdata1, gaussian(xdata1, *popt1), 'r-', label='fit')
ax1.legend()
ax2.plot(xdata2, ydata, 'b+:', label='xdata2')
ax2.plot(xdata2, gaussian(xdata2, *popt2), 'r-', label='fit')
ax2.legend()

标签: pythoncurve-fittinggaussiandata-fitting

解决方案


问题是您第二次拟合高斯的尝试在搜索参数空间时陷入局部最小值:curve_fit 是最小平方的包装器,使用梯度下降来最小化成本函数,这很容易陷入局部最小值

您应该尝试提供合理的起始参数(通过使用curve_fitp0的参数)来避免这种情况:

 #...  your code

 y_max = np.max(y_data)
 max_pos = ydata[ydata==y_max][0]
 initial_guess = [y_max, max_pos, 1] # amplitude, mean, std

 popt2, pcov2 = curve_fit(gaussian, xdata2, ydata, p0=initial_guess)

如您所见,这提供了合理的拟合:

gaussian_fits_with_parameters

您应该编写一个可以提供对起始参数的合理估计的函数。在这里,我刚刚找到了最大y值,并用它来确定初始参数。我发现这适用于拟合正态分布,但您可以考虑其他方法。

编辑:

你也可以通过缩放幅度来解决这个问题:幅度太大以至于参数空间被扭曲,梯度下降只是简单地遵循幅度变化最大的方向,有效地忽略了 sigma。考虑以下参数空间中的图(颜色是给定参数的拟合残差平方和,白色十字表示最佳解决方案):

全参数空间

确保记下 x 和 y 轴的不同比例。

需要在 y(幅度)中进行大量“单位”大小的步骤,以从点 x,y = (0,0) 达到最小值,因为您只需要不到一个“单位”大小的步骤即可达到 x (sigma) 的最小值。该算法只是在幅度上采取步骤,因为这是最陡的梯度。当它达到使成本函数最小化的幅度时,它只是停止算法,因为它似乎已经收敛,并且对 sigma 参数几乎没有变化或没有变化。

解决此问题的一种方法是缩放您的 ydata 以不扭曲参数空间:将您ydata除以 100,您将看到您的适合在不提供任何起始参数的情况下工作!


推荐阅读