层数比较多的神经网络模型在训练时也是会出现一些问题的,其中就包括梯度消失问题(gradient vanishing problem)和梯度爆炸问题(gradient exploding problem)。梯度消失问题和梯度爆炸问题一般随着网络层数的增加会变得越来越明显。
例如,对于下图所示的含有3个隐藏层的神经网络,梯度消失问题发生时,接近于输出层的hidden layer 3等的权值更新相对正常,但前面的hidden layer 1的权值更新会变得很慢,导致前面的层权值几乎不变,仍接近于初始化的权值,这就导致hidden layer 1相当于只是一个映射层,对所有的输入做了一个同一映射,这是此深层网络的学习就等价于只有后几层的浅层网络的学习了。
而这种问题为何会产生呢?以下图的反向传播为例(假设每一层只有一个神经元且对于每一层 y i = σ ( z i ) = σ ( w i x i + b i ) y_{i}=\sigma\left(z_{i}\right)=\sigma\left(w_{i} x_{i}+b_{i}\right) yi=σ(zi)=σ(wixi+bi),其中 σ \sigma σ为sigmoid函数)
可以推导出
∂ C ∂ b 1 = ∂ C ∂ y 4 ∂ y 4 ∂ z 4 ∂ z 4 ∂ x 4 ∂ x 4 ∂ z 3 ∂ z 3 ∂ x 3 ∂ x 3 ∂ x 2 ∂ z 2 ∂ x 2 ∂ x 2 ∂ z 1 ∂ z 1 ∂ b 1 = ∂ C ∂ y 4 σ ′ ( z 4 ) w 4 σ ′ ( z 3 ) w 3 σ ′ ( z 2 ) w 2 σ ′ ( z 1 ) \begin{array}{l} \frac{\partial C}{\partial b_{1}}=\frac{\partial C}{\partial y_{4}} \frac{\partial y_{4}}{\partial z_{4}} \frac{\partial z_{4}}{\partial x_{4}} \frac{\partial x_{4}}{\partial z_{3}} \frac{\partial z_{3}}{\partial x_{3}} \frac{\partial x_{3}}{\partial x_{2}} \frac{\partial z_{2}}{\partial x_{2}} \frac{\partial x_{2}}{\partial z_{1}} \frac{\partial z_{1}}{\partial b_{1}} \\ =\frac{\partial C}{\partial y_{4}} \sigma^{\prime}\left(z_{4}\right) w_{4} \sigma^{\prime}\left(z_{3}\right) w_{3} \sigma^{\prime}\left(z_{2}\right) w_{2} \sigma^{\prime}\left(z_{1}\right) \end{array} ∂b1∂C=∂y4∂C∂z4∂y4∂x4∂z4∂z3∂x4∂x3∂z3∂x2∂x3∂x2∂z2∂z1∂x2∂b1∂z1=∂y4∂Cσ′(z4)w4σ′(z3)w3σ′(z2)w2σ′(z1)
而sigmoid的导数 σ ′ ( x ) \sigma^{\prime}(x) σ′(x)如下图
可见, σ ′ ( x ) \sigma^{\prime}(x) σ′(x)的最大值为1/4,而我们初始化的网络权值 w w w通常都小于1,因此 ∣ σ ′ ( z ) w ∣ ≤ 1 4 \left|\sigma^{\prime}(z) w\right| \leq \frac{1}{4} ∣σ′(z)w∣≤41,因此对于上面的链式求导,层数越多,求导结果 ∂ C ∂ b 1 \frac{\partial C}{\partial b_{1}} ∂b1∂C越小,因而导致梯度消失的情况出现。
这样,梯度爆炸问题的出现原因就显而易见了,即 σ ′ ( z ) w ∣ > 1 \sigma^{\prime}(z) w \mid>1 σ′(z)w∣>1,也就是 w w w比较大的情况。但对于使用sigmoid激活函数来说,这种情况比较少。因为 σ ′ ( x ) \sigma^{\prime}(x) σ′(x)的大小也与 w w w有关( z = w x + b z=wx+b z=wx+b),除非该层的输入值 x x x在一直一个比较小的范围内。
如果说从数学上看不够直观的话,下面几个图可以很直观的说明深层网络的梯度问题。
注:下图中的隐层标号和第一张全连接图隐层标号刚好相反。
图中的曲线表示权值更新的速度,对于下图两个隐层的网络来说,已经可以发现隐藏层2的权值更新速度要比隐藏层1更新的速度慢:
那么对于四个隐层的网络来说,就更明显了,第四隐藏层比第一隐藏层的更新速度慢了两个数量级.
总结:从深层网络角度来讲,不同的层学习的速度差异很大,表现为网络中靠近输出的层学习的情况很好,靠近输入的层学习的很慢,有时甚至训练了很久,前几层的权值和刚开始随机初始化的值差不多。
因此梯度爆炸和梯度消失问题都是因为网络太深,网络权值更新不稳定造成的,本质上是因为梯度反向传播中的连乘效应。
对于更普遍的梯度消失问题,可以考虑用ReLU激活函数取代sigmoid激活函数。另外,LSTM的结构设计也可以改善RNN中的梯度消失问题。
梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸。
另外一种解决梯度爆炸的手段是采用权重正则化(weithts regularization)比较常见的是l1正则,和l2正则,
在各个深度框架中都有相应的API可以使用正则化,比如在tensorflow中,若搭建网络的时候已经设置了正则化参数,则调用以下代码可以直接计算出正则损失:
regularization_loss = tf.add_n(tf.losses.get_regularization_losses(scope='my_resnet_50'))
如果没有设置初始化参数,也可以使用以下代码计算 l 2 l2 l2正则损失:
l2_loss = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables() if 'weights' in var.name])
正则化是通过对网络权重做正则限制过拟合,仔细看正则项在损失函数的形式:
Loss = ( y − W T x ) 2 + α ∥ W ∥ 2 \operatorname{Loss}=\left(y-W^{T} x\right)^{2}+\alpha\|W\|^{2} Loss=(y−WTx)2+α∥W∥2
其中, α \alpha α 是指正则项系数,因此,如果发生梯度爆炸,权值的范数就会变的非常大,通过正则化项,可以部分限制梯度爆炸的发生。
事实上,在深度神经网络中,往往是梯度消失出现的更多一些.
那么relu激活函数是如何避免梯度爆炸和梯度消失的呢?
它的思想也很简单,如果激活函数的导数为1,那么就不存在梯度消失爆炸的问题了,每层的网络都可以得到相同的更新速度。
Re lu ( x ) = max ( x , 0 ) = { 0 , x < 0 x , x > 0 } \operatorname{Re} \operatorname{lu}(\mathrm{x})=\max (\mathrm{x}, 0)=\left\{\begin{array}{l} 0, x<0 \\ x, x>0 \end{array}\right\} Relu(x)=max(x,0)={ 0,x<0x,x>0}
从上图中,我们可以很容易看出,relu函数的导数在正数部分是恒等于1的,因此在深层网络中使用relu激活函数就不会导致梯度消失和爆炸的问题。
relu的主要贡献在于:
同时也存在一些缺点:
因此可以用leakrelu代替。
leakrelu就是为了解决relu的0区间带来的影响,其数学表达为:
LeakyReLU = { x x ≥ 0 p ∗ x x ≤ 0 \text { LeakyReLU }=\left\{\begin{array}{cl} x & x \geq 0 \\ p * x & x \leq 0 \end{array}\right. LeakyReLU ={ xp∗xx≥0x≤0
其中 p p p是leak系数,一般选择0.1或者0.2,或者通过学习而来.
leakrelu解决了0区间带来的影响,而且包含了relu的所有优点.
elu激活函数也是为了解决relu的0区间带来的影响,其数学表达为:
elu激活函数也是为了解决relu的0区间带来的影响,其数学表达为:
{ x , if x > 0 α ( e x − 1 ) , otherwise \left\{\begin{array}{cl} x, & \text { if } x>0 \\ \alpha\left(e^{x}-1\right), & \text { otherwise } \end{array}\right. { x,α(ex−1), if x>0 otherwise
其函数及其导数数学形式为:
但是elu相对于leakrelu来说,计算要更耗时间一些。
Batchnorm是深度学习发展以来提出的最重要的成果之一了,目前已经被广泛的应用到了各大网络中,具有加速网络收敛速度,提升训练稳定性的效果,Batchnorm本质上是解决反向传播过程中的梯度问题。batchnorm全名是batch normalization,简称BN,即批规范化,通过规范化操作将输出信号x规范化到均值为0,方差为1保证网络的稳定性。
此部分大概讲一下batch Normalization解决梯度的问题上。具体来说就是反向传播中,经过每一层的梯度会乘以该层的权重,举个简单例子:
正向传播中 f 2 = f 1 ( w T ∗ x + b ) f2=f1(w^{T}∗x+b) f2=f1(wT∗x+b),那么反向传播中
∂ f 2 ∂ x = ∂ f 2 ∂ f 1 w \frac{\partial f_{2}}{\partial x}=\frac{\partial f_{2}}{\partial f_{1}} w ∂x∂f2=∂f1∂f2w
反向传播式子中有 w w w的存在,所以 w w w的大小影响了梯度的消失和爆炸,batch Normalization就是通过对每一层的输出规范为均值和方差一致的方法,消除了 w w w带来的放大缩小的影响,进而解决梯度消失和爆炸的问题。
这一块原理没怎么看过,等有空会根据论文详细写个原理篇。
深度网络的退化问题至少说明深度网络不容易训练。但是我们考虑这样一个事实:现在你有一个浅层网络,你想通过向上堆积新层来建立深层网络,一个极端情况是这些增加的层什么也不学习,仅仅复制浅层网络的特征,即这样新层是恒等映射(Identity mapping)。在这种情况下,深层网络应该至少和浅层网络性能一样,也不应该出现退化现象。好吧,你不得不承认肯定是目前的训练方法有问题,才使得深层网络很难去找到一个好的参数。
这个有趣的假设让何博士灵感爆发,他提出了残差学习来解决退化问题。对于一个堆积层结构(几层堆积而成)当输入为 x x x时其学习到的特征记为 H ( x ) H(x) H(x) ,现在我们希望其可以学习到残差 F ( x ) = H ( x ) − x F(x)=H(x)-x F(x)=H(x)−x ,这样其实原始的学习特征是 F ( x ) + x F(x)+x F(x)+x 。之所以这样是因为残差学习相比原始特征直接学习更容易。当残差为0时,此时堆积层仅仅做了恒等映射,至少网络性能不会下降,实际上残差不会为0,这也会使得堆积层在输入特征基础上学习到新的特征,从而拥有更好的性能。残差学习的结构如图4所示。这有点类似y于电路中的“短路”,所以是一种短路连接(shortcut connection)。
为什么残差学习相对更容易,从直观上看残差学习需要学习的内容少,因为残差一般会比较小,学习难度小点。不过我们可以从数学的角度来分析这个问题,首先残差单元可以表示为:
y l = h ( x l ) + F ( x l , W l ) x l + 1 = f ( y l ) \begin{array}{l} y_{l}=h\left(x_{l}\right)+F\left(x_{l}, W_{l}\right) \\ x_{l+1}=f\left(y_{l}\right) \end{array} yl=h(xl)+F(xl,Wl)xl+1=f(yl)
其中 x l x_{l} xl 和 x l + 1 x_{l+1} xl+1 分别表示的是第 l l l 个残差单元的输入和输出,注意每个残差单元一般包含多层结构。 F F F 是残差函数,表示学习到的残差,而 h ( x l ) = x l h\left(x_{l}\right)=x_{l} h(xl)=xl 表示恒等映射, f f f 是ReLU激活函数。基于上式,我们求得从浅层 l l l 到深层 L L L 的学习特征为:
x L = x l + ∑ i = l L − 1 F ( x i , W i ) x_{L}=x_{l}+\sum_{i=l}^{L-1} F\left(x_{i}, W_{i}\right) xL=xl+i=l∑L−1F(xi,Wi)
利用链式规则,可以求得反向过程的梯度:
∂ los s ∂ x l = ∂ loss ∂ x L ⋅ ∂ x L ∂ x l = ∂ los s ∂ x L ⋅ ( 1 + ∂ ∂ x L ∑ i = l L − 1 F ( x i , W i ) ) \frac{\partial \operatorname{los} s}{\partial x_{l}}=\frac{\partial \operatorname{loss}}{\partial x_{L}} \cdot \frac{\partial x_{L}}{\partial x_{l}}=\frac{\partial \operatorname{los} s}{\partial x_{L}} \cdot\left(1+\frac{\partial}{\partial x_{L}} \sum_{i=l}^{L-1} F\left(x_{i}, W_{i}\right)\right) ∂xl∂loss=∂xL∂loss⋅∂xl∂xL=∂xL∂loss⋅(1+∂xL∂i=l∑L−1F(xi,Wi))
式子的第一个因子 ∂ l o s s ∂ x L \frac{\partial l o s s}{\partial x_{L}} ∂xL∂loss 表示的损失函数到达 L L L 的梯度,小括号中的1表明短路机制可以无损地传播梯度,而另外一项残差梯度则需要经过带有weights的层,梯度不是直接传递过来的。残差梯度不会那么巧全为-1,而且就算其比较小,有1的存在也不会导致梯度消失。所以残差学习会更容易。要注意上面的推导并不是严格的证明。
关于残差网络的更多内容可以参考文章:你必须要知道CNN模型:ResNet
:LSTM
LSTM全称是长短期记忆网络(long-short term memory networks),是不那么容易发生梯度消失的,主要原因在于LSTM内部复杂的“门”(gates),如下图,LSTM通过它内部的“门”可以接下来更新的时候“记住”前几次训练的”残留记忆“,因此,经常用于生成文本中。目前也有基于CNN的LSTM,感兴趣的可以尝试一下。