【问题标题】:Modular (pythonic) way for fitting combined parameters of a composite function用于拟合复合函数的组合参数的模块化(pythonic)方法
【发布时间】:2019-05-19 01:28:33
【问题描述】:

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

更准确地说,我想描述一个复杂的实验。 实验产生一个一维数组的测量数据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)),其评估给出了模型结果,这取决于 xpars=(a,b,c)

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

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

目前,我正在尝试使用lmfit 找到解决此问题的方法。我还考虑过可能尝试使用sympy 来处理符号“参数”,但我认为并不是所有出现的函数都可以轻松地写成asteval 可以评估的表达式。

有谁知道处理这种情况的自然方法?

【问题讨论】:

    标签: python curve-fitting model-fitting lmfit


    【解决方案1】:

    我认为这个问题肯定会通过更具体的示例(即使用实际代码)得到改进。如果我理解正确,你有一个通用模型

    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 时不是合适的变量,并且在某些情况下您希望 b 不变化适合。你可以这样做

    params['b'].vary = False
    params['d'].vary = False
    

    根据需要。我可以想象你的实际问题比这稍微复杂一些,但我希望这有助于你朝着正确的方向开始。

    【讨论】:

      【解决方案2】:

      感谢您的回答。

      我认为lmfit 可能能够做我想做的事,但我必须自己实现“模块化”。 我的例子只是概念性的和最小的模型。一般来说,函数的“网络”及其依赖关系比我在示例中所做的要复杂得多。

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

      Network 类将有例程来检查这种构造的网络是否一致。此外,它将有一个 (lmfit) Parameters 对象(即节点显式依赖的所有参数的统一)并提供一些方法来生成 lmfit Model

      那我就用lmfit来装修吧。

      至少这是计划。 如果我成功构建了这个,我将使用我的代码发布更新这篇文章。

      【讨论】:

        【解决方案3】:

        自从您提出sympy,我想你应该看看987654321 @,这正是最后一段所要求的。使用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 Prints:

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

        所以你看到这真的是一个代表你画的东西的映射。 (参数在每个集合中没有特定的顺序,但它们将按正确的顺序进行评估,例如,在F 987654331 @。)然后拟合您的数据,只需执行

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

        就是这样!我希望这使得它更清楚为什么我认为symfit确实适合您的用例。对不起,我尚未澄清一下,我仍然将此功能定位到API中,截止现在只存在于开发分支中。但我刚才发布了这个和许多其他功能:)。

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

        【讨论】:

        • 感谢您的回复。我只熟悉sympy,但尚未尝试symfit。从我理解的文档中,它并不完全适合我的usecase,这是一个相当普遍的。我会看到如何程序。如果我最终使用@ 987654337找到解决方案,我将在此发布。 span>
        • @ bermt,由于新版本symfit我更新了我的答案,因为我认为你可能对一些新功能感兴趣。 span>
        猜你喜欢
        • 2018-08-24
        • 1970-01-01
        • 2021-11-21
        • 1970-01-01
        • 2010-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多