在前馈神经网络(CNN也属于前馈神经网络)中,信息的传递是单向的,前馈神经网络可以看作是一个复杂的函数,每次输入都是独立的,即网络的输出只依赖于当前的输入,但是在很多现实任务中,网络的输入不仅和当前时刻的输入相关,也和其过去一段时间的输出相关(时序任务:文本、视频、语音)。前馈神经网络可以模拟任何函数,而循环神经网络可以模拟任何程序

循环神经网络是一个可以具有短期记忆力的神经网络,神经元不但能接受其它神经元的信息,也可以接受自身信息,形成具有环路的网络结构。通过随时间反向传播算法学习,即按照时间的逆序将错误信息一步步地往前传递。

给网络增加记忆能力

为了处理时序任务,我们必须让网络具有短期记忆能力,一般可通过以下三种方式增加记忆能力。

延时神经网络

建立一个额外的延时单元,用来存储历史信息。延时神经网络就是在前馈网络的非输出层都建立一个延时单元,存储最近几次该神经元的输出。在t时刻,第l+1层神经元和第l层的神经元最近p次输出相关:
循环神经网络RNN:Recurrent Neural Network
有外部输入的非线性自相关模型

自相关模型就是以历史变量y来预测自己,其中w为参数,p为超参数:
循环神经网络RNN:Recurrent Neural Network
有外部输入的非线性自相关模型(Nonlinear AutoRegressive with Exogenous Inputs Model,NARX)是自相关模型的拓展,在每个时刻t都有一个外部输入xt,产生一个输出yt。NARX通过一个延时器记录最近几次的外部输入和输出,在t时刻的输出为:循环神经网络RNN:Recurrent Neural Network
f为非线性函数,可以为一个前馈神经网络,p.q为超参数。

循环神经网络

循环神经网络通过使用带自反馈的神经元,能够处理任何长度的时序数据。给定一个序列:x1:T = (x1, x2, . . . , xt, . . . , xT ),RNN中带反馈边隐藏层的活性值ht:
循环神经网络RNN:Recurrent Neural Network
其中h0=0,f(·)为一个非线性函数,也可以为一个前馈神经网络。
循环神经网络RNN:Recurrent Neural Network
循环神经网络可以看成在时间维度上权值共享的神经网络:
循环神经网络RNN:Recurrent Neural Network
Elman Network vs. Jordan Network:

  • Elman Network:将hidden layer的输出保存在memory里
  • Jordan Network:将整个neural network的输出保存在memory里(效果略好
    循环神经网络RNN:Recurrent Neural Network

应用到机器学习

序列到类别模式

序列到类别模式主要用于序列数据的分类问题:输入为序列,输出为类别。比如在文本分类中,输入数据为单词的序列,输出为该文本的类别。(以及:文档的关键字提取)
循环神经网络RNN:Recurrent Neural Network
假设输入为x1:T = (x1, · · · , xT ),输出为y ∈ {1, · · · ,C},可以送入到循环神经网络中,得到不同时刻的隐藏状态h1,h2,…hT,如图(a)所示,将hT作为整个序列的最终表示(特征)送入分类器g(·)进行分类,g(·)可以为简单的线性分类器,也可以为一个前馈神经网络:
循环神经网络RNN:Recurrent Neural Network
除了将最后时刻的状态作为序列表示之外,我们还可以对整个序列的所有状态进行平均,并用这个平均状态来作为整个序列的表示,如图(b)所示,式子如下:
循环神经网络RNN:Recurrent Neural Network

同步的序列到序列模式

同步的序列到序列模式主要用于序列标注(Sequence Labeling)任务,即每一时刻都有输入和输出,输入序列和输出序列的长度相同。比如词性标注(Part-of-Speech Tagging)中,每一个单词都需要标注其对应的词性标签。
循环神经网络RNN:Recurrent Neural Network
输入为x1:T = (x1, · · · , xT ),输出为序列y1:T = (y1, · · · , yT )。样本x按不同时刻输入到循环神经网络中,并得到不同时刻的隐状态h1, · · · ,hT 。如上图所示,每个时刻的隐状态ht代表了当前时刻和历史的信息,并输入给分类器g(·) 得到当前时刻的标签ˆy:
循环神经网络RNN:Recurrent Neural Network

异步的序列到序列模式

异步的序列到序列模式也称为编码器-解码器(Encoder-Decoder)模型,即输入序列和输出序列不需要有严格的对应关系,也不需要保持相同的长度。比如在机器翻译中,输入为源语言的单词序列,输出为目标语言的单词序列。(以及:语音识别)
循环神经网络RNN:Recurrent Neural Network
输入x1:T = (x1, · · · , xT ),输出为y1:M = (y1, · · · , yM),异步的序列到序列模式一般通过先编码后解码的方式来实现。如上图所示(< EOS >表示输入序列结束),先将样本x按不同时刻输入到一个循环神经网络(编码器)中,并得到其编码hT 。然后再使用另一个循环神经网络(解码器),得到输出序列ˆy1:M。为了建立输出序列之间的依赖关系,在解码器中通常使用非线性自回归模型

循环神经网络RNN:Recurrent Neural Network
其中f1(·) 和f2(·)分别为用作编码器和解码器的循环神经网络,g(·) 为分类器,ˆyt为预测输出ˆyt 的向量表示。

参数学习

循环神经网络RNN:Recurrent Neural Network
不失一般性,以同步序列到序列模式来介绍RNN的参数学习,由于每个时刻t都偶一个监督信息yt,我们定义此刻的损失函数为:
循环神经网络RNN:Recurrent Neural Network
其中g(ht)为t时刻的输出,L为可微分的损失函数,整个序列的损失函数L关于参数U的梯度为:
循环神经网络RNN:Recurrent Neural Network
循环神经网络中存在一个递归调用的函数f(·),因此其计算参数梯度的方式和前馈神经网络不太相同。在循环神经网络中主要有两种计算梯度的方式:随时间反向传播(BPTT)算法实时循环学习(RTRL)算法
循环神经网络RNN:Recurrent Neural Network

时间反向传播算法

BPTT算法将循环神经网络看作是一个展开的多层前馈网络,其中“每一层”对应循环网络中的“每个时刻”(见下图)。这样循环神经网络就可以按照前馈网络中的反向传播算法计算参数梯度:
循环神经网络RNN:Recurrent Neural Network
循环神经网络RNN:Recurrent Neural Network
其中,
循环神经网络RNN:Recurrent Neural Network
zk = Uhk−1 +Wxk + b

实时循环学习算法

与反向传播的BPTT算法不同的是,实时循环学习(Real-Time Recurrent Learning,RTRL)是通过前向传播的方式来计算梯度。

假设第t个时刻存在一个监督信息,其损失函数为Lt,就可以同时计算损失函数对uij的偏导数,这样在第t时刻,可以实时地计算损失Lt关于参数U的梯度,并更新参数。参数W和b的梯度也可以同样按上述方法实时计算
循环神经网络RNN:Recurrent Neural Network

算法比较

RTRL算法和BPTT算法都是基于梯度下降的算法,分别通过前向模式和反向模式应用链式法则来计算梯度。

在循环神经网络中,一般网络输出维度远低于输入维度,因此BPTT算法的计算量会更小,但是BPTT算法需要保存所有时刻的中间梯度空间复杂度较高

RTRL算法不需要梯度回传,因此非常适合用于需要在线学习无限序列的任务中。

Bi-RNN

在有些任务中,一个时刻的输出不但和过去时刻的信息有关,也和后续时刻的信息有关。比如给定一个句子,其中一个词的词性由它的上下文决定,即包含左右两边的信息。因此,在这些任务中,我们可以增加一个按照时间的逆序来传递信息的网络层,来增强网络的能力。
循环神经网络RNN:Recurrent Neural Network
RNN在产生yt的时候,这就相当于在看了整个句子。

LSTM

上面提到的RNN只是最简单的版本,并没有对memory的管理多加约束,可以随时进行读取,但现在RNN最常用的memory管理方式叫做长短期记忆(Long Short-term Memory),简称LSTM。

Long-term dependencies

但现实生活中有很多情况,只根据最近的信息是无法进行预测,还要结合更早输入的信息。比如输入的序列是“I grew up in France… I speak fluent ”,我希望预测出“French”,即“I grew up in France… I speak fluent French”。由于France和French之间有很多其他的句子,普通的RNN无法处理这个问题(后面会提到,是由于RNN在训练时出现的梯度消失/梯度爆炸问题

Three gate

在LSTM中,有三个门控机制(Gate):

  • input gate:决定是否将某个神经元输出写进memory cell,如果input gate关闭,则无法被写入
  • output gate:决定了外界是否可以从memory cell中读取值,当output gate关闭的时候,memory里面的内容同样无法被读取
  • forget gate:决定了什么时候需要把memory cell里存放的内容清空
    循环神经网络RNN:Recurrent Neural Network
    LSTM可以视为4个input,1个output。其中对与Zi和Zf、Zo来说,**函数f(·)一般会选sigmoid function,因为它的输出在0~1之间,代表gate被打开的程度
    循环神经网络RNN:Recurrent Neural Network

LSTM for RNN

上面介绍的是一个单独的LSTM模块,那么它与RNN有什么联系呢?其实,LSTM就是作为RNN的一个神经元,门控是由神经元的输入分别乘上矩阵得到的,这些矩阵参数都是可学习的。
循环神经网络RNN:Recurrent Neural Network
下图是单个LSTM的运算情景,每个LSTM的cell所得到的input都是各不相同的,但它们却是可以一起共同运算的,整个运算流程如下图左侧所示:
循环神经网络RNN:Recurrent Neural Network
上述的过程反复进行下去,就得到下图中各个时间点的LSTM值的变化情况,其中与上面的描述略有不同的是,这里还需要把hidden layer的输出ht以及当前cell的值ct连接到下一个时间点的输入上。
循环神经网络RNN:Recurrent Neural Network
上面是单层的LSTM,实际上不可能只有一个hidden layer,往往会叠很多层,下图就是一个两层叠加的LSTM。
循环神经网络RNN:Recurrent Neural Network
目前,使用LSTM的RNN就是标准的RNN(除LSTM之外,还有Simple RNN、GRU)。

Problem of Training

基于RNN的模型往往是很难训练的,如下图所示,模型的error surface是很崎岖的,要么很平坦要么很陡峭,甚至会导致训练的loss无法收敛。
循环神经网络RNN:Recurrent Neural Network
无法收敛的问题可以通过一个很简单的手段解决,就是clipping,例如当前的gradient>>15,但我只把它等效为15,这样在陡峭的地方,梯度就会比较理想地下降,而不是直接飞出去(上图中蓝色虚线部分)。

有人认为是RNN用了Sigmoid function导致的训练困难,实际上与activation function无关,因为有人将Sigmoid替换为ReLU之后,效果更差。

实际上,这是由于普通RNN在训练时,会出现梯度消失/梯度爆炸的现象,从下面这张图的简单例子可以看出,由于时间维度上w被循环利用,y1000的梯度对于t1时刻的w来说经过了太多层,这样很容易出现梯度爆炸/消失,导致学习率很难控制。
循环神经网络RNN:Recurrent Neural Network
P.S 由于RNN 中同样的权重在各个时间上共享,最终的梯度 g = 各个时间步的梯度 g_t 的和,RNN中的梯度消失并不是真正地消失,RNN 所谓梯度消失的真正含义是,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系

LSTM for handle gradient vanishing

使用LSTM可以缓解这个问题,因为它可以解决掉梯度消失的问题(但无法解决梯度爆炸:这可以通过cliping来解决)
循环神经网络RNN:Recurrent Neural Network
循环神经网络RNN:Recurrent Neural Network
(由于上面两个公式不能直接从笔记里copy过来,所以直接用的图片了)

  • 但是在其他路径上,LSTM 的梯度流和普通 RNN 没有太大区别,依然会爆炸或者消失。由于总的远距离梯度 = 各条路径的远距离梯度之和,即便其他远距离路径梯度消失了,只要保证有一条远距离路径(就是上面说的那条高速公路)梯度不消失,总的远距离梯度就不会消失(正常梯度 + 消失梯度 = 正常梯度)。因此 LSTM 通过改善一条路径上的梯度问题拯救了总体的远距离梯度
  • 同样,因为总的远距离梯度 = 各条路径的远距离梯度之和,高速公路上梯度流比较稳定,但其他路径上梯度有可能爆炸,此时总的远距离梯度 = 正常梯度 + 爆炸梯度 = 爆炸梯度,因此 LSTM 仍然有可能发生梯度爆炸。不过,由于 LSTM 的其他路径非常崎岖,和普通 RNN 相比多经过了很多次**函数(导数都小于 1),因此 LSTM 发生梯度爆炸的频率要低得多。实践中梯度爆炸一般通过梯度裁剪来解决。
  • 对于现在常用的带遗忘门的 LSTM 来说,上述的分析依然成立,分为两种情况:其一是遗忘门接近 1(例如模型初始化时会把 forget bias 设置成较大的正数,让遗忘门饱和),这时候远距离梯度不消失;其二是遗忘门接近 0,但这时模型是故意阻断梯度流的,这不是 bug 而是 feature(例如情感分析任务中有一条样本 “A,但是 B”,模型读到“但是”后选择把遗忘门设置成 0,遗忘掉内容 A,这是合理的)。当然,常常也存在 f 介于 [0, 1] 之间的情况,在这种情况下只能说 LSTM 改善(而非解决)了梯度消失的状况。

GRU

Gated Recurrent Unit(GRU):比LSTM更简单(参数量变少,不容易overfitting),只有2个gate,即forget gateinput gate,这两者有一个联动

  • 当input gate打开时,forget gate关闭,forget掉menory里的值
  • 当forget gate打开时,记住memory cell里的值,input gate关闭,不再进行输入。

参考

[1] https://www.zhihu.com/question/34878706/answer/665429718 知乎:LSTM如何来避免梯度弥散和梯度爆炸? 作者:Towser

[2] https://www.bilibili.com/video/BV1JE411g7XF?p=21 李宏毅:深度学习-RNN

[3] https://nndl.github.io/ 邱锡鹏:《神经网络与深度学习》