【arxiv2020.10,ICLR2021】ViT
An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale
之前或是直接CNN连attention或是将attention替换CNN的一部分,并保持CNN整体结构。作者认为这种对于CNN依赖不必要,pure transformer一样可以对于图像patch输入在分类任务有良好表现。最终,作者先在大量数据预训练再转到imagenet,cifar100上训练的结果,相比于现在的sota是competitive的,而且所需计算资源更少。
由于transformer的计算高效性和可扩展性,在nlp领域火热。现有CV领域大多还是resnet-like的架构。作者想直接运用最标准的transformer,做最小的改动,将图像分成patch并形成的linear embedding序列来替换原本NLP中的tokens作为输入来进行有监督的图像分类实验。
结果显示,相较同size的resnet,这样的模型accu要低几个百分点,作者解释为:transformer缺少一些CNN本质上的inductive biases(归纳偏置或者理解为先天优势?),例如translation equivariance(平移等变性)和locality(局部感知性),因此不能在数据不充分的时候进行很好的泛化。但是当他们有大量的训练样本(ImageNet-21k dataset or the in-house JFT-300M dataset )的时候就会战胜这种biases,在迁移学习的过程中提点。最终实验结果:
“the best model reaches the accuracy of 88:55% on ImageNet, 90:72% on ImageNet-ReaL, 94:55% on CIFAR-100, and 77:63% on the VTAB suite of 19 tasks. ”
为将图像变成对应的1-D的序列patch embedding输入,对于2D图像 x ∈ R H × W × C \mathbf x\in \mathbb{R}^{H\times W \times C} x∈RH×W×C将其分为 P × P P \times P P×P的patches, x p ∈ R N × ( P 2 ⋅ C ) \mathbf x_p\in \mathbb{R}^{N\times(P^2\cdot C)} xp∈RN×(P2⋅C),共分成N个, N = H W / P 2 N=HW/P^2 N=HW/P2,最终获得N个 P 2 ⋅ C P^2\cdot C P2⋅C特征大小的向量(768),最后经过MLP的线性变化将其映射到一个固定size的隐特征向量维度D,patch embeddings: x p ′ ∈ R N × D \mathbf {x'_{p}}\in \mathbb{R}^{N\times D} xp′∈RN×D,这里等同于对 x p x_p xp进行 P × P P\times P P×P且步长为P的卷积操作(可见代码实现)
class PatchEmbed(nn.Module):
""" Image to Patch Embedding
"""
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768):
super().__init__()
img_size = to_2tuple(img_size)
patch_size = to_2tuple(patch_size)
num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0])
self.img_size = img_size
self.patch_size = patch_size
self.num_patches = num_patches
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
def forward(self, x):
B, C, H, W = x.shape
# FIXME look at relaxing size constraints
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]})."
x = self.proj(x).flatten(2).transpose(1, 2)
return x
类似于BERT中的[class] token,ViT引入了class token机制,其目的:因为transformer输入为一系列的patch embedding,输出也是同样长的序列patch feature,但是最后要总结为一个类别的判断,简单方法可以用avg pool,把所有的patch feature都考虑算出image feature。但是作者没有用这种方式,而是引入一个类似flag的class token,其输出特征加上一个线性分类器就可以实现分类。其中训练的时候,class token的embedding被随机初始化并与pos embedding相加,因此从图可以看到输入transformer的时候【0】处补上一个新embedding,最终输入长度N+1.代码如下:
# 随机初始化
self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
# Classifier head
self.head = nn.Linear(self.num_features, num_classes) if num_classes > 0 else nn.Identity()
# 具体forward过程
B = x.shape[0]
x = self.patch_embed(x)
cls_tokens = self.cls_token.expand(B, -1, -1) # stole cls_tokens impl from Phil Wang, thanks
x = torch.cat((cls_tokens, x), dim=1)
x = x + self.pos_embed
对于position emb采用1-D embedding,作者没有发现用2-D位置emb会有显著性能提升
此公式对应于流程图
finetune的时候的分辨率比pre-training大一些是有效的,输入更大的图像时仍然保持原有的patch size,理论上ViT可以在内存允许范围内对任意长度的序列进行处理,但是预训练的pos emb可能不再有意义,因此对于改变输入图像大小,作者采用2D interpolation插值的方式解决,但是这样会降低性能,可以finetune再优化,作者声称只有resolution adjustment 和patch extraction时图像2d结构的inductive bias运用到了ViT.
Hybrid Architecture. 可以先用CNN下采样提取特征,对于特征图再进行patch emb,此时patch的size可以为1*1.在后面实验中发现,相同计算量下,ViT优于resnet(2到四倍的计算差距就可以达到相同效果),在小的model size下hybrid优于纯transformer,但是当model变大,这个差异就消失了。
TransMed: Transformers Advance Multi-modal Medical Image Classification
这篇文章将transformer用在医学图像分类上,从内容上看应该就是用来ViT的hybird architecture,强就强在他是在医学图像领域第一个吃螃蟹的人。
而且从ablation上来看没有CNN其实对于最终效果影响很大,或许如果我们没有JFT那样大的预训练集和充足的计算单元,我们应该首先考虑上手hybird architecture.
"未来"的经典之作 ViT:transformer is all you need!
TransMed:Transformer推动多模态医学图像分类