自从Attention机制在提出之后,加入Attention的Seq2Seq模型在各个任务上都有了提升,所以现在的Seq2Seq模型指的都是结合RNN和Attention的模型。传统的基于RNN的Seq2Seq模型难以处理长序列的句子,无法实现并行,并且面临对齐的问题。
所以之后这类模型的发展大多数从三个方面入手:
(1)Input的方向性从单向到多向;
(2)深度从单层到多层;
(3)类型从RNN到LSTM GRU。
但是依旧受到一些潜在问题的制约,神经网络需要能够将源语句的所有必要信息压缩成固定长度的向量。这可能使得神经网络难以应付长时间的句子,特别是那些比训练语料库中的句子更长的句子;每个时间步的输出需要依赖于前面时间步的输出,这使得模型没有办法并行,效率低;仍然面临对齐问题。
再然后CNN由计算机视觉也被引入到Deep NLP中,CNN不能直接用于处理变长的序列样本但可以实现并行计算。完全基于CNN的Seq2Seq模型虽然可以并行实现,但非常占内存,很多的trick,大数据量上参数调整并不容易。
本篇文章创新点在于抛弃了之前传统的Encoder-Decoder模型必须结合CNN或者RNN的固有模式,只用Attention机制。
文章的主要目的在于减少计算量和提高并行效率的同时不损害最终的实验结果。以下为个人对文章详细解读,希望有助于正在学习本篇论文的各位读者。
整体框架很容易理解,但是图1.1又很复杂,可以简化如图1.2。
图1.1 Transformer模型架构图
图1.2 Transformer简化图
我们可以看到,这其实就是一个SeqSeq模型,左边一个Encoder将输入读进去,右边一个Decoder的单独输出。当我们第一眼看到论文中的模型结构图时,我们会产生疑问。左边的Encoder是如何与右边的Decoder结合的呢?其实因为Decoder里面是有N层的,我们再通过一张图可以直观的看到。
图1.3 Encoder-Decoder结合方式图
也就是说,Encoder的输出会和每一层的Decoder进行结合,我们取其中的一层进行详细的展示。
图1.4Encoder-Decoder内部结构图
Google 的一般化 Attention思路也是一个编码序列的方案,因此我们也可以认为它跟 RNN、CNN 一样,都是一个序列编码的层。
Attention用于计算"相关程度",例如在翻译过程中,不同的英文对中文的依赖程度不同,Attention通常可以进行如下描述,表示为将query(Q)和key-value pairs映射到输出上,其中query、每个key、每个value都是向量,输出是V中所有values的加权,其中权重是由Query和每个key计算出来的,计算方法分为三步:
(1) 计算比较Q和K的相似度,用f来表示:
(3) 针对计算出来的权重,对V中所有的values进行加权求和计算,得到Attention向量:
在第一步中的计算方法包括以下四种:
(1)点乘Dot Product:
(3)拼接权重Concat:
计算自注意力的第一步就是从每个编码器的输入向量(每个单词的词向量)中生成三个向量。也就是说对于每个单词,我们创造一个查询向量、一个键向量和一个值向量。这三个向量是通过词嵌入与三个权重矩阵后相乘创建的。可以发现这些新向量在维度上比词嵌入向量更低。他们的维度是64,而词嵌入和编码器的输入/输出向量的维度是512. 但实际上不强求维度更小,这只是一种基于架构上的选择,它可以使多头注意力(multiheaded Attention)的大部分计算保持不变。X1与WQ权重矩阵相乘得到q1, 就是与这个单词相关的查询向量。最终使得输入序列的每个单词的创建一个查询向量、一个键向量和一个值向量。
像大部分NLP应用一样,我们首先将每个输入单词通过词嵌入算法转换为词向量。每个单词都被嵌入为512维的向量,我们用一些简单的方框来表示这些向量。词嵌入过程只发生在最底层的编码器中。所有的编码器都有一个相同的特点,即它们接收一个向量列表,列表中的每个向量大小为512维。在底层(最开始)编码器中它就是词向量,但是在其他编码器中,它就是下一层编码器的输出(也是一个向量列表)。向量列表大小是我们可以设置的超参数——一般是我们训练集中最长句子的长度。将输入序列进行词嵌入之后,每个单词都会流经编码器中的两个子层。
接下来我们看看Transformer的一个核心特性,在这里输入序列中每个位置的单词都有自己独 特的路径流入编码器。在自注意力层中,这些路径之间存在依赖关系。而前馈(feed-forward)层没有这些依赖关系。因此在前馈(feed-forward)层时可以并行执行各种路径。
首先从输入开始理解,Scaled Dot-Product Attention里的Q, K, V从哪里来:按照我的理解就是给我一个输入X, 通过3个线性转换把X转换为Q,K,V。两个单词,Thinking, Machines. 通过嵌入变换会X1,X2两个向量[1 x 4]。分别与Wq,Wk,Wv三个矩阵[4x3]想做点乘得到,{q1,q2},{k1,k2},{v1,v2} 6个向量[1x3]。
图2.2.1 QKV线性转换
向量{q1,k1}做点乘得到得分(Score) 112, {q1,k2}做点乘得到得分96。
图2.2.2 QK点乘结果
对该得分进行规范,除以8。这个在论文中的解释是为了使得梯度更稳定。之后对得分[14,12]做softmax得到比例 [0.88,0.12]。
图2.2.3 softmax处理
用得分比例[0.88,0.12] 乘以[v1,v2]值(Values)得到一个加权后的值。将这些值加起来得到z1。这就是这一层的输出。仔细感受一下,用Q,K去计算一个thinking对thinking, machine的权重,用权重乘以thinking,machine的V得到加权后的thinking,machine的V,最后求和得到针对各单词的输出Z。(译注:自注意力的另一种解释就是在编码某个单词时,就是将所有单词的表示(值向量)进行加权求和,而权重是通过该词的表示(键向量)与被编码词表示(查询向量)的点积并通过softmax得到。),然后即得到自注意力层在该位置的输出(在我们的例子中是对于第一个单词)。
上述例子是单个向量的运算例子。下图展示的是矩阵运算的例子。输入是一个[2x4]的矩阵(单词嵌入),每个运算是[4x3]的矩阵,求得Q,K,V。Q对K转制做点乘,除以dk的平方根。做一个softmax得到合为1的比例,对V做点乘得到输出Z。那么这个Z就是一个考虑过thinking周围单词(machine)的输出。
图2.2.5 矩阵运算表示
QKT其实就会组成一个word2word的Attentionmap!(加了softmax之后就是一个合为1的权重了)。比如说你的输入是一句话 “i have a dream” 总共4个单词,这里就会形成一张4x4的注意力机制的图:
图2.2.6 4*4注意力机制图
这样一来,每一个单词就对应每一个单词有一个权重。主要Encoder里做Self-AttentionDecoder里面叫做masked Self-Attention在这里的masked就是要在做language modelling(或者翻译)的时候,不给模型看到未来的信息。masked就是不给模型看到未来的信息,mask就是沿对角线把灰色的区域用0覆盖掉。详细的来说,i作为第一个单词,只能有和i自己的Attention。have作为第二个单词,有和i, have 两个Attention。a 作为第三个单词,有和i,have,a 前面三个单词的Attention。到了最后一个单词dream的时候,才有对整个句子4个单词的Attention。做完softmax后就像这样,横轴和为1。
图2.2.7 softmax矩阵表示
Multi-Head Attention就是把Scaled Dot-Product Attention的过程做H次,然后把输出Z合起来。论文中,它的结构图如下:
图2.3.1 多头注意力机制结构图
论文中还增加一种称为Multi-Headed注意力机制,可以提升注意力层的性能。我们还是以上面矩阵的形式来解释:
图2.3.2多头注意力矩阵表示
它使得模型可以关注不同位置。虽然在上面的例子中,z1 包含了一点其他位置的编码,但当前位置的单词还是占主要作用, 当我们想知道“The animal didn’t cross the street because it was too tired” 中 it 的含义时,这时就需要关注到其他位置。这个机制为注意层提供了多个“表示子空间”(representation Subspaces)。下面我们将具体介绍:
(1)经过 Multi-Headed,我们会得到和 heads 数目一样多的 Query / Key / Value 权重矩阵组.论文中用了8个,那么每个Encoder/Decoder我们都会得到 8 个集合。这些集合都是随机初始化的,经过训练之后,每个集合会将input Embeddings (或者来自较低编码器/解码器的向量)投影到不同的表示子空间中。
(2)我们重读记忆八次相似的操作,得到八个Zi矩阵。简单来说,就是定义8组权重矩阵,每个单词会做8次上面的Self-Attention的计算这样每个单词就会得到8个不同的加权求和Z。
图2.3.3 单词加权求和
(3)feed-forward处只能接受一个矩阵,所以需要将这八个矩阵压缩成一个矩阵。方法就是先将八个矩阵连接起来,然后乘以一个额外的权重矩阵W0。为了使得输出与输入结构对标 乘以一个线性W0 得到最终的Z 3.Transformer结构
绝大部分的序列处理模型都采用Encoder-Decoder结构,其中Encoder将输入序列(x1,x2,…,xn),然后Decoder生成一个输出序列(y1,y2,…,yn),每个时刻输出一个结果。从框架中我们可以知道Transformer模型延续了这个模型。
因为模型不包括Recurrence/Convolution,因此是无法捕捉到序列顺序信息的,例如将K、V按行进行打乱,那么Attention之后的结果是一样的。但是序列信息非常重要,代表着全局的结构,因此必须将序列的分词相对或者绝对Position信息利用起来。这里每个分词的Position Embeddingg向量维度也是dmodel, 然后将原本的input Embedding和Position Embedding加起来组成最终的Embedding作为Encoder/Decoder的输入。其中Position Embedding计算公式如下:
其中 pos 表示位置index, i 表示dimension index。Position Embedding本身是一个绝对位置的信息,但在语言中,相对位置也很重要,Google选择前述的位置向量公式的一个重要原因是,由于我们根据如下三角函数:
这表明位置p+k的向量可以表示成位置p的向量的线性变换,这提供了表达相对位置信息的可能性。结合位置向量和词向量有几个可选方案,可以把它们拼接起来作为一个新向量,也可以把位置向量定义为跟词向量一样大小,然后两者加起来。在其他NLP论文中,大家也都看过Position Embedding,通常是一个训练的向量,但是Position Embedding只是extra features,有该信息会更好,但是没有性能也不会产生极大下降,因为RNN、CNN本身就能够捕捉到位置信息,但是在Transformer模型中,Position Embedding是位置信息的唯一来源,因此是该模型的核心成分,并非是辅助性质的特征。
图3.1.1 Position Embedding表示
在进行了Attention操作之后,Encoder和Decoder中的每一层都包含了一个全连接前向网络,对每个Position的向量分别进行相同的操作,包括两个线性变换和一个ReLU激活输出:
即在每个Sub-Layer,针对Self-Attention层的输出,先使用一个线性变换,再针对该线性变换的输出使用RELU函数,最后再针对RELU函数的输出使用一个线性变化。那么,做这么繁琐的变换有什么意义呢?
我们将FNN与CNN做对比,其实可以发现,其效果与加上一层卷积核大小为11的CNN是一样的。那么这就好理解了,这层所谓的FNN其实也是做特征提取的。至于它为什么不直接取名为11CNN Layer,这就要去问Tranformer的发明者了。
Encoder有N=6层,每层包括两个Sub-Layers:第一个Sub-Layer是Multi-Headed Self-Attentionmechanism,用来计算输入的Self-Attention;第二个Sub-Layer是简单的全连接网络。在每个Sub-Layer我们都模拟了残差网络,每个Sub-Layer的输出都是:
其中SubLayer(x) 表示Sub-Layer对输入 x 做的映射,为了确保连接,所有的Sub-Layers和Embedding Layer输出的维数都相同dmodel。
图3.3.1 Sub-Layer结构示意图
Decoder也是N=6层,每层包括3个Sub-Layers:
(1) 第一个是Masked multi-head Self-Attention,也是计算输入的Self-Attention,但是因为是生成过程,因此在时刻 i 的时候,大于 i 的时刻都没有结果,只有小于 i 的时刻有结果,因此需要做Mask。
(2) 第二个Sub-Layer是全连接网络,与Encoder相同。
(3) 第三个Sub-Layer是对Encoder的输入进行Attention计算。同时Decoder中的Self-Attention层需要进行修改,因为只能获取到当前时刻之前的输入,因此只对时刻 t 之前的时刻输入进行Attention计算,这也称为Mask操作。
图3.4.1 整体结构图
解码器最后会输出一个实数向量。我们如何把浮点数变成一个单词?这便是线性变换层要做的工作,它之后就是Softmax层。线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的、被称作对数几率(logits)的向量里。不妨假设我们的模型从训练集中学习一万个不同的英语单词(我们模型的“输出词表”)。因此对数几率向量为一万个单元格长度的向量——每个单元格对应某一个单词的分数。接下来的Softmax 层便会把那些分数变成概率(都为正数、上限1.0)。概率最高的单元格被选中,并且它对应的单词被作为这个时间步的输出,同时将Decoder的堆栈输出作为输入,从底部开始,最终进行单词预测。
图 3.5.1 Decoder层预测
最后我们通过实验可以看出,Transformer用了最少的资源得到了state-of-art的输出回报。
表4.1英语-法语、英语-德语机翻结果对比表