- 论文名称: 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》
参考《ViT(Vision Transformer)全文精读》
作者介绍的另一篇论文:《Intriguing Properties of Vision Transformer》(Vision Transformer的一些有趣特性)如下图所示:
上述例子中,卷积神经网络很难判断到底是一个什么物体,但是对于所有的这些例子Vision Transformer都能够处理的很好。
在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模型。
基于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领域呢?
Stand-Alone Axial-Attention
来处理。具体的说,不是输入整张图,而是在一个local window(局部的小窗口)中计算attention。窗口的大小可以控制,复杂度也就大大降低。(类似卷积的操作) 所以,自注意力早已经在计算机视觉里有所应用,而且已经有完全用自注意力去取代卷积操作的工作了。
这些模型虽然理论上是非常高效的,但事实上因为这个自注意力操作都是一些比较特殊的自注意力操作(除了上面举例的最后一篇),要很复杂的工程去加速算子,所以就导致很难训练出一个大模型。因此在大规模的图像识别上,传统的残差网络还是效果最好的。
本文是被transformer在NLP领域的可扩展性所启发,本文想要做的就是直接应用一个标准的transformer作用于图片,尽量做少的修改。好处是可以直接使用NLP中成熟的Transformer架构,不需要再魔改模型,而且Transformer这么多年有很多高效的实现。具体的处理方式见下一节。
上面举例的例4模型,从技术上讲就是
Vision Transformer
。但是作者认为二者的区别,是本文证明了,使用一个标准的Transformer endoder
(类似BERT,不需要任何特殊改动)在大规模的数据集上做预训练的话,就能取得比现在最好的卷积神经网络差不多或者还好的结果。(这是本文的主要论证工作,在1.4.2和1.4.3章节都有体现 )另外就是二者可处理图片的分辨率不同(32×32对比224×224)。
引言的最后部分放出了结论:
inductive bias
,是指一种先验知识或者说是一种提前做好的假设)。CNN的归纳偏置一般来说有两种:
locality
:CNN是以滑动窗口的形式一点一点地在图片上进行卷积的,所以假设图片上相邻的区域会有相邻的特征,靠得越近的东西相关性越强;translation equivariance
(平移等变性或平移同变性):写成公式就是f(g(x))=g(f(x))
,不论是先做 g 这个函数,还是先做 f 这个函数,最后的结果是不变的;其中f代表卷积操作,g代表平移操作。(因为在卷积神经网络中,卷积核就相当于是一个模板,不论图片中同样的物体移动到哪里,只要是同样的输入进来,然后遇到同样的卷积核,那么输出永远是一样的) 简单介绍了一下Transformer在NLP领域应用最广的两大分支BERT和GPT,都是基于自监督的训练方式(MLM任务和Next word prediction)。
直接将图片的像素作为序列输入Transformer是不可行的,所以作者介绍了一下之前的相关处理方式(类似上面引言提到的几个模型)。
第三段介绍了,有一些工作研究了用比ImageNet更大的数据集去做预训练,效果会更好,比如说ImageNet-21k和JFT300M。最终作者也是在这两个数据集上预训练模型。
下图是原论文中给出的关于Vision Transformer(ViT)的模型框架。简单而言,模型由三个模块组成:
Embedding
层(线性投射层Linear Projection of Flattened Patches)Transformer Encoder
(图右侧有给出更加详细的结构)MLP Head
(最终用于分类的层结构)
如上图所示:
embedding
层(图片输入)
patchs
,然后这些patchs一个个输入线性投射层得到Pacth embedding
。比如ViT-L/16
表示每个patchs
大小是16×16。class token
作为这些patchs全局输出,相当于transformer中的CLS(这里的加是concat拼接)。position embedding
(这里的加是直接向量相加,不是concat)。Pacth embedding
+position embedding
+class token
一起输入Transformer Encoder
层得到其输出。MLP Head
得到分类结果。(VIT只做分类任务)整体上看,VIT的模型结构还是很简洁的,难点就是如何将图片转为token输入网络。
对于标准的Transformer模块,要求输入的是token(向量)序列,即二维矩阵[num_token, token_dim]。对于图像数据而言,其数据为[H, W, C]格式的三维矩阵,明显不是Transformer想要的。所以需要先通过一个Embedding层来对数据做个变换。
ViT-B/16
为例(后面都是以此模型举例),将输入图片(224x224)按照16x16大小的Patch尺寸进行划分,划分后会得到 ( 224 / 16 ) 2 = 196 (224/16)^2=196 (224/16)2=196个Patches。[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消融试验部分会讲到。
Transformer Encoder其实就是重复堆叠Encoder Block L
次,主要由以下几部分组成:
[197, 768] -> [197, 3072]
,第二个全连接层会还原回原节点个数[197, 3072] -> [197, 768]
,原来跟transformer中做法一样。Encoder Block
。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
部分,对整个过程用数学公式描述了一次:
在CNN中,locality(局部性)和translate equivariance(平移等变性)是在模型的每一层中都有体现的,这个先验知识相当于贯穿整个模型的始终。
但是对于ViT来说,只有MLP层是局部而且平移等变性的,其它的自注意力层是全局的,这种图片的2d信息ViT基本上没怎么使用。
就是只有刚开始将图片切成patch的时候和加位置编码的时候用到了,除此之外,就再也没有用任何针对视觉问题的归纳偏置了,而且位置编码也是随机初始化的1-D信息。所以在中小数据集上ViT不如CNN是可以理解的。
既然transformer全局建模的能力比较强,卷积神经网络又比较data efficient(不需要太多的训练数据),那么自然想到去搞一个前面层是CNN后面层是transformer的混合网络,也就是Hybrid
混合模型。Hybrid
不再直接将图片打成一个个patch,而是直接送入CNN得到embedding,比如经过Resnet50,最后一个stage输出特征图是14×14,拉直了也是196维向量。这部分细节参考文本1.4实验部分。
之前的工作有表明,使用更大的图片输入尺寸往往模型效果会更好。但是使用一个预训练好的vision transformer,其实是不太好去调整输入尺寸的。如果还是将patch size保持一致,但是图片扩大了,那么序列长度就增加了,提前预训练好的位置编码有可能就没用了。
这个时候位置编码该如何使用?作者发现其实做一个简单的2d的插值就可以了(使用torch官方自带的interpolate函数就完成)。但这只是一个临时的解决方案,如果需要从一个很短的序列变成一个很长的序列时,简单的插值操作会导致最终的效果下降(比如256→512)。这也算是vision transformer在微调的时候的一个局限性。
因为使用了图片的位置信息进行插值,所以这块的尺寸改变和抽图像块是vision transformer里唯一用到2d信息的归纳偏置的地方。
在论文的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数(多头注意力有几个头)。 对比VIT的几个不同配置模型和BiT-L
,以及Noisy
模型。最终效果提升不大,但是ViT
训练时间短很多。(的后面还有其它的对比试验支持作者的这个观点)
TPU-v3-core-days 2.5k
:表示最大的ViT-H/14
模型在TPU-v3上只需要训练2500天。
vision trasformer
预训练需要多大的数据规模?(重要论证) 下图展示了在不同大小的数据集上预训练后,BiTt
和VIT
到底在ImageNet的微调效果如何。
BiT
,上下两条线分别表示使用ResNet50和ResNet152结构;其它五个 ∙ \bullet ∙就是不同配置的ViT
。ImageNet
、ImageNet 21K
、JFT-300M
三种规模的数据集上训练的结果,三种数据集规模分别是1.2Million,14M和300MJFT
数据集上分别采样不同规模子集的实验结果,这里ViT
用作特征提取器而不是微调。(去除了训练时的强约束,比如说dropout、weight decay、label smoothing;而且不同规模的数据集来自同一分布,更能看出模型本身的特性)ViT
,那么得至少是在ImageNet-21k
这种规模的数据集上预训练(数据量14M),否则还不如用CNN。下图再次论证作者观点(VIT训练更便宜):
Average-5
就是他在五个数据集(ImageNet real、pets、flowers、CIFAR-10、CIFAR-100)上做了验证后的平均结果ImageNet
上的验证结果JFT-300M
上预训练号的模型ViT
比BiT
效果要好,这也说明ViT
训练比CNN
更便宜Hybrid
混合模型比其它两者效果都要好Hybrid
精度慢慢的跟ViT
差不多了,甚至还不如在同等计算条件下的ViT
。为什么卷积神经网络抽出来的特征没有帮助ViT
更好的去学习?这里作者对此也没有做过多的解释。ViT
和BiT
都没有性能饱和的迹象。统一见本文1.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
的基础上使用了对比学习。
这部分内容在论文附录的D Additional Analyses部分
虽然最终VIT采用的[CLS]token
和1D-Position Embedding
都和BERT
是一样的,但是作者在论文实验部分也都对此作了很多消融试验。比如:
[CLS]token
是从NLP领域借鉴的,但是之前的CNN网络用于图片分类的特征并非这么做。
Pacth embedding
中不加入[CLS]token
,而是在最后Transformer Encoder
输出序列向量时,用一个GAP
来得到最终全局表征向量,最终结果和使用[CLS]token
的效果几乎差不多 。[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是直接在图像块上做的,而不是在原来全局尺度的像素块上做的,所以在排列组合这种小块或者想要知道这些小块之间相对位置信息的时候,还是相对比较容易的,所以使用任意的位置编码都无所谓。
在论文4.1章节的Model Variants中有比较详细的讲到,就是将传统CNN特征提取和Transformer进行结合。下图绘制的是以ResNet50
作为特征提取器的混合模型Hybrid
,但这里的Resnet与之前讲的Resnet有些不同。
StdConv2d
不是传统的Conv2d
,然后将所有的BatchNorm
层替换成GroupNorm
层。如果有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。
分析完训练成本以后,作者也做了一些可视化,希望通过这些可视化能够分析一下vit内部的表征。
Mean attention distance
ViT-L/16
有24层,所以横坐标网络深度是从0到24 。ViT-L/16
有16个头,每一列其实就是16个点。Mean attention distance
(平均注意力的距离:图上两个像素点的真实距离乘以他们之间的attention weights,因为自注意力是全局都在做,所以平均注意力的距离就能反映模型到底能不能注意到两个很远的像素)Mean attention distance
从10几到100多,有的近有的远,这也就证明了自注意力能够在网络最底层,也就是刚开始的时候就已经能够注意到全局上的信息了,而不是像卷神经网络一样,逐步提升感受野。 Vision Transformer
和NLP领域标准的Transforme
r的自注意力机制区别在于:除了在刚开始抽图像块的时候,还有位置编码用了一些图像特有的归纳偏置,除此之外就再也没有引入任何图像特有的归纳偏置了。这样的好处就是可以直接把图片当做NLP中的token,拿NLP中一个标准的Transformer就可以做图像分类了。
这个策略扩展性非常好,和大规模预训练结合起来的时候效果也出奇的好,而且训练起来还相对便宜。
另外作者在这里对Vision Transformer
还做了一些展望:
除以上几点之外,作者还在
多模态领域
挖了一个大坑。CV和NLP大一统之后,是不是这些任务都可以用一个Transformer取解决呢?后续这一块也是推进的很快。
项目代码github地址、B站代码讲解《使用pytorch搭建Vision Transformer(vit)模型》
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
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
参考:
- 论文《Masked Autoencoders Are Scalable Vision Learners》
- 李沐《MAE 论文逐段精读【论文精读】》、笔记《MAE 论文逐段精读》
本文算法在2.4章节,不想看全文的直接跳过去!!!
自从Vision Transformer
将标准的transformer成功的应用到CV上面以来,就出现了很多相关性的工作,MAE
就是其中的一篇。本文是2021.11.11发布在Arxiv上的文章,主要工作是在Vision Transformer
基础上,引入自监督训练,相当于将BERT
应用到CV领域。通过完形填空来获取对于图片的理解,把整个训练拓展到没有标号的数据上面 ,使得transformer在CV上的应用更加普及。
最终MAE只需要Vision Transformer
百分之一规模的数据集上预训练,就能达到同样的效果。而且在目标检测、实例分割、语义分割等任务上,效果都很好。
本文标题中的
Autoencoders
,是‘自
’而非自动的意思,表示类似自回归这一类模型的特点, 即标号和样本(y和x)来自于同一个东西。比如说在语言模型中,每一次用前面的次去预测下一个词。而对于MAE,则表明模型的标号也是图片本身
。
MAE的自监督训练实现途径非常简单,随机地mask图片中地一些patches
(块),然后再去重构the missing pixels
.,这个思想来自于BERT中的带掩码的语言模型。
在MAE中,自监督训练还有两个核心的设计:
非对称的encoder-decoder架构。
MAE的编码器只编码可见的patches,被masked的块不编码,而解码器需要重构所有块。两者看到的东西是不一样的,所以叫非对称。
mask比例要提到比较高,比如75%
,才能得到一个比较好的自监督训练效果(否则只需要插值还原)。
这等于只编码1/4的图片,训练速度加快了四倍,所以MAE可以扩展到更大的模型。
最终作者只使用一个最简单的ViT-Huge
模型,在ImageNet-1K
上预训练,精度也能达到87.8%
。
下面是作者展示的一些模型重构效果图:
在导言一开始,作者大意就说自监督学习很香,使得Transformer在NLP领域的应用非常火。尽管也有一些工作将BERT应用到CV领域,比如Denoising Autoencoder是在一个图片中加入很多噪音,然后通过去噪来学习对这个图片的理解,但最终这些效果都不及NLP。
MAE也是一种类似去噪 的模型,mask一些patches,也相当于往模型中加入很多噪音
为何masked自编码模型在CV和NLP领域应用不一样呢?
msaked patches
单独剔除,导致后面不好将图片还原(Transformer中可以将masked token直接去掉,和其他词区分开来)。但是ViT
成功将Transformer
应用到图片分类,所以这个问题不再有了75%
),就可以大大降低图片的冗余性。这样就压迫模型必须学习全局信息,也就提高了模型学习的难度。将图片中一大片都去掉, 剩下的块离得比较远,就没那么冗余了。否则模型仅仅学一个局部模型就可以进行插值还原。
关于此点论证可以看下面图2/3/4的效果,仅仅一些局部的,很稀疏的块,就可以重构全局图片。
自编码器的解码器差异
masked tokens
,而token
本身就是一种比较高级一些的语义表示,所以只需要一个简单的全连接层就可以解码这些tokens
。(ViT只是简单的图片分类,所以也只需要MLP就可以解码)the missing pixels
,这些像素是一个很基础的特征。所以可能需要一个比较复杂的转置卷积网络才能完成解码。正是基于以上分析,作者才提出本文的两个核心设计:mask使用高的比例以及非对称编码器-解码器。(为啥是非对称,其实在2.4.2具体实现中,有进一步说明)
在导言最后一部分,作者说MAE可以只在ImageNet 1K
上就能预训练ViT-Large/-Huge
模型(得到很好的效果)。类比ViT
,相同效果后者需要在近100
倍规模的数据集上训练才能达到。
另外,在目标检测、语义分割和实例分割上,MAE比之前所有模型(包括有监督训练模型)效果都要好,而且加大模型会有显著的收益。
简单且拓展性好的算法,是整个深度学习的核心。
- 简单:是指
MAE
在ViT
模型上加了一些简单的扩展,但是模型本身是不简单的。- 拓展性好:是指你有钱就可以无限的加数据了,毕竟MAE不需要标号,你可以训练更大的模型。
自监督学习在最近几年是比较火的,但是在计算机视觉中,还是主要用有标号的数据来训练 。本文在ImageNet
数据集上通过自编码器学习到可以媲美有标号训练的效果,使得CV领域的自监督学习可能走上与NLP类似的轨迹。
另一方面,图像和语言是不同类型的信息,要谨慎处理这种差异。
Broader impacts:MAE可以生成不存在的内容,类似GAN,所以使用要注意。
MAE使用编码器将观察到的信号映射到潜在表示,再使用一个解码器从潜在表示重构原始信号。与经典的自动编码器不同,我们采用了一种非对称设计,允许编码器仅对部分观察信号(无掩码标记)进行操作,模型设计图如下:
ViT
),得到对应的token embeddings
。position embeddings
)。decoder
,解码器会尝试将里面的像素信息全部重构回来,使之成为原始图片。endoder
,将图片切成Patches
后编码得到特征,然后用来处理自己的任务就行。(此时不需要掩码,也就不需要解码器)ViT
一样,MAE也将图片切分成一块块的Patches,然后进行随机抽样
。即:在不替换的情况下,按照均匀分布对patches进行随机采样,采到的样本保留,剩下的全部mask掉。ViT
一样,通过线性投射层Linear Projection
将patches映射成patch embeddings
,再加上position embedding
,得到最终的token embeddings
。唯一不同的是,被mask的块不进行编码(直接去除,不输入encoder层)。linear projection
,其输出通道的数量等于每个patch中包含的像素数量(比如patch size=16×16,那么输出是256维,最后reshape一下就是16×16了)。
- 参考:太阳花的小绿豆帖子《Swin-Transformer网络结构详解》、讲解视频、代码地址、代码讲解视频
- 论文名称:Swin Transformer: Hierarchical Vision Transformer using Shifted Windows、官方开源代码
- Pytorch实现代码及Swin Transformer API、Tensorflow2实现代码
- 《Swin Transformer 论文详解及程序解读》
Swin transformer
(Shifted Windows移动窗口)是微软研究院于2021年3月25日发表,利用transformer架构处理计算机视觉任务,在图像分割,目标检测各个领域已经霸榜(之前的VIT只能做图片分类)。比如打开其官网代码可以看到:(这排名不是卷死?)
点开第一个coco测试集上的精度:
标准的Transformer直接用到视觉领域有一些挑战,即:
- Vision Transformer进行WSA计算时,任何一个patch都要与其他所有的patch都进行attention计算。当patch的大小固定时,计算量与图片的大小成平方增长。
- Swin Transformer中采用了W-MSA,只对window内部进行MSA,当图片大小增大时,计算量仅仅是呈线性增加。
所以最终Swin Transformer计算量比VIT更少,且在ImageNet上的效果也比VIT更好。
对比下Swin Transformer和之前的Vision Transformer,可以看出两点不同:
比如特征图尺寸中有对图像下采样4倍的,8倍的以及16倍的,这样的backbone有助于在此基础上构建目标检测,实例分割等任务。而在之前的Vision Transformer中是一开始就直接下采样16倍,且一直不变
比如在下图的4倍下采样和8倍下采样中,相对于Vision Transformer中直接对整个(Global)特征图进行Multi-Head Self-Attention,Swin Transformer只在窗口内做。这样能够减少计算量,尤其是在浅层特征图很大的时候。
这样做虽然减少了计算量,但也会隔绝不同窗口之间的信息传递。所以在论文中作者又提出了 Shifted Windows Multi-Head Self-Attention(SW-MSA)的概念,通过此方法能够让信息在相邻的窗口中进行传递,后面会细讲。
原论文中给出的关于Swin Transformer(Swin-T)网络的架构图如下:
假设输入的是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)
如图(b)中所示。这两种结构的不同之处仅在于一个使用了W-MSA结构,一个使用了SW-MSA结构。先使用一个W-MSA结构再使用一个SW-MSA结构。
接下来,在分别对Patch Merging
、W-MSA
、SW-MSA
以及使用到的相对位置偏置(relative position bias
)进行详解。关于Swin Transformer Block中的MLP结构和Vision Transformer中的结构是一样的,不再赘述。
上面讲了,在每个Stage中首先要通过一个Patch Merging层进行下采样(Stage1除外)。
如上图所示,假设输入的是一个4x4大小的单通道特征图(feature map),分割窗口大小为2×2,进行的操作如下:
可以看出,通过Patch Merging层后,feature map的高和宽会减半,深度会翻倍(channel由1变为2,高宽由4变为2)。
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网络结构详解》第三章
与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
,一种更加高效的计算方法。(论文中是上图下部分图示)
小绿豆感觉上图不够明晰,又重新画了个。下图左侧是刚刚通过偏移窗口后得到的新窗口,右侧是为了方便大家理解,对每个窗口加上了一个标识。
Efficient batch computation for shifted configuration
简单说,就是通过移动合并,将9个窗口还是变为原来的4个窗口,再进行Masked MSA计算。计算量和普通的MSA一样,只是多了个mask。
Masked MSA是为了解决合并窗口中各个窗口应该单独计算的问题。使用Masked MSA,将不同窗口元素计算的attention score-100,等价于屏蔽掉不同窗口元素的attention结果。最终达到了4个窗口同时进行MSA计算,又保证得到只在在窗口内进行计算的效果。
论文中还提到,使用相对位置偏置后给够带来明显的提升。(下图B就是偏置,ADE20k是图片分割数据集)
上图蓝色框是只使用W-MSA和同时使用W-MSA&SW-MSA的效果对比,后者更好。红色框是使用相对位置偏置之后的结果,效果更好。下面具体讲解什么是Relative Position Bias(假设特征图大小为2×2)
计算相对位置索引:比如蓝色像素,在蓝色像素使用q与所有像素k进行匹配过程中,是以蓝色像素为参考点。然后用蓝色像素的绝对位置索引与其他位置索引进行相减,就得到其他位置相对蓝色像素的相对位置索引,同理可以得到其他位置相对蓝色像素的相对位置索引矩阵(第一排四个位置矩阵)。
展平拼接:将每个相对位置索引矩阵按行展平,并拼接在一起可以得到第二排的这个4x4矩阵。
索引转换为一维:在源码中作者为了方便把二维索引给转成了一维索引。
取出相对位置偏置参数。真正使用到的可训练参数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)
模型结构和参数如下:
以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的个数W-MSA
和SW-MSA
。代码地址、讲解视频
三、 MAE(待补充)
四、