目录
Transformer提出的背景
Transformer 架构
Transformer 解析
Encoder-Decoder解析
Self-Attention
The Beast With Many Heads
位置编码
Transformer模块结构
Encoder模块
Decoder模块
总结
之前介绍了通道注意力SENET、空间注意力Spatial Transformer Networks、混合域注意力CBAM、坐标注意力CANET、自注意力Non-local Neural Networks和自注意力与混合域相结合的DANET。主流的注意力模型基本都了解到了,那么接下来,介绍一下目前大热的Transformer。
在2017年,Google提出的Attention Is All You Need在NIPS发表,它完全去掉了RNN和CNN的网络结构,而仅仅使用注意力来完成RNN的翻译任务,并且Transformer效果更好。虽然它现在还有很多问题需要完善(最后会介绍),但是它优越的性能使它成为了最炙手可热的研究方向。接下来,有了Attention的基础之后,我将学习并分享目前火热的Transformer。今天主要分享一下《Attention Is All You Need》中的核心-Transformer的结构,为后续Transformer打下基础。
作者将Transformer以Attention机制组成,采用self-Attention和Feed Forward Neural Network构成。Transformer采用Attention机制的原因是考虑到RNN(或者LSTM,GRU等)的计算限制为是顺序的,也就是说RNN相关算法只能从左向右依次计算或者从右向左依次计算,这种机制带来了两个问题:
时间片 t的计算依赖 t−1时刻的计算结果,这样顺序执行限制了模型的并行能力。
顺序计算的过程中信息会丢失,尽管LSTM等门机制的结构一定程度上缓解了长期依赖的问题,但是对于特别长期的依赖现象,LSTM依旧无能为力。
Transformer的提出解决了上面两个问题:
首先它使用了Attention机制,将序列中的任意两个位置之间的距离缩小为一个常量;
其次它不是类似RNN的顺序结构,因此具有更好的并行性,符合现有的GPU框架。
先介绍一下Transformer的架构图,有一个整体的印象(可以先不用了解里面的具体模块,下面会详细解释,这里就有一个初步印象即可),下图就是原文中的Transformer架构图:
它由左右两部分组成,左边是encoder,右边部分是decoder,下面接单介绍一下它们的作用:
Encoder:输入是数据的Embedding,再加上位置编码,然后进入一个统一的结构,这个结构可以循环很多次(N次),也就是说有很多层(N层)。每一层又可以分成Attention层和全连接层,再额外加了一些处理,比如Skip Connection,做跳跃连接,然后还加了Normalization层。其实它本身的模型还是很简单的。
Decoder:第一次输入是前缀信息,之后的就是上一次产出的Embedding,加入位置编码,然后进入一个可以重复很多次的模块。该模块可以分成三块来看,第一块也是Attention层,第二块是Attention,而不是Self-Attention,第三块是全连接层。也用了跳跃连接和Normalization。
输出:最后的输出要通过Linear层(全连接层),再通过softmax做预测。
如果你对transformer一点也不了解,那么你就把上面的整体架构图忘了吧(因为它会让你觉得transformer很难)。我们会在下面慢慢详细介绍transformer,看完这篇文章之后,你再返回来看上面的架构设计,就会觉得transforme设计得原来如此简单。在了解具体transformer是如何工作的以前,我们先宏观看一下它的作用,如下图(1)所示:
(1)transformer宏观示意图
将transformer看做一个黑匣子,在机器翻译的模型中,将输入的句子输入transformer模块,然后输出另外一种语言,这就是transformer的作用。
在稍微进一步观看transformer的结构,经过宏观的了解transformer作用之后,再初步的了解一下transformer整体结构,如图(2)所示:
(2)transformer初步示意图
从图2可以看出,transformer是将Input的数据经过transformer之后,翻译成目标语言,transformer里面有encoder和decoder两大模块组成(注意,图中encoder和decoder模块用的是复数形式,说明:transformer可以有多个重复的encoder和decoder组成)。
初步了解transformer之后再进一步,请看下图:
(3)transformer进一步示意图
正如(2)中所说的,transformer有encoder和decoder组成,并且encoder和decoder模块可以由多个组成。原文中,encoder由6个编码block组成,decoder同样由6个解码block组成。
这就是transformer的组成,很简洁吧,下面再继续跟进一步,我们分别详细介绍encoder和decoder的里面的模块设计。
正如上面所说,transformer是由encoder-decoder组成,下面我们想解析这个模块组成。我们还是由宏观开始一层一层剥开分析encoder-decoder结构,先看一下encoder的结构如图(4)所示:
(4)encoder初步结构
图(4)由下往上看,encoder的输入首先进入self-attention模块(如果你看过我之前attention的文章肯定知道该模块的作用),如果你了解一些self-attention的话更容易理解这里。如果你一点不了解attention的话也没关系,我们后面还会详解讲解self-attention的,这里你可以先理解为在编码特定单词的时候,self-attention会查看句子中的其他单词的相关性。数据经过self-attention层处理之后,进入到前馈神经网络层(Feed Forward Neural Network),前馈神经网络输出encoder的结果向量,送入到decoder中。
(5)encoder-decoder初步结构
从图5可以看出,encoder的输出送入到decoder模块中,decoder与encoder结构相似,但是在self-attention和Feed Forward之间多了encoder-decoder attention层(这部分后面解释)。
到这在宏观上解释了encoder-decoder的设计和流程,下面的小节我们会解释一下Input部分。但是在解释Input之前,我们需要对encoder的输入和输出的公式有一个宏观的理解(后面我们会一一解释)。
我们知道,encoder在输入数据之后就进入了self-attention层,会输出一个向量数据Z,这个流程是下面的公式(1):
(1)
数据经过self-attention之后输出向量Z,然后encoder将Z送入到Feed Forward中,Feed Forward是由两个全连接层组成,并且第一层的全连接之后又激活函数Relu,第二层全连接没有激活函数,是一个线性激活。
如果你从没有了解过注意力或者transformer的话,看到这个公式肯定很懵逼。这很正常,我第一次见到它的时候就感觉脑袋很大,个人感觉谁第一次见到这玩意儿都一样。在这里,你知道self-attention是经过这个公式得到向量Z送入到前向反馈网络中就可以了,后面在self-attention小节里面,我们会详细解释这个公式。
在初步了解transformer的encoder-decoder结构之后,我们梳理一下从input-encoder-decoder的流程。输入数据如图(6)所示:
(6)输入向量化
从图6可以看出,论文将每个单词都嵌入到一个大小为 512 的向量中,然后输入到encoder中,如图(7)所示:
(7)encoder数据传递流程
如图(7)将输入的句子经过Input向量化之后得到x1、x2、x3的三个(1,512)向量输入到encoder中。在encoder经过self-attention层得到带注意力的向量Z,再将Z输入到Feed Forward中,再输出到decoder或是下一个encoder中。
上面小节我们了解到了transformer的整体架构和数据流程,现在我们要解释一下transformer的核心内容:self-attention。之前看过我博客的同学或者本身对self-attention有了解的朋友可以更容易的理解self-attention,但是即便你没有这方面的经验也没关系,我保证你看完这小节之后就会完全明白它的原理。
self-attention与attention一样,它的目的是想让输入的数据有一个更广的视野,也就是在输入数据里面有一个权重系数的分配,无论你输入的数据是句子还是图片,例如下面这个句子:
” The animal didn't cross the street because it was too tired
” (1)
这句话中,it指的是什么,对我们来说非常简单,因为我们读完这句话之后,我们就知道it指的是animal。因为我们有一个全局的视野,翻译过来就是说,我们把整个句子都通过眼睛输入到了我们的脑子里面。但是如果我们只看it,我们也不知道它指的是什么。所以,同样的原理,当模型输出力it的时候,如果我们没有self-attention,模型没有全局的一个视野,它无法将it和animal联系起来,那么模型就无法准确的翻译it的原有意思。
当模型处理每个单词(输入序列中的每个位置)时,self-attention允许它查看输入序列中的其他位置以寻找有助于对该单词进行更好编码的线索,我们把这个过程展现到如图(8)所示:
(8) self-attention例子
从图8中可以看出,当我们在encoder中对单词“it”进行编码时,self-attention更专注于“The Animal”,也就是说the和animal被分配了更多的权重,并将其表示的一部分分配到“it”的编码中,这样it的编码中就会更倾向于获取animal的数据分配。
之前我们就讲过数据经过向量化后进入encoder,encoder首先进入self-attention层,它输出带有权重分配的向量数据Z。那么,self-attention是如何得到带有权重分配的向量的呢?就是根据我们的公式(1),现在我们要填一下之前留的这个坑。
self-attention中,每个数据会被分配三个不同的向量(数据可能是特征图也可能是句子,还是拿上面那句话举例子,这里指一个单词),就是说每个单词会分配三个不同的向量,这三个向量分就是公式(1)中的Q、K、V。对于上面的句子来说,每个单词transformer会创建三个向量,一个是Query向量(Q)、Key向量(K)和Value向量(V),论文里面维数都是64(如何来的下面会将讲解),并且这三个向量是可以进行训练的。
肯定会有同学的心中有疑问,Q、K、V都是什么?不要急,稍后我们会讲。在讲解Q、K、V是什么之前,我们先了解一下Q、K、V是如何得到的。Q、K、V三个向量是通过3个矩阵作为参数训练而来,三个矩阵的大小是相同的,原文设置为(512,64),如图(9)所示:
(9)、、示意图
假设我们正在计算图(9)中的一个单词“Thingking”,它是一句话中的一个单词。首先将这个单词进行embedding,将“Thingking”转化为输入数据X1,大小为(1,512),输入到self-attention层,self-attention里面有三个矩阵、、,他们大小是(512,64)。那么,将X与、、三个矩阵相乘就得到了维度为(1,64)的Q、K、V三个向量,如图(10)所示:
(10)Q、K、V计算图
X矩阵中的每一行对应于输入句子中的一个词,这里仅仅指“Thingking”的向量化结果(1,512)。
得到Q、K、V三个向量之后,我们解释一下它们是什么。Q、K、V三个向量是模型计算和思考的抽象表示。就是说这三个向量完成了self-attention的实现,还是举个例子来说,如图(11)所示:
(11)self-attention示意图
图(11)这个例子中,“Thinking”经过embedding转化为(1,512)的向量,与、、三个矩阵相乘,得到Q、K、V三个向量,这里分别是q1、k1、v1。然后计算该向量的得分score = q1·k1,再进行归一化操作(K矩阵长度的平方根 = 8)优化结果,最后进行softmax得出权重分配系数。
我们再根据上面这个self-attention的例子梳理一下transformer的计算流程:
1、在Input层将输入数据向量化X,准备将输入向量X送入self-attention,这是transformer的input层做的事;
2、将数据X送入到self-attention,利用 、、 矩阵,得到Q、K、V三个向量;
3、计算向量中每个值的得分,score = Q·K;
4、优化相似度除以通用参数,这里d的维度是64;
5、将数据进行softmax得到注意力权重系数;
6、将权重系数与v进行点积,得到带注意力的向量v;
7、将每个v进行求和得到输出结果;
看到图(11)的流程,我们明白了Q、K、V的计算过程。但是,肯定不明白为什么这么做?这么做的意义是什么?下面我们介绍一下self-attention,并且说明Q、K、V在self-attention中起的作用。先解释一下Q、K、V的字面意思:
Q:Query,查询,表示要查询的数据的具体属性;
K:key,关键字,表示在查询库中的每个数据的属性;
V:Value,数值,表示查询出来的具体数据;
在上面句子的例子:” The animal didn't cross the street because it was too tired
”,我们先要知道it在句子中的具体含义。it这里就代表Q,句子里面的每个词就代表K,V代表句子中每个词的具体数据。具体流程,请看图(12):
(12)self-attention计算示意图
图(12)中,代表输入句子(1),表示“it”,
Q = X · =
K= X · =
V=X· =
从图(12)可以看出来,与所有的k有关系,与其本身有关系。换句话说:的视野是整个句子和其本身。通过矩阵得到q1,计算q1与整个句子其他数据的相似度,这里使用点积相似度(点积相似度为什么好使请参考我之前的博文:相似度计算)。这样就得到和在整个句子里面的权重分配(transformer是进行归一化优化的),再将权重分配进行softmax得到权重系数,最后再将权重系数与每个数据的结果V进行点积,就得到了带注意力权重的V,最后在将V进行求和,也就是输出结果向量Z。从这个图(12)这个例子的只算过程,大家再回头与之前的公式(1)进行比较,是不是发现计算过程就是这个公式(1):
(1)
并且与上面介绍的transformer的计算流程一致(如果有没看懂的同学可以在下面评论或者直接私信我)。
到这,大家应该能理解transformer的计算流程和self-attention的原理了吧,再附上图(13)再理解一下这个过程吧:
(13)transformer计算流程图
self-attention计算到此结束。结果向量是我们可以发送到前馈神经网络的向量。然而,在实际实现中,为了更快的处理,这个计算是以矩阵形式完成的。最后再用一个清晰明了的矩阵式公式图(14)总结一下self-attention的计算过程:
(14)矩阵形式的self-attention计算示意图
transformer进一步细化了自注意层,增加multi-headed self-attention模块,它是由多个self-attention组合而成。为什么要用“多头兽”呢?因为不同的随机初始化映射矩阵、、 可以将输入向量映射到不同的子空间,这可以让模型从不同角度理解输入的序列。因此同时几个self-attention的组合效果可能会优于单self-attention,这种同时计算多个attention的方法被称为Multi-Head self-Attention。
在一个self-attention模块与三个矩阵参数分别是 、、 ,假设Multi-Head self-Attention由M个self-attention模块组成,那么Multi-Head self-Attention的参数矩阵一共就是3M个。
那么,原来的self-attention层就被Multi-Head self-Attention替换,如图(15)所示:
(15)Multi-Head self-Attention Layer示意图
注意的是,虽然Multi-Head self-Attention层中的每个self-attention都是同一个输入,但是为了得到更多维度的数据,每个self-attention的 、、 参数矩阵都是不同的,所以每个self-attention的输出向量C都是不同的。将self-attention中每个单词得到的输出C进行通道维度的concat从而得到Multi-Head self-Attention的输出。Multi-Head self-Attention的计算流程如图(16)所示:
(16)Multi-Head self-Attention Layer计算流程图
计算流程与self-attention基本一致的,只不过由原来的单个Q、K、V编程了M*(Q、K、V),最后进行数据向量z的concat,得到Multi-Head self-Attention的输出向量Z。
再回头看一下图(8)的例子中,我们使用了self-attention得到了“it”的attention,现在用“多头兽”的话就变成了图(17):
(17)Multi-Head self-Attention 例子
当我们对“it”这个词进行编码时,一个注意力头最关注“animal”,而另一个注意力头关注“累了”——从某种意义上说,模型对“it”这个词的表示在一些表示“animal”和“tired”。增加了attention的关注维度。
虽然“多头兽”增加了attention的维度,但是有一个问题需要解决,如下图所示:
上图中可以看出,我们介绍的Transformer模型并没有捕捉顺序序列的能力,也就是说无论句子的结构怎么打乱,Transformer都会得到类似的结果。换句话说,Transformer只是一个功能更强大的词袋模型而已,它并没有按照得到正确的逻辑,模型中缺少的一件事是一种解释输入序列中单词顺序的方法。
为了解决这个问题,transformer为每个输入embedding添加了一个位置编码。transformer在编码词向量时引入了位置编码(Position Embedding)。具体地说,位置编码会在词向量中加入了输入数据的位置信息,这样Transformer就能区分不同位置的数据了。位置编码是一个长度和embedding之后的数据一致的特征向量,这样便于与输入数据进行单位加的操作,如图18所示:
(18)位置编码示意图
从图(18)可以看出来,输入的时候,不仅有数据的向量,还要加上Positional Encoding(位置编码),即输入模型的整个Embedding是Word Embedding与Positional Embedding直接相加之后的结果。网络做自注意力的时候,不但要知道注意力要聚焦在哪个数据上面,还想要知道两个数据之间的互相距离有多远。为什么要知道数据之间的相对位置呢?因为Transformer模型没有用RNN也没有卷积,所以为了让模型能利用序列的顺序,必须输入序列中元素的位置。所以我们在Encoder模块和Decoder模块的底部添加了位置编码,这些位置编码和输入的向量的维度相同,所以可以直接相加,从而将位置信息注入。
想要知道元素之间的距离,就得知道元素的坐标。有很多不同衡量距离的方式,这里使用不同频率的sin和cos函数:
上面公式中,pos是位置,i是维数。也就是说,位置编码的每个维度对应于一个正弦信号。请看一下面这个例子来解释这个公式:
上面解释了transformer的总体结构、encoder-decoder结构、self-attention、The Beast With Many Heads和位置编码,上面这些模块基本完成了transformer的组成。在这个章节里,我们将完善transformer整体结构的讲解,让大家对transformer有从内到外的清晰认识,测地明白其原理。
encoder模块设计如图(19)所示:
(19)encoder结构图
输入数据经过embedding之后,进入到encoder模块,encoder模块有两层组成:分别是self-attention层和Feed Forward层组成。如图(19),输入数据为“Thinking Machines”送入encoder,进入到enoder中,首先进入self-attention层,得到带注意力的向量z1、z2,进行相加和Normalize得到带注意力的向量Z,并且这个结构是带有残差结构的(图(19)虚线部分),解决深度学习中的梯度消失的问题。接着将向量Z送入全连接层Feed Forward中。最后,得到encoder的输出。
上面小节详细讲述了self-attention层,Feed Forward并没有介绍。这里做一下解析。Feed Forward层是由两个全连接层组成,第一个全连接层有激活函数Relu,第二个为线性函数,公式如下所示:
上面公式清晰的描述了这两个全连接层的流程,经过Feed Forward得到encoder的输出结果。下面这张图是encdoer的模块流程剖析:
上图可以看出来,输入数据经过self-attention(这里是多头兽,道理一致),得到c1...cm向量数据,经过前馈网络Feed Forward得到输出结果,并且前馈神经网络是共享参数的。
decoder与encoder结构相似,仅仅是多了一层encoder-decoder attention层,并且它的self-attention是带有“mask”的:如图(20)所示:
(20) decoder结构图
上图是encoder-decoder结构图,这里我们只看右边的decoder结构。远论文中中Decoder是N=6层堆叠的结构。被分为3个SubLayer,Encoder与Decoder有三大主要的不同:
Decoder SubLayer-1使用的是“Masked” Multi-Headed Attention机制,防止为了模型看到要预测的数据,防止泄露。
SubLayer-2是一个Encoder-Decoder Multi-head Attention。
LinearLayer和SoftmaxLayer作用于SubLayer-3的输出后面,来预测对应的word的probabilities 。
encoder首先处理输入序列,然后将最后的encoder的输出转换为一组注意力向量 K 和 V。 这些将由每个encoder在其“encoder-decoder attention”层中使用(注意:这里是attention,并不是self-attention,所以,该层的输入时encoder的K和V,decoder自己的输入作为Q),帮助decoder专注于输入序列中的适当位置,decoder执行流程如图(21)所示:
(21)decoder流程图
Transformer Decoder的输入:
初始输入:前一时刻Decoder输入+前一时刻Decoder的预测结果 + Positional Encoding;
中间输入:Encoder Embedding。
“Masked” Multi-Headed Attention中的mask是为了使得Decoder不能看见未来的信息。也就是对于一个序列,在time_step为t的时刻,我们的解码输出应该只能依赖于t时刻之前的输出,而不能依赖t之后的输出。因此我们需要想一个办法,把t之后的信息给隐藏起来,从而提高预测准确度。
decoder输出数据后,我们如何把它变成一个人可以理解的数据?这就是最后一个 Linear 层的工作,然后是一个 Softmax 层。线性层是一个简单的全连接神经网络,它将解码器堆栈产生的向量投影到一个更大的向量中,最后经过Softmax 进行预测最大概率输出。
最后在看一下transformer的完整执行图,如图(22):
(22)transformer执行图
我们已经把transformer整体知识基本介绍完毕,最后再看一下第一小节的transformer的架构图,是不是已经非常简单了:
文章的最后做一下总结吧,总结一下transformer的优缺点。
优点:
(1)虽然Transformer也是神经网络的模型,但是它只有全连接和self-attention的结合。它在RNN中已经占有SOTA地位,并且CNN并且取得了非常不错的效果,是一个非常前沿、有前景的研究方向。
(2)Transformer的解决长距离的依赖关系。
(3)算法的并行性非常好,可以多GPU共同训练。
缺点:
(1)模型没有捕捉局部特征的能力。
(2)位置信息的设计是值得优化的地方。
注:下一篇我们将会介绍一下Transformer在CV中的较新论文ViT(《An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale》)