Vision Transformer 超详细解读 (原理分析+代码解读) (十五) - 知乎本系列已授权极市平台,未经允许不得二次转载,如有需要请私信作者。考虑到每篇文章字数的限制, 每一篇文章将按照目录的编排包含两到三个小节,而且这个系列会随着Vision Transformer的发展而长期更新。专栏目录…https://zhuanlan.zhihu.com/p/386955720Tokens-to-Token ViT:真正意义上击败了CNN - 知乎原创文章,未经允许,禁止转载导读前面提到过ViT,但是ViT在数据量不够巨大的情况下是打不过ResNet的。于是ViT的升级版T2T-ViT横空出世了,速度更快性能更强。T2T-ViT相比于ViT,参数量和MACs(Multi-Adds)减少了20…
https://zhuanlan.zhihu.com/p/354522966
文章的行文风格倒是完全不同于vit,==!t2t-vit在参数量小的情况下,只在imagenet上训练,超过了同级别的resnet,vit只在imagenet上训练是达不到这个结果的。
1.Abstract
点出来了vit不行的两个原因以及图t2t-vit是如何改进的。q1:输入图片的简单标记化(tokenization),无法很好的对局部特征进行建模(edges and lines among neighboring pixels).q2:vit的self-attention的backbone设计的不好,信息冗余,计算量高,丰富度不够。怎么解决呢?1.t2t module,2.an efficient backbone with deep-narrow structure for vision transformer.
2.Introduction
上面这个图很重要,可视化vit-l/16和resnet50的部分层的输出信息。观察ResNet,从底层(conv1)到中间层(conv25)逐步捕获所需的局部结构(边、线、纹理等)。ViT的特征却大不相同:结构信息建模很差,而全局关系(例如,整个狗)被所有注意块捕获。结果表明,ViT在直接将图像分割为固定长度的token时忽略了局部结构。此外,我们发现ViT中的许多通道具有零值(图2中以红色突出显示),这意味着ViT的主干不如resnet有效,并且在训练样本不足时提供有限的特征丰富性。(其实上面这个梯度可视化图也间隔说明了在分割问题上,transformer表现的会更好,全局信息的获取对于整个对象的描述似乎更加完整和精细一些)。
vit对于image的切分是如何的呢?其实就是一个kernel_size=p,stride=p的卷积操作,切出来的所有window块是不重叠的,对一张图的局部信息建模能力有限。
3.Token-to-Token:Progressive tokenization
这是为了解决vit切图无法建模局部信息而提出来的解决方案。他分两个阶段re-structurization和soft-split.
re-structurization: Tokens-to-Token 模块的输入是image patches 序列,这个序列通过一个 Transformer Block,即T‘=MLP(MSA(T)),得到了一个(l,c)维的T‘,对T‘进行reshape,还原成原来的image结构,T‘是带有self-attention信息的embedding,此时还原已经相当于带有全局信息的图像结构,从全流程图中看一开始224X224的图的patches是通过unfold操作产生的(没有用linear做映射的),l=hxw。
soft split:在re-constructurization中将transformer后的image patches重新reshape成了hxwxc,这一步是为了加强局部信息,用unfold采样了一部分重叠的patches。
class T2TModule(BaseModule):def __init__(self,img_size=224,in_channels=3,embed_dims=384,token_dims=64,use_performer=False,init_cfg=None,):super(T2TModule, self).__init__(init_cfg)self.embed_dims = embed_dimsself.soft_split0 = nn.Unfold(kernel_size=(7, 7), stride=(4, 4), padding=(2, 2))self.soft_split1 = nn.Unfold(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))self.soft_split2 = nn.Unfold(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))if not use_performer:self.attention1 = T2TTransformerLayer(input_dims=in_channels * 7 * 7,embed_dims=token_dims,num_heads=1,feedforward_channels=token_dims)self.attention2 = T2TTransformerLayer(input_dims=token_dims * 3 * 3,embed_dims=token_dims,num_heads=1,feedforward_channels=token_dims)self.project = nn.Linear(token_dims * 3 * 3, embed_dims)else:raise NotImplementedError("Performer hasn't been implemented.")# there are 3 soft split, stride are 4,2,2 separatelyself.num_patches = (img_size // (4 * 2 * 2))**2def forward(self, x):# step0: soft splitx = self.soft_split0(x).transpose(1, 2)for step in [1, 2]:# re-structurization/reconstructionattn = getattr(self, f'attention{step}')x = attn(x).transpose(1, 2)B, C, new_HW = x.shapex = x.reshape(B, C, int(np.sqrt(new_HW)), int(np.sqrt(new_HW)))# soft splitsoft_split = getattr(self, f'soft_split{step}')x = soft_split(x).transpose(1, 2)# final tokensx = self.project(x)return x
4.T2T-Vit architecture
全流程图也很清楚,t2t模块是放在最前面的,后面接了一个t2t-vit backbone。
本文整体来看,还是对于vit存在的一些问题的修正,vit是很暴力的将patches的信息做了self-attention,发现追求全局信息对局部信息的损害很高,而swin-transformer是对window内的信息做self-attention,self-attention是一定能带来增益的,为了让window内信息有联系,做了shifted windows。t2t-vit在一开始做token时,不那么暴力,用transformer做了一次self-attention(不线性升维),reshape之后在unfold,其实还是在embedding中输入local information,最终的效果会更好。在图像中是全局信息占主导还是局部信息占主导,哪一部分的信息增益更多,更有效。图像是不是需要准确的时序信息相较于文本来说,没那么重要,是值得考虑的。
2021.12.11,paperwithpaper上imagenet上,t2t-vit-14,top1:81.5%,参数量:21.5M,t2t-vit-19,top1:81.9%,t2t-vit-24,top1:82.3%,参数量:64.4M,t2t-vit-19,top1:82.4%,参数量:39.2M,t2t-vit-24,top1:82.6%,参数量:64.4M.