lmfit 不适合 returns 单个标量值的简单示例中的唯一参数

lmfit does not fit the only parameter on a simple example that returns a single scalar value

残差函数 (res) 计算 y>thr (threshold) 的 y 值之和。它 returns 此总和与目标之间的余数。

在这个例子中,target 是为 y>70 计算的,我想在最小化后从 y=60 开始找到 y=70。

import numpy as np
import lmfit

x=np.linspace(0,10,51)
y=(x+4)*(x-14)*-1

def res(params,y,target):
    parvals = params.valuesdict()
    sum=y[y>parvals['thr']].sum()
    return [(target-sum)**2]

target=y[y>70].sum()

pars=lmfit.Parameters()
pars.add('thr', value=60, min=y.min(), max=y.max())

minner = lmfit.Minimizer(res, pars, fcn_args=(y, target))
result = minner.minimize()

为什么拟合不起作用:不返回 70,而是返回 60(初始值)?

感谢您的回答。

拟合不起作用,因为您将连续变量 (pars['thr']) 用作离散值 [y>parvals['thr']]。当拟合为 运行 时,它将尝试通过对变量值进行微小的更改来计算结果的变化。 如果您在函数中添加 print()

def res(params,y,target):
    parvals = params.valuesdict()
    print(parvals['thr'])
    sum=y[y>parvals['thr']].sum()
    return [(target-sum)**2]

你会得到

60.0
60.0
60.0
60.00000089406967
60.0

因为拟合试图找到 thr 应该改变的方式和程度。它会看到更改 thr 对结果没有影响。

基本上,您需要将 thr 的使用从离散变量更改为连续变量。一种简单的方法是使用 erf 函数(或另一个 sigmoidal 函数),在一些小但不是无穷小的间隔内缩放到从 ~0 到 ~1,例如

from scipy.special import erf

def res(params,y,target):
    parvals = params.valuesdict()   
    step = (1 + erf(y-parvals['thr']))/2.0
    sum = (y*step).sum()
    return target-sum   # was [(target-sum)**2]

那个 step 数组将有 non-zero/non-one 接近阈值的值,为拟合提供足够的信号来决定移动到哪里。

此外,请注意,返回 [(target-sum)**2] 会起作用,但仅返回残差 target-sum 将使拟合不仅可以看到幅度,还可以看到失配的符号,并允许适合更快地收敛。

通过这些更改,您应该得到 thr 的正确值 70 以及大约 11 次函数评估。