众所周知,要介绍Transformers,就得介绍Attention,要介绍Attention,就得介绍RNN,所以,先从RNN开始吧。

RNN

Recurrent neural network (RNN) 是经典人工智能神经网络之一,常用于处理时序数据相关任务,如NLP或者股票数据等。全连接网络(MLP)在处理这些任务上存在一些问题:

  • 对于不同的输入样本,输入和输出可能有不同的长度,输入层和输出层的神经元数量无法固定;
  • 从输入文本的不同位置学到的同一特征无法共享;
  • 模型中的参数太多,计算量太大。

RNN对上述问题是一个相对理想的解决方案,它扫描数据输入的方式,使得每一个时间步对所有网络参数是共享的,且每个时间步不仅会接收当前时刻的输入,同时会接收上一个时刻的输出,从而可以利用过去输入的信息来辅助当前时刻的判断。

RNN网络的结构图如下所示,借用吴恩达老师的deep learning图片:

rnn

其中可见两个输入和两个输出,输入$x^{}$表示当前时刻的输入样本,$a^{}$表示上一个节点激活函数的输出;而输出$\hat y^{}$是当前时刻的输出,$a^{}$则是下一个时间步的输入。因此,RNN实际上在t时刻得到的结果是基于前t个输入的,最原始的RNN是单向的,只能够利用前面的信息,但是有时候单向的RNN很难解决问题,因此衍生了双向RNN。

BPTT

bptt

back-propagation through time(BPTT)算法是常用的训练RNN的方法,本质还是BP算法,由于RNN处理时间序列数据,所以基于时间反向传播。BPTT的中心思想和BP算法相同,沿着需要优化的参数的负梯度方向不断寻找更优的点直至收敛。

截断BPTT是完全BPTT的近似,这是长序列的首选,因为完整BPTT的每个参数更新的前向/后向代价在许多时间步骤中变得非常高。缺点是,由于截断,梯度只能返回到目前为止,因此网络无法学习与完整BPTT一样长的依赖关系。

RNN存在的问题

RNN虽然在处理时序数据上有一定优势,但其依然存在一些问题:

  1. RNN数据处理方式难以并行化,训练速度慢;
  2. 在数据较长的情况下,RNN容易出现梯度消失或梯度爆炸的问题。

对于梯度问题的处理,首先需要明确一个问题,RNN和DNN的梯度消失和爆炸的意义并不完全相同。DNN的梯度爆炸消失的本质是由于梯度反向传播造成的连乘效应,当网络太深,网络权值更新不稳定时造成;而RNN的特殊性在于,它的权重是共享的,当距离长了,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系。

那么要解决这个问题,有一种简单但是实用的方案就是梯度裁剪:若梯度大于某个阈值或者小于阈值的负数,就用直接让它等于阈值。另一种方案就是RNN的改进型LSTM和GRU,如下图所示:

img

LSTM

Long Short Term Memory(LSTM)网络,顾名思义就是为了解决RNN的长序列依赖问题,它设计了三个门结构来提供了信息选择性传输的能力。

结构

LSTM的整体结构如下图所示,在每个时间步接收上个时间步的输入有两个,传给下一个时间步的输出也有两个。${x_t}$表示本次输入,$C_{t-1}$表示上一个单元模块的长期记忆输出,$h_{t-1}$表示上一个单元的隐藏状态输出,$C_t$表示本次的记忆输出,$h_t$表示本次的隐藏状态输出:

1

下面会对这几个门结构进行拆分和展示。

遗忘门:有两个输入,当前时间步的输入${x_t}$以及上一层输出的隐藏状态$h_{t-1}$,遗忘门通过这两个输入训练出一个门函数,注意这个门函数的输出是在(0,1)之间的,可以有效的用于判断是保留还是遗忘信息(接近1的值表示保留,接近0的值表示遗忘),将其与上一层输出的全局信息$C_{t-1}$相乘,表示全局信息被选择部分遗忘。

img

输入门:输入同遗忘门,将接收到的$h_{t-1}$和${x_t}$送入激活函数为tanh的小型神经网络,这一部分与传统RNN无异,就是将上一时刻得到的信息与该时刻得到的信息进行整合。将整合信息与门函数的输出相乘,相当于同样选择有保留的提取新信息,并将其直接加在全局信息中去。img

下面就是本轮时间步过后,记忆信息$C_t$输出。

img

输出门:将新的记忆信息$C_t$通过tanh函数后与sigmoid函数的输出相乘,则可以得到该时刻全局信息对下一个cell影响的隐藏状态$h_t$。

img

LSTM存在的问题

虽然LSTM的三个门结构已经提供了一定程度的历史信息保持能力,但是通过长时记忆单元,类似残差链接。但后来加了遗忘门,遗忘门介于0-1,梯度仍有可能消失;梯度仍可能爆炸,但LSTM机制复杂,多了一层激活函数sigmoid,可以通过正则与裁剪解决。

同时,LSTM并没有改变输入方式,时序性的结构依然很难具备高效的并行计算能力,当前状态的计算不仅要依赖当前的输入,还要依赖上一个状态的输出,另一方面,RNN模型总体上更类似于一个马尔可夫决策过程,较难以提取全局信息。

GRU

GRU可以看作一个LSTM的简化版本,将$C_t$与$h_t$整合在一起,并将遗忘门和输入门整合为更新门,输出门变为重置门,一定程度上降低了复杂度,收敛速度有所提升。结构如下图所示:

img

CNN

CNN同样适用于NLP任务中的特征提取,非常类似于基于N-gram的局部编码,但其使用场景与RNN略有不同。

  • 使用残差网络,解决梯度消失问题,解决梯度消失问题的本质是能够加速信息流动,使简单的信息传输可以有更简单的路径,从而使得网络做深的同时,能够保证良好的性能。
  • CNN提取长距离特征的能力收到其卷积核感受野的限制,实验证明,增大卷积核的尺寸,增加网络深度,可以增加CNN的长距离特征捕获能力。
  • 为了防止文本中的位置信息丢失,NLP领域里的CNN的发展趋势是抛弃Pooling层,靠全卷积层来叠加网络深度,并且在输入部分加入位置编码,人工将单词的位置特征加入到对应的词向量中,位置编码的方式可以采用Attention的方案。
  • CNN和RNN要建立输入序列之间的长距离依赖关系,一般可以使用以下两种方法:1. 增加网络的层数,通过一个深层网络来获取远距离的信息交互;2. 是使用全连接网络,但全连接网络无法处理变长序列。
Seq2Seq and Encoder-Decoder

Sequence to Sequence(seq2seq)技术突破了传统的固定大小输入问题框架,可以将神经网络运用于在翻译,摘要和问答相关任务上面。更强调解决问题的目的。

Encoder-Decoder 结构主要是 NLP 领域里的概念。它并不特值某种具体的算法,而是一类算法的统称。Encoder-Decoder 算是一个通用的框架,在这个框架下可以使用不同的算法来解决不同的任务。更强调解决问题的方法。

  • Encoder 又称作编码器。它可以将输入信息编码为语义编码C;
  • Decoder 又称作解码器,它将语义编码C转换为所要的解决方案;
  • Encoder和Decoder具体使用什么模型,都可以自己选择。通常有CNN,RNN,GRU,LSTM。

整个结构的示意图如下所示:

img

由于Encoder-Decoder模型在编码和解码阶段始终由一个不变的语义向量C来联系着,这也造成了如下一些问题:

  • 所有的输入单词 X 对生成的所有目标单词 Y 的影响力是相同的;
  • 编码器要将整个序列的信息压缩进一个固定长度的向量中去,使得语义向量无法完全表示整个序列的信息;
  • 最开始输入的序列容易被后输入的序列给覆盖掉,会丢失许多细节信息,这点在长序列上表现的尤为明显。
Attention

Attention机制的诞生就是为了解决上述encoder-decoder框架存在的问题的。Attention可以在上下文向量和输入之间创立一个关联,这些关联的权重就是专门为输出元素定义的。上下文向量包含了三种信息:1、编码器的隐状态;2、解码器的隐状态;3、源词汇和目标词汇的对齐。

Attention机制的作用就是为模型增添了注意力功能,使其倾向于根据需要来选择句子中更重要的部分。加入Attention机制的Seq2Seq模型框架如下图所示:

img

可以看出,和之前的Encoder-Decoder结构对比,带有注意力机制语义向量C变成了每一次预测都有不同的上下文信息的语义向量$C_i$。而生成单词$y_i$时,使用的也是各自的语义向量$C_i$:${y_i} = f({C_i},{y_1}, \cdots ,{y_{i - 1}})$。

当前时刻的中间语义向量$C_i$为输入信息注意力加权求和之后得到的向量,即: $$ {c_i} = \sum\nolimits_{j = 1}^{T_x} {a_{ij}{h(x_j)}} $$ 以Encoder,Decoder均为RNN为例,其中$T_x$表示输入文本的长度,$h(x_j)$为Encoder端的第j个词输入后生成的隐向量,$a_{ij}$表示Decoder端的第i个词对Encoder端的第j个词的注意力大小,即输入的第j个词对生成的第i个词的影响程度,生成$c_i$最关键的部分就是注意力权重$a_{ij}$的计算。

$a_{ij}$的计算是通过一个对齐函数F来进行计算,对齐函数一般有两类,一类是点积;另一类是MLP网络,输入节点j和输出节点i,经过softmax就可以得到注意力分配的概率。

生成目标单词$y_i$的计算概率为:$p({y_i}|({y_1},…,{y_{i - 1}}),{x_i}) = g({y_{i - 1}},{s_i},{c_i})$,其中$y_i$为当前目标词的ground truth,$y_{i - 1}$是上一个节点的输出单词,$s_i$是当前节点的隐藏状态,$c_i$是生成当前词汇所需的语义向量,g为全连接层函数。

当前Decoder的隐层状态$s_i$由上一层的隐藏状态$s_{i-1}$,输出单词$y_{i-1}$,语义向量$c_i$:${s_i} = f({s_{i - 1}},{y_{i - 1}},{c_i})$。

语义向量$c_i$由注意力权重$a_{ij}$和Encoder的输出$h(x_j)$加权求和构成:${c_i} = \sum\nolimits_{j = 1}^{T_x} {a_{ij}{h(x_j)}}$。

注意力权重$a_{ij}$由${y_i}$受到$x_j$的注意力分配$e_{ij}$得到:${a_{ij}} = \frac{\exp (e_{ij})}{\sum\nolimits_{k = 1}^{T_x}e_{ik}}$。

注意力分配$e_{ij}$由encoder的输出$h(x_j)$和decoder的上一层隐状态$s_{i-1}$计算而成:$e_{ij} = F(s_{i - 1},{h(x_j)})$

以NMT的global attention为例,对齐函数有下面三种实现方式: $$ score(h_t,\bar h_s) = \left{ \begin{array}{c} {h_t^T{\bar h}_s}\ {h_t^T{W_a}{\bar h}_s}\ {v_a^T\tanh ({W_a}[h_t^T;{\bar h}_s])} \end{array} \right.\begin{array}{c} {dot}\ {general}\ {concat} \end{array} $$ 其中${h_t}$表示当前Decoder的隐状态,${\bar h_s}$表示s时刻Encoder的隐状态。dot的好处就是计算简单,不引入额外参数,但是两个矩阵必须size相等;另外两类引入Trainable的矩阵,不需要qv向量size一致,但是增加了计算代价。

机制

Attention机制的实质其实就是一个寻址(addressing)的过程,给定一个和任务相关的查询Query向量q,通过计算与Key的注意力分布并附加在Value上,从而计算Attention Value,这个过程实际上是Attention机制缓解神经网络模型复杂度的体现:不需要将所有的N个输入信息都输入到神经网络进行计算,只需要从X中选择一些和任务相关的信息输入给神经网络。

将Source中的构成元素想象成是由一系列的(Key,Value)数据对构成,此时给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的Attention数值。所以本质上Attention机制是对Source中元素的Value值进行加权求和,而Query和Key用来计算对应Value的权重系数。 从本质上理解,Attention是从大量信息中有选择地筛选出少量重要信息并聚焦到这些重要信息上,忽略大多不重要的信息。聚焦的过程体现在权重系数的计算上,权重越大越聚焦于其对应的Value值上,即权重代表了信息的重要性,而Value是其对应的信息。

注意力机制可以分为三步:一是信息输入;二是计算注意力分布;三是根据注意力分布来计算输入信息的加权平均。

分类

注意力机制的目的是将计算资源分配到更重要的任务。

根据Attention的计算区域由下面几种类型:

  • hard attention:注意力是只关注到某一个位置的信息,通过随机采样或最大采样来选取特征信息。但是其缺点是损失函数与注意力分布之间的函数关系不可导,因此无法使用在反向传播算法进行训练。一般使用软性注意力来代替硬性注意力。硬性注意力需要通过强化学习来进行训练。
  • soft attention/global Attention:全局计算,对所有key求权重概率,每个key都有一个对应的权重,这种方式参考了所有key的内容,通过加权求和的方式选取特征信息,计算量可能会比较大一些。
  • local attention:以上两种方式的折中,对一个窗口区域进行计算。先用Hard方式定位到某个地方,以这个点为中心可以得到一个窗口区域,在这个小区域内用Soft方式来算Attention。

根据所用文本信息分类:

  • general attention:使用除原文之外的额外信息,常用于需要构建两段文本关系的任务,query一般包含了额外信息,根据外部query对原文进行对齐,如阅读理解任务构造问题和文章的关联。
  • self attention:只使用原文本身的信息,key、query和value只和输入原文相关,寻找原文内部关系。

根据结构层次分类:

  • 单层attention:一个query对原文做一次attention;
  • 多层attention:每一层在上一层基础上做attention;
  • 多头attention:使用多个query对原文做多次attention,每个query关注不同部分,相当于重复做多次单层attention,最后将结果拼接。
self attention的一些理解

self attention的另一种思考方式可以参考推荐系统,query和key的点积类似推荐系统两个特征向量之间的点积,可以得到特征对于最终结果的贡献度得分。落到文本上面,对句子本身进行注意力权值计算,使其更能够把握句子中词与词之前的关系,从而提取出句子中的句法特征或语义特征。

另一种思考方式类似检索系统,数据库中的文档是value,文档的索引是key,而输入的数据作为query,attention就是获得query和key的匹配程度加权。

下图是一个self-attention的例子,输入$x_2$对于其他输入做self-attention得到$y_2$输出:

self-attention

Attention 和 CNN 的异同点
  1. 相似点:无论是attention机制还是CNN中的卷积,都是以加权求和的模式对数据进行加工。在Transformer中,Multi-Head Attention在后面还有一个特征融合过程,与CNN中逐通道卷积最后沿着通道求和做特征融合比较相似。甚至可以认为CNN中的卷积是在全图范围内将全部的注意力用于当前卷积窗口。
  2. 不同点:(1)attention的注意力权重是动态的按照输入计算出来的,但是卷积一但训练完毕就和输入无关;attention考虑的是在全输入下找出重点,而卷积更多的是窗口操作模式。(2)在Transformer中有明确的Q、K和V的概念,在卷积中没有。
参考
  1. 图文介绍RNN注意力机制
  2. Attention用于NLP的一些小结
  3. Transformers from scratch