这几天正在看梯度消失/爆炸,在深度学习的理论中梯度消失/爆炸也是极其重要的,所以就抽出一段时间认真地研究了一下梯度消失/爆炸的原理,以下为参考网上的几篇文章总结得出的。
本文分为四个部分:第一部分主要介绍深度学习中一些关于梯度消失/爆炸(Vanishing/Exploding Gradient)的基础。第二部分主要介绍深度学习中什么是梯度消失/爆炸;第三部分主要介绍怎么解决梯度消失/爆炸。
本文主要深度理解在深度学习中的梯度消失/爆炸以及解决方案。
随着神经网络层数的增加,会出现梯度消失/爆炸(Vanishing/Exploding Gradient)的问题,在介绍详细的内容之前,先简单说一说梯度问题的根源——深度神经网络和反向传播。
在目前的深度学习研究中,深度学习的神经网络的发展造就了一种可能:我们可以搭建更深层的网络,完成很复杂的任务,VGG这个论文就是主要提出并证明了深度对于深度学习神经网络性能的提升。除此之外,LSTM等的最终结果也表明了在处理复杂任务上,深层网络比浅层网络具有更好的性能。
但是,我们说目前优化神经网络的方法都是基于反向传播的思想,即根据损失函数计算的误差通过链式法则、梯度下降、反向传播的方式,更新和优化深度学习神经网络中的权值。这么做最大的原因便是基于非线性激活函数对于网络的作用,整个深度网络都可以看做是一个复合非线性函数。
F ( x ) = f n ( . . . f 3 ( f 2 ( f 1 ( x ) ∗ W 1 + b 1 ) ∗ W 2 + b 2 ) ∗ W 3 + b 3 ) . . . ) F(x)=f_{n}(...f_{3}(f_{2}(f_{1}(x) * W_{1} + b_{1}) * W_{2} + b_{2}) * W_{3} + b_{3})...) F(x)=fn(...f3(f2(f1(x)∗W1+b1)∗W2+b2)∗W3+b3)...)
我们希望深度学习可以很好的完成输入到输出之间的映射通过上面的这个多元函数。假设对于不同的输入,输出的最优解是 g ( x ) g(x) g(x),那么,优化网络的目的就是找到合适的权值,让损失函数更小,比如最简单的损失函数:
L o s s = ∣ ∣ g ( x ) − f ( x ) ∣ ∣ 2 2 Loss = ||g(x) - f(x)||_{2}^{2} Loss=∣∣g(x)−f(x)∣∣22
如果损失函数的数据空间是下图这样的,我们寻找最优的权值就是为了寻找下图中的最小值点,对于这种数学寻找最小值问题,目前最主流的方法就是梯度下降法。
梯度消失与梯度爆炸其实是一种情况,那么为什么会出现这两种情况?
为什么会出现梯度消失的现象呢?因为在深层网络中通常采用的激活函数是 s i g m o i d sigmoid sigmoid,在该函数中最小值和最大值的梯度(也就是倒数)都趋近于0。神经网络的反向传播是逐层对函数偏导相乘,当神经网络层数非常深的时候,最后一层产生的偏差就因为乘了很多的小于1的数而越来越小,最终就会变为0,从而导致层数比较浅的权重没有更新。
那么什么是梯度爆炸呢?梯度爆炸就是在深层网络中由于初始化权值过大,前面层会比后面层变化的更快,就会导致权值越来越大,梯度爆炸的现象就发生了。
图中是一个四层的全连接网络,根据公式:
F ( x ) = f n ( . . . f 3 ( f 2 ( f 1 ( x ) ∗ W 1 + b 1 ) ∗ W 2 + b 2 ) ∗ W 3 + b 3 ) . . . ) F(x) = f_{n}(...f_{3}(f_{2}(f_{1}(x) * W_{1} + b_{1}) * W_{2} + b_{2}) * W_{3} + b_{3})...) F(x)=fn(...f3(f2(f1(x)∗W1+b1)∗W2+b2)∗W3+b3)...)
我们可以得到(n+1)层的输入,也就是n层的输出的关系,f是激活函数,
f n + 1 ( x ) = f ( f n ( x ) ∗ W n + 1 + b n + 1 ) f_{n+1}(x) = f(f_{n}(x) * W_{n + 1} + b_{n + 1}) fn+1(x)=f(fn(x)∗Wn+1+bn+1)
如果要更新第 i 隐藏层的权值信息,根据链式求导法则,更新梯度信息:
Δ W i = ∂ L o s s ∂ W i = ∂ L o s s ∂ f 4 ( x ) ∂ f 4 ( x ) ∂ f 3 ( x ) ∂ f 3 ( x ) ∂ f i ( x ) ∂ f i ( x ) ∂ W i \Delta W_{i} = \frac{\partial Loss}{\partial W_{i}} = \frac{\partial Loss}{\partial f_{4}(x)} \frac{\partial f_{4}(x)}{\partial f_{3}(x)} \frac{\partial f_{3}(x)}{\partial f_{i}(x)} \frac{\partial f_{i}(x)}{\partial W_{i}} \qquad ΔWi=∂Wi∂Loss=∂f4(x)∂Loss∂f3(x)∂f4(x)∂fi(x)∂f3(x)∂Wi∂fi(x)
所以说 ∂ f 4 ( x ) ∂ f 3 ( x ) \frac{\partial f_{4}(x)}{\partial f_{3}(x)} ∂f3(x)∂f4(x)就是对激活函数的求导,如果 ∂ f 4 ( x ) ∂ f 3 ( x ) \frac{\partial f_{4}(x)}{\partial f_{3}(x)} ∂f3(x)∂f4(x)大于1,那么层数增多的时候,乘以一个大于1的数会变得更大,最终的求出的梯度更新将以指数形式增加,即发生梯度爆;如果 ∂ f 4 ( x ) ∂ f 3 ( x ) \frac{\partial f_{4}(x)}{\partial f_{3}(x)} ∂f3(x)∂f4(x)小于1,那么随着层数增多,乘以一个小于1的数会变得更小,求出的梯度更新信息将会以指数形式衰减,即发生了梯度消失。
例如:1)大于1时, 1. 2 5 = 2.48832 1.2^{5} = 2.48832 1.25=2.48832;2)小于1时, 0. 8 5 = 0.32768 0.8^{5} = 0.32768 0.85=0.32768。这两个例子是比较直观的数学理解方式了。
下面来看几组通过增加隐藏层层数后的每一层偏置的变化情况(从数学角度上在这里可以理解为在反向传播过程中每一次迭代对每一层偏置的求导结果变化情况,也就是梯度的大小变化情况,也就是需要更新的值的大小变化情况):
可以看出,第一个隐藏层比第四个几乎要慢100倍,这就是梯度消失,如果内层的梯度比外层大很多,就叫做梯度爆炸。
这么看来,梯度消失/爆炸(Vanishing/Exploding Gradient)的根本原因就是反向传播训练法则,这是先天不足,也就是系统误差导致的,如果Hinton提出的 c a p s u l e capsule capsule 能彻底替代反向传播并大范围普及,那将会是一个崭新的时代,那真是一个revolution。
从激活函数来看的话, s i g m o i d sigmoid sigmoid 的问题就比较大了,梯度消失就会很明显,原因看下图,当最大或者最小的时候,整体分布会向取值区间的上下限两端靠近,梯度几乎为0。
s i g m o i d sigmoid sigmoid 导数的图像如下图:
当最大值或者最小值时,梯度(导数)都是0,会导致梯度消失的问题。
总之,梯度消失/爆炸(Vanishing/Exploding Gradient)的根本的问题其实是:在前面的层上的梯度是来自后面的层上项的乘积。当层数过多的时候,就出现了内在本质上的不稳定场景(也就是梯度消失/爆炸)。唯一让所有层都接近相同的学习速度的方式是所有这些项的乘积都能得到一种平衡。是不是可以考虑对于不同的层使用不同的学习率(以我目前的水平来看是未知的)。和前面说的一样,这是个‘天生’的问题,也就是说,这是‘命’,除非‘逆天改命’。(=-=)
梯度消失爆炸的解决方案主要包括以下几个部分:
1、预训练(Pre-training)加微调(Fine-tunning)
此方法来自Hinton在2006年发表的一篇论文,Hinton为了解决梯度的问题,提出采取无监督逐层训练方法。其基本思想是每次训练一层隐节点,训练时将上一层隐节点的输出作为输入,而本层隐节点的输出作为下一层隐节点的输入,此过程就是逐层“预训练”(pre-training);在预训练完成后,再对整个网络进行“微调”(fine-tunning)。 Hinton在训练深度信念网络(Deep Belief Networks中,使用了这个方法,在各层预训练完成后,再利用BP算法对整个网络进行训练。此思想相当于是先寻找局部最优,然后整合起来寻找全局最优,此方法有一定的好处,但是目前应用的不是很多了。如果Hinton提出的 c a p s u l e capsule capsule 能彻底替代反向传播并大范围普及,那会是一个新的解决办法。(因为直接remove了BP,233333)
2、梯度剪切、权重正则(Weights-regularization)
梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后在更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。
另外一种解决梯度爆炸的手段是采用权重正则化(weithts regularization),比较常见的是 L 1 L1 L1 正则,
和 L 2 L2 L2 正则,
正则化是通过对网络权重做正则限制过拟合,仔细看上面两个正则项损失函数的形式, λ \lambda λ 是指正则项系数,因此,如果发生梯度爆炸的话,权值的范数就会变的非常大,损失函数就变的非常大。所以通过正则化项,可以部分限制梯度爆炸的发生。
3、使用不同的激活函数(Activation)
Rectified linear unit(ReLU) 函数是深度神经网络在2015年最流行的激活函数,比较简单,其数学表达为: f ( x ) = m a x ( 0 , x ) f(x)=max(0,x) f(x)=max(0,x),它更加符合神经元的激活原理。
其函数图像和函数的导数图像:
我们可以看到。 R e L U ReLU ReLU函数的导数在正数部分恒为1,因此不存在 s i g m o i d sigmoid sigmoid 函数的问题。尽管relu也有缺点,但是仍然是目前使用最多的激活函数。
leakrelu就是为了解决relu的0区间带来的影响,其数学表达为: f ( x ) = m a x ( k ∗ x , x ) f(x)=max(k * x,x) f(x)=max(k∗x,x),一般选择0.01或者0.02,或者通过学习而来。
其函数图像:
leakrelu解决了0区间带来的影响,而且包含了relu的所有优点。
elu激活函数也是为了解决relu的0区间带来的影响,其数学表达为: f ( x ) = m a x [ α ( e x − 1 ) , x ] f(x) = max[\alpha (e^{x} - 1),x] f(x)=max[α(ex−1),x]。
其函数图像和函数的导数图像:
但是elu相对于leakrelu来说,计算要更耗时间一些。
4、使用Batch Normalization
具体的batchnorm原理非常复杂,在这里不做详细展开,有关batch norm详细的内容可以参考我的另一篇博客:【深度学习】Batch Normalization(批归一化),这里简单说一下,通过对每一层的输出归一化为符合同一均值和方差的分布,把输出拉到非饱和区,也就是解决了梯度消失/爆炸(Vanishing/Exploding Gradient)。
5、使用残差结构(Residual)
事实上,就是残差网络的出现导致了image net比赛的终结,自从残差网络提出之后,几乎所有的深度网络都离不开残差的身影。相比较之前的几层,几十层的深度网络,在残差网络面前都不值一提,残差可以很轻松的构建几百层,一千多层的网络而不用担心梯度消失过快的问题,原因就在于残差的捷径(shortcut)部分。具体的残差,我将在后面的坑中补出来,(未完待续)。
6、使用LSTM网络
LSTM全称是长短期记忆网络(long-short term memory networks),是不那么容易发生梯度消失的,主要原因在于LSTM内部复杂的“门”(gates),如下图,LSTM通过它内部的“门”可以接下来更新的时候“记住”前几次训练的”残留记忆“,因此,经常用于生成文本中。推荐一个文章LSTM(长短期记忆网络)及其tensorflow代码应用,[译] 理解 LSTM 网络。
具体的,我将在后面的坑中补出来,(未完待续)。