前言

在讲述RNN与LSTM的时候曾经说过, RNN一个最大的缺陷就是梯度消失与梯度爆炸问题, 由于这一缺陷,使得RNN在长文本中难以训练, 这才诞生了LSTM及各种变体。

梯度消失与梯度爆炸问题不仅仅在RNN中存在, 在其余深层网路中同样普遍存在, 本文先从梯度消失与梯度爆炸谈起, 然后再回到具体的RNN梯度问题中, 最后, 再探讨LSTM是通过什么技术来解决RNN中的这一问题的。

浅谈神经网路中的梯度爆炸问题

机器学习总结(二):梯度消失和梯度爆炸

神经网路训练中的梯度消失与梯度爆炸

1. 什么是梯度消失,梯度爆炸?

首先,你得知道梯度是什么, 不了解的可以看看我关于梯度下降的那篇文章。

从那篇文章中就可以看出对于神经网路的训练,梯度在训练中起到很关键的作用。 如果在训练过程中发生了梯度消失,这也就意味著我们的权重无法被更新,最终导致训练失败。而梯度爆炸所带来的梯度过大,从而大幅度更新网路参数,造成网路不稳定(可以理解为梯度步伐太大)。在极端情况下,权重的值变得特别大,以至于结果会溢出(NaN值)

注意,梯度消失和梯度爆炸只会造成神经网路中较浅的网路的权重无法更新(毕竟神经网路中是反向传播)

2. 这会造成哪些问题?

  • 梯度消失会导致我们的神经网路中前面层的网路权重无法得到更新,也就停止了学习。
  • 梯度爆炸会使得学习不稳定, 参数变化太大导致无法获取最优参数。
  • 在深度多层感知机网路中,梯度爆炸会导致网路不稳定,最好的结果是无法从训练数据中学习,最坏的结果是由于权重值为NaN而无法更新权重。
  • 在循环神经网路(RNN)中,梯度爆炸会导致网路不稳定,使得网路无法从训练数据中得到很好的学习,最好的结果是网路不能在长输入数据序列上学习。

3. 原因何在?

让我们以一个很简单的例子分析一下,这样便于理解。

如上图,是一个每层只有一个神经元的神经网路,且每一层的激活函数为sigmoid,则有:

y_i = sigma(z_i) = sigma(w_ix_i + b_i) ( sigma 是sigmoid函数)。

我们根据反向传播演算法有:

 frac{delta C}{delta b_1} = frac{delta C}{ delta y_4} frac{delta y_4}{delta z_4} frac{delta z_4}{delta x_4} frac{ delta x_4}{delta z_3} frac{delta z_3}{ delta x_3} frac{ delta x_3}{delta z_2} frac{delta z_2}{ delta x_2} frac{ delta x_2}{delta z_1} frac{delta z_1}{delta b_1} \ = frac{ delta C}{delta y_4} (sigma (z_4) w_4)( sigma(z_3) w_3)( sigma  (z_2) w_2)( sigma  (z_1))

而sigmoid函数的导数公式为:  S(x ) = frac{e^{-x}}{(1+ e^{-x})^2} = S(x)(1- S(x)) 它的图形曲线为:

由上可见,sigmoid函数的导数 sigma(x) 的最大值为 frac{1}{4} ,通常我们会将权重初始值 |w| 初始化为为小于1的随机值,因此我们可以得到 |sigma (z_4) w_4| < frac{1}{4} ,随著层数的增多,那么求导结果 frac{delta C}{delta b_1} 越小,这也就导致了梯度消失问题。

那么如果我们设置初始权重 |w| 较大,那么会有 |sigma (z_4) w_4| > 1 ,造成梯度太大(也就是下降的步伐太大),这也是造成梯度爆炸的原因。

总之,无论是梯度消失还是梯度爆炸,都是源于网路结构太深,造成网路权重不稳定,从本质上来讲是因为梯度反向传播中的连乘效应。

4. RNN中的梯度消失,爆炸问题

参考:RNN梯度消失和爆炸的原因, 这篇文章是我看到讲的最清楚的了,在这里添加一些我的思考, 若侵立删。

我们给定一个三个时间的RNN单元,如下:

我们假设最左端的输入 S_0 为给定值, 且神经元中没有激活函数(便于分析), 则前向过程如下:

S_1 = W_xX_1 + W_sS_0 + b_1 qquad qquad qquad O_1 = W_oS_1 + b_2 \ S_2 = W_xX_2 + W_sS_1 + b_1 qquad qquad qquad O_2 = W_oS_2 + b_2 \ S_3 = W_xX_3 + W_sS_2 + b_1 qquad qquad qquad O_3 = W_oS_3 + b_2

t=3 时刻, 损失函数为 L_3 = frac{1}{2}(Y_3 - O_3)^2 ,那么如果我们要训练RNN时, 实际上就是是对 W_x, W_s, W_o,b_1,b_2 求偏导, 并不断调整它们以使得 L_3 尽可能达到最小(参见反向传播演算法与梯度下降演算法)。

那么我们得到以下公式:

frac{delta L_3}{delta W_0} = frac{delta L_3}{delta O_3} frac{delta O_3}{delta W_0} \ frac{delta L_3}{delta W_x} = frac{delta L_3}{delta O_3} frac{delta O_3}{delta S_3} frac{delta S_3}{delta W_x} + frac{delta L_3}{delta O_3} frac{delta O_3}{delta S_3} frac{delta S_3}{delta S_2} frac{delta S_2}{delta W_x} + frac{delta L_3}{delta O_3} frac{delta O_3}{delta S_3} frac{delta S_3}{delta S_2} frac{delta S_2}{delta S_1}frac{delta S_1}{delta W_x} \ frac{delta L_3}{delta W_s} = frac{delta L_3}{delta O_3} frac{delta O_3}{delta S_3} frac{delta S_3}{delta W_s} + frac{delta L_3}{delta O_3} frac{delta O_3}{delta S_3} frac{delta S_3}{delta S_2} frac{delta S_2}{delta W_s} + frac{delta L_3}{delta O_3} frac{delta O_3}{delta S_3} frac{delta S_3}{delta S_2} frac{delta S_2}{delta S_1}frac{delta S_1}{delta W_s}

将上述偏导公式与第三节中的公式比较,我们发现, 随著神经网路层数的加深对 W_0 而言并没有什么影响, 而对 W_x, W_s 会随著时间序列的拉长而产生梯度消失和梯度爆炸问题。

根据上述分析整理一下公式可得, 对于任意时刻t对 W_x, W_s 求偏导的公式为:

frac{delta L_t}{delta W_x } = sum_{k=0}^t frac{delta L_t}{delta O_t} frac{delta O_t}{delta S_t}( prod_{j=k+1}^t frac{delta S_j}{delta S_{j-1}} ) frac{ delta S_k }{delta W_x} \ frac{delta L_t}{delta W_s } = sum_{k=0}^t frac{delta L_t}{delta O_t} frac{delta O_t}{delta S_t}( prod_{j=k+1}^t frac{delta S_j}{delta S_{j-1}} ) frac{ delta S_k }{delta W_s}

我们发现, 导致梯度消失和爆炸的就在于 prod_{j=k+1}^t frac{delta S_j}{delta S_{j-1}} , 而加上激活函数后的S的表达式为:

S_j = tanh(W_xX_j + W_sS_{j-1} + b_1)

那么则有:

prod_{j=k+1}^t frac{delta S_j}{delta S_{j-1}} = prod_{j=k+1}^t tanh W_s

而在这个公式中, tanh的导数总是小于1 的, 如果 W_s 也是一个大于0小于1的值, 那么随著t的增大, 上述公式的值越来越趋近于0, 这就导致了梯度消失问题。 那么如果 W_s 很大, 上述公式会越来越趋向于无穷, 这就产生了梯度爆炸。

5. 为什么LSTM能解决梯度问题?

在阅读此篇文章之前,确保自己对LSTM的三门机制有一定了解, 参见:LSTM:RNN最常用的变体

从上述中我们知道, RNN产生梯度消失与梯度爆炸的原因就在于 prod_{j=k+1}^t frac{delta S_j}{delta S_{j-1}} , 如果我们能够将这一坨东西去掉, 我们的不就解决掉梯度问题了吗。 LSTM通过门机制来解决了这个问题。

我们先从LSTM的三个门公式出发:

  • 遗忘门: f_t = sigma( W_f cdot [h_{t-1}, x_t] + b_f)
  • 输入门: i_t = sigma(W_i cdot [h_{t-1}, x_t] + b_i)
  • 输出门: o_t = sigma(W_o cdot [h_{t-1}, x_t ] + b_0 )
  • 当前单元状态 c_t : c_t = f_t circ c_{t-1} + i_t circ tanh(W_c cdot [h_{t-1}, x_t] + b_c )
  • 当前时刻的隐层输出: h_t = o_t circ tanh(c_t)

我们注意到, 首先三个门的激活函数是sigmoid, 这也就意味著这三个门的输出要么接近于0 , 要么接近于1。这就使得 frac{delta c_t}{delta c_{t-1}} = f_t, frac{delta h_t}{delta h_{t-1}} = o_t 是非0即1的,当门为1时, 梯度能够很好的在LSTM中传递,很大程度上减轻了梯度消失发生的概率, 当门为0时,说明上一时刻的信息对当前时刻没有影响, 我们也就没有必要传递梯度回去来更新参数了。所以, 这就是为什么通过门机制就能够解决梯度的原因: 使得单元间的传递 frac{delta S_j}{delta S_{j-1}} 为0 或 1。

最后

理解基本的神经网路单元是必要的, 但在上层task的使用中, 基本的神经单元通常只是拿来即用, 更多的在deep, 迁移学习, embedding , attention上做文章,这需要多看paper, 多写代码, 慢慢积累。 最后, 愿我早日超神。

推荐阅读:

相关文章