我的问题是关于由不同参数函数组成的复杂模型的参数拟合。
更准确地说,我想描述一个复杂的实验。
实验产生一维测量数据data
的一维数组,其中每个项对应于一组实验控制变量x
。
我现在是一个理论模型(实际上是多个模型,请参见下文)model(x,pars)
,它使用x
和许多参数pars
来预测data
。但是,并非所有参数都是已知的,我需要调整它们。
此外,该模型的某些细节尚不确定。因此,我实际上拥有一个由多个模型组成的家族,这些模型在某些部分非常相似,但是模型的某些内部组成部分是不同的(但是模型的很大一部分是相同的)。
不幸的是,将一个组件切换为另一个组件可能会引入新的(未知)参数,也就是说,我们现在拥有modelA(x,parsA)
和modelB(x,parsB)
具有不同的参数。
基本上,模型由函数f(x, pars, vals_of_subfuncs)
组成,其中x
是自变量,pars
是f
的一些显式参数,而vals_of_subfuncs
是评估一些较低级函数的结果,这些函数本身取决于其自身的参数(可能还取决于其自身较低级函数的结果等)
显然,不可能进行递归,并且最低级别的功能不依赖于其他功能的值。
此图最好地说明了这种情况:
自变量为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>
有人知道解决这种情况的自然方法吗?
答案 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}}
因此,您看到这实际上是代表您绘制内容的映射。 (参数在每个集合中的顺序没有特定的顺序,但它们会以正确的顺序进行求值,例如G
在F
之前。)然后适合您的数据,只需执行
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)
,但是您还想控制d
和b
是否确实是变量,以及是否将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)
我认为您指定的方式是,您要确保d
在g_with_d=False
时不是适合的变量,并且在某些情况下,您希望{{1 }}保持一致。您可以使用
b
根据需要。我可以想象您的实际问题会比这涉及的更多,但是我希望这可以帮助您正确地开始工作。
答案 2 :(得分:0)
感谢您的回答。
我认为lmfit
也许可以做我想做的事,但是我必须自己实现“模块化”。
我所举的例子只是概念性的模型。通常,功能的“网络”及其依赖关系比我在示例中所做的要复杂得多。
我目前的计划如下:
我将为“网络”编写一个类Network
,其中包含某些Node
。
注释指定了它们可能对子Node
,显式参数和自变量的“符号”依赖性。
Network
类将具有例程来检查这种构造的网络是否一致。而且,它将具有(lmfit
)Parameters
对象(即节点明确依赖的所有参数的统一),并提供某种方法来生成lmfit
{{1} }。
然后我将使用Model
进行拟合。
至少这是计划。 如果我成功构建了此文件,我将用我的代码发布更新此帖子。