【问题标题】:Numercially stable softmax数值稳定的softmax
【发布时间】:2017-07-24 18:44:08
【问题描述】:

下面有计算softmax函数的数值稳定方法吗? 我得到的值在神经网络代码中变成了 Nans。

np.exp(x)/np.sum(np.exp(y))

【问题讨论】:

  • 这里的答案显示了计算 softmax 的更好方法:stackoverflow.com/questions/34968722/softmax-function-python
  • @ajcr 此链接上接受的答案实际上是糟糕的建议。 Abhishek,即使他们最初似乎不明白为什么是正确的做法,OP 所做的事情也是如此。除了溢出之外,softmax 中没有数值上的困难步骤。因此,在数学上等价的同时将所有输入向左移动,消除了溢出的可能性,因此在数值上是一种改进。
  • 是的,尽管该接受答案的作者在 cmets 中承认,减去最大值不会引入“必要项”,但实际上会提高数值稳定性(也许应该编辑答案......)。无论如何,数值稳定性的问题在其他几个答案中得到了解决。 @AbhishekBhatia:您认为该链接是否令人满意地回答了您的问题,或者这里的新答案是否有益?

标签: python numpy nan scientific-computing softmax


【解决方案1】:

softmax exp(x)/sum(exp(x)) 实际上在数值上表现良好。它只有正项,所以我们不用担心失去显着性,而且分母至少和分子一样大,所以结果保证在 0 和 1 之间。

唯一可能发生的意外是指数中的溢出或不足。 x 的单个元素上溢或所有元素下溢都会使输出或多或少无用。

但是很容易通过使用对任何标量 c 成立的恒等式 softmax(x) = softmax(x + c) 来防范:减去 max(来自 xx) 留下一个只有非正数条目的向量,排除溢出和至少一个为零的元素排除消失的分母(在某些情况下为下溢但并非所有条目都是无害的)。

脚注:理论上,灾难性事故总和是可能的,但您需要荒谬个术语。例如,即使使用只能解析 3 个小数的 16 位浮点数——与“正常”64 位浮点数的 15 个小数相比——我们需要介于 2^1431 (~6 x 10^431) 和 2 ^1432 得到 off by a factor of two 的总和。

【讨论】:

    【解决方案2】:

    Softmax 函数容易出现两个问题:overflowunderflow

    溢出:当非常大的数字近似infinity

    时发生

    下溢:当非常小的数字(在数轴上接近零)近似(即四舍五入)为zero

    时会发生这种情况

    为了在进行 softmax 计算时解决这些问题,一个常见的技巧是通过 从所有元素中减去其中的最大元素来移动输入向量。对于输入向量x,定义z,这样:

    z = x-max(x)
    

    然后取新的(稳定的)向量z的softmax


    例子:

    def stable_softmax(x):
        z = x - max(x)
        numerator = np.exp(z)
        denominator = np.sum(numerator)
        softmax = numerator/denominator
    
        return softmax
    
    # input vector
    In [267]: vec = np.array([1, 2, 3, 4, 5])
    In [268]: stable_softmax(vec)
    Out[268]: array([ 0.01165623,  0.03168492,  0.08612854,  0.23412166,  0.63640865])
    
    # input vector with really large number, prone to overflow issue
    In [269]: vec = np.array([12345, 67890, 99999999])
    In [270]: stable_softmax(vec)
    Out[270]: array([ 0.,  0.,  1.])
    

    在上述情况下,我们通过使用stable_softmax()

    安全地避免了溢出问题

    有关详细信息,请参阅deep learning一书中的Numerical Computation一章。

    【讨论】:

    • 我不确定减去最大值是否是处理下溢的最佳方法。但正如保罗的回答所暗示的,下溢不是问题。
    【解决方案3】:

    扩展 @kmario23 的答案以支持 1 或 2 维 numpy 数组或列表(如果您通过 softmax 函数传递一批结果,这很常见):

    import numpy as np
    
    
    def stable_softmax(x):
        z = x - np.max(x, axis=-1, keepdims=True)
        numerator = np.exp(z)
        denominator = np.sum(numerator, axis=-1, keepdims=True)
        softmax = numerator / denominator
        return softmax
    
    
    test1 = np.array([12345, 67890, 99999999])  # 1D
    test2 = np.array([[12345, 67890, 99999999], [123, 678, 88888888]])  # 2D
    test3 = [12345, 67890, 999999999]
    test4 = [[12345, 67890, 999999999]]
    
    print(stable_softmax(test1))
    print(stable_softmax(test2))
    print(stable_softmax(test3))
    print(stable_softmax(test4))
    
     [0. 0. 1.]
    
    [[0. 0. 1.]
     [0. 0. 1.]]
    
     [0. 0. 1.]
    
    [[0. 0. 1.]]
    

    【讨论】:

    • 这对我来说仍然是下溢
    • 我已经使用它很长一段时间了,没有任何问题。你确定你没有输入 NaN 或 Infs 吗?
    • 我现在明白了 - np.seterr(all='raise') 会抱怨大值的下溢即使函数正常工作。这确实是最好的解决方案。
    【解决方案4】:

    感谢Paul Panzer's 的解释,但我想知道为什么我们需要减去max(x)。因此,我找到了更详细的信息,希望对与我有相同问题的人有所帮助。 请参阅以下链接文章中的“最大减法是怎么回事?”部分。

    https://nolanbconaway.github.io/blog/2017/softmax-numpy

    【讨论】:

      【解决方案5】:

      在您的情况下计算 softmax 函数没有任何问题。问题似乎来自爆炸梯度或您的训练方法的此类问题。通过“裁剪值”或“选择正确的初始权重分布”来关注那些问题。

      【讨论】:

      • “按照你的情况计算 softmax 函数没有任何问题。” 尝试用它计算 softmax(800)
      • 在那个规模下做任何事情都会导致“inf”如果你试图在那个规模下工作,python 中的任何事情都是不稳定的。
      猜你喜欢
      • 2020-08-09
      • 1970-01-01
      • 2019-11-05
      • 2020-08-04
      • 1970-01-01
      • 1970-01-01
      • 2010-10-17
      • 2016-12-23
      • 2011-10-14
      相关资源
      最近更新 更多