作者丨科技猛兽
编辑丨极市平台
本文对VisionTransformer的原理和代码进行了非常全面详细的解读,一切从Self-attention开始、Transformer的实现和代码以及Transformer+Detection:引入视觉领域的首创DETR。
Transformer是Google的团队在年提出的一种NLP经典模型,现在比较火热的Bert也是基于Transformer。Transformer模型使用了Self-Attention机制,不采用RNN顺序结构,使得模型可以并行化训练,而且能够拥有全局信息。本文将对VisionTransformer的原理和代码进行非常全面的解读。考虑到每篇文章字数的限制,每一篇文章将按照目录的编排包含三个小节,而且这个系列会随着VisionTransformer的发展而长期更新。
目录(每篇文章对应一个Section,目录持续更新。)
Section11一切从Self-attention开始1.1处理Sequence数据的模型1.2Self-attention1.3Multi-headSelf-attention1.4PositionalEncoding
2Transformer的实现和代码解读(NIPS)(来自GoogleResearch,BrainTeam)2.1Transformer原理分析2.2Transformer代码解读
3Transformer+Detection:引入视觉领域的首创DETR(ECCV)(来自FacebookAI)3.1DETR原理分析3.2DETR代码解读
Section24Transformer+Detection:DeformableDETR:可变形的Transformer(ICLR)(来自商汤代季峰老师组)4.1DeformableDETR原理分析4.2DeformableDETR代码解读
5Transformer+Classification:用于分类任务的Transformer(ICLR)(来自GoogleResearch,BrainTeam)5.1ViT原理分析5.2ViT代码解读
6Transformer+ImageProcessing:IPT:用于底层视觉任务的Transformer(来自北京华为诺亚方舟实验室)6.1IPT原理分析
Section37Transformer+Segmentation:SETR:基于Transformer的语义分割(来自复旦大学,腾讯优图等)7.1SETR原理分析
8Transformer+GAN:VQGAN:实现高分辨率的图像生成(来自德国海德堡大学)8.1VQGAN原理分析8.2VQGAN代码解读
9Transformer+Distillation:DeiT:高效图像Transformer(来自FacebookAI)9.1DeiT原理分析
1一切从Self-attention开始1.1处理Sequence数据的模型:Transformer是一个SequencetoSequencemodel,特别之处在于它大量用到了self-attention。
要处理一个Sequence,最常想到的就是使用RNN,它的输入是一串vectorsequence,输出是另一串vectorsequence,如下图1左所示。
如果假设是一个singledirectional的RNN,那当输出
时,默认
都已经看过了。如果假设是一个bi-directional的RNN,那当输出
任意时,默认
都已经看过了。RNN非常擅长于处理input是一个sequence的状况。
那RNN有什么样的问题呢?它的问题就在于:RNN很不容易并行化(hardtoparallel)。
为什么说RNN很不容易并行化呢?假设在singledirectional的RNN的情形下,你今天要算出
,就必须要先看
再看
再看
再看
,所以这个过程很难平行处理。
所以今天就有人提出把CNN拿来取代RNN,如下图1右所示。其中,橘色的三角形表示一个filter,每次扫过3个向量
,扫过一轮以后,就输出了一排结果,使用橘色的小圆点表示。
这是第一个橘色的filter的过程,还有其他的filter,比如图2中的黄色的filter,它经历着与橘色的filter相似的过程,又输出一排结果,使用黄色的小圆点表示。
图1:处理Sequence数据的模型图2:处理Sequence数据的模型所以,用CNN,你确实也可以做到跟RNN的输入输出类似的关系,也可以做到输入是一个sequence,输出是另外一个sequence。
但是,表面上CNN和RNN可以做到相同的输入和输出,但是CNN只能考虑非常有限的内容。比如在我们右侧的图中CNN的filter只考虑了3个vector,不像RNN可以考虑之前的所有vector。但是CNN也不是没有办法考虑很长时间的dependency的,你只需要堆叠filter,多堆叠几层,上层的filter就可以考虑比较多的资讯,比如,第二层的filter(蓝色的三角形)看了6个vector,所以,只要叠很多层,就能够看很长时间的资讯。
而CNN的一个好处是:它是可以并行化的(canparallel),不需要等待红色的filter算完,再算黄色的filter。但是必须要叠很多层filter,才可以看到长时的资讯。所以今天有一个想法:self-attention,如下图3所示,目的是使用self-attentionlayer取代RNN所做的事情。
图3:YoucantrytoreplaceanythingthathasbeendonebyRNNwithselfattention所以重点是:我们有一种新的layer,叫self-attention,它的输入和输出和RNN是一模一样的,输入一个sequence,输出一个sequence,它的每一个输出
都看过了整个的输入sequence,这一点与bi-directionalRNN相同。但是神奇的地方是:它的每一个输出
可以并行化计算。
1.2Self-attention:那么self-attention具体是怎么做的呢?
图4:self-attention具体是怎么做的?首先假设我们的input是图4的
,是一个sequence,每一个input(vector)先乘上一个矩阵
得到embedding,即向量
。接着这个embedding进入self-attention层,每一个向量
分别乘上3个不同的transformationmatrix
,以向量
为例,分别得到3个不同的向量
。
图5:self-attention具体是怎么做的?接下来使用每个query
去对每个key
做attention,attention就是匹配这2个向量有多接近,比如我现在要对
和
做attention,我就可以把这2个向量做scaledinnerproduct,得到
。接下来你再拿
和
做attention,得到
,你再拿
和
做attention,得到
,你再拿
和
做attention,得到
。那这个scaledinnerproduct具体是怎么计算的呢?
式中,
是
跟
的维度。因为
的数值会随着dimension的增大而增大,所以要除以
的值,相当于归一化的效果。
接下来要做的事如图6所示,把计算得到的所有
值取
操作。
图6:self-attention具体是怎么做的?取完
操作以后,我们得到了
,我们用它和所有的
值进行相乘。具体来讲,把
乘上
,把
乘上
,把
乘上
,把
乘上
,把结果通通加起来得到
,所以,今天在产生
的过程中用了整个sequence的资讯(Consideringthewholesequence)。如果要考虑local的information,则只需要学习出相应的
,
就不再带有那个对应分支的信息了;如果要考虑global的information,则只需要学习出相应的
,
就带有全部的对应分支的信息了。
图7:self-attention具体是怎么做的?同样的方法,也可以计算出
,如下图8所示,
就是拿query
去对其他的
做attention,得到
,再与value值
相乘取weightedsum得到的。
图8:self-attention具体是怎么做的?经过了以上一连串计算,self-attentionlayer做的事情跟RNN是一样的,只是它可以并行的得到layer输出的结果,如图9所示。现在我们要用矩阵表示上述的计算过程。
图9:self-attention的效果首先输入的embedding是
,然后用
乘以transformationmatrix
得到
,它的每一列代表着一个vector
。同理,用
乘以transformationmatrix
得到
,它的每一列代表着一个vector
。用
乘以transformationmatrix
得到
,它的每一列代表着一个vector
。
图10:self-attention的矩阵计算过程接下来是
与
的attention过程,我们可以把vector
横过来变成行向量,与列向量
做内积,这里省略了
。这样,
就成为了
的矩阵,它由4个行向量拼成的矩阵和4个列向量拼成的矩阵做内积得到,如图11所示。
在得到
以后,如上文所述,要得到
,就要使用
分别与
相乘再求和得到,所以
要再左乘
矩阵。
图11:self-attention的矩阵计算过程到这里你会发现这个过程可以被表示为,如图12所示:输入矩阵
分别乘上3个不同的矩阵
得到3个中间矩阵
。它们的维度是相同的。把
转置之后与
相乘得到Attention矩阵
,代表每一个位置两两之间的attention。再将它取
操作得到
,最后将它乘以
矩阵得到输出vector
。
图12:self-attention就是一堆矩阵乘法,可以实现GPU加速1.3Multi-headSelf-attention:
还有一种multi-head的self-attention,以2个head的情况为例:由
生成的
进一步乘以2个转移矩阵变为
和
,同理由
生成的
进一步乘以2个转移矩阵变为
和
,由
生成的
进一步乘以2个转移矩阵变为
和
。接下来
再与
做attention,得到weightedsum的权重
,再与
做weightedsum得到最终的
。同理得到
。现在我们有了
和
,可以把它们concat起来,再通过一个transformationmatrix调整维度,使之与刚才的
维度一致(这步如图13所示)。
图13:multi-headself-attention图13:调整b的维度从下图14可以看到Multi-HeadAttention包含多个Self-Attention层,首先将输入
分别传递到2个不同的Self-Attention中,计算得到2个输出结果。得到2个输出矩阵之后,Multi-HeadAttention将它们拼接在一起(Concat),然后传入一个Linear层,得到Multi-HeadAttention最终的输出
。可以看到Multi-HeadAttention输出的矩阵
与其输入的矩阵
的维度是一样的。
图14:multi-headself-attention这里有一组Multi-headSelf-attention的解果,其中绿色部分是一组query和key,红色部分是另外一组query和key,可以发现绿色部分其实更