softmax的上下溢问题

问题描述

在解决多分类问题时,常常使用softmax作为激活函数:\(softmax(x_i) = \frac {e^{x_i}}{\sum _{j=1}^{n}{e^x_j}}\)

一次计算过程基本如下:

def softmax(inputs):
    length = len(inputs)
    exps = []
    sum = 0
    for item in inputs:
        exp_val = math.exp(item) # item就是x_i
        sum = sum + exp_val # sum就是 $\sum (e^x_j)$
        exps.append(exp_val)
    return exps/sum # 返回一个list,list中每个元素对应于每个x_i计算后的值

计算过程当然是没什么问题,但是对于计算机而言,它能够计算、存储的数值大小、精度总要有个上下限:

  • 当inputs中的数比较大时:可能出现上溢
    inputs = [1000, 2000, 3000],那么e^x_i 就会超过上限,从而报错:OverflowError,更别提后续的求和、求商。这就是上溢。
  • 当inputs中的数比较小时:可能出现下溢
    inputs = [-1000, -2000. -3000],e^x_i 同样超过了计算机的精度范围,也就直接记为0,从而使得分母的值为0,那么整个计算结果在python中就会显示ZeroDivisionError,在numpy中标记为“nan"。这就是下溢。

对于上下溢问题(通常是下溢),一个典型表现就是对NN做训练时,准确率没有变化:因为经过softmax的输出并不是一个数,后续的损失、优化无法进行,参数得不到更新,那自然每次都是一样的结果。

解决:softmax的冗余性

根据上述softmax公式进行推导:
\(softmax(x_i+x)=\frac {e^{x_i+x}}{\sum _{j=1}^{n}{e^{x_j+x}}} =\frac {e^x \cdot e^{x_i}} {\sum _{j=1}^{n} {(e^x \cdot e^x_j)}} =\frac {e^x \cdot e^{x_i}} {e^x \cdot \sum _{j=1}^{n} {e^x_j}} =\frac {e^{x_i}} {\sum _{j=1}^{n}{e^x_j}} =softmax(x_i)\)

我们可以看到对于任意一个数a, x+a和x在softmax中的结果都是一样的,即\(softmax(x) = softmax(x+a)\),这个结论被称为softmax的冗余性。
我们令 \(x=x+a\),其中\(a=-max(x)\),则:

  • \((x+a)\)的最大值等于0,避免了上溢的问题;
  • 同时,因为一定有\(x+a=0\), 所以分母中的各个加数至少有一个为1,也就不可能为0,由此避免下溢的问题。

参考

https://blog.csdn.net/zx1245773445/article/details/86443099

posted @ 2022-07-27 11:47  YIYUYI  阅读(731)  评论(0编辑  收藏  举报