赞
踩
在学习Transformer之前,你需要学习一下Seq2Seq Attention的内容,可以参考:
几张图彻底搞定Seq2Seq
Seq2Seq Attention(这三篇就够了,精心发掘整理)
Seq2Seq Attention输入输出维度分析(最全面细致)
好了,那下面进入正文
Transformer在2017年Google的一篇论文“Attention Is All You Need”被提出,是2018年大杀器Bert网络结构的基础。提到Transformer就不得不提Seq2Seq Attention,Seq2Seq Attention作为一个Encoder-Decoder结构,可认为由两个RNN网络结构组成,尽管RNN在NLP处理任务中很受欢迎,但其也有不容忽视的问题:1)循环网络结构导致并行计算受限,计算效率低;2)长距离依赖仍不能得到很好地解决。尽管LSTM一定程度上解决了RNN所具有的长距离依赖和梯度爆炸问题,但仍遗留了序列化的计算问题。
而Transformer最突出的特点正是其优秀的并行计算能力,同时也将上述的计算次数控制在了一个常量的水平(尽管以一定的解决效率作为代价)。接下来让我们具体看看,Transformer到底长什么样:
上图中左边是Encoder部分,右边是Decoder部分,在论文中,N等于6,也就是说Transformer由6个Encoder和6个Decoder组成以及相应的输入输出组成。
我们首先看一下Transformer相对Seq2Seq Attention增加的地方:
Self-Attention
Multi-Headed-Attention
Positional Encoding
Residuals
Layer Norm
Masked
接下来我会为你一一介绍这些部分的原理和作用。下面我们将引入几段长度不等的输入数据从而从不同的侧面来说明Transformer的原理。
比如我们有一段法语翻译成英语的翻译,使用Transformer的大致流程如下:
此时,Transformer有点像一个黑盒子,而黑盒子的内部基本单元是由Encoder和Decoder组成的,这一点和Seq2Seq Attention的基本结构是类似的。但是与Seq2Seq Attention不同的是,Transformer内部由多个Encoder和多个Decoder组成(论文中分别都是6个),并且各个Encoder(以及Decoder)之间不共享权重,如下图:
由上图可知,第一个Encoder接收来自Input的向量,而输出的向量作为第二个Encoder的输入,以此类推。而最后一个时刻Encoder的输出作为整个Decoder输入的一部分。不过为什么Encoder和Decoder堆叠的是6层呢?
我们再看一个寻找与代词it相关性单词的例子:
The animal didn’t cross the street because it was too tired.
上面左图是使用了2层Encoder(因为Layer从0开始计算)而右图使用了6层Encoder,颜色越深表示与目标单词的相关性,可见,若果层次太浅则无法寻找到与it相关的单词(最相关的是自己),但经过6层的训练,it与animal的相关性最强已经显而易见了。这也是Transformer需要多层Encoder-Decoder的原因。
好了,我们再回过头来看看Encoder的内部结构:
以及Decoder的内部结构:
可见每个Encoder由一个Self-Attention和一个Feed Forward组成,而一个Decoder则在此基础上多了一个Encoder-Decoder Attention。
细心的朋友会发现了,你这里给出的Encoder和Decoder内部结构和文章开头Transformer的Encoder和Decoder内部结构不一样呀,说的没错,不过别急,接下来我们会一步步的拆解Encoder和Decoder的内部结构。
在上图的基础上我们拆解一下Encoder的内部结构:
这里我们为了简单,一句话只有两个单词Thinking Machines。然后每个单词都映射到4维的向量空间中。当然句子的长度可以比较长,并且你可以映射到几十几百维,我们这里只是为了画图方便而做的简化。
词向量x经过Self-Attention后生成向量z,再经过Feed Forward网络生成高阶向量r进入下一个Encoder。
你可能又会要问,那图中的词向量x又是如何经过Self-Attention得到向量z的呢?在进一步拆解Self-Attention之前,我们先来回归一下Seq2Seq Attention机制。如下图的右上部分:
撇开具体细节,大致就是Query和Key计算出相关度或者说权重系数(Attention score,也就是e),这个权重系数再分别乘以对应的Value(这里的Key和Value是相同的),得到的乘积求和后便得到了Attention Value。
其实Transformer也是借鉴了这一点,具体的q,k,v的计算方式也如上图中所示,由词向量x分别乘以系数矩阵W得到。每个单词均有对应的一套q,k,v。需要注意的是,这里的系数矩阵W同时起到了转换维度的作用,本例中是将4维的X向量转换成了3维,但是实际情况中可以转换为你需要的维度,这里同样是为了简化从而作图方便。
得到了三维向量q,k,v之后呢?
第一步,我们计算Attention score(et,i,t为当前时刻,i为相关联的时刻),这里采用了下图et,i的计算公式,先分别求出q1和k1,k2的点积再除以根号d。这里的dk是Attention的输出维度大小,一般默认为64,所以加个根号便是8了。也就是说我们虽然在次数画的是三维向量,但是实际上一般默认为64维度的向量。这样便分别得到了e11和e12了。
第二步,对上述的Attention score值进行softmax转换得到概率值α11和α12(也可以认为是value的系数)了,对于softmax不太清楚的同学可以点击超链接进行复习。
第三步,将上述概率值(α11和α12)分别乘以对应的v(v1和v2)值,每个维度对应相加后便得到了z1。
第四步,以此类推得到z2的值。
上面是以每个单词的词向量为单位进行计算的,其实我们可以把单词的词向量按照行拼接成一个输入单词的矩阵,如下图,X的每一行代表着一个单词向量。这样我们同样便得到了分别由q,k,v组成的矩阵Q,K,V.与X类似,Q的每一行代表着一个q向量,K,V与此相同。则Z便可由下图右边的公式直接得到。
好了,到这里Encoder中的Self-Attention已经拆解完毕了。
接下来…
我们开始上Multi-Headed-Attention
其实所谓的Multi-Headed-Attention就是多个Self-Attention拼接在一起而已
上图:
这里展示了2个Head,输入单词矩阵X分别与一套系数矩阵W相乘后得到相应的Q,K,V.需要注意的是,这里各个Head之间除了系数矩阵W以及Q,K,V矩阵对应的形状是一致的外,没有任何关系。这样,计算机就可以同时处理多个Head了,这也是Transformer相对于Seq2Seq Attention可以很好的并行化的原因。得到Q,K,V之后,我们再得到对应的Z:
我们把每个Head得到的Z按行拼接,如下图所示。
但是肉眼可见的是,Z的列数变成了以前的8倍,这无疑增加了后续的计算量,别急!调节矩阵形状的方法就是乘以一个特定形状的系数矩阵,如下图所示,8个[2,3]矩阵拼接的[2,24]矩阵乘以一个形状为[24,4]系数矩阵后便可得到一个[2,4]的矩阵,并且这个转换后的维度必须和词向量x的维度是一样的,至于原因,我们后面会提到。这里的Z其实相当于Seq2Seq中的Attention向量ct。
好了,我么来用一张图总结一下Multi-Headed-Attention:
有必要说明一下图中的R是怎么回事。我们一般不是有6个Encoder吗,除了第一个Encoder的input是X,其他的Encoder的输入就是R了,也就是上一个Encoder的output。
我们再来具体看一下Multi-Headed-Attention是怎么起作用的,如下图所示,左图是一个Head的情况,而右图是2个Head的情况(绿色表示另一个Head)。也就是多个Head综合考虑的信息更加全面(图中训练的不够,所以展示的效果并不好,只是要表达这个意思)
好的,搞定Multi-Headed-Attention之后,我们继续…
不知道你有没有注意到,上文的模型并没有描述词与词之间的顺序关系,也就是将一个句子的词打乱顺序,我们获得的Attention是不会发生改变的,这显然不符合我们对于句子的理解逻辑。为了解决这个问题,论文中加入了自定义位置编码(Positional Encoding),位置编码和word embedding 具有长度相同的特征向量,这样位置编码便可以和word embedding进行求和操作了。按上文的例子,x的词向量是4维的,那么位置编码也是4维的。两者求和便得到了新的x向量,在Transformer中,我们上述的计算都是基于这种带位置编码的x向量的。如下图:
维度确定了,那具体的值如何确定呢?
论文中给出了下面的公式:
其中i表示从0开始的维度,而pos的值表示了该位置编码对应的词向量在句子中的位置,从0开始取值。dmodel表示词向量的维度大小。这里PE的小括号中除了有位置pos信息,还有2i和2i+1,一个表示偶数,另一个表示奇数,也就是说这个公式应该是偶数维度使用正弦函数,奇数维度使用余弦函数。但是这个奇偶性和接下来的图例是有冲突的。也就是说,位置编码可以按照这种奇偶性来进行编码,也可以按照下面的方式来进行编码,也就是位置编码不是唯一的。
我们通过一个例子来说明各个维度的位置编码的值是如何计算的:
首先我们将维度分为均等的两段,如上图的4维向量,则前2维一段而后2维为另一段。前一段我们采用第一个公式也就是sin函数来计算位置编码的值,而后一段我们采用cos函数来计算位置编码的值。需要注意的是,对于后一段来说,i从0开始记。也就是说,对于本例,i的值取0和1两个值。
对于第一个单词的位置编码,此时pos = 0,i取0时,sin(0) = 0,i取1时,sin(0) = 0,i=0时cos(0) = 1,i=1时,cos(0) = 1,所以第一个位置的编码为[0,0,1,1].对于第二个单词的位置编码,pos = 1,i = 0时,sin(1) = 0.84,i = 1时,sin(PE) = 0.0001,i = 0时,cos(PE) = 0.54,i = 1时,cos(PE) = 1,依次类推。
下图是一句含有20个单词的句子的位置编码值的示意,总维度为512维。绿色表示接近-1,黄色表示接近1。
好的,恭喜你,讲到这里我们我们已经把Positional Encoding的内容学完了。
接下来…
我们继续对Encoder中Self-Attention后面的结构进行拆解。在我们经过Self-Attention之后,我们获得了和输入词向量X维度相同的向量Z,还记得我们在上文提到的为何z的维度一定要和x相同吗?看这里不就用上了吗。如下图所示,X+Z后再进行LayerNorm归一化。什么是LayerNorm呢?简单来说就是以单个样本为目标(本例中因为输入是两个单词向量,所以是两个样本),把样本所有维度的值进行归一化。经过LayerNorm归一化后得到了新的z1和z2,然后将新的z1和z2输入进入Feed Forward网络,也就是我们前面说的全连接,你可以理解为又经历了一个线性转换,线性转换后的值再和z1和z2进行相加然后LayerNorm归一化的操作输出高阶向量R(也即r1和r2按行拼接)。
至于这里引入的残差(Residuals),个人认为Add这一步其实就是利用了残差的思想,将Self-Attention这一步得到的向量Z和原始的X向量相加再得到新的Z向量,这其实就是对Z向量的一个补充。同样的,Z向量经过Feed Forward之后生成R向量再与Z相加实际上也是对R向量的一个补充,也利用了残差的思想。
这样一个完整的Encoder便完成了。
当经过一定数量的Encoder(论文中为6)后,我们得到的高阶向量R作为每个Decoder的输入之一,如下所示:
那当R进入Decoder之后又经历了哪些操作呢?从上图我们可以看到,高阶特征R向量进入的是Decoder中的Encoder-Decoder Attention,这一步其实相当于对Decoder中的Z向量加入了Encoder中的Attention value,让Decoder中的输出考虑到了Encoder中输入值的影响。
我们再来回顾一下第一张图:
Decoder中的内部结构与Encoder的内部结构具有一定的相似性,比如在Encoder中通过Multi-Head Attention实现并行化计算,而在Decoder中与之对应的是Masked Multi-Head Attention。前面加了一个Masked是干什么的呢?
要想理解Masked(掩码),我们还得回过头来看一下Encoder中的Self-Attention的计算过程,由于输入的单词向量我们是已知的,所以我们在计算每个单词的Attention score(也即e)时候,是可以全部计算出来的,遗忘的朋友回过头去再看看Attention score的计算过程。但是在Decoder中,我们是先解出第一个单词,在将第一个单词作为输入依次解出后续的单词的,那我们在解第一个单词的时候由于后面所有的单词都是未知的,所以我们也就无法计算出第一个单词完整的Attention score。那找这个逻辑,Decoder就无法完成解码操作了呀。好在,我们在这里引入了Mask的概念进行掩码操作。
接下来我们再来了解一下什么是掩码。
由上文我们知道,当我们在解码当前单词的时候是不知道未来的信息的,于是我们对当前时刻以后的信息进行掩盖,具体的,我们将对应位置Attention score(e)值为负无穷,这样在进行softmax转换后得到对应的值便为0了。这也就是说,我们在解码当前单词时对未来的信息是没有“注意”的。
为了便于理解,我们假设输出的是一个含有10个单词的句子(在这篇文章中我们为了说明不同的问题已经引入了好几个不同长度组成的句子了)。我们在上文Encoder的Self-Attention中提到,q1和k1点积后除以根号下dmodel便得到了对应的Attention score (e11)了,由此可计算出来的α11。由于第一时刻的单词和后续的单词均无关,所以第一个时刻与后续单词的权重系数α值均为0(相对应的Attention score为负无穷大才能满足)。得到的Attention score(e11)的矩阵形式为[原值,负极大,负极大,负极大,负极大,负极大,负极大,负极大,负极大,负极大]。计算第二个单词的权重系数时,只能得到与第一,第二个单词的值,所以其Attention score的矩阵形式为[原值,原值,负极大负极大,负极大,负极大,负极大,负极大,负极大,负极大],以此类推,第十个单词Attention score的矩阵形式为[原值,原值,原值,原值,原值,原值,原值,原值,原值,原值]。提醒一下这里对于softmax一定要比较熟悉才能完全理解。如下图所示,用mask矩阵作用于每个Head的output矩阵上便可得到对应的Attention score的矩阵。得到的Attention score矩阵再经过softmax变换便可得到value的系数α,进而得到Attention的值z向量。
我们来看下比较完整的Transformer在翻译中应用的流程图
可以看出,初始的词向量经过Encoder后生成高阶向量R,而这个高阶向量R也作为Key和Value,Decoder中经过归一化(Add&Normalize)后的Z向量作为Query, 在Decoder中的Encoder-Decoder Attention这一步进行Attention value的计算(可以在上文中找到含有Encoder-Decoder Attention的详细流程图帮助你理解)。完整的步骤见下图,直到Decoder 中输入最后一个单词student的时候输出,标志着Decoder输出结束为止。
Decoder最后输出的是一个向量,但是我们最终需要的是单词,这要怎么做呢?这就是我们Linear和Softmax层所要解决的问题了。
Linear层就是一个全连接神经网络,他将Decoder输出的较小维度的向量映射到字典大小维度的向量,这个向量就是logits vector(对数向量)。如果我们训练集的字典大小是20000,那么这个对数向量的维度就是20000。这个向量再经过Softmax 转换为各个维度对应的概率值,最高概率对应维度所对应的单词便是Decoder这一步所要输出的单词。这也就是为什么再Linear层要将Decoder输出向量映射到字典大小维度向量的原因。
恭喜你,整个Transformer的前向训练过程已经学完了,那么Transormer是如何进行反馈训练的呢?现在我们假定词汇表(字典)大小是6,如下图所示,使用来代替。
一旦我们的词汇表确定之后,词汇表中的单词便可以用one-hot编码。下例中我们用one-hot编码对单词“am”进行了编码。
有了one-hot编码我们再来分析一下模型训练前后的变化,比如我们要在Decoder中训练输出单词“thanks”,如下图,上面是未经训练的模型输出,也就是模型参数都是随机初始化的,下面是我们期望的理想化的模型输出。我们要做的就是在随机模型参数的基础上不断的预测以接近我们期望的理想值。
那么两个概率怎么比较呢?在这里我们一般采用交叉熵损失.
更一般的,我们要翻译出一句话“I am a student”,如下左图,而我们经过了足够多的训练次数后,我们会生成下右图。当预测值与理想值的差值也就是交叉熵损失小到一定程度时,我们说这个模型就达到了我们的要求。
现在,模型一次产生一个输出,我们可以假设模型选择了概率分布中概率最高的词并且把其他词和概率丢弃了。这种选择方式称为贪婪解码(greedy decoding)。另一种选择方式是保留,比如,保留概率最高的两个单词(假设是“i”和“me”),然后下一步,模型运行两次:一次是假设第一个位置输出的是单词“i”,第二次假设第一个位置输出的单词是“me”,然后模型考虑位置#1和#2,保留错误更少的作为第一个位置(#1)的输出。重复这个步骤在位置#2和#3…等等。这种方式称为“束搜索”(beam search)。
参考文章:
Transformer 和 Transformer-XL——从基础框架理解BERT与XLNet
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。