在Scipy中使用curve_fit拟合向量函数

4
我希望使用Scipy的curve_fit或其他更合适的工具来拟合一个向量输出的函数。例如,考虑以下函数:
import numpy as np
def fmodel(x, a, b):
    return np.vstack([a*np.sin(b*x), a*x**2 - b*x, a*np.exp(b/x)])

每个组件都是不同的功能,但它们共享我希望适合的参数。理想情况下,我会这样做:
x = np.linspace(1, 20, 50)
a = 0.1
b = 0.5
y = fmodel(x, a, b)
y_noisy = y + 0.2 * np.random.normal(size=y.shape)

from scipy.optimize import curve_fit
popt, pcov = curve_fit(f=fmodel, xdata=x, ydata=y_noisy, p0=[0.3, 0.1])

但是,curve_fit不能处理向量输出的函数,并会抛出错误信息Result from function call is not a proper array of floats.。相反,我将其输出展开,如下:

def fmodel_flat(x, a, b):
    return fmodel(x[0:len(x)/3], a, b).flatten()

popt, pcov = curve_fit(f=fmodel_flat, xdata=np.tile(x, 3),
                       ydata=y_noisy.flatten(), p0=[0.3, 0.1])

这种方法是可行的。如果我不仅要拟合一个向量函数,而是要拟合多个具有不同输入但共享模型参数的函数,我可以连接输入和输出。

是否有更适合使用Scipy或其他附加模块拟合向量函数的方法?对我来说,主要考虑因素是效率 - 实际要拟合的函数要复杂得多,拟合需要一些时间,因此,如果这种使用curve_fit的方法会导致过长的运行时间,我想知道应该怎么做。


1
你可能会对lmfit感兴趣。他们还建议使用“flatten”方法处理多维数据。 - chthonicdaemon
scipy.optimize.least_squares 强制要求指定残差函数。你可以把压缩(或者其他更精细的操作)放在那里。 - Nathan Lloyd
2个回答

2
如果我可以直言推荐我的软件包symfit,我认为它正是你所需要的。关于使用共享参数进行拟合的示例可以在文档中找到。
您上述提出的具体问题将变成:
from symfit import variables, parameters, Model, Fit, sin, exp

x, y_1, y_2, y_3 = variables('x, y_1, y_2, y_3')
a, b = parameters('a, b')
a.value = 0.3
b.value = 0.1

model = Model({
    y_1: a * sin(b * x), 
    y_2: a * x**2 - b * x, 
    y_3: a * exp(b / x),
})

xdata = np.linspace(1, 20, 50)
ydata = model(x=xdata, a=0.1, b=0.5)
y_noisy = ydata + 0.2 * np.random.normal(size=(len(model), len(xdata)))

fit = Fit(model, x=xdata, y_1=y_noisy[0], y_2=y_noisy[1], y_3=y_noisy[2])
fit_result = fit.execute()

查看文档以获取更多信息!


1
我认为从效率角度来看,你所做的是完全正确的。我会尝试查看实现并提出更具量化的建议,但目前这是我的推理。
在曲线拟合期间,您正在优化参数 (a,b),使其满足以下条件。
res = sum_i |f(x_i; a,b)-y_i|^2

这里的“minimal”是指您拥有任意维度的数据点(x_i,y_i),两个参数(a,b)和一个拟合模型,该模型在查询点x_i处逼近数据。

曲线拟合算法从起始(a,b)对开始,将其放入计算上述平方误差的黑匣子中,并尝试提出新的(a',b')对以产生更小的误差。我的观点是,上述误差实际上对于拟合算法来说是一个黑匣子:拟合的配置空间仅由(a,b)参数定义。如果您想象一下如何实现简单的曲线拟合函数,您可以想象尝试使用平方误差作为代价函数进行梯度下降。

现在,对于黑盒如何计算误差,它与拟合过程无关。很容易看出,对于标量函数来说,x_i 的维度实际上是无关紧要的,因为你可以有 1000 个一维查询点需要进行拟合,也可以有一个 10x10x10 的三维空间网格。重要的是你有 1000 个点 x_i 需要从模型中计算出 f(x_i) ~ y_i
唯一需要注意的微妙之处是,在向量值函数的情况下,误差的计算并不是简单的。在我看来,使用向量值函数的 2 范数来定义每个 x_i 点的误差是可以接受的。但是,嘿:在这种情况下,点 x_i 的平方误差为:
|f(x_i; a,b)-y_i|^2 == sum_k (f(x_i; a,b)[k]-y_i[k])^2

这意味着每个分量的平方误差都被累加。这只是意味着你现在所做的是正确的:通过复制你的 x_i 点并单独考虑每个函数分量,你的平方误差将恰好包含每个点上误差的 2-范数。
所以我的观点是,你所做的在数学上是正确的,我不希望拟合过程的任何行为取决于如何处理多元/向量值函数的方式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接