有没有一种数值稳定的方法来计算下面的softmax函数? 我在神经网络代码中得到了一些变成NAN的值。
np.exp(x)/np.sum(np.exp(y))
np.exp(x)/np.sum(np.exp(y))
softmax exp(x)/sum(exp(x)) 实际上在数值上表现很好。它只有正项,因此我们不必担心精度丢失,分母至少与分子一样大,因此结果保证落在0和1之间。
唯一可能发生的意外是指数爆炸或者下溢。单个元素的上溢或所有元素的下溢将使输出几乎无用。
但是可以通过使用恒等式 softmax(x) = softmax(x + c) 来防止这种情况,其中c是任意标量:从x中减去max(x)得到一个只有非正元素的向量,消除了上溢,至少有一个元素为零,消除了分母无穷小的情况(某些元素的下溢是无害的,但不是所有元素)。
注:理论上,总和中的灾难性事故是可能的,但您需要一个荒谬的数量级。例如,即使使用只能解决3个小数位的16位浮点数——与“正常”的64位浮点数的15个小数位相比——我们需要2 ^ 1431(〜6 x 10 ^ 431)和2 ^ 1432之间才能得到一个总和,该总和 误差为两倍。
Softmax函数容易出现两个问题:溢出和下溢
溢出:当非常大的数字被近似为无穷大
时会发生
下溢:当非常小的数字(接近数轴上的零)被近似(即四舍五入)为零
时会发生
在进行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()
成功避免了溢出问题。
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 numpy
test2 = np.array([[12345, 67890, 99999999], # 2D numpy
[123, 678, 88888888]]) #
test3 = [12345, 67890, 999999999] # 1D list
test4 = [[12345, 67890, 999999999]] # 2D list
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.]]
np.seterr(all='raise')
将会抱怨大值的下溢,即使函数是正确的。这确实是最好的解决方案。 - Nihar Karve在你的情况下计算softmax函数没有问题。问题似乎来自于梯度爆炸或者是训练方法中出现的这种问题。要么通过"剪裁值",要么"选择正确的权重初始分布"来解决这些问题。
softmax(800)
。 - Warren Weckesser