Transformer最初是针对NLP领域提出的,因为Transformer在NLP中的拓展性很好,越大的数据或模型,最后的performance就会一直上升,并且没有饱和现象,那么如果把Transformer应用到CV领域中会有什么样的效果呢?本篇论文《An Image Is Worth 16x16 Words: Transformers For Image Recognition At Scale》就是受到其启发,尝试将Transformer应用到CV领域上,但是这样也会遇到一些问题,比如在Transformer中输入的是序列,那么在CV领域就要考虑如何将一张2d的图片转化为1d的序列,而且在Transformer中序列的长度并不是无限长,而是有限的。正常的bert序列也就512,如果将一张224224的图片按照像素进行转化为1d就是224224=50176,这就太长了,导致复杂度很高。那么如何处理呢?
在模型设计中,我们尽可能遵循原始Transformer,因为这样就避免我们做过多的更改,可扩展的NLP Trans former架构及其高效性几乎可以开箱即用。
下图是原论文中给出的关于Vision Transformer的模型框架,模型由三个模块组成:
如下图,token0-9对应的都是向量,以ViT-B/16为例,每个token向量长度为768,对于一个图像来说,它是一个三维的向量,明显与Transformer的输入不符,Transformer Encoder需要的是一个向量,shape为[num_token, token_dim],对于图片数据来说,shape为[H,W,C]是不符合要求的,所以就需要转换,要将图片数据通过这个Embedding层转换成token,所以要通过一个Embedding层来对数据进行变换。
我们可以将图片按照给定大小分成一堆patches。这样做的原因是:将一个个小块图像视为token(token可以理解为NLP中的一个字或词),在Transformer中计算每个token之间的相关性。这一点就和卷积神经网络有很大区别了。以往的CNN,以卷积 + 池化的方式不断下采样,这样理论上模型可以通过加深模型深度,达到增大感受野的目的。不过这样会有两个缺点:
以VIT-B/16为例,将输入图片(224✖️224)按照16*16大小的patch进行划分,划分后会得到(224/16)^2 = 196个Patches。接着通过线性映射将每个Patch映射到一维向量中,每个Patch数据shape[16,16,3]通过映射到一个长度为768的向量(后面都直接称为token)。[16,16,3]-->[768]
根据源码看,这里的维度转换就是通过一个卷积层来实现,以ViT-B/16为例,直接使用一个卷积核大小为16*16,stride=16,卷积核个数为768的卷积来实现,通过卷积[224,224,3]->[14,14,768],然后把H以及W两个维度展平即可[14,14,768]---->[196,768]
,此时正好变成了一个二维矩阵,正是Transformer想要的。
类似于 BERT 的类别 token ,在输入Transformer Encoder之前需要加上[class]token以及Position Embedding。在原论文中,在输入的一堆token中要插入一个专门的用于分类的【class】token,这个【class】token是一个可训练的参数,数据格式和其他token一样都是一个向量,就像NLP中做句子分类的 就是一句话有很多词,我们在前面加上cls它就能吸取整个句子里面的信息,用它就可以代表整个句子,这里的【class】token能够从一些相互交流tokens中吸取到有用的信息,然后输出。以ViT-B/16为例,就是一个长度为768的向量,与之前从图片中生成的tokens进行拼接,即Cat([1,768],[196,768])->[197,768]
。然后关于Position Embedding 就与Transformer中的一样,因为图片的每个patch是固定的,打乱顺序就不是原始图片了,所以要加上位置信息,这里采用的是一个可训练的参数(1D Pos.Emb.),是直接叠加在tokens上的(add),所以shape要一样。刚刚拼接[class]token后shape[197,768],那叠加Position Embedding的shape也是【197,768】。
论文中对于Position Embedding 的表示方法,比如无位置嵌入,1-D 位置嵌入 ,2-D 位置嵌入 ,以及相对位置嵌入,做了对比实验,结果表明如下:
第一行是完全没有位置编码,即没有提供位置信息,相当于将一堆patch直接输入进去;第二行是一维位置编码,即将输入patch看作是序列;第三行是二维位置编码,将输入看作是二维的patch网格;第四行是相对位置编码,考虑到patch之间的相对距离,将空间信息编码为而不是其绝对位置。
注意:如果要使用相对位置编码,一定要考虑好自己的任务需不需要绝对位置信息,如目标检测,由于要输出预测的边界框的坐标,因此绝对位置信息是必须的,这时使用相对位置编码就不合适了
最后发现如果 不提供位置编码效果会差,但其它各种类型的编码效果效果都接近,这主要是因为 ViT 的输入是相对较大的图像块而非像素,所以学习位置信息相对容易很多。Transformer 原文中默认采用 固定位置编码,ViT 则采用 标准可学习/训练的 1-D 位置编码嵌入。
Transformer Encoder其实就是重复堆叠Encoder Block L次,主要由以下几部分组成:
上面的结构通过Transformer Encoder 后输出的shape是保持不变的,以ViT-B/16为例,输入的是[197,168] 输出的还是[197,168] 。注意,在Transformer Encoder 后其实还有一个LN(源码中有),这里我们只需要分类的信息,所以我们只需要提取出[class]token生成的对应结果就行,也就是从[197,768] 中抽出[class] token对应的[1,768]。接着我们通过MLP Head 得到我们最终的分类结果。MLP Head 原论文中说在训练ImageNet21K时是由Linear+tanh激活函数+Linear组成。但是迁移到ImageNet1K或者自己的数据上,只用一个Linear即可
在论文中讲到了Hybride模型,就是上面提到的如何解决向量序列长度问题,这里是用传统的CNN特征提取和传统的Transformer进行结合。下图是根据ResNet50作为特征提取器的混合模型,但是略有区别,这里的R50的卷积层采用的是StdConv2d不是传统的Conv2d,将所有的BN换成GN,在原始的Res50网络中,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
————————————————
参考原文链接:https://blog.csdn.net/qq_37541097/article/details/118242600
————————————————
基于自注意力的架构,尤其是Transformer,已经成为模型首选,其基本方法就是在大型数据集上进行预训练,然后再较小或特定的任务数据集上进行微调,虽然有很多工作尝试将CNN与自注意力机制相结合,甚至有些工作直接用自注意力代替卷积,后一种虽然在理论上有效,但由于使用了特定的自注意力机制,尚未在硬件加速器上有效的扩展,因此在大规模的图像识别上,Resnet架构仍最先进,从实验给出的结果,在当时也达到了SOTA,但是相比于CNN,它需要更多的数据集。在小数据集上训练出来的精度是不如CNN的,但在大数据集上ViT精度更高。一个直观的解释是:ViT因为self-attention独特的机制,更多的利用token与token跨像素之间的信息,而CNN只是对领域的像素进行计算,所以相同参数的情况下,ViT获得的信息更多,在某种程度上可以看成是模型深度更深。所以小数据集上ViT是欠拟合的。实际开发中的做法是:基于大数据集上训练,得到一个预训练权重,然后再在小数据集上Fine-Tune。