Fork me on GitHub

Transformer-Attention_is_all_you_need

Transformer是在2017年的Google发布的论文《Attention is all you need》中提出,主要用于解决RNN相关网络无法捕获序列的长期依赖,以及网络无法并行化的问题,其网络结构示意图如下

从示意图中可以看出在Transformer中包含多个组件,分别为Multi-Head Attention, Masked Multi-Head Attention, Add, Norm, Feed Forward, Position Encoding,会在接下来的内容中结合Transformer的前向传播过程依次解释。

Seq2Seq

NLP领域内的相关任务为机器翻译,语音识别,问答系统,文本分类等等,多数情况下处理的是序列结构的数据,长久以来,RNN是序列模型的首选工具,因为RNN基于时间步传递,天然的可以捕获序列的前后语义关系和位置关系,这就是目前广泛使用的Seq2seq模型结构,包含Encoder部分和Decoder部分,Encoder部分负责对输入的数据进行编码处理,Decoder部分根据Encoder的输出进行解码输出,最开始的Decoder接受Encoder部分的最后一个时间步的隐藏层输出作为其初始状态输入,但是这很显然的会存在两个问题,将输入序列编码之后信息只是用最后一个时间步的隐藏层输出是不合理的,因为时间步储存的信息是优先的,无法捕获输入数据的长期依赖,距离该时间步近的序列保存的多,远离的保留的就少;另一个问题,在解码的时候使用最后一层时间步,无法捕获输出信息和输入信息之间的依赖关系。为了解决Encoder和Decoder之间的依赖问题,就在Decoder阶段引入了Attention机制,可以看做是当前步的Decoder输出和整个Encoder的输出序列的依赖关系。但是seq2seq模型还存在一个问题,那就是基于RNN建模会导致模型无法并行运算,因为在RNN运算时是基于时间传播的,当前时间步接收的输入为上一个时间步的输出和当前时间步的输入,因此无法进行并行运算。针对这些问题,提出了多种改进方案,Transformer正是从提升seq2seq模型运算速度的角度出发进行设计的。更多关于seq2seq的相关内容,可以参看另一篇文章深度学习-PythonTutorial_LSTM_GRU_Attention_LNLSTM_LNGRU

Why Transformer?

首先需要说明的是为什么设计开发了Transformer这种模型结构,也就是目前的模型存在着哪些问题。

  1. RNN运行速度的问题。对于序列相关数据结构,使用RNN基于时间步进行传递,很天然的能够有效处理序列数据,但是同样是由于RNN的信息是基于时间步传递的,会存在无法并行化的问题,在RNN中,当前时间步的计算结果是由上一个时间步的输出结果和当前时间步的输入序列数据相关的,也就是说当前的计算是依赖于上一个时间步的,在没计算出上一个时间步的情况下是无法计算当前步的,这就导致了模型无法进行并行化训练,模型的运行时间过长。

    针对RNN结构无法并行化的问题,提出了一些改进措施,例如使用跨步卷积,wavenet等,跨步卷积由于感受野的范围有限只能通过增加网络深度的方法进行感受野的扩增。而Transformer是通过的设计了新的Attention计算方式实现并行运算

  2. 捕获序列的长时依赖。在RNN的变体LSTM,GRU中使用门控的方式来捕获序列之间的长时依赖,但是实验证明,当序列过长时,LSTM,GRU仍然无法有效捕获这种依赖,此外在机器翻译任务或者语音识别,TTS等任务中存在着输入序列或者输入序列中同字异音,同字异意的问题,这就需要设计更好的模型来捕获序列的内部依和序列之间的依赖。

Transformer正是针对上述这两点进行设计的,在论文中整体的框架仍然是seq2seq,但是摒弃了RNN结构,全部使用经过合理设计的Attention结构代替,实现了当时的SOTA结果。

Structure

整个模型的结构如文首所述,摒弃了RNN结构,全部使用的Attention结构。在论文中和一般的博客中是按照的各个组件进行的介绍和讲解,本文将针对整个模型结构进行剖析和解释。

按照数据前向传播的方式进行简要说明

  • 输入序列的序列内部依赖关系提取

    对输入的序列数据转化为词向量和位置编码,使用多头Attention进行输入序列间依赖关系的提取,并使用残差网络的连接方式连接多头Attention的输出结果和输入的分析序列,经过层归一化处理,之后使用前向网络进行维度转换并再次进行层归一化处理

  • 输出序列的序列间内部关系提取

    和输入的序列处理方式类似,只是在使用多头Attention的时候要加入mask的机制,这是为了保证在预测输出的时候只能使用当前位置之间的序列信息,因为在预测的时候,输出序列是未知的,当前待计算的位置之后的部分的信息无法给当前位置的预测提供帮助

  • 输入序列和输出序列之间的依赖关系

    在该阶段和传统的Attention相同,捕获的是预测的节点和输入序列经过编码之后的输出序列之间的依赖关系。

  • 预测输出

    在预测输出时,每个节点使用softmax的方式进行输出,输出每个节点属于不同字符的概率,这里可以使用贪心搜索的方式或者集束搜索的方式进行预测输出。

整个结构中包含多个组件,下面进行分别说明。

Positional Encoding

顾名思义,对序列进行位置编码,因为在设计的Attention和传统的RNN结构不同,对于RNN结构是对数据按照时间步进行计算的,但是Attention是针对整个序列进行的计算,因此需要在输入的序列中不但要进行词向量的转化,还要进行位置编码,由此得到的序列既包含序列的语义信息又包含序列的位置信息。

根据论文中所述,在位置编码的时候将序列按照时间步的不同进行三角函数变换,使其满足不同位置之间能通过线性变换的方式进行转换。在序列的位置$pos$的计算方式,假设序列的维度为$d_{model}$,计算公式为

在维度方向上,在偶数位置上使用正弦函数,在奇数位置上使用余弦函数。对于正余弦函数存在关系式

由此对于位置$POS(pos+k)​$可以经过线性变换得到

Multi-Head Attention

在Encoder阶段,序列经过词向量转化和位置编码,二者的维度均为$d_{model}​$,将二者相加得到的序列认为包含着语义信息和位置信息,接下来将该序列作为待分析的序列,并使用Multi-Head Attention计算,对于Multi-Head Attention又叫做self-Attention,主要是为了提取序列内部的依赖关系。对于传统的Attention,是为了捕获输出序列和输入序列之间的关系,引入新的关系式query表示Decoder部分序列输出,key,value表示Encoder部分的输出,使用Decoder和key进行Attention score的计算,包括加性注意力(BahdanauAttention),点乘注意力(LuongAttention),自注意力(SelfAttention)和关键值注意力(KeyValueAttention),对计算结果使用softmax进行归一化计算得到Attention weight,和value计算得到了contex,将contex和query合并在一起,作为Decoder当前位置的新输出,并将该输出传递至下一个时刻。具体模型如下所示

公式过程为

上面式子中的score用来计算目标序列和原序列的对齐成对,一般使用加性,点积的方式

对于加性attention和点乘attention的计算方式,二者在计算复杂度上式相似的,但是点乘注意力一般更快并且具有更高效的存储,因为点乘的方式可以使用矩阵操作更高效的实现,在低纬度情况下,加性注意力和点乘注意力相似,但是在高纬度上,加性注意力比点乘注意力小姑更好。


对于Multi-head Attention的序列间依赖关系的计算和传统Attention计算方式相同,不同的有两点

  • 序列内部依赖关系,query,key,value均为当前序列
  • 多头Attention,针对多个维度空间计算Attention,最后再合并多头Attention的计算结果

下面分别说明

计算方法

  • score的计算

    对于Multi-Head Attention,使用query,key,和value来表示待匹配的序列,原始序列和目标序列,如果要捕获序列内部的依赖关系,存在关系为$query=key=value=sequence​$,对于序列依赖关系的计算在Multi-Head Attention中使用的点积的方式,也就是

    因为query和key来自同一个序列,如果使用点积的方式会存在当query和key的维度变大的时候二者的点乘结果要么很大要么很小,在这里引入了Scale dot product Attention的方式,算是对DotAttention的一种修正。

  • Scale dot product Attention

    假设query和key均为标准正太分布,并且存在关系式

​ 也就是数据和的方差和方差的和是相等的,那么对于$socre​$的计算过程中就会存在

​ 其中$d_k​$对应着序列的维度

​ 得到的期望为0,方差为$d_k$,如果$d_k$很大的话,那么內积的总和就会很大,使得softmax计算之后就会变成非零即1。

​ 对score进行缩放,这样score的计算变成了

​ 也就是对传统Attention score计算的基础上,对计算结果进行了缩放,在Attention score计算时,使用的有相加 的方式或者前文所述的点乘的方式,相比较而言两者效果差不多,但是使用点乘的方式只是用一步操作而使用相加的方式要进行尺度缩放之后相加,因此点乘的方式运行的时候会更快,在序列维度$d_k$不大的情况下,二者效果可以认为一致,但是当$d_k$变大的时候,对于点乘的方式就会存在结果过大或者过小的问题,因此需要进行缩放,前文中对此已经进行了一定叙述。

  • Attention weight的计算

    在得到Attention score之后同样使用softmax的方式对socre进行归一化处理,得到Attention weight,可以认为这就是query和key之间的依赖的分布,将得到的结果和value点乘,就得到了被更新之后的序列

    整个计算公式如下所示

​ 和Encoder-Decoder Attention不同的是,这里不再计算contex,而是对value进行更新,去掉了原来的求和操作。

Multi的解释

之所以叫做多头Attention,是相比较传统的Attention而言的,传统Attention对整个序列只进行一次计算,并且是直接计算整个序列的所有维度,多头表示针对序列的不同维度空间进行计算,对整个维度空间进行线性投影,投影到几个相同的更小的维度空间,并对该维度空间进行Attention计算,实际上在实现的过程中是很简单的,下面进行阐述。

假设序列的维度是$d_{model}$,如果想在$n$个维度空间下进行Attention的计算,那么可以得到每个维度空间下的维度为$d_{feature}$,并且满足关系式$d_{model}=n*d_{feature}$,根据$d_{model}$得到$d_{feature}​$的方法使用线性变换的方式得到,其中线性变换的参数是学习参数

如此重复$n$次,可以得到$n$个不同的$d_{feature}$,将每个$d_{feature}$进行Attention计算,并将$n$个计算结果在维度方向上拼接,就得到了Multi-Head Attention的输出,也就是如图三中所显示的那样。

ResidualConnect & LayerNormal

在模型前向传播过程中,每个Transformer block中存在两个部分,一个部分就是前文所述的Multi-head Attention,另一个就是一个简单的前馈神经网络FFD。

在完成Multi-head Attention到传递至FFD之间使用看了残差连接和层归一化的方法,如图4所示

残差连接,使得模型在训练时,微小的变化可以被注意到,更多的可以查看残差网络相关论文及文章,本人在其他文章中已有所述

层归一化,最常和batch normalization进行比较,layer normalization的优点在于它是独立计算的,也就是针对单一样本进行正规化,batch normalization则是针对各维度,因此和batch size有所关联。

归纳出数学公式就是

其中$Sublayer$对应着Multi-Head Attention和一个前馈神经网络,如图四中所示

Masked Multi-head Attention

Decoder的运作模式和Encoder大同小异,也都是经过residual connections再到layer normalization。Encoder中的self Attention在计算时,key, value, query都是来自Encoder前一层的输出,Decoder亦然。

不同的是,在Decoder阶段使用的是添加Mask的多头Attention,来计算目标序列的内部依赖关系,Mask是为了避免在解码的时候,还在翻译前半段时,就突然翻译到后半段的句子,会在计算self-Attention时的softmax前先mask掉未来的位置(设定成-∞)。这个步骤确保在预测位置i的时候只能根据i之前位置的输出,其实这个是因应Encoder-Decoder Attention 的特性而做的配套措施,因为Encoder-Decoder Attention可以看到Encoder的整个句子

Encoder-Decoder Attention

该步骤是为了将目标序列和输入序列进行对齐,也就是常用的Attention结构,在计算的时候query对应着Decoder的self-Attention的结果,而key和value对应着Encoder的self-Attention的结果,再次不再赘述,可以参考另一文章深度学习-PythonTutorial_LSTM_GRU_Attention_LNLSTM_LNGRU

Final Layer and Softmax

在Encoder-Decoder之后得到了整个序列的输出,经过线性连接维度转换,实际上就是将最终的到的维度控制在和整个字表的大小一致的情况,采用softmax计算得分,选择得分最大的作为当前点的输出,在论文中使用的贪心的方法直接对每个点进行输出,当然也可以使用集束搜索的方法,设置输出的备选范围,拿到每个点上输出最大的k个,之后再使用这几个计算下一个单位,这样就可以得到累计概率,最后输出累计概率最大的一个序列语句,这显然就是一种动态规划的思想。

overview

整个过程说的比较简单,实际上可以记住Transformer的模型结构图,内部的组件包括position encoding,Multi-head Attention,residual connect,layernormal,masked-Multi-head Attention,Encoder-deocder Attention,以及最后的linear和softmax等,每个组件都不复杂,最核心的就是位置编码和Multi-Head Attention,masked Multi-head Attention了。

可以结合参考文献中的的jupyter notebook进行运行和测试。

Reference

-------------本文结束知识分享,方便你我-------------

本文标题:Transformer-Attention_is_all_you_need

文章作者:ShiXiaofeng

发布时间:2019年02月15日 - 17:42

最后更新:2019年03月04日 - 15:25

原始链接:http://xiaofengshi.com/2019/02/15/Transformer-Attention_is_all_you_need/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%