【问题标题】:Least square optimalization with huge numbers具有大量数字的最小二乘优化
【发布时间】:2021-11-02 16:10:06
【问题描述】:

我有以下功能,我需要使用最小二乘法来最小化(我正在使用 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 无效)。所以它可能会溢出。

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

【问题讨论】:

  • 为什么不切换profitlist 中的单位以使其与utilitylist 中的数字更具可比性?否则,我会怀疑任何答案的数值稳定性。
  • @JohnColeman 我该怎么做?如果我将利润列表中的所有数字除以 1 000 000 比我认为的结果不一样?
  • 如果您不信任他们,为什么要获得相同的结果?无论如何,以百万美元来衡量利润是很常见的。您还可以探索诸如将 utilitylist 中的数字乘以 100 的动作,这样单位就是百分比。
  • 尝试使用更大的值作为参数b的初始猜测值。

标签: python numpy lmfit


【解决方案1】:

如您所见,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))

【讨论】:

  • 谢谢。第一件事 - 为什么 profiList 的最大值需要除以 700?你是怎么得到这个号码的?其次 - 它处理巨大的正数,但不处理负数......如果 profitList 是这样的:[-10000, 10, 100, 1000, 10000] 问题仍然存在:/
  • 并且即使没有使用您提供的代码设置最小 b (我尝试 1e18),巨大的正数也可以工作。更大的负数是问题......所以如果在利润列表中不是更大的正数,你可以获得b的最小值,它不起作用......:/所以正如我在之前的评论中提到的数组不起作用例如...
  • @Nyrnius 至于为什么将利润列表的最大值除以700,想法是保持指数的arg低于+700(负端没那么重要,将指数发送到0) .但是,如果您使用的是exp(-x/b) 而不是exp(x/b),那么也许您应该使用max(abs(profitList))/700
  • 再次感谢。它有所帮助 - 但如果我有类似以下的数组,它仍然会失败:[-10000, 10, 100, 10000] - 似乎负数比正数更快地炸毁 np.exp。所以将 min 设置为 b=max(abs(profitlist))/700 - 在这种情况下 b=10000/700 并且它仍然失败......如果我将 b 设置为 max(abs(profitList))*10/700 那么它可以工作,但是 b 比必要的高得多,而且似乎我越来越频繁地获得高 SSE 但这可能只是巧合
  • np.exp() 的参数需要 np.exp(-x/b),而x 是-100,000,那么b 必须大于142。要清楚,exp(700) 是1e304。此评论中没有足够的空间来写出这么多的零!
猜你喜欢
  • 1970-01-01
  • 2015-09-24
  • 2021-08-28
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-05
相关资源
最近更新 更多