基本概念
一般的神经网络(BP以及CNN)只对预先确定的大小起作用:它们接受固定大小的输入并产生固定大小的输出。它们的输出都是只考虑前一个输入的影响而不考虑其它时刻输入的影响, 比如简单的猫,狗,手写数字等单个物体的识别具有较好的效果。但是, 对于一些与时间先后有关的, 比如视频的下一时刻的预测,文档前后文内容的预测等, 这些算法的表现就不尽如人意了。
循环神经网络 (Recurrent Neural Network, RNN) 一般是指时间递归神经网络而非结构递归神经网络 (Recursive Neural Network),其主要用于对序列数据进行建模。它是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络。
RNN是一种特殊的神经网络结构, 它是根据人的认知是基于过往的经验和记忆这一观点提出的。它与CNN不同的是: 它不仅考虑前一时刻的输入,而且赋予了网络对前面的内容的一种记忆功能。
RNN之所以称为循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。
1. RNN
1.1 基本网络结构
不同于传统的前馈神经网络接受特定的输入得到输出,RNN 由人工神经元和一个或多个反馈循环构成,如下图左侧所示。其中,
x
t
x_t
xt 为输入层,
h
t
h_t
ht 为带有循环的隐含层,
y
t
y_t
yt 为输出层。其中隐含层包含一个循环,将循环进行展开,展开后的网络结构如下图右侧所示:
对于展开后的网络结构,其输入为一个时间序列
{
…
,
x
t
−
1
,
x
t
,
x
t
+
1
,
⋯
}
\{\dots,x_{t-1},x_t,x_{t+1}, \cdots \}
{…,xt−1,xt,xt+1,⋯},其中
x
t
∈
R
n
x_t\in\mathbf R^n
xt∈Rn,n 为输入层神经元个数。相应的隐含层为
{
…
,
h
t
−
1
,
h
t
,
h
t
+
1
,
⋯
}
\{\dots,h_{t-1},h_t,h_{t+1},\cdots\}
{…,ht−1,ht,ht+1,⋯},
h
t
∈
R
m
h_t\in\mathbf R^m
ht∈Rm,m 为隐含层神经元个数。隐含层节点使用较小的非零数据进行初始化可以提升整体的性能和网络的稳定性。隐含层定义了整个系统的状态空间 (state space),或称之为 memory ,
即 h t = f H ( o t ) \displaystyle h_t=f_H(o_t) ht=fH(ot), o t = W I H ⋅ x t + W H H ⋅ h t − 1 + b h \displaystyle o_t=W_{IH}\cdot x_t+W_{HH}\cdot h_{t-1}+b_h ot=WIH⋅xt+WHH⋅ht−1+bh,
其中 f H ( ⋅ ) f_H(\cdot) fH(⋅) 为隐含层的激活函数, b h b_h bh 为隐含层的偏置向量, W I H W_{IH} WIH 为输入神经元与当前节点的连接权重, W H H W_{HH} WHH为前一层神经元与当前节点的连接权重。对应的输出层为 { … , y t − 1 , y t , y t + 1 , ⋯ } \{\dots,y_{t-1},y_t,y_{t+1}, \cdots \} {…,yt−1,yt,yt+1,⋯},其中 y t ∈ R p y_t\in\mathbf R^p yt∈Rp,p 为输出层神经元个数。
则对应的输出为: y t = f o ( W H O ⋅ h t + b o ) \displaystyle y_t=f_o(W_{HO}\cdot h_t+b_o) yt=fo(WHO⋅ht+bo),
其中 W H O W_{HO} WHO为隐含层节点与输出节点的连接权重,其中 f o ( ⋅ ) f_o(\cdot) fo(⋅) 为输出层的激活函数, b o b_o bo 为输出层的偏置向量。
RNN中常用的激活函数为 t a n h ( x ) tanh(x) tanh(x) 函数。
1.2 RNN的其他结构
1.2.1 one-to-one
即最基本的单层网络:
1.2.2 one-to-many
这种结构可以进行:图像生成文字、从类别生成语音或音乐等操作。
有两种结构,第一种,只把 X 作为第一阶段的输入,其他阶段不输入:
第二种,把 X 作为每个阶段的输入:
1.2.3 many-to-many
经典的RNN结构,具体见 1.1描述。
1.2.4 many-to-one
要处理的问题输入是一个序列,输出是一个单独的值而不是序列。此时,只在最后一个 h 上进行变换输出。
1.3 RNN存在的问题
1.3.1 梯度消失 (Vanishing Gradients)和梯度爆炸(Exploding Gradients)
对RNN网络进行前向计算和反向传播,如下:
假设激活函数为
t
a
n
h
tanh
tanh,
t
a
n
h
tanh
tanh 函数及其导数的函数取值范围如下图所示:
因此,有:
1.3.2 短期记忆问题
RNN 隐藏节点以循环结构形成记忆,每一时刻的隐藏层的状态取决于它的过去状态,这种结构使得 RNN 可以保存、记住和处理长时期的过去复杂信号。
有的时候,我们仅需利用最近的信息来处理当前的任务。例如:考虑一个用于利用之前的文字预测后续文字的语言模型,如果我们想预测 “the clouds are in the sky” 中的最后一个词,我们不需要太远的上下信息,很显然这个词就应该是 sky。即 待预测位置与相关的信息之间的间隔较小,RNN 可以有效的利用过去的信息。
但当需要更多的上下文信息,或需要更早出现的文本信息时。考虑需要预测的文本为 “I grew up in France … I speak fluent French”。较近的信息表明待预测的位置应该是一种语言,但目的是确定具体是国家时,却很难做到。理论上 RNN 有能力处理这种长期依赖,但在实践中 RNN 却很难解决这个问题。
为了说明这个问题,先看看RNN是如何工作的,RNN具体工作流程如下:
可以发现,RNN中短期的记忆影响较大(如橙色区域),但是长期的记忆影响就很小(如黑色和绿色区域)。因此,RNN 有短期记忆问题,无法处理很长的输入序列,而训练 RNN 又需要投入极大的成本。
2. LSTM(长短时记忆网络,Long Short Term Memroy)
RNN 的逻辑是固定的,越晚的输入影响越大,越早的输入影响越小,且无法改变这个逻辑。LSTM 做的最大的改变就是打破了这个固定的逻辑,而改用了一套灵活了逻辑——只保留重要的信息。
2.1 网络结构
2.1.1 RNN和LSTM网络结构的对比
所有的循环神经网络都是由重复的模块构成的一个链条。在标准的 RNN 中,这个重复的模块的结构比较简单,仅包含一个激活函数为
t
a
n
h
tanh
tanh 的隐含层,如下图所示:
LSTM 也是类似的链条状结构,但其重复的模块的内部结构不同。模块内部并不是一个隐含层,而是四个,并且以一种特殊的方式进行交互,如下图所示:
图中符号含义为:每条线都包含一个从输出节点到其他节点的整个向量,粉红色的圆圈表示逐元素的操作,黄色的矩形为学习到的神经网络层,线条的合并表示连接,线条的分叉表示内容的复制并转移到不同位置。具体如下图所示:
2.1.2 LSTM 的单元状态
LSTM 的关键是单元的状态 (cell state),即下图中顶部水平穿过单元的直线。单元的状态像是一条传送带,其直接运行在整个链条上,同时仅包含少量的线性操作。因此,信息可以很容易得传递下去并保持不变。
2.1.3 LSTM 的门控机制
LSTM 具有向单元状态添加或删除信息的能力,这种能力被由一种称之为**“门” (gates)** 的结构所控制。门是一种可选择性的让信息通过的组件,其由一层以 Sigmoid 为激活函数的网络层和一个逐元素相乘操作构成的,如下图所示:
Sigmoid 层的输出值介于 0 和 1 之间,代表了所允许通过的数据量。0 表示不允许任何数据通过,1 表示允许所有数据通过。一个 LSTM 单元包含 3 个门用于控制单元的状态。
2.1.4 LSTM 的三个门
2.2 LSTM 工作流程
2.2.1 遗忘
第一步是要决定从单元状态中所忘记的信息,这一步是通过一个 “遗忘门 (forget gate)” 的 Sigmoid 网络层控制。该层以上一时刻隐含层的输出 h t − 1 h_{t-1} ht−1 和当前这个时刻的输入 x t x_t xt 作为输入,输出是一个介于 0 和 1 之间的值,1 代表全部保留,0 代表全部丢弃。
即在语言模型中,单元状态(cell)可能包含当前主题的种类,这样才能使用正确的名词。当看到一个新的主题的时候,会想要遗忘旧的主题的种类。
2.2.2 输入
第二步需要决定要在单元状态中存储什么样的新信息,这包含两个部分。第一部分为一个 “输入门 (input gate)” 的 Sigmoid 网络层,用来决定更新那些数据。第二部分为一个 Tanh 网络层,它将产生一个新的候选值向量 C t ~ \tilde{C_t} Ct~ 并用于添加到单元状态中。之后会将两者进行整合,并对单元状态进行更新。
在语言模型中,即将新的主题种类添加到单元状态中,并替代需要忘记的旧的主题种类。
2.2.3 单元状态更新
第三步需要将旧的单元状态
C
t
−
1
C_{t-1}
Ct−1 更新为
C
t
C_t
Ct。将旧的单元状态乘以
f
t
f_t
ft 以控制需要忘记多少之前旧的信息,再加上
i
t
∗
C
t
~
i_t*\tilde{C_t}
it∗Ct~ 用于控制单元状态的更新。
在语言模型中,该操作真正实现了对之前旧的主题种类的遗忘和对新信息的增加。
2.2.4 输出
最后一步,需要确定单元的输出,该输出将基于单元的状态,但为一个临时版本。首先利用一个 Sigmoid 网络层来确定单元状态的输出,然后对单元状态进行
t
a
n
h
tanh
tanh 操作 (将其值缩放到 -1 和 1 之间) 并与之前 Sigmoid 层的输出相乘,最终得到需要输出的信息。
在语言模型中,由于它仅关注一个主题,它可能会输出与一个动词相关的信息,以防后面还有其他的词。比如,它可能输出这个主题是单数还是复数,让我们知道如果后面还有东西,动词才会对应出现。
2.3 LSTM 的变种
2.3.1 “窥视孔连接 (peephole connections)”
由 Gers 和 Schmidhuber 提出的结构,添加了一种“窥视孔连接(peephole connections)”,这使得每一个门结构都能够窥视到单元的状态。
2.3.2 使用多个遗忘门和输入门
不同于一般的 LSTM 中分别确定需要遗忘和新添加的信息,成对的遗忘门和输入门仅在需要添加新输入是才会忘记部分信息,同理仅在需要忘记信息时才会添加新的输入,即同时做出决策。
2.3.3 GRU(循环门单元,Gated Recurrent Unit )
由 Cho出,组合了遗忘门和输入门到一个单独的 “更新门” 中。它也合并了cell state和hidden state,并且添加了一个 “重置门”。结果模型比标准LSTM模型更简单。
其执行过程为:
(1)首先计算更新门 z t z_t zt 和 重置门 r t r_t rt;
(2)其次计算候选隐含层 (candidate hidden layer) h t ~ \tilde{h_t} ht~,其中 r t r_t rt 用于控制保留多少之前的信息;
(3)最后计算需要从之前的隐含层 h t − 1 h_{t-1} ht−1 遗忘多少信息,同时加入多少新的信息 h t ~ \tilde{h_t} ht~, z t z_t zt 用于控制这个比例。
因此,对于短距离依赖的单元重置门的值较大,对于长距离依赖的单元更新门的值较大。如果 r t = 1 r_t=1 rt=1 并且 z t = 0 z_t=0 zt=0 ,则 GRU 退化为一个标准的 RNN。
2.4 LSTM和GRU的比较
(1)GRU 是 LSTM 的一个变体。他保留了 LSTM 记录重点,遗忘不重要信息的特点,在long-term 传播的时候也不会被丢失。
(2)GRU主要是在 LSTM 的模型上做了一些简化和调整(参数比LSTM少),在训练数据集比较大的情况下可以节省很多时间。
(3)LSTM比GRU严格来说更强,因为它可以很容易地进行无限计数,而GRU却不能。这就是GRU不能学习简单语言的原因,而这些语言是LSTM可以学习的。