VisionTransformer
1、简介
论文原文:Vit](https://arxiv.org/abs/2010.11929))
Transformer最初是针对NLP领域提出的, 并且在NLP领域大获成功。Vision-Transformer尝试将Transformer应用到CV领域。通过原论文中的实验发现,论文给出的模型在ImageNet1k上能够达到88.55%的准确率, 说明Transformer在CV领域确实能取得不错的效果。
2、模型详解
原论文中,作者主要拿ResNet, Vit(纯Transformer模型)以及Hybrid(卷积和Transformer混合模型)三个模型进行比较。
下图是原论文中Vit的模型架构, 简单而言, 模型由三部分组成:
1、Linear Project of Flattened Patches(Embedding层)
2、Transformers Encoder(图右侧给出更加详细的结构)
3、MLP Head(用于分类的层结构)
Embedding层结构
对于标准的Transformers模块, 要求输入的是token(向量)序列, 即二维矩阵[num_token, token_dim], 如下图, token0-9对应的都是向量, 以Vit-B/16为例, 每个token向量长度为768。
对于图像数据而言, 其数据格式为[H, W, C], 它是三维矩阵形式, 该形式明显不是Transformer想要的, 所以首先要通过Embedding层对数据做变换。如下图所示, 首先将一张图片按给定大小分成一堆patches。以Vit-B/16为例, 将输入图片(224×224)按照16×16大小的Patch进行划分, 划分后得到个Patches。接着通过线性映射将每个Patch映射到一维向量中, 每个Patches数据shape[16, 16, 3], 通过映射得到长度为768的向量(后面直接称为token)。[16, 16, 3]->[768]>。
在代码实现中,直接通过一个卷积层来实现, 以Vit-B/16为例, 直接使用一个卷积核大小为16×16, 步距为16, 卷积核个数为768的卷积来实现。通过卷积[224, 224, 3] ->[14, 14, 768], 然后把H以及W两个维度展平即可:[14, 14, 768]->[194, 768],此时正好变成了一个二维矩阵, 正式Transformers想要的。
在输入Transformer Encoder之前需要加上[class]token以及Position Embedding。原论文中, 作者参考了Bert, 在刚刚得到的一堆tokens中插入一个专门用于分类的[class] token, 这个[class]token是一个可训练的参数, 数据格式和其他token一样都是一个向量, 以Vit-B/16为例, 就是一个长度为768的向量, 与之前从图片中生成的tokens拼接在一起。Cat([1, 768], [196, 768]) - > [197, 768]。Position Embedding与之前在Transformers中的Position Embedding一样, 直接叠加在tokens上(add), 所以shape是一样的。刚刚拼接了[class] token之后的shape是 [197, 768], 所以这里的Position Embedding也是 [197, 768]。
对于Position Embedding作者也做了一系列对比实验, 在源码中默认使用的是 1D Pos. Emb., 对比不使用Position Embedding准确率大概提升了3个点。和2D Pos. Emb.相比, 效果没有太大的差别。
Transformer Encoder详解
Transformer Encode其实就是重复Encoder Block L次, 如下图所示,主要由以下几个部分组成。
Layer Norm, 这种Normalization方法主要是针对NLP领域提出的, 这里是对每个token进行Norm处理。
Mliti-Head Attention, 与之前Transformers中讲解的一样, 这里不再赘述。
Dropout/Droppath. 原论文的代码中直接使用的是Dropout层, 但在rwightman实现的代码中使用的是DropPath(stochastic depth), 可能后者的效果更好一点。
MLP Block, 就是全连接层+GELU激活函数+Dropout组成。 全连接层节点个数变化如下: [197, 768] -> [197, 3072] -> [197, 768]
MLP Head详解
上面通过Transformers Encoder之后输入与输出的shape保持不变, 以Vit-B/16为例, 输入的是[197, 768]输出还是[197, 768]。 这里我们只需要分类的信息, 所以我们只需要提取出[class]token生成对应的结果捷星, 即从[197, 768]中提取[class token]对应的[1, 768]。 然后通过MLP Head得到最终的分类结果。 原论文中是由Linear + tanh激活函数+Linear组成。但是迁移到别的数据集上时只用一个Linear即可。
绘制自己的Vision Transformers结构
Hybrid模型详解
原论文中详细介绍了Hybird混合模型, 就是将传统的CNN提取特征和Transformer进行结合。下图绘制的是以Resnet50作为特征提取器的混合模型,但是这里的resnet50与之前的有所不同,这里的resnet50采用的是stdconv2d而不是传统的conv2d, 然后将所有的batch Normalization换成了group norm层。 在原来的resnet50中, stage1重复堆叠3次, stage2重复堆叠4次, stage3重复堆叠6次, stage4重复堆叠3次。 但在这里的resnet50中, 把stage4中的3个Block移到了stage3中, 所以stage3中总共重复堆叠9次。
通过R50 Backbone提取特征后, 得到的特征矩阵shape是[14, 14, 1014], 接着再输入Patch Embedding层, 注意Patch Embedding中卷积层的Conv2d的kernel size都变成1, 只是用来调整channel, 后面的部分与前面的vit讲的完全一样,这里不再赘述。
下表是论文中对比Vit, Resnet以及Hybrid模型的效果, 对比发现, 在训练epoch较少时, Hybrid效果优于Vit。
Vit模型参数
Layers是Transformer Encoder中重复堆叠Encoder Block的次数
Hidden Size是通过Embedding层后每个token的dim
MLP size是Transformer Encoder中MLP Block第一个全连接的节点个数(是Hidden Size的四倍)
Heads代表Transformer中Multi-Head Attention的heads数