这是第10个任务,本次任务主要是一下几个方面:
本文主要接受Transformer 原理,2017年,Google发表论文《Attention is All You Need》,提出经典网络结构Transformer,全部采用Attention结构的方式,代替了传统的Encoder-Decoder框架必须结合CNN或RNN的固有模式。并在两项机器翻译任务中取得了显著效果。该论文一经发出,便引起了业界的广泛关注,同时,Google于2018年发布的划时代模型BERT也是在Transformer架构上发展而来。
和大多数seq2seq模型一样,transformer的结构也是由encoder和decoder组成。通俗结构图如下所示:
将上述图浓缩一下就是如下图,其中灰色长方形框框就是 Encoder和Decoder都如下所示:
再通俗一点的图,可能你在其他博客里看到的图,如下所示:
在本文中,Embedding操作不是普通的Embedding而是加入了位置信息的Embedding,我们称之为Position Embedding。因为在本文的模型中,已经没有了循环神经网络这样的结构,因此序列信息已经无法捕捉。但是序列信息非常重要,代表着全局的结构,因此必须将序列的分词相对或者绝对position信息利用起来,每个时刻的输入是Word Embedding+Position Embedding。位置信息的计算公式如下:
P E p o s , 2 i = s i n ( p o s 1000 0 2 i d m o d e l ) PE_{pos,2i}=sin(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) \\ PEpos,2i=sin(10000dmodel2ipos)
P E p o s , 2 i + 1 = c o s ( p o s 1000 0 2 i d m o d e l ) PE_{pos,2i+1}=cos(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) PEpos,2i+1=cos(10000dmodel2ipos)
其中:
即奇数位置用余弦编码,偶数位置用正弦编码,最终得到一个512维的位置向量。位置编码是不参与训练的,而词向量是参与训练的。作者通过实验发现,位置编码参与训练与否对最终的结果并无影响。
Encoder由N=6个相同的layer组成,每一个layer就是上图左侧的单元,最左边有个“N”参数,这里N=6。每个Layer由两个sub-layer组成,分别是multi-head self-attention mechanism(左图中橙色部分)和fully connected feed-forward network(左图中蓝色部分)。其中每个sub-layer都加了residual connection和normalisation,因此可以将sub-layer的输出表示为:
s u b _ l a y e r _ o u t p u t = L a y e r N o r m ( x + ( S u b L a y e r ( x ) ) ) sub\_layer\_output = LayerNorm(x+(SubLayer(x))) sub_layer_output=LayerNorm(x+(SubLayer(x)))
接下来按顺序解释一下这两个sub-layer。
普通attention机制可由如下表示:
a t t e n t i o n _ o u t p u t = A t t e n t i o n ( Q , K , V ) attention\_output = Attention(Q,K,V) attention_output=Attention(Q,K,V)
第一部就是从每个编码器的输入向量(每个单词的词向量)中生成三个向量。也就是说对于每个单词,我们创造一个查询向量 Q Q Q、一个键向量 K K K和一个值向量 V V V。这三个向量是通过词嵌入与三个权重矩阵后相乘创建的。
其中:
Q i = X i ∗ W i Q K i = X i ∗ W i K V i = X i ∗ W i V Q_i=X_i * W^{Q}_i \\ K_i=X_i* W^{K}_i \\ V_i=X_i* W^{V}_i Qi=Xi∗WiQKi=Xi∗WiKVi=Xi∗WiV
X 1 X1 X1与 W Q WQ WQ权重矩阵相乘得到 q 1 q_1 q1, 就是与这个单词相关的查询向量。最终使得输入序列的每个单词的创建一个查询向量、一个键向量和一个值向量。可以发现这些新向量在维度上比词嵌入向量更低。他们的维度是64,而词嵌入和编码器的输入/输出向量的维度是512,但实际上不强求维度更小,这只是一种基于架构上的选择,它可以使多头注意力(multiheaded attention)的大部分计算保持不变。
第二部是计算得分,假设我们在为这个例子中的第一个词“Thinking”计算自注意力向量,我们需要拿输入句子中的每个单词对“Thinking”打分。这些分数决定了在编码单词“Thinking”的过程中有多重视句子的其它部分。这些分数是通过打分单词(所有输入句子的单词)的键向量 K K K与“Thinking”的查询向量 Q Q Q相点积来计算的。所以如果我们是处理位置最靠前的词的自注意力的话,第一个分数是 q 1 q_1 q1和 k 1 k_1 k1的点积,第二个分数是 q 1 q_1 q1和 k 2 k_2 k2的点积。
第三步和第四步是将分数除以8(8是论文中使用的键向量的维数64的平方根,这会让梯度更稳定。这里也可以使用其它值,8只是默认值),然后通过softmax传递结果。softmax的作用是使所有单词的分数归一化,得到的分数都是正值且和为1。
这个softmax分数决定了每个单词对编码当下位置(“Thinking”)的贡献。显然,已经在这个位置上的单词将获得最高的softmax分数,但有时关注另一个与当前单词相关的单词也会有帮助。
第五步是将每个值向量 V V V乘以softmax分数(这是为了准备之后将它们求和),不过这里有一个问题,比如输入有3个单词,就有3个 q q q和3个 k k k,比如第一个单词评分分别是 q 1 ∗ k 1 q_1*k_1 q1∗k1、 q 1 ∗ k 2 q_1*k_2 q1∗k2、 q 1 ∗ k 3 q_1*k_3 q1∗k3,这里就有3个softmax值,这3个值分别跟对应的 v i v_i vi相乘,就有3个向量。这里的直觉是希望关注语义上相关的单词,并弱化不相关的单词(例如,让它们乘以0.001这样的小数)。
第六步是对加权值向量求和,然后即得到自注意力层在该位置的输出(在我们的例子中是对于第一个单词),也就是第五步中我们说的3个向量。自注意力的另一种解释就是在编码某个单词时,就是将所有单词的表示(值向量)进行加权求和,而权重是通过该词的表示(键向量)与被编码词表示(查询向量)的点积并通过softmax得到。不过现在仔细想想,求和是各个向量同位置上的求和还是所有向量累加?看下图就是多个向量同位置的元素相加。
这里还有一个“多头”注意力(“multi-headed” attention)的机制,论文进一步完善了自注意力层,并在两方面提高了注意力层的性能:
1.它扩展了模型专注于不同位置的能力。在上面的例子中,虽然每个编码都在z1中有或多或少的体现,但是它可能被实际的单词本身所支配。如果我们翻译一个句子,比如“The animal didn’t cross the street because it was too tired”,我们会想知道“it”指的是哪个词,这时模型的“多头”注意机制会起到作用。
2.它给出了注意力层的多个“表示子空间”(representation subspaces)。接下来我们将看到,对于“多头”注意机制,我们有多个查询 Q Q Q、键 k k k、值 v v v权重矩阵集(Transformer使用八个注意力头,因此我们对于每个编码器/解码器有八个矩阵集合)。这些集合中的每一个都是随机初始化的,在训练之后,每个集合都被用来将输入词嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间中。
multi-head attention则是通过h个不同的线性变换对Q,K,V进行投影,最后将不同的attention结果拼接起来:
M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , . . . , h e a d h ) W O h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) MultiHead(Q,K,V) = Concat(head_1,...,head_h)W^O \\ head_i = Attention(QW_{i}^{Q},KW_{i}^{K},VW_{i}^{V}) \\ MultiHead(Q,K,V)=Concat(head1,...,headh)WOheadi=Attention(QWiQ,KWiK,VWiV)
从 h e a d i head_i headi公式中我们可以看出每一个 h e a d i head_i headi都独立维护一套Q/K/V的权值矩阵。假设我们有8个head,那么最终我们可以得到8个head值,然后拼接在一起作为一个向量,与 W 0 W_0 W0相乘,作为Multi-head self-attention 层的输出,计算流程如下图所示:
self-attention则是取Q,K,V维度相同。文章中attention的计算采用了scaled dot-product,即:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dkQKT)V
于是,图中的 Z = s o f t m a x ( Q K T d k ) V Z=softmax(\frac{QK^T}{\sqrt{d_k}})V Z=softmax(dkQKT)V,其中 d k d_k dk表示为Q,K,V维度。同时,作者同样提到了另一种复杂度相似但计算方法additive attention,在 d k d_k dk很小的时候和dot-product结果相似, d k d_k dk大的时候,如果不进行缩放则表现更好,但dot-product的计算速度更快,进行缩放后可减少影响。
Transformer以三种不同的方式使用了多头attention:
位置全链接前馈网络——MLP变形。之所以是position-wise(i/o维度一样)是因为处理的attention输出是某一个位置i的attention输出。。
Feed Forward Neural Network全连接有两层dense,第一层的激活函数是ReLU(或者其更平滑的版本Gaussian Error Linear Unit-gelu),第二层是一个线性激活函数,如果multi-head输出表示为Z,则FFN可以表示为:
F F N ( Z ) = m a x ( 0 , Z 1 W 1 + b 1 ) Z 2 + b 2 FFN(Z)=max(0,Z_1W_1+b_1)Z_2+b_2 FFN(Z)=max(0,Z1W1+b1)Z2+b2
Position-wise feed forward network其实就是一个MLP 网络,每个 d_model 维向量 x 在此先由 x W 1 + b 1 xW_1+b_1 xW1+b1变为 d f d_f df维的 x’,再经过 m a x ( 0 , x ′ ) W 2 + b 2 max(0,x')W_2+b_2 max(0,x′)W2+b2回归 d_model 维。hidden_size变化为:768->3072->768(或者512->2048->512)。之后就是对hidden层进行dropout,最后加一个resnet并normalization(tensor的最后一维,即feature维进行)。Transformer通过对输入的文本不断进行这样的注意力机制层和普通的非线性层交叠来得到最终的文本表达。
我们可以看大Decoder和Encoder的结构差不多, 不同的是Decoder有两个多头attention机制:
Decoder有N(默认是6)层,每层包括三个sub-layers(自下往上):
这里先明确一下decoder的输入输出和解码过程:
输入:encoder的输出 和 对应 x i − 1 x_{i-1} xi−1位置decoder的输出。所以中间的attention不是self-attention,它的K,V来自encoder,Q来自上一位置decoder的输出。向量K(键向量)和V(值向量)的注意力向量集来自 Encoder。这些向量将被每个解码器用于自身的“编码-解码注意力层”,并作用在Decoder的每一层。
解码:这里要特别注意一下,编码可以并行计算,一次性全部encoding出来,但解码不是一次把所有序列解出来的,而是像RNN一样一个一个解出来的,因为要用上一个位置的输入当作attention的query
输出:对应输入 x i x_i xi的输出词的概率分布
明确了解码过程之后最上面的图就很好懂了,这里主要的不同就是新加的attention多加了一个mask,因为训练时的output都是ground truth,这样可以确保预测 x i x_i xi时不会接触到未来的信息,下面是一个详细解析图:
以下动态图,可以体验下,第一个动图只展示了两个Encoder和两个Decoder,正常是各自6个,这里怕读者误解。生成第一个单词“I”。
上面的动图可以看到,Encoders的输出结果值对一个Decoders的输出有影响,也就是对第一个单词“I”有影响,对第二个单词“am”没有什么作用。这个需要继续研究一下。再说一下
不知道大家注意到没有,如下图红色箭头:
这个红色箭头表明这个可能是一个残差链接,残差连接其实在很多网络机构中都有用到。原理很简单,假设一个输入向量x,经过一个网络结构,得到输出向量 f ( x ) f(x) f(x),加上残差连接,相当于在输出向量中加入输入向量,即输出结构变为 f ( x ) + x f(x)+x f(x)+x,这样做的好处是在对x求偏导时,加入一项常数项1,避免了梯度消失的问题。详细的可以去参考这方面知识-Residual connection。
mask的思想非常简单:就是对输入序列中没某些值进行掩盖,使其不起作用。在论文中,做multi-head attention的地方用到了padding mask,在decode输入数据中用到了sequence mask。
Transformer是第一个用纯attention搭建的模型,不仅计算速度更快,在翻译任务上也获得了更好的结果。Google现在的翻译应该是在此基础上做的,但是请教了一两个朋友,得到的答案是主要看数据量,数据量大可能用transformer好一些,小的话还是继续用rnn-based model。
图解Transformer
【NLP】Transformer详解
Transformer注解及PyTorch实现
BERT大火却不懂Transformer?读这一篇就够了
详解Transformer (Attention Is All You Need)