首页 > 解决方案 > 具有大量数字的最小二乘优化

问题描述

我有以下功能,我需要尽量减少使用最小二乘法(我正在使用 lmfit)。

y = a * exp(-x/b) + c

例如,我有以下数据:

profitlist = [-10000, 100.00, 1000.00, 100000.00, 1000000.00]
utilitylist = [0, 0.2, 0.4, 0.6, 1]

应用程序返回以下错误:

ValueError: NaN values detected in your input data or the output of your objective/model function - fitting algorithms cannot handle this! Please read https://lmfit.github.io/lmfit-py/faq.html#i-get-errors-from-nan-in-my-fit-what-can-i-do for more information.

问题似乎是:如果 profitList 包含任何更大的负数(-1000 有效,-100000 无效),则exp(-x/b)返回inf-inf 。所以它可能会溢出。

ProfitList 中的值可以是非常大的浮点数,并且它们并不总是相同的。那么如何用这些巨大的数字来优化它呢?似乎 lmfit 不支持可以解决问题的十进制数字......我该怎么做才能让它工作?

class LeastSquares:
def __init__(self, profitList, utilityList):
    self.profitList = np.asarray(profitList)
    self.utilityList = np.asanyarray(utilityList)

def function(self, params, x):
    a = params["a"]
    b = params["b"]
    c = params["c"]

    return a * np.exp(-x/b) + c

def residual(self, params, x, y):
    return (y - self.function(params, x))**2

def setParameters(self, a_start, b_start, c_start):
    parameters = Parameters()
    parameters.add(name="a", value=a_start, min=None, max=0, vary=True)
    parameters.add(name="b", value=b_start, vary=True, min=0.1, max=None)
    parameters.add(name="c", value=c_start, vary=True)
    return parameters 

def startOptimalization(self):
    parameters = self.setParameters(-1, 1, 1)    
    result = minimize(self.residual, parameters, args=(self.profitList, self.utilityList), method="leastsq")
    result.params.pretty_print()

    print(fit_report(result))
    print("SSE")
    print(np.sum(result.residual))

标签: pythonnumpylmfit

解决方案


如您所见,numpy.exp(arg)对于大于 ~709 的任何参数都给出 Infinity,您需要避免这种极端值。底层求解器根本无法解决它们。既然你的论点arg-x/b,你需要确保它b不会小到把论点炸毁numpy.exp()

b事实上,您的代码显示您确实设置了0.1的下限。
但是随着值profitlist扩展到 1e7,该下限太小而无法阻止 Infinity - 您的下限b必须在 14,000 左右。

如果每次优化运行时您的值profitlist都在变化,您可能需要执行以下操作(在您的 中startOptimization):

   parameters = self.setParameters(-1, 1, 1)    
   parameters['b'].min = max(abs(self.profitList))/700.0
   result = minimize(self.residual, parameters, args=(self.profitList, self.utilityList), method="leastsq")
   result.params.pretty_print()

此外,在拟合指数变化时,计算指数模型函数通常很有帮助,然后将残差作为数据的对数和模型的对数,有效地在对数空间中进行拟合,就像您可能会绘制的那样数据。

最后,不要自己取差的平方或平方和,只需返回带有符号的残差数组。也就是说,你可能会更好地使用类似的东西:

def residual(self, params, x, y):
    return np.log(y) - np.log(self.function(params, x))

推荐阅读