拟合曲线:为什么小数字更好?

时间:2013-09-06 17:40:29

标签: python numpy

这些天我花了一些时间来解决问题。我有一组数据:

y = f(t),其中y是非常小的浓度(10 ^ -7),t是秒。 t从0到大约12000不等。

测量遵循既定模型:

y = Vs * t - ((Vs - Vi) * (1 - np.exp(-k * t)) / k)

我需要找到Vs,Vi和k。所以我使用了curve_fit,它返回了最佳拟合参数,并绘制了曲线。

然后我使用了类似的模型:

y = (Vs * t/3600 - ((Vs - Vi) * (1 - np.exp(-k * t/3600)) / k)) * 10**7

通过这样做,t是小时数,y是0到大约10之间的数字。返回的参数当然是不同的。但是当我绘制每条曲线时,这就是我得到的:

http://i.imgur.com/XLa4LtL.png

绿色贴合是第一个模型,蓝色贴合是“标准化”模型。红点是实验值。

拟合曲线不同。我认为这不是预期的,我不明白为什么。如果数字是“可合理的”,计算是否更准确?

3 个答案:

答案 0 :(得分:4)

docstring for optimize.curve_fit说,

p0 : None, scalar, or M-length sequence
    Initial guess for the parameters.  If None, then the initial
    values will all be 1 (if the number of parameters for the function
    can be determined using introspection, otherwise a ValueError
    is raised).

因此,首先,参数的初始猜测默认为1.

此外,曲线拟合算法必须对各种参数值的函数进行采样。最初选择“各种值”时,初始步长大约为1.如果参数值的变化大约为1,则数据会有一定程度的平滑变化,算法会更好。

如果函数变化很大,参数变化大约为1,那么算法可能会错过最佳参数值。

请注意,即使算法在调整参数值时使用自适应步长,如果初始调整距离标记太远以至于产生大残差,并且如果在某个其他方向上进行调整则会产生较小的残差残差,然后算法可能在错误的方向上漂移并错过局部最小值。它可能会找到一些其他(不需要的)局部最小值,或者根本无法收敛。因此,使用具有自适应步长的算法不一定会省去你。

故事的寓意是扩展数据可以提高算法找到所需最小值的机会。


一般来说,数值算法在应用于幅度大约为1的数据时往往更有效。这种偏差以多种方式进入算法。例如,optimize.curve_fit依赖optimize.leastsq,而the call signature for optimize.leastsq依赖:

def leastsq(func, x0, args=(), Dfun=None, full_output=0,
            col_deriv=0, ftol=1.49012e-8, xtol=1.49012e-8,
            gtol=0.0, maxfev=0, epsfcn=None, factor=100, diag=None):

因此,默认情况下,公差ftolxtol大约为1e-8。如果找到最佳参数值需要更小的容差,那么这些硬编码的默认数字将导致optimize.curve_fit错过优化参数值。

为了使这更具体,假设您正在尝试最小化f(x) = 1e-100*x**2。因子1e-100会使y - 值变得非常大,以至于x - 范围的广泛值(上面提到的参数值)将在1e-8的容差范围内。因此,如果使用不理想的缩放,leastsq将无法很好地找到最小值。


使用大小为1的浮点数的另一个原因是因为区间[-1,1]中有更多(IEEE754)浮点数远远超过1。例如,

import struct
def floats_between(x, y):
    """
    http://stackoverflow.com/a/3587987/190597 (jsbueno)
    """
    a = struct.pack("<dd", x, y)
    b = struct.unpack("<qq", a)
    return b[1] - b[0]

In [26]: floats_between(0,1) / float(floats_between(1e6,1e7))
Out[26]: 311.4397707054894

这表明,在区间[1e6,1e7]中,表示0到1之间的数字的浮点数超过300倍。 因此,在其他条件相同的情况下,如果使用小数而不是非常大的数字,通常会得到更准确的答案。

答案 1 :(得分:2)

我认为它更多地与您传递给曲线拟合的初始参数估计有关。如果您没有通过任何我认为它们都默认为1.规范化您的数据会使这些初始估计更接近事实。如果您不想使用标准化数据,请自行传递初始估算并给出合理的值。

答案 2 :(得分:2)

其他人已经提到你可能需要一个良好的开始猜测你的健康。在这种情况下,我通常会尝试找到一些快速而肮脏的技巧,以至少获得参数的大概估计。在您的情况下,对于较大的t,指数会很快衰减到零,因此对于大t,您有

y == Vs * t - (Vs - Vi)  / k

按照

进行一阶线性拟合
[slope1, offset1] = polyfit(t[t > 2000], y[t > 2000], 1)

您将获得slope1 == Vsoffset1 == (Vi - Vs) / k

从你拥有的所有点中减去这条直线,得到指数

residual == y - slope1 * t - offset1 == (Vs - Vi) * exp(-t * k)

记录双方的日志,你得到

log(residual) == log(Vs - Vi) - t * k

所以再做一次

[slope2, offset2] = polyfit(t, log(y - slope1 * t - offset1), 1)

将为您提供slope2 == -koffset2 == log(Vs - Vi),因为您已经知道Vi,因此Vs应该可以解决这个问题。您可能必须将第二个拟合限制为t的小值,否则您可能会记录负数的对数。收集您使用这些拟合获得的所有参数,并将它们用作curve_fit的起点。

最后,你可能想要做一些加权拟合。关于曲线指数部分的信息仅包含在前几个点中,因此您可能应该给予更高的权重。以统计上正确的方式执行此操作并非易事。