李沐论文精读系列二:Vision Transformer、MAE、MoCo、Swin-Transformer(持续更新 )
文章目录
- 一、Vision Transformer论文精读
- 1.1 引言
- 1.1.1 前言
- 1.1.2 摘要
- 1.1.3 引言
- 1.2 相关工作
- 1.3 ViT
- 1.3.1 整体结构
- 1.3.2 Embedding层结构详解
- 1.3.3 Transformer Encoder详解
- 1.3.4 MLP Head和`ViT-B/16`模型结构图
- 1.3.5 归纳偏置
- 1.3.6 Hybrid混合模型试验
- 1.3.7 更大尺寸上的微调
- 1.4 实验部分
- 1.4.1 ViT三个尺寸模型参数对比
- 1.4.2 对比其它最新模型
- 1.4.3 `vision trasformer`预训练需要多大的数据规模?(重要论证)
- 1.4.5 ViT可视化
- 1.4.6 自监督训练
- 1.5 附录
- 1.5.1 [CLS]token和1D-Position Embedding消融实验
- 1.5.2 Hybrid混合模型试验(不感兴趣可跳过)
- 1.6 可视化
- 1.7 论文结论
- 1.8 PyTorch代码实现
- 1.8.1 PatchEmbed层
- 1.8.2 Attention层(实现多头注意力Multi-Head Attention)
- 1.8.3 待续
- 1. 9 PyTorch应用实例(待补充)
- 二、MAE
- 2.1 导言
- 2.1.1 前言
- 2.1.2 摘要
- 2.1.3 导言: 自编码模型为何在CV领域应用不及NLP?
- 2.2 结论&讨论(沐神习惯,看完导言看结论)
- 2.3 相关工作(都是老故事,略)
- 2.4 算法
- 2.4.1 MAE主要结构
- 2.4.2 具体实现
- 2.4.3 简单试验
- 二、Swin-Transformer
- 2.1 模型简介
- 2.2 模型结构
- 2.3 Patch Merging
- 2.4 W-MSA和SW-MSA
- 2.4.1 W-MSA
- 2.4.2 SW-MSA(Shifted Windows Multi-Head Self-Attention)
- 2.4.3 Masked MSA
- 2.5 Relative Position Bias
- 2.6 模型详细配置参数
- 2.7 代码讲解(待补充)
一、Vision Transformer论文精读
- 论文名称: An Image Is Worth 16x16 Words: Transformers For Image Recognition At Scale、论文源码
- 参考《Vision Transformer详解》、bilibili视频讲解、李沐《ViT论文逐段精读【论文精读】》
- PytorchAPI、Pytorch官方源码、Tensorflow2实现代码:tensorflow_classification/vision_transformer
- transformer原理可参考我的博文《多图详解attention和mask。从循环神经网络、transformer到GPT2》
1.1 引言
参考《ViT(Vision Transformer)全文精读》
1.1.1 前言
作者介绍的另一篇论文:《Intriguing Properties of Vision Transformer》(Vision Transformer的一些有趣特性)如下图所示:
- 图a表示的是遮挡,在这么严重的遮挡情况下,不管是卷积神经网络,人眼也很难观察出图中所示的是一只鸟
- 图b表示数据分布上有所偏移,这里对图片做了一次纹理去除的操作,所以图片看起来比较魔幻
- 图c表示在鸟头的位置加了一个对抗性的patch
- 图d表示将图片打散了之后做排列组合
上述例子中,卷积神经网络很难判断到底是一个什么物体,但是对于所有的这些例子Vision Transformer都能够处理的很好。
1.1.2 摘要
在VIT之前,self-attention
在CV领域的应用很有限,要么和卷积一起使用,要么就是把CNN里面的某些模块替换成self-attention,但是整体架构不变。
VIT的出现,打破了AlexNet出现以来CNN网络在CV领域的统治地位。VIT表明,在图片分类任务中,只使用纯的Vision Transformer
结构也可以取的很好的效果(最佳模型在ImageNet1K
上能够达到88.55%的准确率)开启CV新时代。而且 VIT将CV直接当做NLP来做,还打破了CV和NLP的模型壁垒,推进了多模态领域的发展。
下图
Ours-JFT
表示在在Google自家的JFT数据集上进行了预训练,Ours-I21K
表示在ImageNet 21K上预训练,再在ImageNet 1K上测试。实验分数也可以在paperwithcode
的Dataset排行榜上看到。
在这篇文章中,作者主要拿ResNet
、ViT
(纯Transformer模型)以及Hybrid
(卷积和Transformer混合模型)三个模型进行比较,所以本博文除了讲ViT模型外还会简单聊聊Hybrid模型。
1.1.3 引言
基于self-attention的模型架构,特别是Transformer,在NLP领域几乎成了必选架构。现在的主流方式就是:在大型语料库上训练一个大模型,然后迁移到小的数据集上进行微调。多亏了Transformer的高效性和可扩展性,现在已经可以训练超过1000亿参数的大模型,随着模型和数据集的增长,还没有看到性能饱和的现象。 (英伟达和微软联合推出的大型语言生成模型Megatron-Turing,有5300亿参数,还能在各种任务上大幅度提升性能,而没有达到性能饱和)
Transformer是对输入序列做self-attention,其复杂度是 O ( n 2 ) O(n^2) O(n2),现在支持的最大序列长度一般就是几百几千。如果直接把图片每个像素值拉平当做输入序列,那么序列就太长了。所以在CV领域CNN一直占主导地位。
受NLP启发,很多工作尝试将CNN和self-attention结合起来。那怎么降低输入序列把它用到CV领域呢?
- Non-local Neural Networks(CVRP,2018):将原始图片中间层输出的特征图作为输入序列,来传入Transformer(比如ResNet50在最后一个Stage的特征图size=14×14)。
- 《Stand-Alone Self-Attention in Vision Models》(NeurIPS,2019):使用孤立注意力
Stand-Alone Axial-Attention
来处理。具体的说,不是输入整张图,而是在一个local window(局部的小窗口)中计算attention。窗口的大小可以控制,复杂度也就大大降低。(类似卷积的操作) - 《Axial-DeepLab: Stand-Alone Axial-Attention for Panoptic Segmentation》(ECCV,2020a):将图片的2D的矩阵想办法拆成2个1D的向量。等于是先在高度的维度上做一次self-attention,然后再在宽度的维度上再去做一次self-attention,大幅度降低了计算的复杂度。
- 《On the Relationship between Self-Attention and Convolutional Layers》( ICLR, 2020.):在相关工作中,作者介绍了,和本文最相似的工作是这一篇文章。其作者从输入图片中抽取2×2的图片patch,然后一样的做self-attention(只在CIFAR-10数据集上做了实验,其数据集大小为32×32,所以patch size=2×2就行)
- image GPT:在相关工作中,作者也说到image GPT和本文很相近。但是image GPT是一个生成模型,虽然用了Transformer,但最终结果差很多(ImageNet上的最高的分类准确率也只能到72)。不过受此启发,后面出现了MAE,用一个自监督模式训练的生成式的模型,做出了比之前判别式的模型更好的效果(分类和检测)。
所以,自注意力早已经在计算机视觉里有所应用,而且已经有完全用自注意力去取代卷积操作的工作了。
这些模型虽然理论上是非常高效的,但事实上因为这个自注意力操作都是一些比较特殊的自注意力操作(除了上面举例的最后一篇),要很复杂的工程去加速算子,所以就导致很难训练出一个大模型。因此在大规模的图像识别上,传统的残差网络还是效果最好的。
本文是被transformer在NLP领域的可扩展性所启发,本文想要做的就是直接应用一个标准的transformer作用于图片,尽量做少的修改。好处是可以直接使用NLP中成熟的Transformer架构,不需要再魔改模型,而且Transformer这么多年有很多高效的实现。具体的处理方式见下一节。
上面举例的例4模型,从技术上讲就是
Vision Transformer
。但是作者认为二者的区别,是本文证明了,使用一个标准的Transformer endoder
(类似BERT,不需要任何特殊改动)在大规模的数据集上做预训练的话,就能取得比现在最好的卷积神经网络差不多或者还好的结果。(这是本文的主要论证工作,在1.4.2和1.4.3章节都有体现 )另外就是二者可处理图片的分辨率不同(32×32对比224×224)。
引言的最后部分放出了结论:
- 在中型大小的数据集上(比如说ImageNet)上训练的时候,如果不加比较强的约束,Vit的模型其实跟同等大小的残差网络相比要弱一点。
作者对此的解释是:transformer跟CNN相比,缺少了一些CNN所带有的归纳偏置(inductive bias
,是指一种先验知识或者说是一种提前做好的假设)。CNN的归纳偏置一般来说有两种:locality
:CNN是以滑动窗口的形式一点一点地在图片上进行卷积的,所以假设图片上相邻的区域会有相邻的特征,靠得越近的东西相关性越强;translation equivariance
(平移等变性或平移同变性):写成公式就是f(g(x))=g(f(x))
,不论是先做 g 这个函数,还是先做 f 这个函数,最后的结果是不变的;其中f代表卷积操作,g代表平移操作。(因为在卷积神经网络中,卷积核就相当于是一个模板,不论图片中同样的物体移动到哪里,只要是同样的输入进来,然后遇到同样的卷积核,那么输出永远是一样的)- 一旦神经网络有了这两个归纳偏置之后,他就拥有了很多的先验信息,所以只需要相对较少的数据就可以学习一个相对比较好的模型。但是对于transformer来说,它没有这些先验信息,所以它对视觉的感知全部需要从这些数据中自己学习。
- 为了验证这个假设, 作者在更大的数据集(ImageNet 22k数据集&JFT 300M数据集)上做了预训练,然后发现大规模的预训练效果要比归纳偏置好。 如下图所示:
- 上图中VTAB也是作者团队所提出来的一个数据集,融合了19个数据集,主要是用来检测模型的稳健性,从侧面也反映出了VisionTransformer的稳健性也是相当不错的。
1.2 相关工作
简单介绍了一下Transformer在NLP领域应用最广的两大分支BERT和GPT,都是基于自监督的训练方式(MLM任务和Next word prediction)。
直接将图片的像素作为序列输入Transformer是不可行的,所以作者介绍了一下之前的相关处理方式(类似上面引言提到的几个模型)。
第三段介绍了,有一些工作研究了用比ImageNet更大的数据集去做预训练,效果会更好,比如说ImageNet-21k和JFT300M。最终作者也是在这两个数据集上预训练模型。
1.3 ViT
1.3.1 整体结构
下图是原论文中给出的关于Vision Transformer(ViT)的模型框架。简单而言,模型由三个模块组成:
Embedding
层(线性投射层Linear Projection of Flattened Patches)Transformer Encoder
(图右侧有给出更加详细的结构)MLP Head
(最终用于分类的层结构)
如上图所示:
embedding
层(图片输入)- 一张图片先分割成n个
patchs
,然后这些patchs一个个输入线性投射层得到Pacth embedding
。比如ViT-L/16
表示每个patchs
大小是16×16。 - 在所有tokens前面加一个新的
class token
作为这些patchs全局输出,相当于transformer中的CLS(这里的加是concat拼接)。 - 和transformer一样,引入位置信息。VIT的做法是在每个token前面加上位置向量,即
position embedding
(这里的加是直接向量相加,不是concat)。
self-attention本身没有考虑输入的位置信息,无法对序列建模。而图片切成的patches也是有顺序的,打乱之后就不是原来的图片了。
- 一张图片先分割成n个
Pacth embedding
+position embedding
+class token
一起输入Transformer Encoder
层得到其输出。- class token的输出当做整个图片的特征,经过
MLP Head
得到分类结果。(VIT只做分类任务) - 整个模型是使用有监督的训练(相对于NLP中,transformer类模型很多都是无监督训练)
整体上看,VIT的模型结构还是很简洁的,难点就是如何将图片转为token输入网络。
1.3.2 Embedding层结构详解
对于标准的Transformer模块,要求输入的是token(向量)序列,即二维矩阵[num_token, token_dim]。对于图像数据而言,其数据为[H, W, C]格式的三维矩阵,明显不是Transformer想要的。所以需要先通过一个Embedding层来对数据做个变换。
- 如下图所示,首先将一张图片按给定大小分成一堆Patches。以
ViT-B/16
为例(后面都是以此模型举例),将输入图片(224x224)按照16x16大小的Patch尺寸进行划分,划分后会得到 ( 224 / 16 ) 2 = 196 (224/16)^2=196 (224/16)2=196个Patches。 - 接着通过线性映射将每个Patch映射到一维向量中。具体的,每个Patche数据shape为[16, 16, 3],通过映射得到一个长度为768的向量(token)。
[16, 16, 3] -> [768]
在代码实现中,直接通过一个卷积层来实现。卷积核大小为16x16,步距为16,卷积核个数为768。通过卷积
[224, 224, 3] -> [14, 14, 768]
,然后把H以及W两个维度展平即可[14, 14, 768] -> [196, 768]
,此时正好变成了一个二维矩阵,正是Transformer想要的。
如果模型更大的话,Pacth embedding
可以映射到更大的维度,也就是论文中提到的参数D
。
3. 加上[class]token以及Position Embedding。
在原论文中,作者说参考BERT,添加一个[class]token(用来表示序列特征),然后与之前从图片中生成的tokens拼接在一起,Cat([1, 768], [196, 768]) -> [197, 768]
这里的Position Embedding采用的是一个可训练的参数(1D Pos. Emb.),是直接叠加在tokens上的(add),所以shape要一样,也是[197, 768]
。
关于为什么使用[class]token
和1D Pos. Emb
,在本文1.5.1消融试验部分会讲到。
1.3.3 Transformer Encoder详解
Transformer Encoder其实就是重复堆叠Encoder Block L
次,主要由以下几部分组成:
- Layer Norm层标准化:这种Normalization方法主要是针对NLP领域提出的,对每个token进行Norm处理。
- Multi-Head Attention:多头注意力
- Dropout/DropPath:在原论文的代码中是直接使用的Dropout层,在但rwightman实现的代码中使用的是DropPath(stochastic depth),可能后者会更好一点。
- MLP Block,如图右侧所示,就是全连接+GELU激活函数+Dropout组成。需要注意的是第一个全连接层会把输入节点个数翻4倍
[197, 768] -> [197, 3072]
,第二个全连接层会还原回原节点个数[197, 3072] -> [197, 768]
,原来跟transformer中做法一样。 - 详细结构见下一节结构图右侧部分
Encoder Block
。
1.3.4 MLP Head和ViT-B/16
模型结构图
上面通过Transformer Encoder
后输出的shape和输入的shape是保持不变的,以ViT-B/16为例,输入的是[197, 768]输出的还是[197, 768]。对于分类,我们只需要提取出[class]token生成的对应结果就行,即[197, 768]
中抽取出[class]token
对应的[1, 768]
。
接着我们通过MLP Head得到我们最终的分类结果。MLP Head原论文中说在训练ImageNet21K时是由Linear+tanh激活函数+Linear组成。但是迁移到ImageNet1K上或者你自己的数据上时,只定义一个Linear即可。
下面是小绿豆绘制的ViT-B/16
模型结构图:(Pre-Logits就是Linear+tanh,一般迁移学习是可以不用的。)
作者在论文3.1 VIT
部分,对整个过程用数学公式描述了一次:
- X p X_p Xp表示图像块的patch,一共有n个patch,所以有 X p 1 X_{p}^{1} Xp1到 X p N X_{p}^{N} XpN,E表示线性投影的全连接层,得到一些patch embedding。
- 第一层transformer endoder的输入是 z 0 z_0 z0;MSA表示多头自注意力(Multi head self attention)。
- z L 0 z_{L}^{0} zL0表示经过L层transformer endoder后输出的第一个位置的token(CLS)的结果
1.3.5 归纳偏置
在CNN中,locality(局部性)和translate equivariance(平移等变性)是在模型的每一层中都有体现的,这个先验知识相当于贯穿整个模型的始终。
但是对于ViT来说,只有MLP层是局部而且平移等变性的,其它的自注意力层是全局的,这种图片的2d信息ViT基本上没怎么使用。
就是只有刚开始将图片切成patch的时候和加位置编码的时候用到了,除此之外,就再也没有用任何针对视觉问题的归纳偏置了,而且位置编码也是随机初始化的1-D信息。所以在中小数据集上ViT不如CNN是可以理解的。
1.3.6 Hybrid混合模型试验
既然transformer全局建模的能力比较强,卷积神经网络又比较data efficient(不需要太多的训练数据),那么自然想到去搞一个前面层是CNN后面层是transformer的混合网络,也就是Hybrid
混合模型。Hybrid
不再直接将图片打成一个个patch,而是直接送入CNN得到embedding,比如经过Resnet50,最后一个stage输出特征图是14×14,拉直了也是196维向量。这部分细节参考文本1.4实验部分。
1.3.7 更大尺寸上的微调
之前的工作有表明,使用更大的图片输入尺寸往往模型效果会更好。但是使用一个预训练好的vision transformer,其实是不太好去调整输入尺寸的。如果还是将patch size保持一致,但是图片扩大了,那么序列长度就增加了,提前预训练好的位置编码有可能就没用了。
这个时候位置编码该如何使用?作者发现其实做一个简单的2d的插值就可以了(使用torch官方自带的interpolate函数就完成)。但这只是一个临时的解决方案,如果需要从一个很短的序列变成一个很长的序列时,简单的插值操作会导致最终的效果下降(比如256→512)。这也算是vision transformer在微调的时候的一个局限性。
因为使用了图片的位置信息进行插值,所以这块的尺寸改变和抽图像块是vision transformer里唯一用到2d信息的归纳偏置的地方。
1.4 实验部分
1.4.1 ViT三个尺寸模型参数对比
在论文的4.1章节的Table1中有给出三个模型(Base/ Large/ Huge
,对应BERT)的参数,在源码中除了有Patch Size
为16x16的外还有32x32的。其中:
Layers
:Transformer Encoder中重复堆叠Encoder Block的次数Hidden Size
:对应通过Embedding层后每个token的dim(向量的长度)MLP size
:Transformer Encoder中MLP Block第一个全连接的节点个数(是Hidden Size的四倍)Heads
:代表Transformer中Multi-Head Attention的heads数(多头注意力有几个头)。
1.4.2 对比其它最新模型
对比VIT的几个不同配置模型和BiT-L
,以及Noisy
模型。最终效果提升不大,但是ViT
训练时间短很多。(的后面还有其它的对比试验支持作者的这个观点)
TPU-v3-core-days 2.5k
:表示最大的ViT-H/14
模型在TPU-v3上只需要训练2500天。
1.4.3 vision trasformer
预训练需要多大的数据规模?(重要论证)
下图展示了在不同大小的数据集上预训练后,BiTt
和VIT
到底在ImageNet的微调效果如何。
- 下图中灰色
■
\blacksquare
■表示
BiT
,上下两条线分别表示使用ResNet50和ResNet152结构;其它五个 ∙ \bullet ∙就是不同配置的ViT
。 - 左图是在
ImageNet
、ImageNet 21K
、JFT-300M
三种规模的数据集上训练的结果,三种数据集规模分别是1.2Million,14M和300M - 右图是在
JFT
数据集上分别采样不同规模子集的实验结果,这里ViT
用作特征提取器而不是微调。(去除了训练时的强约束,比如说dropout、weight decay、label smoothing;而且不同规模的数据集来自同一分布,更能看出模型本身的特性)
结论:如果想用ViT
,那么得至少是在ImageNet-21k
这种规模的数据集上预训练(数据量14M),否则还不如用CNN。
下图再次论证作者观点(VIT训练更便宜):
- 左图的
Average-5
就是他在五个数据集(ImageNet real、pets、flowers、CIFAR-10、CIFAR-100)上做了验证后的平均结果 - 右图是
ImageNet
上的验证结果 - ViT和Hybrid混合模型都是在
JFT-300M
上预训练号的模型
结论: - 同等复杂度下,
ViT
比BiT
效果要好,这也说明ViT
训练比CNN
更便宜 - 小规模的模型上,
Hybrid
混合模型比其它两者效果都要好 - 随着模型规模增大,
Hybrid
精度慢慢的跟ViT
差不多了,甚至还不如在同等计算条件下的ViT
。为什么卷积神经网络抽出来的特征没有帮助ViT
更好的去学习?这里作者对此也没有做过多的解释。 - 随着规模的增大,
ViT
和BiT
都没有性能饱和的迹象。
1.4.5 ViT可视化
统一见本文1.6 可视化部分。
1.4.6 自监督训练
这部分放在正文而不是负类中,是因为作者认为:在NLP领域,Transformer
这个模型确实起到了很大的推动作用,但另外一个真正让Transformer
火起来的原因其实是大规模的自监督训练,二者缺一不可。
NLP中自监督方式是MLM任务或者Next word prediction,本文模仿的是BERT,所以作者考虑构造专属于ViT
的目标函数Mask patch prediction
。具体来说,给定一张图片,将它打成很多patch,然后将某些patch随机抹掉,然后通过这个模型将这些patch重建出来。
但是最后ViT-Base/16
在ImageNet只能达到80的左右的准确率,比最好的有监督方式训练,还是差了四个点,所以作者后面的工作是考虑引入对比学习。
对比学习是所有自监督学习中表现最好的,紧接着出现的
ViT MoCo v3
和DINO
就是在ViT
的基础上使用了对比学习。
1.5 附录
1.5.1 [CLS]token和1D-Position Embedding消融实验
这部分内容在论文附录的D Additional Analyses部分
虽然最终VIT采用的[CLS]token
和1D-Position Embedding
都和BERT
是一样的,但是作者在论文实验部分也都对此作了很多消融试验。比如:
[CLS]token
是从NLP领域借鉴的,但是之前的CNN网络用于图片分类的特征并非这么做。- 比如ResNet50模型,是在最后一个stage5上输出的特征图(size=14×14)上做一个全局平均池化(GAP),拉直为一个一维向量,来作为整个图片的全局特征。最后用这个特征去做分类。
- 作者也试验了在
Pacth embedding
中不加入[CLS]token
,而是在最后Transformer Encoder
输出序列向量时,用一个GAP
来得到最终全局表征向量,最终结果和使用[CLS]token
的效果几乎差不多 。 - 下图表示CLS和GAP最后收敛结果差不多,只是学习率会不一样。最终作者还是使用了
[CLS]token
,因为本文目的是跟原始的Transformer尽可能地保持一致,不想大家觉得效果好可能是因为某些trick或者某些针对cv的改动而带来的,作者就是想证明, 一个标准的Transformer照样可以做视觉。
- 对于
Position Embedding
作者也有做一系列对比试验,在源码中默认使用的是1-D Pos. Emb
.,对比不使用Position Embedding准确率提升了大概3个点,和2-D Pos. Emb.
以及Rel. Pos. Emb.
比起来没太大差别。
2-D Pos. Emb.
(2D位置编码)就是用patch的横纵坐标表示其位置,而不是一个一维的序号。其中横坐标有D/2的维度,纵坐标也有D/2的维度,两者拼接到得到了一个D维的向量。
Rel. Pos. Emb.
:相对位置编码。两个patch之间的距离可以使用绝对位置编码,也可以使用相对位置编码,最终结果差不多。
作者对此解释是:ViT是直接在图像块上做的,而不是在原来全局尺度的像素块上做的,所以在排列组合这种小块或者想要知道这些小块之间相对位置信息的时候,还是相对比较容易的,所以使用任意的位置编码都无所谓。
1.5.2 Hybrid混合模型试验(不感兴趣可跳过)
在论文4.1章节的Model Variants中有比较详细的讲到,就是将传统CNN特征提取和Transformer进行结合。下图绘制的是以ResNet50
作为特征提取器的混合模型Hybrid
,但这里的Resnet与之前讲的Resnet有些不同。
- 首先这里的ResNet50的卷积层采用的
StdConv2d
不是传统的Conv2d
,然后将所有的BatchNorm
层替换成GroupNorm
层。 - 在原Resnet50网络中,stage1重复堆叠3次,stage2重复堆叠4次,stage3重复堆叠6次,stage4重复堆叠3次,但在这里的ResNet50中,把stage4中的3个Block移至stage3中,所以stage3中共重复堆叠9次。
如果有stage4就是下采样32倍,改为只有stage3就是下采样16倍,这样224×224的图片输出的就是14×14大小。
通过ResNet50 Backbone
进行特征提取后,得到的特征矩阵shape是[14, 14, 1024],接着再输入Patch Embedding层,注意Patch Embedding中卷积层Conv2d的kernel_size和stride都变成了1,只是用来调整channel。
后面的部分和前面ViT中讲的完全一样,就不在赘述。
简单说,ViT是图片经过Conv2d
卷积层得到Patchs,而Hybrid是多加了一步,图片经过ResNet50 Backbone
进行特征提取后,经过卷积得到Patchs。然后都是加上class token和位置向量输入Transformer Encoder,得到class token都是输出。再经过MLP得到分类结果。
下表是论文用来对比ViT,Resnet(和刚刚讲的一样,使用的卷积层和Norm层都进行了修改)以及Hybrid模型的效果。通过对比发现,在训练epoch较少时Hybrid优于ViT,但当epoch增大后ViT优于Hybrid。
1.6 可视化
分析完训练成本以后,作者也做了一些可视化,希望通过这些可视化能够分析一下vit内部的表征。
- patch embedding可视化
- 下图左侧展示了E(第一层linear projection layer)是如何对图片像素进行embedding。
- 只展示了开头28个token的可视化结果,可以看到结果类似CNN,都是提取的一些颜色和纹理这样的底层特征,作者说它们可以用来描述每一个图像块的底层的结构。
- position embedding可视化
- 下图中间部分展示了不同patch之间,位置编码的cos相似性,越接近1 表示越相似。
- 可以看到黄色(最相似)出现位置和patch本身所处位置对应,说明1D编码已经学习到2D的位置信息,所以换成2D位置编码效果几乎不变。
- 自注意力是否起作用了?
为了了解ViT如何利用self-attention来整合图像中的信息,我们分析了不同层的Mean attention distance
ViT-L/16
有24层,所以横坐标网络深度是从0到24 。- 图中五颜六色的点就是每一层的transformer block中多头自注意力的头。
ViT-L/16
有16个头,每一列其实就是16个点。 - 纵轴所表示的是
Mean attention distance
(平均注意力的距离:图上两个像素点的真实距离乘以他们之间的attention weights,因为自注意力是全局都在做,所以平均注意力的距离就能反映模型到底能不能注意到两个很远的像素) - 结论;
- 网络的开始几层,
Mean attention distance
从10几到100多,有的近有的远,这也就证明了自注意力能够在网络最底层,也就是刚开始的时候就已经能够注意到全局上的信息了,而不是像卷神经网络一样,逐步提升感受野。 - 网络的后半部分,模型的自注意力的距离已经非常远了(most heads attend widely across tokens.),也就是说它已经学到了带有语义性的概念,而不是靠邻近的像素点去进行判断。即模型学到的特征越来越高级,越来越具有语义信息。
- 网络的开始几层,
- out token输出
- 在本文的4.5实验部分,作者对上面attention的结果又进行了一次验证。
- 作者用
output token
的自注意力折射回原来的输入图片,可以发现模型确实是学习到了这些概念。 - 因为输出的token是融合了所有的信息(全局的特征),模型已经可以关注到与最后分类有关的图像区域
1.7 论文结论
Vision Transformer
和NLP领域标准的Transforme
r的自注意力机制区别在于:除了在刚开始抽图像块的时候,还有位置编码用了一些图像特有的归纳偏置,除此之外就再也没有引入任何图像特有的归纳偏置了。这样的好处就是可以直接把图片当做NLP中的token,拿NLP中一个标准的Transformer就可以做图像分类了。
这个策略扩展性非常好,和大规模预训练结合起来的时候效果也出奇的好,而且训练起来还相对便宜。
另外作者在这里对Vision Transformer
还做了一些展望:
- 将VIT从分类扩展到目标检测和分割,所以后面出现了一系列将Transformer应用在在视觉领域的模型:
- ViT-FRCNN
- SETR,即Segmentation Transformer
- DETR,即Detection Transformer
- 2021年3月Swin-Transformer横空出世,将多尺度的设计融合到了Transformer中,更加适合做视觉的问题了。
- 探索一下自监督的预训练方案(NLP领域常用做法)。
本文也做了一些初始实验,证明了用这种自监督的训练方式也是可行的,但是跟有监督的训练比起来还是有不小的差距。 - 将Vision Transformer做得更大,效果应该会更好。
半年后,本文作者团队又出了一篇Scaling Vision Transformer,训练出更大的VIT网络,即ViT-G,将ImageNet图像分类的准确率提高到了90以上。
除以上几点之外,作者还在
多模态领域
挖了一个大坑。CV和NLP大一统之后,是不是这些任务都可以用一个Transformer取解决呢?后续这一块也是推进的很快。
1.8 PyTorch代码实现
项目代码github地址、B站代码讲解《使用pytorch搭建Vision Transformer(vit)模型》
1.8.1 PatchEmbed层
class PatchEmbed(nn.Module):
"""
2D Image to Patch Embedding
"""
def __init__(self, img_size=224, patch_size=16, in_c=3, embed_dim=768, norm_layer=None):
super().__init__()
img_size = (img_size, img_size)
patch_size = (patch_size, patch_size)
self.img_size = img_size
self.patch_size = patch_size
self.grid_size = (img_size[0] // patch_size[0], img_size[1] // patch_size[1])
self.num_patches = self.grid_size[0] * self.grid_size[1] # 在VIT-B/16中就是16*16
self.proj = nn.Conv2d(in_c, embed_dim, kernel_size=patch_size, stride=patch_size)
# 默认不传入norm_layer, nn.Identity()表示不做任何操作
self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
def forward(self, x):
B, C, H, W = x.shape # x就表示传入的图片
# 需要注意的是,VIT模型不像传统的CNN模型那样,可以更改图片的入网尺寸。
assert H == self.img_size[0] and W == self.img_size[1], \
f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
# flatten: [B, C, H, W] -> [B, C, HW],最后两个维度拉平
# transpose: [B, C, HW] -> [B, HW, C],后两个维度交换
x = self.proj(x).flatten(2).transpose(1, 2)
x = self.norm(x) # 得到 Patch Embedding
return x
1.8.2 Attention层(实现多头注意力Multi-Head Attention)
class Attention(nn.Module):
def __init__(self,
dim, # 输入token的dim
num_heads=8, # 多头注意力使用几个head
qkv_bias=False, # 生成qkv时是否使用偏置
qk_scale=None,
attn_drop_ratio=0., # dropout概率,下同
proj_drop_ratio=0.):
super(Attention, self).__init__()
self.num_heads = num_heads
head_dim = dim // num_heads # 每个head的维度,一般要求dim能整除num_heads
self.scale = qk_scale or head_dim ** -0.5 # 不传入qk_scale时,self.scale=根号dk
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) # 使用一个全连接层得到qkv
self.attn_drop = nn.Dropout(attn_drop_ratio)
self.proj = nn.Linear(dim, dim) # 多头拼接后用Wo映射,Wo就是全连接层实现
self.proj_drop = nn.Dropout(proj_drop_ratio)
def forward(self, x):
# [batch_size, num_patches + 1, total_embed_dim]
# num_patches + 1就是196+1(class token),total_embed_dim=768
B, N, C = x.shape
# qkv(): -> [batch_size, num_patches + 1, 3 * total_embed_dim]
# reshape: -> [batch_size, num_patches + 1, 3, num_heads, embed_dim_per_head]
# permute: -> [3, batch_size, num_heads, num_patches + 1, embed_dim_per_head]
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
# [batch_size, num_heads, num_patches + 1, embed_dim_per_head]
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
# transpose: -> [batch_size, num_heads, embed_dim_per_head, num_patches + 1]
# @: multiply -> [batch_size, num_heads, num_patches + 1, num_patches + 1]
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
# @: multiply -> [batch_size, num_heads, num_patches + 1, embed_dim_per_head]
# transpose: -> [batch_size, num_patches + 1, num_heads, embed_dim_per_head]
# reshape: -> [batch_size, num_patches + 1, total_embed_dim]
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x
1.8.3 待续
1. 9 PyTorch应用实例(待补充)
二、MAE
参考:
- 论文《Masked Autoencoders Are Scalable Vision Learners》
- 李沐《MAE 论文逐段精读【论文精读】》、笔记《MAE 论文逐段精读》
本文算法在2.4章节,不想看全文的直接跳过去!!!
2.1 导言
2.1.1 前言
自从Vision Transformer
将标准的transformer成功的应用到CV上面以来,就出现了很多相关性的工作,MAE
就是其中的一篇。本文是2021.11.11发布在Arxiv上的文章,主要工作是在Vision Transformer
基础上,引入自监督训练,相当于将BERT
应用到CV领域。通过完形填空来获取对于图片的理解,把整个训练拓展到没有标号的数据上面 ,使得transformer在CV上的应用更加普及。
最终MAE只需要Vision Transformer
百分之一规模的数据集上预训练,就能达到同样的效果。而且在目标检测、实例分割、语义分割等任务上,效果都很好。
本文标题中的
Autoencoders
,是‘自
’而非自动的意思,表示类似自回归这一类模型的特点, 即标号和样本(y和x)来自于同一个东西。比如说在语言模型中,每一次用前面的次去预测下一个词。而对于MAE,则表明模型的标号也是图片本身
。
2.1.2 摘要
MAE的自监督训练实现途径非常简单,随机地mask图片中地一些patches
(块),然后再去重构the missing pixels
.,这个思想来自于BERT中的带掩码的语言模型。
在MAE中,自监督训练还有两个核心的设计:
-
非对称的encoder-decoder架构。
MAE的编码器只编码可见的patches,被masked的块不编码,而解码器需要重构所有块。两者看到的东西是不一样的,所以叫非对称。 -
mask比例要提到比较高,比如
75%
,才能得到一个比较好的自监督训练效果(否则只需要插值还原)。
这等于只编码1/4的图片,训练速度加快了四倍,所以MAE可以扩展到更大的模型。
最终作者只使用一个最简单的ViT-Huge
模型,在ImageNet-1K
上预训练,精度也能达到87.8%
。
下面是作者展示的一些模型重构效果图:
- 图2:MAE在ImageNet验证集上构造出来的图片,验证集没有参与训练,所以相当于是测试结果。
- 红色框选的这一块,左侧列表示80%的patches被mask,中间列表示MAE重构的结果,右侧列表示原始图片。
- 可以看到被mask的地方非常多,很难看出原图是什么,但是重构之后还原度非常高,特别神奇。
- 图3:MAE在COCO验证集上的结果,MAE重构效果也很好。
- 图4:MAE取不同的遮盖比例时,模型重构效果对比。可以看到即使mask比例达到95%,还是能还原出大致图像,很玄乎。
2.1.3 导言: 自编码模型为何在CV领域应用不及NLP?
在导言一开始,作者大意就说自监督学习很香,使得Transformer在NLP领域的应用非常火。尽管也有一些工作将BERT应用到CV领域,比如Denoising Autoencoder是在一个图片中加入很多噪音,然后通过去噪来学习对这个图片的理解,但最终这些效果都不及NLP。
MAE也是一种类似去噪 的模型,mask一些patches,也相当于往模型中加入很多噪音
为何masked自编码模型在CV和NLP领域应用不一样呢?
- 之前的CV领域都是用的CNN网络,而CNN中的卷积,是不好做掩码操作的。
因为卷积核滑动时,不好将msaked patches
单独剔除,导致后面不好将图片还原(Transformer中可以将masked token直接去掉,和其他词区分开来)。但是ViT
成功将Transformer
应用到图片分类,所以这个问题不再有了 - 两者信息密度不一样。
- 在自然语言中,一个词就是一个语义的实体,比如说字典中对一个词的解释就是很长的一段话,所以一句话中很难去去掉几个词(这样做完形填空才有意义)。
- 在图片中,像素是比较冗余的,取决于相机的分辨率有多大。所以如果只是简单去掉一些块的话,很容易进行插值还原。
- 作者的做法是,mask很高比例的像素块(比如
75%
),就可以大大降低图片的冗余性。这样就压迫模型必须学习全局信息,也就提高了模型学习的难度。
将图片中一大片都去掉, 剩下的块离得比较远,就没那么冗余了。否则模型仅仅学一个局部模型就可以进行插值还原。
关于此点论证可以看下面图2/3/4的效果,仅仅一些局部的,很稀疏的块,就可以重构全局图片。
-
自编码器的解码器差异
- NLP中需要预测
masked tokens
,而token
本身就是一种比较高级一些的语义表示,所以只需要一个简单的全连接层就可以解码这些tokens
。(ViT只是简单的图片分类,所以也只需要MLP就可以解码) - MAE中需要还原
the missing pixels
,这些像素是一个很基础的特征。所以可能需要一个比较复杂的转置卷积网络才能完成解码。
- NLP中需要预测
正是基于以上分析,作者才提出本文的两个核心设计:mask使用高的比例以及非对称编码器-解码器。(为啥是非对称,其实在2.4.2具体实现中,有进一步说明)
在导言最后一部分,作者说MAE可以只在ImageNet 1K
上就能预训练ViT-Large/-Huge
模型(得到很好的效果)。类比ViT
,相同效果后者需要在近100
倍规模的数据集上训练才能达到。
另外,在目标检测、语义分割和实例分割上,MAE比之前所有模型(包括有监督训练模型)效果都要好,而且加大模型会有显著的收益。
2.2 结论&讨论(沐神习惯,看完导言看结论)
简单且拓展性好的算法,是整个深度学习的核心。
- 简单:是指
MAE
在ViT
模型上加了一些简单的扩展,但是模型本身是不简单的。- 拓展性好:是指你有钱就可以无限的加数据了,毕竟MAE不需要标号,你可以训练更大的模型。
自监督学习在最近几年是比较火的,但是在计算机视觉中,还是主要用有标号的数据来训练 。本文在ImageNet
数据集上通过自编码器学习到可以媲美有标号训练的效果,使得CV领域的自监督学习可能走上与NLP类似的轨迹。
另一方面,图像和语言是不同类型的信息,要谨慎处理这种差异。
- 对于语言来讲,一个token是一个语义单元,它含有的语义信息比较多(这种信息在ViT里提取出来了)。
- 在图片中,虽然一个patch也含有一定的语义信息,但它不是语义的分割(即某个patch中并不含有特定的物体,可能含有多个物体的一小块,或者是某一个物体重叠的一块)。但即使是在这样的情况下,MAE也能做很复杂的一些任务。作者认为MAE(transformer)确实能够学到隐藏的比较好的语义表达。
Broader impacts:MAE可以生成不存在的内容,类似GAN,所以使用要注意。
2.3 相关工作(都是老故事,略)
2.4 算法
2.4.1 MAE主要结构
MAE使用编码器将观察到的信号映射到潜在表示,再使用一个解码器从潜在表示重构原始信号。与经典的自动编码器不同,我们采用了一种非对称设计,允许编码器仅对部分观察信号(无掩码标记)进行操作,模型设计图如下:
- 预训练时:
- 首先将图像切成一个个的patch(图像块)
- 将没有被mask的块输入到encoder层(类似
ViT
),得到对应的token embeddings
。 - 将序列拉长,填入灰色块像素对应的embedding(就是一个
position embeddings
)。 - 将上面得到的长向量输入
decoder
,解码器会尝试将里面的像素信息全部重构回来,使之成为原始图片。 - 上图中编码器比解码器更宽,表示模型的主要计算部分在编码器(最重要的就是编码像素得到特征)
- 微调时:
- 只需用到
endoder
,将图片切成Patches
后编码得到特征,然后用来处理自己的任务就行。(此时不需要掩码,也就不需要解码器)
- 只需用到
2.4.2 具体实现
- Masking
和ViT
一样,MAE也将图片切分成一块块的Patches,然后进行随机抽样
。即:在不替换的情况下,按照均匀分布对patches进行随机采样,采到的样本保留,剩下的全部mask掉。 - Encoder
这部分也和ViT
一样,通过线性投射层Linear Projection
将patches映射成patch embeddings
,再加上position embedding
,得到最终的token embeddings
。唯一不同的是,被mask的块不进行编码(直接去除,不输入encoder层)。 - 解码器
- 解码器可以看到所有的块,包括被编码的patches和被mask的patches。后者通过共享的可学习向量来表示,也就是说每一个被盖住的块都表示成同样的向量 。同时也要加入位置编码,否则模型无法区分它是哪一个掩码。
- 像之前说的,解码器仅在预训练期间用于重构图像,下游任务只需要编码器对图像进行编码就行,这样解码器可以独立于编码器,设计比较灵活。
- 所以从结构图可以看到,最终解码器模块确实设计的比编码器更简单(更窄、更浅),相比编码器,其计算量只有其10%。通过这种非对称设计,使得tokens仅由轻量级解码器处理,大大减少了预训练时间。
- 重构图像
- 解码器最后一层是一个线性层
linear projection
,其输出通道的数量等于每个patch中包含的像素数量(比如patch size=16×16,那么输出是256维,最后reshape一下就是16×16了)。 - 损失计算时,只对被mask的patches做计算(类似BERT),且使用MSE函数,也就是预测像素值和真实像素值的MSE。
- 对要预测的像素可以做一次normalization,这样数据更稳定。
- 解码器最后一层是一个线性层
2.4.3 简单试验
二、Swin-Transformer
- 参考:太阳花的小绿豆帖子《Swin-Transformer网络结构详解》、讲解视频、代码地址、代码讲解视频
- 论文名称:Swin Transformer: Hierarchical Vision Transformer using Shifted Windows、官方开源代码
- Pytorch实现代码及Swin Transformer API、Tensorflow2实现代码
- 《Swin Transformer 论文详解及程序解读》
2.1 模型简介
Swin transformer
(Shifted Windows移动窗口)是微软研究院于2021年3月25日发表,利用transformer架构处理计算机视觉任务,在图像分割,目标检测各个领域已经霸榜(之前的VIT只能做图片分类)。比如打开其官网代码可以看到:(这排名不是卷死?)
点开第一个coco测试集上的精度:
标准的Transformer直接用到视觉领域有一些挑战,即:
- 多尺度问题,即图片的尺度不统一。
- 相比较于文本信息,图片有更大的像素分辨率,Transformer的计算复杂度是token数量的平方(每个token都要与其他token计算QK值)。如果将每个像素值算作一个token,其计算量非常大,不利于在多种机器视觉任务中的应用。
- Vision Transformer进行WSA计算时,任何一个patch都要与其他所有的patch都进行attention计算。当patch的大小固定时,计算量与图片的大小成平方增长。
- Swin Transformer中采用了W-MSA,只对window内部进行MSA,当图片大小增大时,计算量仅仅是呈线性增加。
- 滑动窗口在局部不重叠的窗口中计算自注意力,并允许跨窗口连接。window self-attention大大降低了降低了计算复杂度。同时通过Shiting(移动)的操作可以使相邻的两个窗口之间进行交互,也因此上下层之间有了cross-window connection,从而变相达到了全局建模的能力。
- 分层结构使得模型能够灵活处理不同尺度的图片,并且计算复杂度与图像大小呈线性关系。
所以最终Swin Transformer计算量比VIT更少,且在ImageNet上的效果也比VIT更好。
2.2 模型结构
对比下Swin Transformer和之前的Vision Transformer,可以看出两点不同:
- 层次化构建方法(Hierarchical feature maps) :Swin Transformer使用了类似卷积神经网络中的层次化构建方法,
比如特征图尺寸中有对图像下采样4倍的,8倍的以及16倍的,这样的backbone有助于在此基础上构建目标检测,实例分割等任务。而在之前的Vision Transformer中是一开始就直接下采样16倍,且一直不变
- W-MSA :使用窗口(Window)的形式将特征图划分成了多个不相交的区域,并且只在每个窗口(Window)内进行Multi-Head Self-Attention,大大减少计算量。
比如在下图的4倍下采样和8倍下采样中,相对于Vision Transformer中直接对整个(Global)特征图进行Multi-Head Self-Attention,Swin Transformer只在窗口内做。这样能够减少计算量,尤其是在浅层特征图很大的时候。
这样做虽然减少了计算量,但也会隔绝不同窗口之间的信息传递。所以在论文中作者又提出了 Shifted Windows Multi-Head Self-Attention(SW-MSA)的概念,通过此方法能够让信息在相邻的窗口中进行传递,后面会细讲。
原论文中给出的关于Swin Transformer(Swin-T)网络的架构图如下:
- Patch Partition:如上图:
- Patch Partition层会用一个4x4大小的窗口对输入图片进行分割(每4x4大小的相邻像素为一个Patch)
- 对每个窗口都在channel方向展平(每个像素三通道,16个像素就48channel)
- 最后通过Linear Embeding层将每个像素的channel数调整为C,并对每个channel做了一次Layer Norm。(swin-transformer有T、S、B、L等不同大小,其C的值也不同)。
假设输入的是RGB三通道图片,那么每个patch就有4x4=16个像素,然后每个像素有R、G、B三个值所以展平后是16x3=48,所以通过
Patch Partition
后图像shape由[H, W, 3]
变成了[H/4, W/4, 48]
。
线性变换时由48变成C。图像shape由[H/4, W/4, 48]
变成了[H/4, W/4, C]
。
其实在源码中Patch Partition
和Linear Embeding
就是直接通过一个卷积层实现的,和之前Vision Transformer中讲的 Embedding层结构一模一样。(kernel size=4×4,stride=4,num_kernel=48)
- 通过四个Stage构建不同大小的特征图,除了Stage1中先通过一个Linear Embeding层外,剩下三个stage都是先通过一个Patch Merging层进行下采样。(2倍下采样的同时通道数翻倍)
- 重复堆叠Swin Transformer Block偶数次。这是因为Block其实有两种结构,两个结构是成对使用的。
如图(b)中所示。这两种结构的不同之处仅在于一个使用了W-MSA结构,一个使用了SW-MSA结构。先使用一个W-MSA结构再使用一个SW-MSA结构。
- 分类网络,后面还会接上一个Layer Norm层、全局池化层以及全连接层得到最终输出。图中没有画,但源码中是这样做的。
接下来,在分别对Patch Merging
、W-MSA
、SW-MSA
以及使用到的相对位置偏置(relative position bias
)进行详解。关于Swin Transformer Block中的MLP结构和Vision Transformer中的结构是一样的,不再赘述。
2.3 Patch Merging
上面讲了,在每个Stage中首先要通过一个Patch Merging层进行下采样(Stage1除外)。
如上图所示,假设输入的是一个4x4大小的单通道特征图(feature map),分割窗口大小为2×2,进行的操作如下:
- 分割:Patch Merging会将每个2x2的相邻像素划分为一个patch,然后将每个patch中相同位置(同一颜色)像素给拼在一起,得到了4个feature map。
- caoncat拼接:将这四个feature map在深度方向进行concat拼接
- LayerNorm处理
- 最后通过一个全连接层对每个像素的channel进行改变,将feature map的深度由C变成C/2。
可以看出,通过Patch Merging层后,feature map的高和宽会减半,深度会翻倍(channel由1变为2,高宽由4变为2)。
2.4 W-MSA和SW-MSA
2.4.1 W-MSA
W-MSA也就是window Multi-heads Self-attention
模块。普通的Multi-heads Self-attention
会对feature map
中的每个像素(或称作token,patch)都要计算Self-Attention
。而W-MSA模块,首先将feature map按照MxM大小划分成一个个Windows,然后单独对每个Windows内部进行Self-Attention。
目的:减少计算量(下图可以看出两种方式分别是多少计算量)
缺点:窗口之间无法进行信息交互,等于减少了感受野,无法看到全局信息
具体的计算过程可以参考:太阳花的小绿豆帖子《Swin-Transformer网络结构详解》第三章
2.4.2 SW-MSA(Shifted Windows Multi-Head Self-Attention)
与W-MSA不同的地方在于这个模块存在滑动,所以叫做shifted window。滑动距离是window_size//2,方向是向右和向下。
滑动窗口是为了解决W-MSA计算attention时,窗口与窗口之间无法进行信息传递的问题。如下图所示,左侧是网络第L层使用的W-MSA模块,右侧是第L+1层使用SW-MSA模块。对比可以发现,窗口(Windows)发生了偏移。
比如对于第一行第2列的2x4的窗口,它能够使第L层的第一排的两个窗口信息进行交流。再比如,第二行第二列的4x4的窗口,他能够使第L层的四个窗口信息进行交流,其他的同理。那么这就解决了不同窗口之间无法进行信息交流的问题。
但是这样也有一个问题:偏移后,窗口数由原来的4个变成了9个,后面又要对每个窗口内部进行MSA,非常麻烦。为此,作者又提出而了Efficient batch computation for shifted configuration
,一种更加高效的计算方法。(论文中是上图下部分图示)
2.4.3 Masked MSA
小绿豆感觉上图不够明晰,又重新画了个。下图左侧是刚刚通过偏移窗口后得到的新窗口,右侧是为了方便大家理解,对每个窗口加上了一个标识。
- 0对应的窗口标记为区域A
- 3和6对应的窗口标记为区域B
- 1和2对应的窗口标记为区域C
- 将区域A和C移到最下方
- 再将区域A和B移至最右侧
- 分成4个4x4的窗口
如下图所示,移动完后,4是一个单独的窗口;5和3合并成一个窗口;7和1合并成一个窗口;8、6、2和0合并成一个窗口。这样又和原来一样是4个4x4的窗口了,所以能够保证计算量是一样的。
- Masked MSA计算。
上图这样划分之后,还有个问题,就是合并的窗口本身是不相邻的,比如5和3,如果直接进行MSA计算,是有问题的。我们希望是能够单独计算区域3和区域5各自区域内的MSA,论文中给的方法是Masked MSA,来隔绝不同区域的信息。
如上图,区域5和区域3为例。对于该窗口内的每一个像素,都要先生成对应的query,key和value,然后与每一个像素进行attention计算。设 α 0 , 0 \alpha _{0,0} α0,0代表 q 0 q^0 q0与像素0对应的 k 0 k^0 k0进行匹配得到的attention score,同理可以得到 α 0 , 1 \alpha _{0,1} α0,1至 α 0 , 15 \alpha _{0,15} α0,15。
接下来就是SoftMax操作,但是对于Masked MSA,不同区域之间的像素计算的attention score会减去100(attention score∈[0,1],减去100是一个很大的负数,经过softmax之后基本等于0了,后续加权求和结果也是0),这样等于不同区域之间的像素计算的attention被置为0,被屏蔽了。最终像素0计算的attention结果,还是和本区域5各个像素计算之后的结果。
注意:在计算完后还要把数据给挪回到原来的位置上(例如上述的A,B,C区域)
Efficient batch computation for shifted configuration
简单说,就是通过移动合并,将9个窗口还是变为原来的4个窗口,再进行Masked MSA计算。计算量和普通的MSA一样,只是多了个mask。
Masked MSA是为了解决合并窗口中各个窗口应该单独计算的问题。使用Masked MSA,将不同窗口元素计算的attention score-100,等价于屏蔽掉不同窗口元素的attention结果。最终达到了4个窗口同时进行MSA计算,又保证得到只在在窗口内进行计算的效果。
2.5 Relative Position Bias
论文中还提到,使用相对位置偏置后给够带来明显的提升。(下图B就是偏置,ADE20k是图片分割数据集)
上图蓝色框是只使用W-MSA和同时使用W-MSA&SW-MSA的效果对比,后者更好。红色框是使用相对位置偏置之后的结果,效果更好。下面具体讲解什么是Relative Position Bias(假设特征图大小为2×2)
-
计算相对位置索引:比如蓝色像素,在蓝色像素使用q与所有像素k进行匹配过程中,是以蓝色像素为参考点。然后用蓝色像素的绝对位置索引与其他位置索引进行相减,就得到其他位置相对蓝色像素的相对位置索引,同理可以得到其他位置相对蓝色像素的相对位置索引矩阵(第一排四个位置矩阵)。
-
展平拼接:将每个相对位置索引矩阵按行展平,并拼接在一起可以得到第二排的这个4x4矩阵。
-
索引转换为一维:在源码中作者为了方便把二维索引给转成了一维索引。
- 首先在原始的相对位置索引上加上M-1(M为窗口的大小,在本示例中M=2),加上之后索引中就不会有负数了。
- 接着将所有的行标都乘上2M-1
- 最后将行标和列标进行相加。这样即保证了相对位置关系,而且不会出现直接相加后位置重叠的问题。(0±1和-1+0结果都一样,但其实其位置不一样)
-
取出相对位置偏置参数。真正使用到的可训练参数B 是保存在relative position bias table表里的,其长度是等于 ( 2 M − 1 ) × ( 2 M − 1 ) (2M-1) \times (2M-1) (2M−1)×(2M−1)。相对位置偏置参数B,是根据相对位置索引来查relative position bias table表得到的,如下图所示。
为啥表长是 ( 2 M − 1 ) × ( 2 M − 1 ) (2M-1) \times (2M-1) (2M−1)×(2M−1)?考虑两个极端位置,(0,0)能取到的相对位置极值为(-1,-1),(-1,-1)能取到的极值是(1,1),即行和列都能取到(2M-1)个数。考虑到所有的排列组合,表的长度就是 ( 2 M − 1 ) × ( 2 M − 1 ) (2M-1) \times (2M-1) (2M−1)×(2M−1)
2.6 模型详细配置参数
模型结构和参数如下:
以Swin-T举例:
- 模型一开始的
Patch Partition
和Linear Embeding
层,其效果和Patch Merging
层一样,都是进行下采样和调整channel。 concat4×4,96d,LN表示宽高都下采样4倍,调整后channel数为96,再经过一个Layer Norm层。 - 堆叠两个
Swin Transformer Block
,其中:win. sz. 7x7
表示使用的窗口的大小(window size)dim
表示这个层输出的feature map的channel深度(或者说token的向量长度)head
表示多头注意力模块中head的个数
- 后面以此类推,堆叠2、6、2个block。堆叠时是交替使用
W-MSA
和SW-MSA
。
2.7 代码讲解(待补充)
代码地址、讲解视频
三、 MAE(待补充)
四、