【问题标题】:Python curve fitting with constraints带约束的 Python 曲线拟合
【发布时间】:2024-01-10 15:36:01
【问题描述】:

我一直在寻找带有约束的 Python 曲线拟合。一个选项是使用lmfit 模块,另一个选项是使用penalization 来强制执行约束。我有以下代码,我试图在其中强制执行 a+b=3.6 作为约束。换句话说,y=3.6x=1x 在我的情况下总是>=1

import numpy as np
import scipy.optimize as sio
def func(x, a, b, c):
    return a+b*x**c

x = [1, 2, 4, 8, 16]
y = [3.6, 3.96, 4.31, 5.217, 6.842]
lb = np.ones(3, dtype=float)
ub = np.ones(3, dtype=float)*10.
popt, pcov = sio.curve_fit(func, x, y)
print(popt)

理想情况下,我想使用lmfit 方法并花费大量时间尝试理解示例但未能成功。有人可以举个例子吗?

【问题讨论】:

    标签: python numpy scipy curve-fitting


    【解决方案1】:

    如果我正确理解你的问题,你想用一些数据建模

    def func(x, a, b, c):
        return a+b*x**c
    

    对于一组特定的数据,您要施加a+b=3.6 的约束。您可以,只是“硬连线”,将功能更改为

    def func2(x, b, c):
        a = 3.6 - b
        return a+b*x**c
    

    现在您有了一个只有两个变量的模型函数:bc

    这不是很灵活,但可以工作。

    使用 lmfit 可以恢复一些灵活性。要进行完全无约束的拟合,您会说

    from lmfit import Model
    
    mymodel = Model(func)
    params = mymodel.make_params(a=2, b=1.6, c=0.5)
    result = mymodel.fit(y, params,  x=x)
    

    (顺便说一句:scipy.optimize.curve_fit 允许您不指定参数的初始值,并在不告诉您的情况下将它们隐式设置为 1。这是一个可怕的错误功能 - 始终提供初始值)。

    如果您确实想施加约束a+b=3.6,那么您可以这样做

    params['a'].expr = '3.6-b'
    
    result2 = mymodel.fit(y, params, x=x)
    print(result2.fit_report())
    

    当我使用您提供的数据执行此操作时,会打印(请注意,它报告 2 个变量,而不是 3 个):

    [[Model]]
        Model(func)
    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 34
        # data points      = 5
        # variables        = 2
        chi-square         = 0.01066525
        reduced chi-square = 0.00355508
        Akaike info crit   = -26.7510142
        Bayesian info crit = -27.5321384
    [[Variables]]
        a:  3.28044833 +/- 0.04900625 (1.49%) == '3.6-b'
        b:  0.31955167 +/- 0.04900626 (15.34%) (init = 1.6)
        c:  0.86901253 +/- 0.05281279 (6.08%) (init = 0.5)
    [[Correlations]] (unreported correlations are < 0.100)
        C(b, c) = -0.994
    

    您的代码暗示使用(但实际上并未使用)参数值的上限和下限。 lmfit 也可以实现这些,就像

    params['b'].min = 1
    params['b'].min = 10
    

    等等。我不确定您是否需要它们,并且会提醒您不要尝试将界限设置得太紧。

    【讨论】:

    • 非常感谢,尤其是这一点:“顺便说一句:scipy.optimize.curve_fit 允许您不指定参数的初始值,并在不告诉您的情况下将它们隐式设置为 1。”