【问题标题】:How to set variable arguments in scipy.optimize.curve_fit in Python?如何在 Python 的 scipy.optimize.curve_fit 中设置变量参数?
【发布时间】:2018-06-29 04:39:03
【问题描述】:

我正在使用 scioy.optimize.curve_fit 在 Python 中解决优化问题。 curve_fit 函数将输入作为 curve_fit(f, xdata, ydata, ...),其中 f 可调用,是模型函数 f(x, ...)。它必须将自变量作为第一个参数,并将适合的参数作为单独的剩余参数。

但是,就我而言,其余参数是可变的。例如,f 可以是 f = fun(indepent_var, a, b, c, d) 或 f = fun(indepent_var, a, c, d)。

f 的实际参数由用户在解决优化问题时定义。例如,一个用户可能想要使用 a、b、c、d 作为参数,而另一个用户想要使用 a、c、d,而第三个用户可能使用 b、c。然后,我的问题是如何在f 中设置变量参数,然后用户可以配置自己的参数?

def func(data, a, b, c, d):
    return a * np.exp(-b * data) + c        

#def func(data, a, c, d):
#    return a * np.exp(-c * x)

popt, pcov = curve_fit(func, xdata, ydata)

"config_1.ini"
params_to_be_optimized = a, b, c, d

"config_2.ini"
params_to_be_optimized = a, c, d

我正在尝试做这样的事情:

def func(data, **kwards):
    a = kwargs['a'] if a in kwargs else 0
    ...
    return ...

popt, pcov = curve_fit(lambda(...), xdata, ydata)

但我不知道如何解决这个问题。我想lambda 函数可能有效,但谁能给我一个例子?

【问题讨论】:

标签: python scipy variadic-functions curve-fitting


【解决方案1】:

使用curve_fit 文档中的示例:

In [260]: def func(x, a, b, c):
     ...:     return a * np.exp(-b * x) + c
     ...: 
In [261]: xdata = np.linspace(0, 4, 50)
     ...: y = func(xdata, 2.5, 1.3, 0.5)
     ...: np.random.seed(1729)
     ...: y_noise = 0.2 * np.random.normal(size=xdata.size)
     ...: ydata = y + y_noise

如果没有p0 参数,拟合参数的数量由内省决定,即检查func 代码和属性

p0 : None, scalar, or N-length sequence, optional

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).

在这种情况下,它标识了 3 个参数:

In [263]: optimize.curve_fit(func, xdata, ydata)
Out[263]: 
(array([ 2.55423706,  1.35190947,  0.47450618]),
 array([[ 0.0158905 ,  0.00681778, -0.0007614 ],
        [ 0.00681778,  0.02019919,  0.00541905],
        [-0.0007614 ,  0.00541905,  0.00282595]]))

我可以使用*args定义一个等效函数:

In [264]: def f1(x, *args):
     ...:     return func(x, *args)
     ...: 

但如果我尝试适应它,我会得到一个错误:

In [266]: optimize.curve_fit(f1, xdata, ydata)
ValueError: Unable to determine number of fit parameters.

但我可以指定p0

In [268]: optimize.curve_fit(f1, xdata, ydata, p0=np.ones(3))
Out[268]: 
(array([ 2.55423706,  1.35190947,  0.47450618]),
 ....)

如果我提供其他 p0 尺寸,我会收到错误,因为 f1 将其参数传递给 func。我可以定义f1,这样它就更能容忍其他参数数字,但我可能会得到OptimizeWarning

【讨论】:

  • 您好,您的问题(或答案)似乎与我有关,但实际上并没有回答我的问题。
【解决方案2】:

这可能不是一个完整的答案,但对于评论来说太长了。 当然,提供一个更具体的示例来说明您正在尝试做的事情肯定有助于澄清您的问题并带来更好的答案。

我并不完全清楚你的函数的签名是什么,但可能的基本问题是如何让用户选择哪些函数参数成为适合的变量,同时让其他参数不 成为变量,要么保持在某个固定值,要么被用作实际使用何种功能形式的开关(这似乎是您澄清评论想要表达的意思)。

无论哪种情况,您都会发现 lmfit (https://lmfit.github.io/lmfit-py/) 很有用。它有一个模型类,支持基于 scipy.optimize 的曲线拟合,但与curve_fit 分开(并且比curve_fit 更高级别)。 lmfit.Model 可以将任何“模型函数”转换为可用于拟合数据的模型,并使用检查将函数参数转换为拟合中使用的参数。任何参数都可以是可变的或固定的(或具有边界或由简单的约束表达式设置),这可能是您正在寻找的:用户可以决定某些函数参数不适合变量。

此外,如果模型函数的关键字参数没有数值默认值,它们不会成为拟合变量,但可以用作可选开关,或许可以控制所使用的函数形式。也就是说,如果你有一个像

这样的模型函数
def func(x, a=1, b=2, c=3, d=0, option=True):
     if option:
         return a * np.exp(-b*x) + c + d*x
     else:
         return a * np.exp(-b*x**2) + c + d*x

然后你可以创建一个拟合模型和参数

from lmfit impor Model
mymodel = Model(func)
params = mymodel.make_params(a=100, b=-1, c=0, d=0)

然后(例如)将 d 修复为 0:

params['d'].value = 0
params['d'].vary = False

或应用其他起始值、界限等。

要更改 option 参数的值,您可以将其视为另一个自变量,将其作为关键字参数传递给 mymodel.fit()mymodel.eval(),如下所示:

xdata = <array of data>
ydata = <array of data>

result = mymodel.fit(ydata, params, x=xdata, option=False)
print(result.fit_report())

这将允许用户选择模型函数中使用的选项。

希望能指引你正确的方向......

问题更新后:

一般来说,支持**kws 会非常困难:用户可以将任何内容传递给这个dict,并且它在函数中的使用方式是未知的。例如,拟合变量必须是浮点数。但是,您可能也意识到了这一点

def func(data, **kwargs):
    a = kwargs['a'] if a in kwargs else 0
    ...

可以改成

def func(data, a=0, **kwargs):
    ...

并且这个支持的。这意味着您必须为所有可能成为变量的量显式地创建函数参数,但这不应该太难。完成后,用户可以决定其中哪些实际上是在特定的配合中变化的。

你绝对可以使用类似的东西

def func(x, a=1, b=2, c=3, d=0):
     ...

# read from "config_1.ini"
params_to_be_optimized = 'a', 'b', 'c', 'd'

# read from "config_2.ini"
params_to_be_optimized = 'a', 'c', 'd'

类似的东西

mymodel = Model(func)
params = mymodel.make_params()   

for parname, param in params.items():
    param.vary = parname in params_to_be_optimized

控制哪些参数将被优化,哪些将被修复。

【讨论】:

  • 感谢您的回答。但是您的方法似乎无法真正解决我的问题,因为拟合函数的参数是可变的。我再次编辑了我的问题。你能帮我解决一下吗?
  • 我更新了答案以回应更新后的问题。
  • 非常感谢。但是,由于某些要求,我想坚持使用 scipy.optimize.curve_fit。您有什么想法可以直接在curve_fit中解决吗?谢谢。
  • @Junyong 这种用例是我们编写 lmfit 的很大一部分原因,并且让 Parameter 对象具有像 vary 这样的名称和属性,而不是普通的浮点变量。我可能是错的,但我不认为curve_fit 可以处理关键字参数或多个独立参数。它现在可以处理边界,但边界列表的排序与变量列表和函数的位置参数相匹配。该界面很丑陋,并且对 Parameter 对象大喊大叫。因此,lmfit。
  • @M Newville 是的,我在我的代码中尝试了使用curve_fit 的不同方法,但它们都不起作用。我一定会尝试 lmfit。非常感谢您的帮助。
猜你喜欢
  • 2015-10-20
  • 2012-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
相关资源
最近更新 更多