Vison Transformer 介绍
Vison Transformer论文- An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale, 论文由Google 发表于2020 CVPR上。
VIT模型的效果
从图中可以看出VIT不仅在NLP领域应用,在CV领域的效果也非常好 。
VIT模型架构
- 首先输入一张图片,然后将图片分成一个个
patches .对于模型VIT-L/16 ,这里的16指的是每个patches 的大小是16x16 的。 - 然后将所有
patches 输入到Embedding 层,也就是这里的“Linear Projection of Flattened Patches” ,通过Embedding 层之后我们就可以得到一个个向量,通常称为Token 。对应图上每个Patch对应得到一个Token 。在Token 最前面增加了一个带*的Token ,专门用来分类的class token . - 同时需要增加一个位置信息,在之前博客中我们讲到它的结构中是没有考虑到位置信息的,因此加上了一个
Position Embedding ,对应上图Embedding 层得到的0,1,2,3,4,5,6,7,8,9 标识的向量。 - 然后将得到的一系列
Token 包括class token 以及position ,然后输入到Transformer Encoder 中。Transformer Encoder 的结构如上图所示,网络将Encoder Block 重复堆叠L 次。 - 紧接着将
class token 的所对应的输出,输入到MLP Head 中,得到我们最终的分类结果。(我们在之前的博客讲到的muti-head attention 模块,输入几个变量就能得到几个输出,这些都是一一对应的。由于这里我们只是分类,所以只提取class token 经过Transformer Encoder 对应的输出)。
根据VIT 模型的网络结构,我们将模型分为3个部分:
- Embedding 层(Linear Projection of Flattened Patches)
- Transformer Encoder层
- MLP Head(最终用于分类的层结构)
Embedding 层
- 对于标准的Transformer模块,要求输入的是
token 向量序列,即二维矩阵[num_token,token_dim] , 如下图,token0-9 对应的都是向量 在代码的实现中,直接通过一个卷积层来实现,以VIT-B/16 为例,使用卷积核大小为16x16 ,stride为16 ,卷积核个数为768 ,这里的768 对应的是token 的维度。对输入为224 x 224 x 3 的图片,经过卷积后得到14 x 14 x 768 的特征矩阵,然后将宽高 纬度信息展平,得到输出特征矩阵196 x 768 ,对应为196 个纬度为768 的token。这个过程就对应于上图 的Embedding 层。 - 紧接着加上一个
class token ,通过初始化一个可训练的参数,纬度为1x768 。与我们通过将图片的patches,通过embedding层之后得到196x768 的特征矩阵进行concate拼接,就得到了197x768 的特征矩阵。得到的特征矩阵在输入Transformer Embedding 之前需要叠加一个Position Embedding ,它的纬度为197 x 768 。
Position Embedding
- 如果不去使用
Position Embedding 会有什么样的结果呢? 论文进行了实验对比,如下 对比发现没有使用位置编码,准确率为61.32 ,使用了1-D ,2-D ,Rel 位置编码都能达到64 个点,使用了位置编码可以提升大概3 个点。但位置编码的不同形式对最终效果影响不大,所以源码中一般使用的是1 D 位置编码。 - 我们会在每个
token 上加上一个位置编码,假设输入图片大小为224 x 224 ,patches的大小为32 , 我们会得到7x7 个token ,在每个token上会加一个位置编码。我们对各个位置的位置编码求余弦相似度,可以得到如下的图形: 可以看出位置编码与它所在行和列的余弦相似度比较大 。
Transformer Encoder层
Transformer Encoder 就是将我们的Encoder Block 重复堆叠L 次。对于Encoder Block 的说明如下:
- 首先,将
Embedding层 的输出,经过Layer Norm (层归一化),紧接着通过Mutli-Head Attention ,然后通过dropout 层或者DropPath 层(一般使用DropPath 的效果会好点)得到的输出与捷径分支的输出进行Add 相加操作。 - 紧接着将输出通过
Layer Norm 操作,然后通过MLP Block ,紧接着通过Dropout 或者DropParh 的输出与捷径分支的输出进行Add 相加操作。 - 其中
MLP Block 的结构如下图,通过全连接层,GELU 激活函数,Dropout ,全连接层 以及最后Dropout 层得到输出。注意第一个全连接层的节点个数3072 ,是输入节点个数768 的4倍,通过第二个全连接层之后节点个数又变回了768 。
MLP Head
- 上面通过
Transformer Encoder 后输出的shape 和输入的shape 是保持不变的,以ViT-B/16 为例,输入的是[197, 768 ]输出的还是[197, 768] 。注意,在Transformer Encoder前有个Dropout 层,Transformer Encoder后其实还有一个Layer Norm没有画出来。后面通过手绘的ViT的模型可以看到详细结构。 - 这里我们只是需要分类的信息,所以我们只需要提取出
[class]token 生成的对应结果就行,即[197, 768] 中抽取出[class]token对应的[1, 768] 。接着我们通过MLP Head得到我们最终的分类结果。MLP Head原论文中说在训练ImageNet21K 时是由Linear+tanh激活函数+Linear 组成。但是迁移到ImageNet1K 上或者你自己的数据上时,只用一个Linear 即可。后面如果需要得到每个类别的概率的话,需要接上一个softmax 激活函数。 以上就是VIT 模型网络结构的讲解。
Vision Transformer详细的网络结构
为了方便理解,博主:太阳花小绿豆手绘了更详细的图(以ViT-B/16为例):
- 图中
MLP Head 如果是对于自己的数据集进行训练的话,Pre-Logits 可以去掉,直接通过Linear 全连接层输出结果。如果训练ImageNet21K时是,则Pre-Logits 对应的是Linear+tanh 激活函数
Hybrid模型详解
在论文4.1章节的Model Variants 中有比较详细的讲到Hybrid 混合模型,就是将传统CNN特征提取和Transformer进行结合。下图绘制的是以ResNet50 作为特征提取器的混合模型,但这里的Resnet 与之前讲的Resnet有些不同 。首先这里的R50的卷积层采用的StdConv2d不是传统的Conv2d,然后将所有的BatchNorm层替换成GroupNorm层。在原Resnet50网络中,stage1重复堆叠3次,stage2重复堆叠4次,stage3重复堆叠6次,stage4重复堆叠3次,但在这里的R50中,把stage4中的3个Block移至stage3中,所以stage3中共重复堆叠9次。
通过R50 Backbone 进行特征提取后,得到的特征矩阵shape是[14, 14, 1024] ,接着再输入Patch Embedding 层,注意Patch Embedding中卷积层Conv2d的kernel_size 和stride都变成了1,只是用来调整channel。后面的部分和前面ViT中讲的完全一样,就不在赘述。 下表是论文用来对比ViT ,Resnet (和刚刚讲的一样,使用的卷积层和Norm层都进行了修改)以及Hybrid模型的效果。通过对比发现,在训练epoch较少时Hybrid优于ViT,但当epoch 增大后ViT优于Hybrid 。
ViT模型搭建参数
在论文的Table1 中有给出三个模型(Base/ Large/ Huge) 的参数,在源码中除了有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数。
参考:Vision Transformer详解
|