cs224n(2019)-Lecture 7 梯度消失和有趣的RNN

文章目录
  1. 1. 梯度消失的直观表示
  2. 2. 梯度消失的证明过程
  3. 3. 为什么梯度消失会是一个问题?
  4. 4. 梯度消失在RNN-LM模型上的影响
  5. 5. 为什么梯度爆炸也是一个问题?
  6. 6. 解决梯度爆炸的办法:梯度裁剪
  7. 7. 如何解决梯度消失的问题?
  8. 8. Long Short-Term Memory(LSTM)
  9. 9. Multi-layer RNN(多层RNN)

看到Richard老师的时候我真的崩溃了,大佬我跟不上啊啊啊啊,所以cs224n 2019启动!
后面的笔记以2019版本为准…Richard老师对不起…

梯度消失的直观表示

梯度下降的过程中会存在这样一个问题:如果信号会越变越小,那么信号会出现消失。
因为累积的梯度是中间梯度的乘积。

梯度消失的证明过程

从上图可以看出,梯度计算的中间过程是一个累积的过程。

注:diag:是对角矩阵的意思,对角线上的元素由里面的sigmoid函数产生,其他元素都是0。
完整的梯度求导式子可以写作:

给上面的式子加上L2范式:

Pascanu等人证明过,如果Wh的值比1小,那么梯度值会以指数形式缩小。
特殊地,我们可以看到我们所感兴趣的梯度是比Wh的i-j次的乘积的范式要小或者是相等。所以,如果Wh变小,整个式子会不断变小。

为什么梯度消失会是一个问题?

下面给出一张图展示出为什么梯度消失会是一件不好的事情。
第二步中的损失也和第一步中的隐藏状态有关。

邻近的梯度信号量级(magnitude)要比远处的梯度信号量级(magnitude)大得多。这就意味着,当你更新模型权重时,你要从附近得到的信号要远远超过远处的信号。基本上你只要是学习,你只是要优化这些邻近的影响,而不是远端(long-term)的影响。
所以,在临近的影响中你会失去远端的影响。

所以,在临近的影响中你会失去远端的影响。

对于梯度问题的另一种解释:梯度可以看作是过去对未来结果影响的一种度量。
梯度就是,如果改变某个权重或是某个激活量一点点,那么它将会对这件事的未来产生多大的影响。

所以,特别的事,如果梯度在较长的距离上(从 t 到 t+n )变得越来越小,那么我们将无法判断是否存在以下两种情况:
1.步骤 t 和 t+n 之间不存在任何依赖;
2.我们使用了错误的参数来获取步骤 t 和 t+n 之间的真实依赖。

梯度消失在RNN-LM模型上的影响

LM注:Language Model

对于RNN-LM模型的问题是能否轻松回答这个问题?他们会在这个特定的语言建模示例中做得好吗?
答案是:是的。RNN-LM模型可以从训练数据中学习这种例子,因此,如果它解决了训练数据中的示例,那么RNN-LM模型将需要对依赖关系进行建模来学习第七步中的单词 tickets 和 最后的目标单词 tickets 间的关系。

但是,如果存在梯度消失的问题,这些梯度将会非常小,因为这是一个相当长的距离,这就意味着该模型将无法实现轻松地或完全地学习这种依赖性。所以,如果模型不能学习这种依赖性,然后模型将无法预测在测试时类似的各种长距离依赖。

syntactic recency:句法近因

syntactic recency:句法近因


由上述两个例子引出的问题是:RNN语言模型在顺序近因(sequential recency)方面学习比在sicta-句法近因(sicta-syntactic recency)更好。

梯度消失是造成这个问题的原因,如果你的单词距离较远,可能就会很难使用语法上最近的单词中的信息,特别是顺序近因(sequential recency)的单词中有很多强信号。

为什么梯度爆炸也是一个问题?

SGD效果如下:

这个模型的参数使用θ来表示,类似于旧的前提(old premisses),然后你朝着负梯度下降的方法迈进了一步,因为需要最小化 J 的损失函数。
所以,问题是,当梯度变得很大时,那么SGD更新步骤也将变得非常大。
最差的结果就是:网络中出现 INF 或者 NaN的情况。

解决梯度爆炸的办法:梯度裁剪

梯度裁剪:如果梯度的范数大于某个阈值(阈值是一个自己选定的超参数),则在应用SGD更新之前将其缩小。

g_hat这个向量,它是误差对前提(premises)的导数,如果说梯度的范式比阈值大,那么只需要缩小它即可。重要的是,它仍然指向同一方向,但只是一小步(take a step in the same direction, but a smaller step)。
简单来说就是:

其中 W 和 b 都是一个标量,z 轴表示损失,毫无疑问,损失应该越小越好。
上图中有一个非常陡的悬崖(cliff),这里的损失(loss)变化很快,如果没有裁剪梯度,那么就会遇到左侧图中所示的情况,这是一个非常差的更新,而现在它的损失比之前的大得多。
同时,右边部分的图示也是一个非常差的更新,因为它只是投掷,可能非常随机的配置 w 和 b。

所以,在这些对比之下,可以看到:
采用梯度裁剪,从悬崖处下降到沟渠,经过裁剪之后,最终离开悬崖一点点,但是不会有太多,因为梯度被裁剪了。
然后在悬崖上,又有一个非常陡峭的梯度,同时它也没有采取很大的一步,因为再次修剪了渐变,所以它有点回归。


结论:可以通过梯度裁剪的方法来有效避免任何疯狂的步骤来找到在沟渠底部真正的最小值。

如何解决梯度消失的问题?

主要的问题是RNN很难学会在许多时间步骤中保存信息。
在vanilla RNN中,隐藏状态是不断被重写更新的:

因此这促使我们思考,是否具有某种独立记忆(separate memory)的RNN?
如果有一些单独的地方来存储后续想要的信息,这会让RNN更容易学习在更多时间内保存信息。
这些就是 LSTM 和 Long Short-Term Memory RNN 背后的理念。

Long Short-Term Memory(LSTM)

LSTM被作为解决梯度消失的主要方案。
在步骤 t 中,包含了当前状态 h(t),以及细胞状态 c(t),h(t)和c(t)具有相同的长度。在这个神经网络的改进中,细胞(cell)意味着存储我们的长期信息,即存储单元。 另一个更重要的地方是,LSTM 可以擦除并写入,并从单元格中读取信息。


同时,LSTM决定是否要擦除,写入,读取信息并决定多少和哪些信息的方式,这些都由各种门来进行控制。如果门是打开的(用 1 表示),则信息可以通过;反之,如果门是关闭的(用0表示),则信息无法通过。重要的是,门是动态的,他们的值是基于当前的文本计算出来的。

在时间步长 t 上,我们有一个序列输入 x(t),我们将需要计算一个序列的隐藏状态 h(t) 和细胞状态 c(t):

f(t)是一个介于0和1之间的向量,当在做一个基于f(t)和前一个单元状态c(t-1)元素积(element-wise product)时,你真正在做的是掩盖掉从之前的隐藏状态中输出的一些信息。当 f(t) 是1时,表示复制这些信息,当 f(t) 是 0 时,表示摆脱这些信息,也就是删除或是忘记它。

上述的六个计算步骤的直观展示如下图所示:


计算步骤如上图中标识所示,具体计算过程我写在纸上,如下:


## LSTM如何解决梯度消失的问题?

LSTM的结构更容易保存时间步长上的信息:
1.如果遗忘门(forget gate)被设置为记住每一步的一切,细胞中的信息在许多时间步长上将被无限期的保留。
2.相比之下,vanilla RNN更难学习一个循环权重矩阵Wh来保存隐藏状态的信息。

注意:LSTM并不保证不会出现梯度消失或者是梯度爆炸的情况,但它确实提供了一种更容易的放来来让模型学习长距离依赖。

## Gated Recurrenr Units(GRU)

GRU 要比 LSTM 更简单。
在GRU中没有细胞状态(cell state),只有一个隐藏的状态。它和 LSTM 的相似之处就在于会使用门结构来进行控制信息流,GRU一共有两个门,更新门(Update gate)和重置门(Reset gate)的式子如下:

更新门:控制着隐藏状态中哪个部分需要被更新和保存。
重置门:控制着先前状态中隐藏状态的哪个部分将用于计算新的内容。




新隐藏状态的内容:重置门选择了先前隐藏状态中有用的部分。使用这些和当前的输入来计算新的隐藏内容。
隐藏状态:更新门同时控制从以前的隐藏状态保留的内容,以及更新到新的隐藏状态内容的内容。




为什么GRU有助于梯度消失问题?
因为GRU和LSTM一样没有明确的内存细胞(memory cell)。

如果u(t)设置为0,然后我们将会在每一步保持隐藏状态相同(可能不是个好主意,但是容易实现),这是为了保留长距离的信息,这就是GRU比RNN更容易保持远距离信息的一个原因。
GRU结构图如下:

参考来源:http://colah.github.io/posts/2015-08-Understanding-LSTMs/

## LSTM vs GRU

1.GRU计算速度更快并且参数更少。
2.并没有决定性的证据来一致证明其中哪种模型更好。
3.如果训练数据较多或者数据有特殊的长期以来,LSTM则被默认是更好的选择。
4.经验法则:先用LSTM,如果需要加快效率,则使用GRU。

## 梯度消失/爆炸只是RNN中的问题吗?

不是,所有的神经网络中都会存在梯度爆炸或是梯度消失的问题。
1.在前馈网络和卷积网络中,经常会有一个渐渐消失的渐变,由于链式法则,梯度会在反向传播变小。
2.较低层的网络学习非常慢,难以训练。
解决办法:在网络中添加更多的直接连接,不仅仅是临近的层,而是更远的层, 这使得梯度更容易流动,整体训练网络会更容易。

### For example-Residual Connection
1.Residual connections(残差连接)aka “ResNet”
2.跳跃连接
3.标识连接默认保存信息
4.使得深层网络更容易被训练

假设有一个初始化网络和初始化权重层得到的小的随机值,如果他们很小并且接近0,然后你会有一个带噪音的确认函数(identity function)。因此,默认情况下,将通过所有层来保存信息。

### For example-Dense Connections
1.Dense connections(密集连接)aka “DenseNet”
2.把一切连接到一些。


### For example-Highway Connections
1.Highway connections(高速公路连接)aka “HighwayNet”
2.类似于残差连接,但是确认连接(identity connection)和转置层(transformation layer)由动态门控制(dynamic gate)。
3.这项工作由LSTM启发,但是应用于深度前馈网络和卷积神经网络。

### 结论
梯度消失和梯度爆炸是一个很常见的问题,由于重复使用相同的矩阵,RNN非常不稳定。

## Bidirectional RNNs(双向RNN)

用于情感分类中的RNN结构如下所示:


这个例子当中,terribly exciting这个词需要连在一起看,如果单独把 terribly 这个词取出来,那么有可能整个句子会被划归到negative的分类里。然而,terribly exceting表示的是令人非常兴奋的意思,所以必须了解正确的语义背景。

well,这就是双向RNN在实践中发挥的作用,双向RNN结构如下所示:

前向RNN(forward RNN)从左到右编码句子,后向RNN(Backward RNN)与前向RNN具有完全独立的权重,它做的也是同样的事情,从右到左编码序列,每个隐藏状态都是基于右边的一个隐藏状态计算出来的,然后,只要把两个隐藏状态的RNN连接在一起,就可以得到你的最后一种表示。

双向RNN在时间步长上的计算:


双向RNN的简化图表如下:

Q:前向RNN和反向RNN是分别训练的吗?
A:两个方向上的RNN一起训练更常见,这(似乎)是标准做法。

注:不能使用Bi-RNN做语言建模,因为语言建模仅允许访问左部的上下文。

Multi-layer RNN(多层RNN)

多层RNN允许了神经网络计算更复杂的表示:
低阶RNN可以计算低阶的特征,相应地,高阶RNN应该能计算更高阶的特征,比如语义特征。

三层RNN的工作结构图示:

上图所示是一个单项RNN,但它也可以是双向的,如果你可以访问整个输入序列,一个RNN层的隐藏状态主要作为下一个RNN层的输入。


Q:用什么顺序来计算这些隐藏状态?
A:计算顺序具有很强的灵活性,可以先计算所有RNN的第一层,然后是RNN的第二层,continue。但是,如果这个多层RNN是双向的,那就必须先完成第一层的计算,才能进入第二层。