object function可以return None带一定的参数,Curvefit收敛时如何跳过或避免?

Object function can return None with certain parameters, how to skip or avoid while converging in Curvefit?

在我的情况下,objective 函数是一个数值过程,包含一个通过二分法求方程根的过程。 对于某些参数集,方程没有中间变量的根。我认为制作二分求根例程 return None 可以解决这样的问题。 由于具有一组日期的对象函数被 scipy.optimize.curve_fit 回归,p0 被这种情况分开,然后错误停止该过程。

为了研究这个案例,显示了一个简化的案例。

import numpy as np

#Define object function:
def f(x,a1,a2):
    if a1 < 0:
        return None
    elif a2 < 0:
        return np.inf
    else:
        return a1 * x**2 + a2

#Making data:
x = np.linspace(-5,5,10)
i = 0
y = np.empty_like(x)
for xi in x:
    y[i] = f(xi,1,1)
    i += 1

import scipy.optimize as sp

para,pvoc = sp.curve_fit(f,x,y,p0=(-1,1))
#TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'

para,pvoc = sp.curve_fit(f,x,y,p0=(1,-1))
#RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 600.

我也试了inf,显然不行。 我应该如何 return 来继续 curve_fit 过程? 想象一下它正在尝试收敛,curve_fit遇到这种情况会发生什么。

补充思考: 我尝试了 try...except... 来忽略错误并模拟了 p0 在可解范围内的情况,但会将不可解的段传递给真正的拟合。

import numpy as np

def f(x,a1,a2):
    if a1 < 0:
        return None
    elif a1 < 2:
        return a1 * x**2 + a2
    elif a2 < 0:
        return np.inf
    else:
        return a1 * x**2 + a2

def ff(x,a1,a2):
    output = f(x,a1,a2)
    if output == None:
        return 0
    else:
        return output

x = np.linspace(-5,5,10)
i = 0
y = np.empty_like(x)
for xi in x:
    y[i] = f(xi,1,1)
    i += 1


import scipy.optimize as sp

#para,pvoc = sp.curve_fit(f,x,y,p0=(-1,1))
#TypeError: unsupported operand type(s) for -: 'NoneType' and 'float':
#para,pvoc = sp.curve_fit(f,x,y,p0=(1,-1))

try:
    para,pvoc = sp.curve_fit(f,x,y,p0=(-3,1))
except TypeError:
    pass

显然收敛时遇到错误,已经报错,排除。 怎么才能让curve_fit按照原来的收敛方向继续呢? 连我都能让步,我怎么能告诉curve_fit到return最后一次尝试到a1

另一方面,当出现错误时,我尝试将对象函数中的 try... except... 设置为 return 0。 结果如我所料:

para,pvoc = sp.curve_fit(ff,x,y,p0=(-3,1))

#OptimizeWarning: Covariance of the parameters could not be estimated
  category=OptimizeWarning)

我认为您想采取不同的方法。也就是说,您已将 objective 函数写入 return NoneInf 以在 a1a2 的值超出范围时发出信号边界:a1<0a2<0 不是 objective 函数可接受的值。

如果这是对您要执行的操作的正确解释,最好在 a1a2 上设置界限,这样 objective 函数就永远不会得到那些价值观。要使用 curve_fit 做到这一点,您需要为下限和上限创建一个数组元组,其顺序与您的 p0 相匹配,因此

pout, pcov = sp.curve_fit(f, x, y, p0=(1, 1), bounds=([0, 0], [np.inpf, np.inf])

顺便说一句:我不知道您为什么要为 a1 使用小于 0 的起始值,因此超出范围。看来你是在自找麻烦。

为了获得更好的拟合参数边界设置体验,您可以考虑使用 lmfit,这样您就可以编写:

import numpy as np
from lmfit import Model

def f(x, a1, a2):
    return a1 * x**2 + a2

fmod = Model(f)

params = fmod.make_params(a1=1, a2=0.5)
params['a1'].min = 0
params['a2'].min = 0

x = np.linspace(-5, 5, 10)

np.random.seed(0)
y = f(x, 1, 1) + np.random.normal(size=len(x), scale=0.02)

result = fmod.fit(y, params, x=x)
print(result.fit_report())

这将打印出来

[[Model]]
    Model(f)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 13
    # data points      = 10
    # variables        = 2
    chi-square         = 0.00374066
    reduced chi-square = 4.6758e-04
    Akaike info crit   = -74.9107853
    Bayesian info crit = -74.3056151
[[Variables]]
    a1:  0.99998038 +/- 7.6225e-04 (0.08%) (init = 1)
    a2:  1.01496025 +/- 0.01034565 (1.02%) (init = 0.5)
[[Correlations]] (unreported correlations are < 0.100)
    C(a1, a2) = -0.750

希望对您有所帮助。