softmax自身导致的数值问题
对于
x=[x1,x2,⋯,xn],softmax公式:
softmax(x)=[a1,a2,⋯,an],ai=∑j=1nexjexi
原始softmax公式的数值问题:
- 若
xi 过大,
exi 上溢产生nan
- 若
xi 过小,
exi 下溢,结果为0,若所有的
xi都过小,则导致
ai 的分母为0
解决办法:
先减去所有元素中的最大值,记为
maxx,再进行标准的softmax。这在数学上是等价的,通过对原始softmax公式分子分母同时除以
emaxx 即可证明。即:
ai=∑j=1nexjexi=∑j=1nexj/emaxxexi/emaxx=∑j=1nexj−maxxexi−maxx
经过减去最大值的步骤后,最大的指数为0,因此不会再遇到第一个问题;而由于该指数0的存在,分母至少为1,因此也不会有第二个问题。
代码实现:
import numpy as np
def softmax(x,axis=-1):
ex=np.exp(x-np.max(x,axis=axis,keepdims=True))
return ex/np.sum(ex,axis=axis,keepdims=True)
softmax与交叉熵损失一起使用时导致的数值问题
但是注意,这种处理并不能避免
xi 过小,
exi 下溢,结果为0这个现象。而且,假如
maxx 是正数,还会加剧这个现象。
其实这个现象本身没有问题,不会导致 softmax 的结果在数学上产生大的误差,但是我们经常会对 softmax 函数的结果使用交叉熵损失,其中含有对数函数
log,而
log(0)=−∞,因此会导致数值问题。
所以,一般的深度学习框架都会建议使用将softmax和交叉熵一起计算的损失函数,例如Tensorflow中的 tf.nn.softmax_cross_entropy_with_logits,其底层实现可以参考 这个帖子
这样做的理由是:
CrossEntropy=−i∑yilog(ai)=−i∑yilog(∑j=1nexj−maxxexi−maxx)=−i∑yi[(xi−maxx)−logj=1∑nexj−maxx]
可以看到 softamx 中分子的指数运算和交叉熵的对数运算抵消了,所以即使
xi−maxx 是一个很小的负数,也会在Loss中直接得到这个数,而不会由于经过指数运算后下溢为 0,进而得到
log(0)=−∞