拟合复合函数组合参数的模块化(pythonic)方法

时间:2018-12-18 15:27:43

标签: python curve-fitting model-fitting lmfit

我的问题是关于由不同参数函数组成的复杂模型的参数拟合。

更准确地说,我想描述一个复杂的实验。 实验产生一维测量数据data的一维数组,其中每个项对应于一组实验控制变量x

我现在是一个理论模型(实际上是多个模型,请参见下文)model(x,pars),它使用x和许多参数pars来预测data。但是,并非所有参数都是已知的,我需要调整它们。

此外,该模型的某些细节尚不确定。因此,我实际上拥有一个由多个模型组成的家族,这些模型在某些部分非常相似,但是模型的某些内部组成部分是不同的(但是模型的很大一部分是相同的)。

不幸的是,将一个组件切换为另一个组件可能会引入新的(未知)参数,也就是说,我们现在拥有modelA(x,parsA)modelB(x,parsB)  具有不同的参数。

基本上,模型由函数f(x, pars, vals_of_subfuncs)组成,其中x是自变量,parsf的一些显式参数,而vals_of_subfuncs是评估一些较低级函数的结果,这些函数本身取决于其自身的参数(可能还取决于其自身较低级函数的结果等) 显然,不可能进行递归,并且最低级别的功能不依赖于其他功能的值。

此图最好地说明了这种情况:

Modular model architecture

自变量为x(蓝色),参数为a,b,c,d(红色),子功能的值以绿色箭头的形式出现在代表功能的节点中。

在(1)中,我们有一个没有子功能的最低级函数G(x; (a,b); {})和一个较高级的函数F(x; c; G(x; (a,b)),其评估给出了模型结果,取决于{{1} }和x

在(2)和(3)中,我们分别更改了模型的组件(pars=(a,b,c)和(F->F')。这改变了最终模型的参数依赖性。

现在,我正在寻找一种最pythonic /模块化的方法来解决在这种情况下实现参数拟合的问题,而不必每次交换/更改模型组件时都必须重新编写fit函数,从而可能引入新参数。

目前,我正在尝试使用G->G'找到解决此问题的方法。我还考虑过也许尝试使用lmfit处理符号“参数”,但是我认为出现的所有函数都不能轻易地编写为可由sympy求值的表达式。 / p>

有人知道解决这种情况的自然方法吗?

3 个答案:

答案 0 :(得分:0)

自从您提出sympy以来,我认为您应该看看symfit,它正是您在上一段中所要求的。使用symfit,您可以编写符号表达式,然后将其与scipy配合使用。它将使您轻松组合willy-nilly的不同子模型。

让我使用symfit实现第二个示例:

from symfit import variables, parameters, Fit, Model

a, b, c = parameters('a, b, c')
x, G, F = variables('x, G, F')

model_dict = {
    G: a * x + b,
    F: b * G + c * x
}
model = Model(model_dict)
print(model.connectivity_mapping)

我选择了这些相当琐碎的功能,但是显然您可以选择任何想要的功能。要查看此模型是否与您的插图匹配,connectivity_mapping将打印以下内容:

{F: {b, G, x, c}, G: {b, a, x}}

因此,您看到这实际上是代表您绘制内容的映射。 (参数在每个集合中的顺序没有特定的顺序,但它们会以正确的顺序进行求值,例如GF之前。)然后适合您的数据,只需执行

fit = Fit(model, x=xdata, F=Fdata)
fit_results = fit.execute()

就是这样!我希望这可以弄清楚为什么我认为symfit适合您的用例。抱歉,我之前无法澄清,我仍在将该功能最终确定为API,因此到目前为止,它仅存在于development分支中。但是我刚刚发布了具有此功能和许多其他功能的版本:)。

免责声明:我是symfit的作者。

答案 1 :(得分:0)

我认为,使用一个更具体的示例(即使用实际代码)肯定会改善此问题。如果我理解正确,那么您有一个通用模型

def model_func(x, a, b, c, d):
     gresult = G(x, a, b, d)
     return F(x, b, c, gresult)

,但是您还想控制db是否确实是变量,以及是否将c传递给F。正确吗?

如果这是正确的(或至少体现了精神),那么我认为您可以使用lmfit(免责声明:我是第一作者)通过向模型函数添加关键字参数的组合来做到这一点。并设置一些固定的参数值。

例如,您可以像这样进行一些重新排列:

def G(x, a, b=None, d=None):
    if b is not None and d is None:
       return calc_g_without_d(x, a, b)
    return calc_g_with_d(x, a, d) 

def F(x, gresult, b, c=None):
    if c is None:
       return calc_f_without_c(x, gresult, b)
    return calc_f_with_c(x, gresult, b, c) 

def model_func(x, a, b, c, d, g_with_d=True, f_with_c=True):
     if g_with_d:
        gresult = G(x, a, d)
     else:
        gresult = G(x, a, b)
     if f_with_c:
         return F(x, gresult, b, c=c)
     else:
         return F(x, gresult, b)

现在,当您制作模型时,可以覆盖默认值f_with_c和/或g_with_d

import lmfit
mymodel = lmfit.Model(model_func, f_with_c=False)
params = mymodel.make_params(a=100, b=0.2201, c=2.110, d=0)

然后使用mymodel.eval()评估模型或使用mymodel.fit()进行拟合,并为关键字参数f_with_c和/或g_with_d传递明确的值,例如

test = mymodel.eval(params, x=np.linspace(-1, 1, 41), 
                    f_with_c=False, g_with_d=False)

result = mymodel.fit(ydata, params, x=xdata, g_with_d=False)

我认为您指定的方式是,您要确保dg_with_d=False时不是适合的变量,并且在某些情况下,您希望{{1 }}保持一致。您可以使用

b

根据需要。我可以想象您的实际问题会比这涉及的更多,但是我希望这可以帮助您正确地开始工作。

答案 2 :(得分:0)

感谢您的回答。

我认为lmfit也许可以做我想做的事,但是我必须自己实现“模块化”。 我所举的例子只是概念性的模型。通常,功能的“网络”及其依赖关系比我在示例中所做的要复杂得多。

我目前的计划如下: 我将为“网络”编写一个类Network,其中包含某些Node。 注释指定了它们可能对子Node,显式参数和自变量的“符号”依赖性。

Network类将具有例程来检查这种构造的网络是否一致。而且,它将具有(lmfitParameters对象(即节点明确依赖的所有参数的统一),并提供某种方法来生成lmfit {{1} }。

然后我将使用Model进行拟合。

至少这是计划。 如果我成功构建了此文件,我将用我的代码发布更新此帖子。