学习时间:2022.04.22~2022.04.24
Encoder-Decoder 通常称作 编码器-解码器,是深度学习中常见的模型框架,很多常见的应用都是利用编码-解码框架设计的。
编码(encode),由一个编码器将输入序列转化成一个固定维度的稠密向量;
解码(decode),就是将之前生成的固定维度的稠密向量再转化成输出序列。
Encoder-Decoder 并不是一个具体的模型,而是一个通用的框架,并不是特指某一个具体的算法。Encoder 和 Decoder 部分可以是任意文字,语音,图像,视频数据,模型可以是 CNN,RNN,LSTM,GRU,Attention 等等。所以,基于 Encoder-Decoder,我们可以设计出各种各样的模型。
特别说明:
Seq2Seq(Sequence-to-Sequence)如字面意思,输入一个序列,输出另一个序列。Seq2Seq是Encoder-Decoder模型框架的一个典型代表,可以看作是Encoder-Decoder针对某一类任务的模型框架。
所谓的Seq2Seq任务主要是泛指一些Sequence到Sequence的映射问题,Sequence在这里可以理解为一个字符串序列,当我们在给定一个字符串序列后,希望得到与之对应的另一个字符串序列(如 翻译后的、如语义上对应的)时,这个任务就可以称为Seq2Seq了。
Encoder-Decoder强调的是模型设计(编码-解码的一个过程),Seq2Seq强调的是任务类型/目的(序列到序列的问题),满足输入序列,输出序列的任务都可以统称为Seq2Seq模型。
应用场景:机器翻译、文本生成、语言模型、语音识别、抽象文本摘要、文本摘要、语义分析、问答、语音转换。
以机器翻译为例,可以将法语翻译成英语,满足这样任务的模型也可以叫做Seq2Seq。
早期的Seq2Seq,其编码和解码都使用RNN、LSTM、GRU等(最早的提出就用了两个RNN),但缺点显而易见:
一是语义向量无法完全表示整个序列的信息;二是先输入的内容携带的信息会被后输入的信息稀释掉,或者说,被覆盖了,输入序列越长,这个现象就越严重。这就使得在解码的时候一开始就没有获得输入序列足够的信息, 那么解码的准确度自然也就要打折扣。
因此,后续又提出了Attention机制、或者采用CNN(CNN可以并行,改善了lstm的性能瓶颈)来改善任务效果。
目前的Seq2Seq模型主要分为三种:
补充:Seq2Seq模型使用技巧——“Teacher Forcing”
Teacher Forcing 用于训练阶段,主要针对上面第三种Decoder模型来说的,第三种Decoder模型神经元的输入包括了上一个神经元的输出 y ′ y' y′。如果上一个神经元的输出是错误的,则下一个神经元的输出也很容易错误,导致错误会一直传递下去。
而 Teacher Forcing 可以在一定程度上缓解上面的问题,在训练 Seq2Seq 模型时,Decoder 的每一个神经元并非一定使用上一个神经元的输出,而是有一定的比例采用正确的序列作为输入。即把正确的输出当做输入的一部分。
为了解决上面的弊端,提升模型效果,就需要用到Attention注意力机制来解决该问题(Attention一般是跟Seq2Seq模型一起用的)。
在解码的时候,让生成词不是只能关注全局的语义编码向量C,而是增加了一个“注意力范围”来动态处理和解码(即没有把所有的内容都放到一个向量中)。在解码当前词时,会寻找于原语句中相对应的几个词语,然后结合之前已经翻译的序列来翻译下一个词。
通过这种方法,模型能够有选择地关注输入序列的有用部分,从而了解它们之间的对齐关系,有助于模型更好地处理输入较长的句子。
直观理解:
当我们翻译 Knowledge 时,我们只需要将注意力集中在 “知识” 上面,翻译 “is” 的时候,只需要将注意力集中在 “就是” 上面,翻译 “力量” 时将注意力集中在 “power” 上面。这样,当 Decoder 在预测目标翻译的时候,就可以看到 Encoder 的所有信息,而不仅局限于模型的定长隐向量,并且不会丢失重要信息。
本部分参考:Attention原理详解。Seq2Seq +Attention的实现大致如下:
语义编码C随着decode的不同时刻,内容是动态变化的。用decode的每一个时刻的隐藏层输出去和encode的所有时刻的隐藏层输出计算一个Attention Score, 也就是decode当前时刻和enocde的每一个时刻的相关性,相关性大的计算的权重就大,最后对enocde的隐藏层做一个加权和作为decode当前时刻的语义编码C,这个C和decode的隐藏层做连接,然后接个全连接层(维度变一致)作为decode当前时刻的输出。
在Attention模型中,语义编码c1、c2、c3各不相同,y1、y2、y3的生成方法为: y 1 = f 1 ( C 1 ) ; y 2 = f 1 ( C 2 , y 1 ) ; y 3 = f 1 ( C 3 , y 1 , y 2 ) y_1 = f1(C_1);\ y_2 = f1(C_2, y_1); y_3 = f1(C_3,y_1,y_2) y1=f1(C1); y2=f1(C2,y1);y3=f1(C3,y1,y2)。
比如输入的是英文句子:Tom chase Jerry,生成:“汤姆”,“追逐”,“杰瑞”。注意力分配概率分布值的通用计算过程如下(以第一个词“Tom”为例):
当前输出词 Y i Y_i Yi针对某一个输入词j的注意力权重由当前的隐层 H i H_i Hi,以及输入词j的隐层状态 h j h_j hj共同决定;然后再接一个Softmax得到 [ 0 , 1 ] [0, 1] [0,1]的概率值。即通过函数 F ( h j , H i ) F(h_j,H_i) F(hj,Hi)来获得目标单词 Y i Y_i Yi和每个输入单词对应的对齐可能性。 a i j a_{ij} aij 衡量编码中第 j j j阶段的 h j h_j hj和解码时第 i i i阶段的相关性,最终解码中第 i i i阶段的输入的上下文信息 C i C_i Ci就来自于所有 h j h_j hj对 a i j a_{ij} aij的加权和。
那么整个翻译过程就可以表示为(每一个 C C C会自动去选取与当前所要输出的 y y y最合适的上下文信息):
C 汤 姆 = g ( 0.6 × f 2 ( T o m ) , 0.2 × f 2 ( C h a s e ) , 0.2 × f 2 ( J e r r y ) ) C 追 逐 = g ( 0.2 × f 2 ( T o m ) , 0.7 × f 2 ( C h a s e ) , 0.1 × f 2 ( J e r r y ) ) C 杰 瑞 = g ( 0.3 × f 2 ( T o m ) , 0.2 × f 2 ( C h a s e ) , 0.5 × f 2 ( J e r r y ) ) C_{汤姆} = g(0.6×f2(Tom),\ 0.2×f2(Chase),\ 0.2×f2(Jerry))\\ C_{追逐} = g(0.2×f2(Tom),\ 0.7×f2(Chase),\ 0.1×f2(Jerry))\\ C_{杰瑞} = g(0.3×f2(Tom),\ 0.2×f2(Chase),\ 0.5×f2(Jerry)) C汤姆=g(0.6×f2(Tom), 0.2×f2(Chase), 0.2×f2(Jerry))C追逐=g(0.2×f2(Tom), 0.7×f2(Chase), 0.1×f2(Jerry))C杰瑞=g(0.3×f2(Tom), 0.2×f2(Chase), 0.5×f2(Jerry))
如下图所示,红色代表输入输出相关性很强,相应的权重会很大,比如“ I ”和“我”的相关性就很大,“China” 和 “中”、“国” 两个输入的相关性很大。
输入的序列是“我爱中国”,因此,Encoder中的h1、h2、h3、h4就可以分别看做是“我”、“爱”、“中”、“国”所代表的信息。在翻译成英语时,第一个上下文c1应该和“我”这个字最相关,因此对应的 a11就比较大,而相应的 a12 、a13 、 a14 就比较小。c2应该和“爱”最相关,因此对应的 a22 就比较大。最后的c3和h3、h4最相关,因此 a33 、 a34的值就比较大。
总结一下就是:通过为每个单词分配一个权重,注意力机制能够保证当前翻译的单词对原文各个单词的关注点不同(就是对照着原文翻译)。由于这个权重可能大于1,为了方便我们使用softmax进行归一化,得到归一化权重,然后计算Encoder隐藏状态和其对应归一化权重的加权和,得上下文向量C(语义编码)。
注意力层的实现可以分为7个步骤。
首先计算第一个解码器隐藏状态(红色)和所有可用的编码器隐藏状态(绿色)。下图中有4个编码器隐藏状态和当前解码器的隐藏状态。要想输出Decoder的第一个隐藏的状态,需要给Decoder一个初始状态和一个输入,例如采用Encoder的最后一个状态作为Decoder的初始状态,输入为0。
计算Decoder的第一个隐藏状态(当前单词)和Encoder所有的隐藏状态的相关性。计算相似性/相似度的方法有很多(比如dot、general、concat等),这里采用点积的方式(默认两个向量长度一样)。
把得到的分数输入到softmax层进行归一化,归一化之后的分数(标量)加起来等于1,归一化后的分数代表注意力分配的权重 。
通过将每个编码器的隐藏状态与其softmax之后的分数(标量)相乘,我们得到对齐向量 或标注向量。这正是对齐产生的机制。
对齐向量进行求和,生成上下文向量 C 1 C_1 C1(语义编码)。上下文向量是前一步对齐向量的聚合信息。
将上下文向量输入到Decoder中进行解码,具体的解码器输入方式和模型有关。
Decoder每个时刻都会将第5步的注意力权重输入到Decoder中,此时Decoder中的输入有:Encoder的输出向量,以及Decoder上一时刻的隐向量。
反向传播
通过不断迭代,更新编码器和解码器的权重参数(以及Score中的权重,如果有的话),Decoder可以输出最终翻译的序列。
完整过程如下
score函数涉及点积运算(点积、余弦相似度等),其思想是度量两个向量之间的相似度。对于前馈神经网络评分函数,其思想是让模型在变换的同时学习对齐权值。
下图是几种主要的计算方式:
接下来会介绍3个基于Seq2Seq的NMT(Neural Machine Translation,机器翻译)架构,通过了解具体的使用方式来加深对Attention的理解,来源:Attention原理详解。
注意力机制用于机器翻译的开篇之作,作者在WMT’14英语 - 法语数据集上获得了26.75的BLEU分数。
[BLEU得分](https://blog.csdn.net/weixin_36815313/article/details/106649367#:~:text=BLEU 得分是一个有用的单一实数评估指标,用于评估生成文本的算法,判断输出的结果是否与人工写出的参考文本的含义相似。 不过它并没有用于语音识别(,speech recognition )。):是一个有用的单一实数评估指标(a single real number evaluation metric),用于评估生成文本的算法,判断输出的结果是否与人工写出的参考文本的含义相似。
标题中的“Align”意思是在训练模型的同时直接调整负责得分的权重。以下是该模型的特点:
编码器是一个双向(正向+反向)门控循环单元(BiGRU)。解码器是一个GRU,其初始隐藏状态是从反向编码器GRU的最后隐藏状态修改而来的向量;
注意层中的score函数是Additive/Concat;
下一个解码器时间步的输入是前一个解码器时间步(粉红色)的输出与当前时间步(深绿色)的上下文向量之间的拼接。
在WMT’15英德测试中,该模型的BLEU得分为25.9。这篇论文的关键点如下:
GNMT,Google的神经机器翻译,是前面两个例子的组合(深受第一个例子的启发)。该模型在WMT’14英法测试上达到38.95 BLEU,在WMT’14英德测试上达到24.17 BLEU。这篇论文的关键点如下:
Self Attention是提出Transformer的论文《Attention is all you need》中提出的一种新的注意力机制。
在机器翻译中,一般输入Source和输出Target内容是不一样的,如英文翻译成中文,Source是英文,Target是中文,上一节所讲的Attention机制发生在Target元素和Source中所有元素之间。
而Self-Attention顾名思义,指的不是Target和Source之间的Attention机制,而是Source内部元素之间或者Target内部元素之间发生的Attention机制,也可以理解为Target = Source的特殊情况下的Attention机制。其具体计算过程是一样的,只是计算对象发生了变化而已。
因此,我们可以把上一节所讲的Attention机制称为Seq2Seq Attention或传统Attention机制,Self Attention也可以被称为intra Attention(内部Attention)。
Seq2Seq Attention:本质上是目标语单词和源语单词之间的一种单词对齐机制;
Self Attention:可以捕获同一个句子中单词之间的一些句法特征或者语义特征。
本节来源:超详细图解Self-Attention。
在谈论Self Attention之前我们首先认识一下以KQV模型来解释的Attention机制,这也是Transformer最核心的部分。键值对Attention最核心的公式如下图。
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
其中, Q Q Q是Query、 K K K是Key、 V V V是Value。假定词的输入为 Q Q Q(Query),内存中以键值对 ( K , V ) (K,V) (K,V)形式存储其上下文。那么任何注意力机制其实都是Query到一系列键值对(Key, Value)上的映射函数。而Self Attention也即是Key=Value=Query。
接下来一步步拆解:
我们先抛开Q、K、V三个矩阵不谈,Self-Attention最原始的形态其实长这样(X是一个矩阵):
S o f t m a x ( X X T ) X Softmax(XX^T)X Softmax(XXT)X
第一步,看其中的 X X T XX^T XXT,一个矩阵乘以它自己的转置。
矩阵 X X T XX^T XXT是一个方阵,我们以行向量的角度理解,里面保存了每个向量与自己和其他向量进行内积运算的结果。向量的内积表征两个向量的夹角,表征一个向量在另一个向量上的投影。投影的值大,说明两个向量相关度高。
第二步,进行Softmax运算。
Softmax操作的意义就是进行归一化。Softmax之后,这些数字的和为1了。
Attention机制的核心就是加权求和,权重的来源就是这些归一化之后的数据。
最后,和矩阵 X X X再次相乘。
S o f t m a x ( X X T ) Softmax(XX^T) Softmax(XXT)的一个行向量与 X X X相乘,得到了一个与 X X X维度相同的行向量。在新的向量中,每一个维度的数值都是由三个词向量在这一维度的数值加权求和得来的,这个新的行向量就是"早"字词向量经过注意力机制加权求和之后的表示。
一张更形象的图是这样的,图中右半部分的颜色深浅,其实就是我们上图中黄色向量中数值的大小,意义就是单词之间的相关度。
Q、K、V三个矩阵并不是公式中最本质的内容,但他们究竟是什么,可以看下图:
其实,Q、K、V三个矩阵的来源是 X X X与权重矩阵 W W W的乘积,本质都是 X X X的线性变换。加入权重矩阵的原因是为了提高模型的拟合能力,权重矩阵 W W W是可以随网络一起训练而优化的。
我们可以看到 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,最终的公式中 Q K T QK^T QKT还会多除了一个 d k \sqrt d_k dk。 d k d_k dk是一个key vectors( K K K向量)的维度。加入的原因在于:
假设 Q , K Q, K Q,K里的元素的均值为0,方差为1,那么中 A T = Q T K A^T = Q^TK AT=QTK中元素的均值为0,方差为 d k d_k dk。当 d k d_k dk变得很大时, A A A中的元素的方差也会变得很大,如果 A A A中的元素方差很大,那么 S o f t m a x ( A ) Softmax(A) Softmax(A)的分布会趋于陡峭(分布的方差大,分布集中在绝对值大的区域)。
总结一下就是 S o f t m a x ( A ) Softmax(A) Softmax(A)的分布会和 d k d_k dk有关。因此 A A A中每一个元素除以 d k d_k dk后,方差又变为1。这使得 S o f t m a x ( A ) Softmax(A) Softmax(A)的分布“陡峭”程度与 d k d_k dk解耦,从而使得训练过程中梯度值保持稳定。
了解了核心的公式,我们可以对Self-Attention整个的计算过程做一个了解(来源:The Illustrated Transformer),一共6步:
计算Self-Attention的第一步是根据编码器,为每个输入的向量创建三个向量(在本例中为每个单词嵌入的词向量Embedding)。因此,对于每个单词,我们都有创建一个Query向量 Q Q Q、Key向量 K K K、Value向量 V V V。
这3个向量是通过将词向量(Embedding)乘以训练过程中的三个权重矩阵( W Q 、 W K 、 W V W^Q、W^K、W^V WQ、WK、WV)来生成的。这3个向量的维数小于Embedding向量,为64维(在本例中)。
注意:Embedding和Decoder输入/输出向量的维数都是512,这是Transformer的一种结构选择,可以使大部分的Multi-Head Attention计算恒定,维数不用更小。
计算Self-Attention的第二步是计算分数(相关性)。假设我们正在计算这个例子中第一个词“Thinking”的Self-Attention,我们需要根据输入句子的每个单词对这个词进行评分。分数决定了当我们在某个位置对单词进行编码时,将多少权重(注意力)放在输入句子的其他单词上。
分数的计算方法是将查询向量的点积与我们评分的相应单词的关键向量相乘( Q K T QK^T QKT)。因此,如果我们处理"Thinking"的自我关注,则第一个分数将是 q 1 q_1 q1和 k 1 k_1 k1的点积、第二个分数是 q 1 q_1 q1和 k 2 k_2 k2的点积。
将第二步所得到的分数除以 d k \sqrt d_k dk(本例中为8= 6 4 \sqrt64 64),使其具有更稳定的梯度,然后通过softmax对分数进行标准化操作传递结果。
此时Softmax分数决定了每个单词在此位置(对于当前单词)的表达量。显然,此位置的单词(当前单词本身)将具有最高的softmax分数,但有时关注与当前单词相关的另一个单词很有用。
第五步是将每个Value向量 V V V都乘以上一步计算得出的Softmax分数。
这一步的目的是保持我们想要关注的单词的值不变,并减小不相关的单词的权重(例如,将它们乘以0.001等小数字)。
第六步是汇总加权上一步与Softmax相乘后的Value向量。这就是对于当前单词(本例中为“Thinking”)产生的Self-Attention的输出。
最后,Self Attention会把每一个输入的词向量都做一遍Attention,然后在网络中不断更新迭代参数。
也是因为,它跟每一个input vector都做attention,所以没有考虑到input sequence的顺序。更通俗来讲,每一个词向量都与其他词向量计算内积,得到的结果就丢失了我们原来文本的顺序信息,你打乱词向量的顺序,得到的结果仍然是相同的。
但是对于具有时序性的文本数据,使用这种Self-Attention结构会破坏时序性。Transformer论文中使用了position embedding来解决,但只是一个权宜之计。
Transformer里面一共有三种Self-Attetnion,其介绍如下(后面有详细介绍):
RNN本身对于长距离的依赖关系有一定的捕捉能力,但由于序列模型是通过门控单元使得信息保持流动,并且选择性地传递信息。但这种方式在文本长度越来越长的条件下,捕捉依赖关系的能力越来越低,因为每一次递归都伴随着信息的损耗,所以有了Attention机制来增强对我们所关注的那部分依赖关系的捕捉。除此之外,序列模型也不能对层次结构的信息进行有效的表达。
引入Self Attention后会更容易捕获句子中长距离的相互依赖的特征,在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征;
Self Attention不依赖序列运算,可以增加计算的并行性。
Transformer架构最早是由谷歌在 2017 年的论文《Attention Is All You Need》中引入的,抛弃了以往深度学习任务里面使用到的CNN和RNN。它受欢迎的主要原因是其架构引入了并行化,Transformer 利用了强大的 TPU 和并行训练,从而减少了训练时间。此后在此基础上又出现了GPT、Bert等优秀模型,这些优秀模型都是在Transformer的基础上衍生出来的。
这里先对Transformer的大体框架做一个介绍,具体的解释放在后面。
Transformer由6个Encoder和6个Decoder堆叠而成,其结构如下图(其中: N × N× N×表示有 6 个这样的结构):
首先,模型需要对输入的数据进行一个Embedding操作,也可以理解为类似w2c的操作,Embedding结束之后,输入到Encoder层。
每一个Encoder有两个子层:Multi-Head Self-Attention层和Position-Wise Feed-Forward Network全连接前馈神经网络层(即全连接层),子层之间使用残差(Redidual Connection)和LayerNorm连接,避免梯度消失和爆炸。其中,全连接层可以并行计算,得到的输出会输入到下一个Encoder层。
Add表示残差Residual Connection,是为了解决多层神经网络训练困难的问题,通过将前一层的信息无差的传递到下一层,可以有效的仅关注差异部分。是借鉴了CNN中ResNet模型的思想。
Norm表示Layer Normalization,通过对当层的激活值归一化,加速模型的训练过程,使其更快的收敛。
每个Decoder也同样具有和Encoder类似的层级结构,有一点例外的是在Masked Multi-Head Self-Attention层和Position-Wise Feed-Forward Network全连接层之间还有一个Encoder-Decoder Attention层(也称为Multi-Head Context-Attention),用于帮助Decoder在解码时专注于输入句子中对应的那个单词。
Masked操作:其作用就是防止在训练的时候使用未来的输出的单词。比如训练时, 第一个单词不能参考第二个单词的生成结果。Masking就会把这个信息变成0,用来保证预测位置i的信息只能基于i前面的输出。
Transformer中缺少一种解释输入序列中单词顺序的方法,它跟序列模型还不一样。为了处理这个问题,Transformer给Encoder层和Decoder层的输入添加了一个额外的向量Positional Encoding,保存单词在序列中的相对或绝对位置,维度和Embedding的维度一样。
这个向量采用了一种很独特的方法来让模型学习到这个值,这个向量能决定当前词的位置,或者说在一个句子中不同的词之间的距离。这个位置向量的计算方法有很多种,论文中选取了三角函数的方式,如下:
P E ( p o s , 2 i ) = s i n ( p o s 1000 0 2 i / d ) P E ( p o s , 2 i + 1 ) = c o s ( p o s 1000 0 2 i / d ) PE(pos,2i)=sin(\frac{pos}{10000^{2i/d}})\\ PE(pos,2i+1)=cos(\frac{pos}{10000^{2i/d}}) PE(pos,2i)=sin(100002i/dpos)PE(pos,2i+1)=cos(100002i/dpos)
其中 p o s pos pos是指当前词在句子中的位置, i i i是指向量中每个值的index, d d d表示 P E PE PE的维度 (与词 Embedding 一样)。可以看出,在偶数位置( 2 i 2i 2i),使用正弦编码 s i n sin sin;在奇数位置( 2 i + 1 2i+1 2i+1),使用余弦编码 c o s cos cos。
最后把这个Positional Encoding与Embedding的值相加(加法),作为输入送到下一层。
如果假设词向量的维度为4,则一个Positional Encoding的真实示例可能是下面这样:
对于一个有8个词(Token Position为10),其词向量维度(Embedding Dimension)为64维的的句子,其外观可能如下图所示:
第一步:获取输入句子的每一个单词的表示向量X,X由单词的Embedding和单词位置的Embedding相加得到;
第二步:将得到的单词Embedding矩阵传入编码器 中,经过6个Encoder block后可以得到句子所有单词的编码信息矩阵 Z Z Z,(第一个Encoder的输入为句子单词的表示向量矩阵,后续Encoder的输入是前一个Encoder的输出,最后一个Encoder输出的矩阵就是编码信息矩阵 Z Z Z);
第三步:将 Encoder 输出的编码信息矩阵 Z Z Z传递到解码器中,编码器依次会根据当前翻译过的 1~ i 个单词,翻译下一个单词 i+1。在使用的过程中,翻译到单词 i+1 的时候需要通过Mask (掩盖) 操作遮盖住 i+1 之后的单词;
第四步:解码器接收了编码器的信息矩阵 Z Z Z,然后先传入一个翻译开始符 “< Begin >”,预测第一个单词 “I”,并输出;然后传入翻译开始符 “< Begin >” 和预测的第一个单词 “I”,预测下一个单词 “have”,并输出,以此类推。
总体的动态流程图如下:
接下来按照Encoder层到Decoder层的顺序,对Transformer内部的部件一个个进行详细分析。
Multi-Head Attention,也称为Multi-Head Seft-Attention。在Self Attention一节,我们已经知道Self-Attention是如何计算得到输出的了,而Multi-Head Self-Attention是由多个Self-Attention组合形成的。
下图是论文中Multi-Head Attention结构图( h h h即为Self-Attention的个数):
将输入矩阵 X X X分别传递到 h h h个不同的Self-Attention中,每一个Self-Attention都将有不同的Query/Key/Value权重矩阵(即不同的 W Q 、 W K 、 W V W^Q、W^K、W^V WQ、WK、WV),由此计算得到 h h h个不同的输出矩阵 Z i , i = 1 , 2 , … , h Z_i,\ i = 1,2,…,h Zi, i=1,2,…,h。原论文中 h = 8 h=8 h=8,即有8个Self-Attention。
但Self-Attention后面接的全连接层并不需要8个矩阵,而是只要1个矩阵(每个单词的向量)。因此,我们需要一种方法将这8个缩减到1个矩阵中。论文中采取的办法是将8个矩阵 Z i Z_i Zi相连接,然后乘以额外的权重矩阵 W O W^O WO。
最后,整个Multi-Head Attention的计算过程可以通过一张图表示:
Multi-Head的使用,可以从两个方面提高Self-Attention的性能:
它扩展了模型专注于不同位置的能力。在单个Self-Attention中, Z Z Z虽然包含了一点点其他编码的信息,但实际上可能还是由实际单词本身主导。如果我们翻译一个句子,比如“动物没有过马路,因为它太累了”,我们会想知道“它”指的是“动物”;
它为Attention层提供了多个“表示子空间”(representation subspaces)。在训练之后,每一组Query/Key/Value的权重矩阵集合,都会将输入的词嵌入(或上一层编码器/解码器的向量)投影到不同的表示子空间中。
可类比CNN中同时使用多个卷积核的作用,直观上讲,多头有助于网络捕捉到更丰富的特征/信息。
在Transformer中,每个编码器中的每一个子层之后,都会有一个Add(residual connection,残差连接)和一个Norm(layer-normalization,层标准化)操作。表示如下:
L N ( X + F ( X ) ) LN(X+F(X)) LN(X+F(X))
其中的 F ( X ) F(X) F(X)可以是Multi-Head Attention的 X X X,也可以是Feed Forward的 X X X。在网络中图示如下:
残差(residual):在数理统计中是指实际观察值与估计值(拟合值)之间的差。
ResNet残差连接的简化示意如下:
残差连接简单说就是:计算几层输出( F ( X ) F(X) F(X))之后再把 X X X加进来,可以让网络只关注当前差异的部分,从而有效解决梯度消失和网络退化的问题,通常用于解决多层网络训练的问题。
Norm指Normalization,通常用于RNN结构,其归一化数据的方法有很多种,但都有一个共同的目标:将每一层神经元的输入都转换成均值为0方差为1的数据,这样可以规范优化空间,加速收敛。然后再将数据输入激活函数(防止输入数据集中在激活函数的饱和区)。
Batch Normalization(BN):在每一层的每一批数据上进行归一化。
Layer normalization(LN):在每一个样本上计算均值和方差。
L N ( X i ) = α X i − μ i σ 2 + ε + β LN(X_i) = α\frac{X_i−μ_i}{\sqrt{σ^2+ε}}+β LN(Xi)=ασ2+εXi−μi+β
Feed Forward层比较简单,就是一个2层的全连接层。第一层的激活函数为Relu,第二层不使用激活函数,对应的公式: F F N ( X ) = M a x ( 0 , X ⋅ W 1 + b 1 ) ⋅ W 2 + b 2 FFN(X) = Max(0, X·W_1 + b_1)·W_2 + b_2 FFN(X)=Max(0,X⋅W1+b1)⋅W2+b2。
这个FFN的作用就是空间变换,FFN的加入引入了非线性(ReLu激活函数),变换了Attention Output的空间,从而增加了模型的表现能力(把FFN去掉模型也是可以用的,但是效果会差很多)。
Decoder层的第一个Multi-Head Attention采用了Masked操作,因为在翻译的过程中是顺序翻译的,即翻译完第i个单词,才可以翻译第i+1个单词。通过Masked操作可以防止第i个单词知道i+1个单词之后的信息(将第i+1之后的单词掩盖住)。
Mask表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。
Decoder在训练的过程中使用Teacher Forcing进行训练,即将正确的单词序列和对应输出传递到Decoder中。其步骤和Mutil-Head Attetion基本相同:
第一步:输入Decoder的向量矩阵 X X X和Mask矩阵,通过输入矩阵 X X X计算得到 Q , K , V Q, K, V Q,K,V矩阵。然后计算 Q Q Q和 K T K^T KT的乘积 Q K T QK^T QKT。
第二步:在 Softmax之前需要使用Mask矩阵遮挡住每一个单词之后的信息,遮挡操作如下(黑色部分即为0):
其实,Transformer模型里面涉及两种mask,分别是Padding Mask和Sequence Mask。其中,Padding Mask在所有的Scaled dot-product Attention里面都需要用到,而Sequence Mask只有在 Decoder的Self-Attention里面用到(Sequence Mask就是我们上面说的这种)。
Encoder-Decoder Attention是Decoder中的第二个Multi-Head Attention,与Multi-Head Attention差别不大,其主要区别在于其中Self-Attention的 K , V K, V K,V矩阵不是使用上一个Decoder的输出计算的,而是使用Encoder的编码信息矩阵 Z Z Z计算的。
即,根据Encoder的输出 Z Z Z计算得到 K , V K, V K,V,根据上一个Decoder的输出计算 Q Q Q(如果是第一个Decoder则使用输入矩阵 X X X进行计算),后续的计算方法与之前描述的一致。这样做的好处是在 Decoder 的时候,每一位单词都可以利用到Encoder所有单词的信息(这些信息无需Masked)。
Output层:当Decoder层全部执行完毕后,最后会输出一个实数向量,只需要在结尾再添加一个全连接层和softmax层,就可以把得到的向量映射为我们需要的词。
全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的、被称作对数几率(logits)的向量里。接下来的Softmax 层便会把那些分数变成概率(都为正数、上限1.0)。概率最高的单元格被选中,并且它对应的单词被作为这个时间步的输出。
假如我们的词典是1w个词,那么通过全连接层把输出的词向量升维到1w维(称为logits向量),再通过softmax将其转为概率,概率值最大的对应的词就是我们最终的结果(One Hot)。
到此,Transformer的内容也就介绍完了,最后再回顾一下Transformer的结构如下:
优点:
缺点:
实际特定任务下可以通过对Transformer里面的模块做相应的调整,从而使得模型可以适应多种特定的任务。有若干种在Transformer基础上引进额外Embedding的模型、改造Attention机制的模型、扩展Transformer的模型,来完成特定任务的适配,具体内容可见:transformer结构扩展(一)、transformer结构扩展(二)、transformer结构扩展(三)。
CMU联合Google Brain在2019年1月推出的一篇新论文《Transformer-XL:Attentive Language Models beyond a Fixed-Length Context》同时结合了RNN序列建模和Transformer自注意力机制的优点,在输入数据的每个段上使用Transformer的注意力模块,并使用循环机制来学习连续段之间的依赖关系。
具体介绍可见:Transformer-XL解读。