4.8 梯度消失与梯度爆炸
前面给大家留了一个思考题,在我们介绍的几种激活函数中,哪种激活函数的效果是最好的。其实这个问题的答案很简单,在介绍它们的时候,一般排在越后面的说明效果就越好,所以ReLU激活函数是最好的。开个玩笑,下面我们来具体分析一下这几个激活函数的不同效果。
4.8.1 梯度消失
根据上文BP算法中的推导,我们从式(4.49)、式(4.50)和式(4.51)中可以知道,权值的调整ΔW是跟学习信号δ相关的。同时我们从式(4.46)、式(4.47)和式(4.48)中可以知道,在学习信号δ表达式中存在f′(x)。也就是说激活函数的导数会影响学习信号δ的值,而学习信号δ的值会影响权值调整ΔW的值。那么激活函数的值越大,ΔW的值就越大;激活函数的值越小,ΔW的值也就越小。
假设激活函数为sigmoid函数,前文中我们已经知道了sigmoid函数的表达式,sigmoid函数的导数f′(x)=f(x)[1-f(x)],sigmoid函数的导数如图4.19所示。
图4.19 sigmoid函数的导数
从图4.19中我们可以发现,当x=0时,sigmoid函数的导数可以取得最大值0.25。x的取值较大或较小时,sigmoid函数的导数很快就趋向于0。不管怎么样,sigmoid函数的导数都是一个小于1的数。学习信号δ乘以一个小于1的数,那么δ就会减小。学习信号从输出层一层一层向前反向传播的时候,每传播一层,学习信号就会变小一点,经过多层传播后,学习信号就会接近于0,从而使得权值ΔW调整接近于0。ΔW接近于0,那就意味着该层的参数不会发生改变,不能进行优化。参数不能优化,那整个网络就不能再进行学习了。学习信号随着网络传播逐渐减小的问题也被称为梯度消失(Vanishing Gradient)的问题。
我们再考虑一下tanh函数的导数,tanh函数的表达式,tanh函数的导数f′(x)=1-(f(x))2,tanh函数的导数如图4.20所示。
图4.20 tanh函数的导数
tanh函数的导数图像看起来比sigmoid函数的要好一些。当x=0时,tanh函数的导数可以取得最大值1。x的取值较大或较小时,tanh函数的导数很快就趋向于0。不管怎么样,tanh函数的导数的取值总是小于等于1的,所以tanh作为激活函数也会存在梯度消失的问题。
对于softsign函数,softsign函数的表达式,softsign函数的导数为:,softsign函数的导数如图4.21所示。
图4.21 softsign函数的导数
当x=0时,softsign函数的导数可以取得最大值1。x的取值较大或较小时,softsign函数的导数很快就趋向于0。不管怎么样,softsign函数的导数的取值总是小于等于1的,所以softsign作为激活函数也会存在梯度消失的问题。
4.8.2 梯度爆炸
当我们使用sigmoid函数、tanh函数和softsign函数作为激活函数时,它们的导数的取值范围都是小于等于1的,所以会产生梯度消失的问题。那么我们可能会想到,如果使用导数大于1的函数作为激活函数,情况会如何?
如果学习信号δ乘以一个大于1的数,那么δ就会变大。学习信号从输出层一层一层向前反向传播的时候,每传播一层,学习信号就会变大一点。经过多层传播后,学习信号就会接近于无穷大,从而使得权值ΔW调整接近于无穷大。ΔW接近于无穷大,那就意味着该层的参数处于一种极不稳定的状态,那么网络就不能正常工作了。学习信号随着网络传播逐渐增大的问题也被称为梯度爆炸(Exploding Gradient)的问题。
既然激活函数的导数不能小于1也不能大于1,那我们可能会想到,能不能使用线性函数y=x,这个函数的导数等于1,它既不会梯度消失,也不会梯度爆炸。确实如此,线性函数的导数为1的特性是很好,但是它是一个线性函数,也就是说它不能处理非线性问题,比如异或分类问题,其就无法解决。而在实际应用中,非常多的应用都是属于非线性问题的,所以使用线性函数来作为激活函数存在很大的局限性,所以也不适合。
4.8.3 使用ReLU函数解决梯度消失和梯度爆炸的问题
我们知道ReLU函数的表达式为f(x)=max(0,x)。当x<0时,f(x)的取值为0;当x>0时,f(x)的取值等于x。ReLU函数的导数如图4.22所示。
图4.22 ReLU函数的导数
前面我们讨论了当激活函数的导数小于1时,网络会产生梯度消失;当激活函数的导数大于1时,网络会产生梯度爆炸。那么当我们使用ReLU函数作为激活函数的时候,x<0时,ReLU函数的导数为0;x>0时,ReLU函数的导数为1。导数为1是一个很好的特性,不会使得学习信号越来越小,也不会让学习信号越来越大,可以让学习信号比较稳定地从后向前传播。解决了梯度消失和梯度爆炸的问题,同时计算方便,可以加速网络的训练。
ReLU函数还有一个优点,它是一个非线性的激活函数,可以用来处理非线性问题,它的非线性特性在4.5节中已经介绍过。
认真思考的同学这个时候可能会发现,ReLU函数看起来是挺好的,既是非线性函数,导数又为1,但是它好像也存在一些问题,当x<0时,ReLU函数输出为0,导数也为0,有些信号不就丢失掉了吗?
如果你是这么想的,那你就想对了,确实是丢失了一些信号,但是没关系。在神经网络中,信号是冗余的,也就是说其实网络最后在做预测的时候并不需要从前面传过来的所有的信号,实际上只需要一部分的信号网络就可以进行预测。并且使用部分信号来进行预测与使用全部信号来进行预测得到的结果相差不大。
比如我们把网络中输出为0的神经元看作不工作的神经元,那么使用ReLU函数以后,会产生大量不工作的神经元。网络中存在不工作的神经元,我们可以称这个网络具有一定的稀疏性(Sparsity)。不工作的神经元越多,网络就越稀疏。使得网络产生稀疏性的方式很多,除使用ReLU激活函数外,还可以使用L1正则化(L1 Regularization)和Dropout,这两个技术在后面的章节中会有详细介绍。所以,使得神经网络变稀疏并不是什么稀奇的事,也不一定是坏事。
稀疏性这一特性也存在于生物体内的神经网络中,大脑中神经网络的稀疏性高达95%~99%,也就是说在同一时刻,其实大脑中大部分的神经元都是不工作的。人工神经网络中比较常见的网络稀疏性是50%~80%。