四、常见 RNN 变种
4.1 双向 RNN
前面介绍的
RNN
网络隐含了一个假设:时刻 的状态只能由过去的输入序列 ,以及当前的输入 来决定。实际应用中,网络输出 可能依赖于整个输入序列。如:语音识别任务中,当前语音对应的单词不仅取决于前面的单词,也取决于后面的单词。因为词与词之间存在语义依赖。
双向
RNN
就是为了解决这种双向依赖问题,它在需要双向信息的应用中非常成功。如:手写识别、语音识别等。典型的双向
RNN
具有两条子RNN
:代表通过时间向未来移动的子
RNN
的状态,向右传播信息; 代表通过时间向过去移动的子RNN
的状态,向左传播信息。时刻的输出 同时依赖于过去、未来、以及时刻 的输入 。
模型的数学表示:
单个样本的损失:
更新方程:
其中输入到隐状态的权重为 ,隐状态到输出的权重为 ,隐状态到隐状态的权重为 , 为输入偏置向量和输出偏置向量。
如果输入是 2 维的(如图像),则双向
RNN
可以扩展到4个方向:上、下、左、右。每个子
RNN
负责一个时间移动方向, 时刻的输出 同时依赖于四个方向、以及时刻 的输入 。与
CNN
相比:RNN
可以捕捉到大多数局部信息,还可以捕捉到依赖于远处的信息;CNN
只能捕捉到卷积窗所在的局部信息。RNN
计算成本通常更高,而CNN
的计算成本较低。
4.2 深度 RNN
前述
RNN
中的计算都可以分解为三种变换:从输入 到隐状态 的变换、从前一个隐状态 到下一个隐状态 的变换、从隐状态 到输出 的变换。这三个变换都是浅层的,即:由一个仿射变换加一个激活函数组成。事实上,可以对这三种变换中引入深度。实验表明:引入深度会带来好处。
- 方式一:通过将
RNN
的隐状态分为多层来引入深度。 - 方式二:在这三种变换中,各自使用一个独立的
MLP
(可能是较浅的,也可能是较深的)。 - 方式三:在第二种方式的基础上,类似
ResNet
的思想,在 “隐状态-隐状态” 的路径中引入跳跃连接。
- 方式一:通过将
通过将
RNN
的隐状态分为多层来引入深度:如下所示,隐状态有两层: 和 。隐状态层中层次越高,对输入提取的概念越抽象。模型的数学表示:
单个样本的损失:
更新方程:
其中输入到隐状态的权重为 ,隐状态到输出的权重为 ,隐状态到隐状态的权重为 , 为输入偏置向量和输出偏置向量。
在这三种变换中,各自使用一个独立的
MLP
(可能是深度的),如下图所示。该方法有一个主要问题:额外深度将导致从时间步 到时间步 的最短路径变得更长,这可能导致优化困难而破坏学习效果。
在第二种方式的基础上,类似
ResNet
的思想,在 “隐状态-隐状态” 的路径中引入跳跃连接,从而缓解最短路径变得更长的问题。
4.3 LSTM 和 GRU
目前实际应用中最有效的序列模型是门控
RNN
,包括基于LSTM: long short-term memory
的循环网络,和基于门控循环单元GRU: gated recurrent unit
的循环网络。围绕门控
RNN
这一主题可以设计更多的变种。然而一些调查发现:这些LSTM
和GRU
架构的变种,在广泛的任务中难以明显的同时击败这两个原始架构。门控
RNN
的思路和渗漏单元一样:生成通过时间的快捷路径,使得梯度既不消失也不爆炸。- 可以手动选择常量的连接权重来实现这个目的,如跳跃连接。权重为固定的常量,且不随时间改变。
- 可以使用参数化的连接权重来实现这个目的,如渗漏单元。权重是样本的函数,且不随时间改变。
- 门控
RNN
将其推广为:连接权重在每个时间步都可能改变。权重是样本和时间的函数,随时间改变。
渗漏单元允许网络在较长持续时间内积累信息,但它有个缺点:有时候希望一旦某个信息被使用(即:被消费掉了),那么这个信息就要被遗忘(丢掉它,使得它不再继续传递)。
门控
RNN
能够学会何时清除信息,而不需要手动决定。
4.3.1 LSTM
LSTM
在手写识别、语音识别、机器翻译、为图像生成标题等领域获得重大成功。LSTM
循环网络除了外部的RNN
循环之外,还有内部的LSTM cell
循环(自环)。LSTM
的cell
代替了普通RNN
的隐单元,而LSTM
的 是cell
的一个输出。LSTM
引入cell
循环以保持梯度长时间持续流动。其中一个关键是:cell
循环的权重视上下文而定,而不是固定的。具体做法是:通过
gate
来控制这个cell
循环的权重,而这个gate
由上下文决定。- 注意:
cell
输出是 ,而不是整个RNN
单元的输出 。 cell
之间的连接是通过 来连接的。
- 注意:
LSTM
最重要的就是cell
状态 ,它以水平线在图上方贯穿运行。sigmoid
函数 () 的输出在0
到1
之间,描述每个部分有多少量可以通过。它起到门gate
的作用:0
表示不允许通过,1
表示允许全部通过,0~1
之间表示部分通过。LSTM
拥有三个门:遗忘门、输入门、输出门。遗忘门:控制了
cell
上一个状态 中,有多少信息进入当前状态 。与渗漏单元类似,
LSTM cell
也有线性自环。遗忘门 控制了自环的权重,而不再是常数 :写成向量形式为:( 为逐元素的
sigmoid
函数)其中: 为遗忘门的偏置, 为遗忘门的输入权重, 为遗忘门的循环权重。
输入门:控制了输入 中,有多少信息进入
cell
当前状态 。输入门 的方程:
写成向量的形式为:( 为逐元素的
sigmoid
函数)其中: 为输入门的偏置, 为输入门的输入权重, 为输入门的循环权重。
图中的 就是
输出门:控制了
cell
状态 中,有多少会进入cell
的输出 。输出门 的更新方程:
写成向量的形式: ( 为逐元素的
sigmoid
函数)其中: 为输出门的偏置, 为输出门的输入权重, 为输出门的循环权重。
cell
状态更新:cell
状态 由两部分组成:- 一部分来自于上一次的状态 :它经过了遗忘门 的控制,使得只有部分状态进入下一次。
- 一部分来自于输入(包括 ):输入需要经过 非线性层变换之后,然后经过输入门 的控制,使得只有部分输入能进入状态更新。
因此
cell
状态更新方程为:写成向量的形式为: ( 为逐元素的函数, 为逐元素的向量乘积)
其中: 为
cell
的偏置, 为cell
的输入权重, 为cell
的循环权重。cell
输出更新:cell
输出就是 ,它是将cell
状态经过了 非线性层之后,再通过输出门 控制输出的流量。写成向量的形式: ( 为逐元素的函数, 为逐元素的向量乘积)
一旦得到了
cell
的输出 ,则获取整个RNN
单元的输出 就和普通的RNN
相同。令节点 ,根据激活函数的性质: ,,则有:
考虑到 的后续节点为:当 为最后一个节点时,后续节点为 ;当 不是最后一个节点时,后续节点为 。因此有:
考虑到:
因此有:
- 由于 中存在常量部分 ,因此
LSTM
可以缓解梯度消失。 - 由于各种门的存在,因此 中的非常量部分会被缩小,因此可以缓解梯度爆炸。
- 由于 中存在常量部分 ,因此
考虑到 的后续节点为:当 为最后一个节点时,后续节点为 ;当 不是最后一个节点时,后续节点为 。因此有:
考虑到 对于每个输出 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个状态 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个遗忘门 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个输入门 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个输出门 都有贡献,则有:
其中 表示 的第 个分量。
也可以选择使用
cell
状态 作为这些门的额外输入。此时 就多了额外的权重参数,这些参数对应于 的权重和偏置。
4.3.2 GRU
门控循环单元
GRU
比LSTM
模型更简单:GRU
的单个门控单元同时作为遗忘门和输入门,整个GRU
模型只有两个门:更新门、复位门。GRU
不再区分cell
的状态 和cell
的输出 。
更新门:控制了新的信息 ( 由 生成)、旧的信息 中各有多少信息进入了 。
更新门 的更新方程:
写成向量的形式为:( 为逐元素的
sigmoid
函数)其中: 为更新门的偏置, 为更新门的输入权重, 为更新门的循环权重。
复位门:控制了新的信息 中, 之间的比例。它表示在新的信息中,旧的信息多大程度上影响新的信息。如果 ,则旧的信息不影响新的信息,可以理解为复位。
复位门 的更新方程:
写成向量的形式为:( 为逐元素的
sigmoid
函数)其中: 为复位门的偏置, 为复位门的输入权重, 为复位门的循环权重。
cell
输出:cell
输出就是 。cell
更新方程:写成向量的形式:(其中 为逐元素的向量乘积; 为逐元素的函数)
令 ,它刻画了本次的更新。于是
cell
的输出更新方程为:其中: 为
cell
的偏置, 为cell
的输入权重, 为cell
的循环权重。一旦得到了
cell
的输出 ,则获取整个RNN
单元的输出 就和普通的RNN
相同。令节点 ,根据激活函数的性质: ,,则有:
考虑到 的后续节点为:当 为最后一个节点时,后续节点为 ;当 不是最后一个节点时,后续节点为 。记 ,因此有:
考虑到:
因此有:
- 由于 中存在常量部分 ,因此
GRU
可以缓解梯度消失。 - 由于各种门的存在,因此 中的非常量部分会被缩小,因此可以缓解梯度爆炸。
- 由于 中存在常量部分 ,因此
考虑到 对于每个输出 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个状态 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个复位门 都有贡献,则有:
其中 表示 的第 个分量。
考虑到 对于每个更新门 都有贡献,则有:
其中 表示 的第 个分量。
4.3.3 讨论
在
LSTM
与GRU
中有两种非线性函数:sigmoid
与tanh
。sigmoid
用于各种门,是因为它的阈值为0~1
,可以很好的模拟开关的关闭程度。tanh
用于激活函数,是因为它的阈值为 -1~1,它的梯度的阈值为 0~1。如果使用
sigmoid
作为激活函数,则其梯度范围为 0~0.5,容易发生梯度消失。如果使用
relu
作为激活函数,则前向传播时,信息容易爆炸性增长。另外
relu
激活函数也会使得输出只有大于等于0
的部分。
前面给出的
LSTM
和GRU
中, 是通过feature map
直接相加,如LSTM
中的状态更新方程:事实上,也可以通过
feature map
进行拼接,如:其中 表示将两个向量进行拼接。
4.4 编码-解码架构
前面介绍的多长度输入序列的模式中,输出序列和输入序列长度相同。实际任务中,如:语音识别、机器翻译、知识问答等任务,输出序列和输入序列长度不相等。
编码-解码
架构就是为了解决这类问题。设输入序列为 ,输出序列为 。长度 。设
C
为输入的一个表达representation
,包含了输入序列的有效信息。- 它可能是一个向量,也可能是一个固定长度的向量序列。
- 如果
C
是一个向量序列,则它和输入序列的区别在于:序列C
是定长的、较短的;而输入序列是不定长的、较长的。
整个
编码-解码
结构分为:编码器,解码器。编码器(也叫作读取器,或者输入
RNN
):处理输入序列。编码器的最后一个状态 通常就是输入序列的表达
C
, 并且作为解码器的输入向量。解码器(也叫作写入器,或者输出
RNN
):处理输入的表达C
。解码器有三种处理
C
的方式:输入C
作为每个时间步的输入、输入C
作为初始状态 且每个时间步没有额外的输入、结合上述两种方式。训练时,编码器和解码器并不是单独训练,而是共同训练以最大化 :
编码-解码架构中:
- 输入序列长度 和输出序列长度 可以不同。
- 对于编码器与解码器隐状态是否具有相同尺寸并没有限制,它们是相互独立设置的。
编码-解码架构的主要缺点:编码器
RNN
输出的上下文C
的维度太小,难以恰当的概括一个长的输入序列的完整信息。可以通过引入
attention
机制来缓解该问题。
4.5 attention
attention
是一种提升encoder - decoder
模型效果的机制,一般称作attention mechanism
。attention
被广泛用于机器翻译、语音识别、图像标注Image Caption
等领域。如:机器翻译中,为句子中的每个词赋予不同的权重。attention
本身可以理解为一种对齐关系,给出了模型输入、输出之间的对齐关系,解释了模型到底学到了什么知识。在机器翻译中,解释了输入序列的不同位置对输出序列的影响程度。如下图所示为机器翻译中,输入-输出的
attention
矩阵。在图像标注中,解释了图片不同区域对输出文本序列的影响程度。如下图所示为图像标注中,影响输出单词的图像块。
设输入序列为 ,输出序列为 ,长度 。设
encoder
的隐向量为 ,decoder
的隐向量为 。对于传统的
encoder-decoder
模型,decoder
的输入只有一个向量,该向量就是输入序列经过encoder
编码的上下文向量 。通常将
encoder
的最后一个隐单元的隐向量 作为上下文向量。对于
attention encoder-decoder
模型,decoder
的输入是一个向量序列,序列长度为 。decoder
位置 的输入是采用了attention
机制的上下文向量 ,不同位置的上下文向量不同。上下文向量 由
encoder
的所有隐向量加权得到: 。其中 。
权重 刻画了:在对第 个输出进行解码时,第 个输入的重要程度。
一个直觉上的方法是:首先计算 与 的相关性,然后对所有的 归一化即可得到权重系数。即:
其中
score
由多种计算方式,不同的计算方式代表不同的attention
模型( 为待学习的参数,n
为向量的维度):
4.5.1 local attention
上述的
attention
机制中为了计算上下文向量 , 需要考虑encoder
的所有隐向量。当输入序列较长时(如一段话或一篇文章),计算效率较低。local attention
在计算上下文向量 时只需要考虑encoder
的部分隐向量:首选预测encoder
端对齐的位置 ,然后基于位置 选择一个窗口来计算上下文向量 。其中 为待学习的参数, 为人工指定的固定常量。
虽然
local attention
可以提高计算效率,但是会带来两个问题:- 当
encoder
的输入序列长度 并不是很长时,计算量并没有显著减小。 - 位置 的预测并不是非常准确,这就直接影响了计算
attention
的准确性。
`
- 当
4.5.2 self attention
传统的
attention
是基于encoder
端和decoder
端的隐向量来计算attention
的,得到的是输入序列的每个input
和输出序列的每个output
之间的依赖关系。self attention
计算三种attention
:- 在
encoder
端计算自身的attention
,捕捉input
之间的依赖关系。 - 在
decoder
端计算自身的attention
,捕捉output
之间的依赖关系。 - 将
encoder
端得到的self attention
加入到decoder
端得到的attention
中,捕捉输入序列的每个input
和输出序列的每个output
之间的依赖关系。
- 在
设输入序列为 ,输出序列为 ,长度 。
encoder
端的self attention
:首先经过 映射得到
key
向量序列 、经过 映射得到query
向量序列 。然后计算归一化的
self attention
:其中 表示各输入与第 个输入的相关因子,。
最后得到经过加权的
encoder
输出向量: 。
decoder
端的self attention
:考虑到解码时 时刻不知道 时刻及其之后的结果,因此仅考虑 时刻之前的attention
,这被称作masked attention
。因此对于时刻 :首先经过 映射得到
key
向量序列 、经过 映射得到query
向量序列 。然后计算归一化的
self attention
:其中 表示各输入与第 个输入的相关因子,。
最后得到经过加权的
encoder
输出向量: 。
encoder
和decoder
的attention
:计算归一化的
self attention
:其中 表示各输入与第 个输出的相关因子, 。
最后得到经过加权的
attention
上下文向量: 。
最后将上下文向量 作为一个前馈神经网络的输入,其输出就是 。
上述
self attention
机制完全抛弃了RNN
的架构,著名的Transformer
架构就是基于它来构建的。self attention
未能考虑到输入序列的先后顺序,因此Transformer
架构中引入了位置embedding
来解决该问题。理论上序列的 的
self attention
只需要简单计算:引入两个映射矩阵是为了更好的泛化:
attention
并不仅仅考虑序列的原始信息,而是考虑了从序列中抽取的信息。
4.5.3 Hierarchical attention
在论文
《Hierarchical Attention Networks for Document Classification》
中提出了分层attention
用于文档分类。论文提出了两个层次的attention
:- 第一个层次是对句子中每个词进行
attention
,即word attention
。 - 第二个层次是对文档中每个句子进行
attention
,即sentence attention
。
- 第一个层次是对句子中每个词进行
层次
attention
涉及到四个部分:word encoder
:(对于句子 )word embedding
: 。GRU
隐向量(原始论文中采用双向GRU
):
word attention
:其中 表示这个单词序列的总信息,称作单词上下文。它是随机初始化并从网络训练得到。
sentence encoder
:sentence attention
:其中 表示这个句子序列的总信息,称作句子上下文。它是随机初始化并从网络训练得到。