繁体   English   中英

scipy curve_fit 无法拟合高帽函数

[英]scipy curve_fit cannot fit a tophat function

我正在尝试将顶帽函数拟合到某些数据,即。 f(x) 对于整条实线是常数,除了一个有限长度的线段等于另一个常数。 我的参数是 tophat 函数的两个常量、中点和宽度,我正在尝试使用 scipy.optimize.curve_fit 来获得所有这些。 不幸的是,curve_fit 无法获得帽子的宽度。 无论我做什么,它都拒绝测试除我开始使用的宽度值之外的任何宽度值,并且非常不适合其余数据。 下面的代码片段说明了这个问题:

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

def tophat(x, base_level, hat_level, hat_mid, hat_width):
    ret=[]
    for xx in x:
        if hat_mid-hat_width/2. < xx < hat_mid+hat_width/2.:
            ret.append(hat_level)
        else:
            ret.append(base_level)
    return np.array(ret)

x = np.arange(-10., 10., 0.01)
y = tophat(x, 1.0, 5.0, 0.0, 1.0)+np.random.rand(len(x))*0.2-0.1

guesses = [ [1.0, 5.0, 0.0, 1.0],
            [1.0, 5.0, 0.0, 0.1],
            [1.0, 5.0, 0.0, 2.0] ]

plt.plot(x,y)

for guess in guesses:
    popt, pcov = curve_fit( tophat, x, y, p0=guess )
    print popt
    plt.plot( x, tophat(x, popt[0], popt[1], popt[2], popt[3]) )

plt.show()

为什么curve_fit 在正确处理这个问题上非常糟糕,我该如何解决?

首先, tophat的定义可以使用numpy.where而不是循环:

def tophat(x, base_level, hat_level, hat_mid, hat_width):
    return np.where((hat_mid-hat_width/2. < x) & (x < hat_mid+hat_width/2.), hat_level, base_level)

其次,棘手的不连续目标函数抵抗了curve_fit调用的优化算法。 Nelder-Mead 方法通常更适合用于粗糙函数,但看起来curve_fit不能使用它。 所以我设置了一个目标函数(只是偏差的绝对值的总和)并将其最小化:

def objective(params, x, y):
    return np.sum(np.abs(tophat(x, *params) - y))

plt.plot(x,y)

for guess in guesses:
    res = minimize(objective, guess, args=(x, y), method='Nelder-Mead')
    print(res.x)
    plt.plot(x, tophat(x, *(res.x)))

结果更好,因为从宽度为 2 的太宽帽子开始使其缩小到正确的大小(请参阅三个猜测中的最后一个)。

[9.96041297e-01 5.00035502e+00 2.39462103e-04 9.99759984e-01]
[ 1.00115808e+00  4.94088711e+00 -2.21340843e-05  1.04924153e-01]
[9.95947108e-01 4.99871040e+00 1.26575116e-03 9.97908018e-01]

不幸的是,当起始猜测太窄时,优化器仍然卡住了。

拟合函数

您可以尝试其他优化方法/目标函数组合,但我还没有找到使帽子可靠扩展的方法。

要尝试的一件事是不要使用接近真实水平的参数; 这有时可能会造成伤害。

guesses = [ [1.0, 1.0, 0.0, 1.0],
            [1.0, 1.0, 0.0, 0.1],
            [1.0, 1.0, 0.0, 2.0] ]

我曾经设法得到

[ 1.00131181  4.99156649 -0.01109271  0.96822019]
[ 1.00137925  4.97879423 -0.05091561  1.096166  ]
[ 1.00130568  4.98679988 -0.01133717  0.99339777]

这对于所有三个宽度都是正确的。 然而,这只是几次尝试中的一些(优化过程的初始化存在一些随机性)。 其他一些具有相同初始点的尝试失败了; 该过程不够稳健。

就其性质而言,与curve_fit()一样的非线性最小二乘curve_fit()合适用于实数、浮点数,并且不擅长处理离散变量。 在拟合过程中,对每个变量进行小的更改(例如,在 1e-7 级别),并且使用该小的更改对拟合结果的影响来决定如何更改该变量以改进拟合。 对于离散采样的数据,您的hat_mid和/或hat_width微小变化很容易小于数据点的间距,因此对拟合完全没有影响。 这就是为什么curve_fit在这个问题上“非常糟糕”。

您可能会发现,为步骤提供有限宽度(即,与离散数据的步长相当)有助于更好地找到您帽子的边缘所在的位置。

您也可以尝试拟合 f = A0 (-Erf[A4*(u - A1) - A3] + Erf[A4*(u + A1) - A3]) + A2

这里A0将与步高成正比,A1将与步宽成正比,A2将是基线的垂直偏移量,A3应该是步中间距0的水平距离,步的斜率将成正比到 A4。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM