4.一脚踹进ViT——ViT再审视与DeiT的实现

1.ViT的几个问题

1.1 为什么不在MLP中做LayerNorm?

其实我们在MLP之后会做Norm,我们MLP层后会有Residual加法,若我们在MLP中加入Norm,走完MLP进行残差链接后还需要再去做一次,于是就在最后做一次就行了。

1.2 cls token 与 pos_embedding

cls token用的expand的方式,需要对每一个样本都去增加cls token去做分类

pos_embedding使用的是boardcast(广播方式),所有图片都是使用统一的方式去切patch,所以每一个patch的顺序是一样的,我们可以使用广播机制,对于batch中的每一个样本去加同一个position embedding,如果我们的position是1,2,3顺序的话,我们每一个样本都是按照这样加进去的,所以可以使用同一个pos_embedding。

ViT训练模型很难,要求算力较高,那如何更有效的训练ViT模型

2.DeiT(Data-efficient image Transformers)

DeiT被提出,在ViT-B上改进后,最终acc提升了约6个点,解决的最大的问题:

  1. ViT模型性能被大幅度提高了

  2. ViT模型能够用8卡甚至4卡训练

DeiT与ViT结构差不多,它进行了一些改进,能够取得更好的效果的方法:

  1. Better Hyperparameter(更好超参数设置)——保证模型更好收敛

  2. Data Augmentation(多个数据增广)——可以使用小数据训练

  3. Distillation(知识蒸馏)/Teacher-Student——进一步提升性能

2.1 知识蒸馏

即用老师模型去训练学生模型,老师模型(B)是提前预训练好的,学生模型(A)就是我们当前要训练的,B模型是已经在某些数据集上达到较好的效果,是不拿来训练的,只是帮助模型A去提高性能。

我们Teacher Model已经可以提出一个带有分类信息的Feature Vector,已经可以对其做softmax,在softmax之前对其除了一个数(后面会说),在softmax之后,我们得到了对类别的概率表示,我们叫做Soft Labels,先存放起来。

在对Student Model训练时,它也会走一个前向过程,经过Softmax,之后也会得到一个预测结果,它会与Teacher Model分类的结果计算Loss(KLDivLoss),也叫做Teacher Loss;同样,他会与Ground Truth进行比对,计算CrossEntroyLoss,最终两个Loss加权得到最终Loss

Teacher Loss前向操作在Softmax之前会计算除以τ,我们的数据在Softmax之前若除以一个数字,如果除的越大,最终得到的Feature Var会越小,经过softmax之后就会越平滑。所以,τ来控制Softmax之后概率分布更平滑还是更抖动,这种方式因为有Soft Label和Soft Prediction ,也叫做Soft Distillation(软蒸馏),使用老师的预测分布和学生的预测分布来算Loss。

也可以直接取最终的标签,来与训练的模型输出Hard Prediction来算CELoss,这叫做Hard Distillation,貌似说这种方式效果更好。

那在ViT中是如何使用这种方式呢?

ViT中用Class Token来提取分类信息,我们可以加入一个新的Distill Token,也去学与其他Patch之间的Attention,学完后它产生的Loss连接的是Teacher Model中,Teacher Model的Loss与class Token与分类器的Loss(Student部分)进行加权,去算总Loss,去训练。

如果在预测时,Teacher 和 Student 部分都会产生一个feature,它们加权,就会得到最终的结果表达,经过分类器后,得到最终分类结果。

Teacher Model中用的已经训练好的CNN中的RegNet网络,也可以用其他网络。

2.2 更好的参数设置

通常网络初始化用Kaiming Norm,或者给一些常数,DeiT中用了一个新的方式,即

Truncated Norm Distribution(截断高斯标准分布):

将两侧以外的部分截断

论文所用的一些超参数设置如下:

学习率是随着Batch Size变大逐渐变大的

Learning rate decay 是加了一个warm up,先增加后面使用cos的函数将lr减小为0

2.3 数据增广

介绍的数据增广主要是RandomErase,Mixup,Cutmix,RandomAug,Droppath,EMA

RandomErase:随机将图像中的一块抹掉,随机填充一些填充的值

Mixup:将一个batch中的两个图的像素值取平均相加,label也变为了soft label,混合了两类的label

CutMix:将一个batch中的一个图切出来贴到另一个图像中,按比例去融合,比如图中0.3的比例给猫,0.7的比例给狗

RandomAug:是由google做的AutoAug的基础上设计的

我们有很多种方式来改变图像,例如:错切、平移、旋转、亮度调节、对比度调节等等,AutoAug为搜索出了25种变化,每次随机选择一条变化(每一条中有两个变化方式),这样用效果很好。

而RandAug认为AutoAug效果一般,它设计了13个policy,一次性随机选择6个方式依次对图像进行处理。

EMA(Exponential Moving Average)指数滑动平均:每次要把历史的权重取个平均

mt就是此时的权重,α是动量,一般设置的很接近1(0.9996),θ就是我们当前的模型,模型基本保持稳定的状态,来提升性能。

Label smoothing:看图

正常one-hot vector,属于第0类,即第0类是1,其他是0,smoothing是设置一个α,将真实Label减去α=0.1,将减去的值平均分给其他的类,0.1/5,其余每个分0.02

Droppath

假设Batch是128,我们会有一个概率会把128个样本中n个样本置0,类似于Dropout

3.ViT + Distiall实现

看程序先找入口

设计DeiT函数,在模型前向过程中,加入一个x_distill部分与分类器相连

在patch_embedding中加入一个类似cls_token的distill_token,维度和cls_token一致,所以最后位置编码部分,shape 第二维度应该变为n_patches+2

在Encoder部分,最终得到的x的shape为

torch.Size([4, 198, 768])

返回cls tokens 和 distill tokens,即取第一个和第二个

return x[:,0],x[:,1]

其shape都为torch.Size([4, 768])

import torch
import torch.nn as nnfrom torchinfo import summaryclass Mlp(nn.Module):def __init__(self,embed_dim, mlp_ratio=4.0, dropout=0.):super().__init__()self.fc1  = nn.Linear(embed_dim, int(embed_dim* mlp_ratio))self.fc2  = nn.Linear(int(embed_dim* mlp_ratio),embed_dim)self.act  = nn.GELU()self.dropout = nn.Dropout(p=dropout)def forward(self,x):x = self.fc1(x)x = self.act(x)x = self.dropout(x)x = self.fc2(x)x = self.dropout(x)return xclass Attention(nn.Module):def __init__(self,embed_dim, num_heads, qkv_bias=False, dropout=0.,attention_dropout=0.):super().__init__()self.embed_dim =embed_dimself.num_heads =num_headsself.head_dim = int(embed_dim/num_heads)self.all_head_dim = self.head_dim*num_heads# 把所有q 写在一起, 所有k、V写在一起,然后拼接起来,前1/3代表了所有head的Q,每一个head的尺寸已经定义好,要用的时候切就行了self.qkv = nn.Linear(embed_dim,self.all_head_dim*3,bias=False if qkv_bias is False else None)self.scale = self.head_dim ** -0.5self.softmax = nn.Softmax(-1)self.proj = nn.Linear(self.all_head_dim,embed_dim)def transpose_multi_head(self,x):# x: [B, N, all_head_dim]new_shape = x.shape[:-1] + (self.num_heads, self.head_dim)x = x.reshape(new_shape)# x: [B, N, num_heads, head_dim]x = x.permute(0,2,1,3)# x: [B, num_heads, num_patches, head_dim]return xdef forward(self,x):B,N ,_ = x.shapeqkv = self.qkv(x).chunk(3,-1)# [B, N, all_head_dim]* 3 , map将输入的list中的三部分分别传入function,然后将输出存到q k v中q, k, v = map(self.transpose_multi_head,qkv)# q,k,v: [B, num_heads, num_patches, head_dim]attn = torch.matmul(q,k.transpose(-1,-2))   #q * k'attn = self.scale * attnattn = self.softmax(attn)# dropout# attn: [B, num_heads, num_patches, num_patches]out = torch.matmul(attn, v)  # 这里softmax(scale*(q*k')) * vout = out.permute(0,2,1,3)# out: [B,  num_patches,num_heads, head_dim]out = out.reshape([B, N, -1])out = self.proj(out)#dropoutreturn outclass EncoderLayer(nn.Module):def __init__(self,embed_dim=768, num_heads=4, qkv_bias=True, mlp_ratio=4.0, dropout=0., attention_drop=0.):super().__init__()self.attn_norm = nn.LayerNorm(embed_dim)self.attn = Attention(embed_dim,num_heads)self.mlp_norm = nn.LayerNorm(embed_dim)self.mlp = Mlp(embed_dim,mlp_ratio)def forward(self,x):# PreNormh = x   #residual# print("imhere---------------")# print(x.shape)x = self.attn_norm(x)x = self.attn(x)x = x+hh = xx = self.mlp_norm(x)x = self.mlp(x)x = x+hreturn xclass Encoder(nn.Module):def __init__(self,embed_dim,depth):super().__init__()layer_list = []for i in range(depth):encoder_layer = EncoderLayer()layer_list.append(encoder_layer)self.layers = nn.ModuleList(layer_list)self.norm = nn.LayerNorm(embed_dim)def forward(self,x):for layer in self.layers:x = layer(x)x = self.norm(x)# TODO:return cls and distill tokensreturn x[:,0],x[:,1]class PatchEmbedding(nn.Module):def __init__(self,image_size=224, patch_size=16, in_channels=3, embed_dim=768 ,dropout=0.):super().__init__()n_patches = (image_size//patch_size) * (image_size//patch_size)self.patch_embedding = nn.Conv2d(in_channels = in_channels,out_channels= embed_dim,kernel_size=patch_size,stride=patch_size)self.dropout = nn.Dropout(dropout)self.embed_dim =embed_dim# TODO: add cls tokenself.class_token = nn.Parameter(nn.init.constant_(torch.zeros(1,1,embed_dim,dtype=torch.float32),1.0))# TODO: add distill embeddingself.distill_token = nn.Parameter(nn.init.trunc_normal_(torch.zeros(1,1,embed_dim,dtype=torch.float32),std=.02))# TODO: add position embeddingself.position_embedding = nn.Parameter(nn.init.trunc_normal_(torch.randn(1,n_patches+2,embed_dim,dtype=torch.float32),std=.02))def forward(self,x):# 定义并不知道batch_size是多少,我们cls_token是对样本进行分类class_tokens =self.class_token.expand((x.shape[0], -1, -1)) #for batchdistill_tokens =self.distill_token.expand((x.shape[0], -1, -1)) #for batch# x: [N, C, H, W]x = self.patch_embedding(x) # x: [n, embed_dim, h', w']x = x.flatten(2)   #[n, embed_dim, h'*w']x = x.permute(0, 2, 1)  #[n,  h'*w', embed_dim]x = torch.concat([class_tokens, distill_tokens, x], axis=1)# print('embeding中:',x.shape)x = x + self.position_embedding# x = self.dropout(x)return xclass DeiT(nn.Module):def __init__(self,image_size=224,patch_size=16,in_channels=3,num_classes=1000,embed_dim=768,depth=3,num_heads=8,mlp_ratip=4,qkv_bias=True,dropout=0.,attention_drop=0.,droppath=0.):super().__init__()self.patch_embedding = PatchEmbedding(224, 16, 3, 768)self.encoder = Encoder(embed_dim,depth)self.head = nn.Linear(embed_dim,num_classes)self.head_distill = nn.Linear(embed_dim,num_classes)def forward(self,x):# x:[N, C, H, W]x = self.patch_embedding(x)   # [N, embed_dim, h', w']x,x_distill = self.encoder(x)  # [N, num_patches,embed_dim]# print(x.shape)x = self.head(x)x_distill = self.head_distill(x_distill)if self.training:return x, x_distillelse:return (x + x_distill)/2def main():model = DeiT()print(model)summary(model,input_size=(4,3, 224, 224))if __name__ == '__main__':main()

模型结构输出如下:

DeiT((patch_embedding): PatchEmbedding((patch_embedding): Conv2d(3, 768, kernel_size=(16, 16), stride=(16, 16))(dropout): Dropout(p=0.0, inplace=False))(encoder): Encoder((layers): ModuleList((0): EncoderLayer((attn_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(attn): Attention((qkv): Linear(in_features=768, out_features=2304, bias=False)(softmax): Softmax(dim=-1)(proj): Linear(in_features=768, out_features=768, bias=True))(mlp_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(mlp): Mlp((fc1): Linear(in_features=768, out_features=3072, bias=True)(fc2): Linear(in_features=3072, out_features=768, bias=True)(act): GELU(approximate=none)(dropout): Dropout(p=0.0, inplace=False)))(1): EncoderLayer((attn_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(attn): Attention((qkv): Linear(in_features=768, out_features=2304, bias=False)(softmax): Softmax(dim=-1)(proj): Linear(in_features=768, out_features=768, bias=True))(mlp_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(mlp): Mlp((fc1): Linear(in_features=768, out_features=3072, bias=True)(fc2): Linear(in_features=3072, out_features=768, bias=True)(act): GELU(approximate=none)(dropout): Dropout(p=0.0, inplace=False)))(2): EncoderLayer((attn_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(attn): Attention((qkv): Linear(in_features=768, out_features=2304, bias=False)(softmax): Softmax(dim=-1)(proj): Linear(in_features=768, out_features=768, bias=True))(mlp_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(mlp): Mlp((fc1): Linear(in_features=768, out_features=3072, bias=True)(fc2): Linear(in_features=3072, out_features=768, bias=True)(act): GELU(approximate=none)(dropout): Dropout(p=0.0, inplace=False))))(norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(head): Linear(in_features=768, out_features=1000, bias=True)(head_distill): Linear(in_features=768, out_features=1000, bias=True)
)

模型其中每层输出以及参数量如下:

==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
DeiT                                     [4, 1000]                 --
├─PatchEmbedding: 1-1                    [4, 198, 768]             153,600
│    └─Conv2d: 2-1                       [4, 768, 14, 14]          590,592
├─Encoder: 1-2                           [4, 768]                  --
│    └─ModuleList: 2-2                   --                        --
│    │    └─EncoderLayer: 3-1            [4, 198, 768]             7,085,568
│    │    └─EncoderLayer: 3-2            [4, 198, 768]             7,085,568
│    │    └─EncoderLayer: 3-3            [4, 198, 768]             7,085,568
│    └─LayerNorm: 2-3                    [4, 198, 768]             1,536
├─Linear: 1-3                            [4, 1000]                 769,000
├─Linear: 1-4                            [4, 1000]                 769,000
==========================================================================================
Total params: 23,540,432
Trainable params: 23,540,432
Non-trainable params: 0
Total mult-adds (M): 554.21
==========================================================================================
Input size (MB): 2.41
Forward/backward pass size (MB): 170.33
Params size (MB): 93.55
Estimated Total Size (MB): 266.28
=========================================================================================

4.一脚踹进ViT——ViT再审视与DeiT的实现相关推荐

  1. 2.一脚踹进ViT——Attention机制原理及实现

    2.一脚踹进ViT--Attention机制原理及实现 同样是百度飞浆课程的笔记,视频中的图就拿来用了 1. 注意力(Attenetion)机制原理 先来看传统RNN结构如何最终演变到我们目前的注意力 ...

  2. 一体计算机屏幕只脚,这台嵌在显示器支架里的电脑 手撕笔记本 脚踹一体机!...

    19年9月,戴尔为大家带来了一款形似「显示器支架」的条形模块化台式机.它不额外占据任何桌面空间,只要将其"隐藏"在适配的显示器支架内,再扣上显示器.接通电源,它就能像一体机电脑一样 ...

  3. 【TransformerCNNTiDE】从CNN到ViT,再从ViT到TiDE,回顾近十年顶刊和会议发表的关于Attention自注意力、Conv卷积机制以及最新诞生的TiDE模型的发展历程

    目录 一.CV中的Transformer介绍 二.Attention机制增强CNN 前言: 1. Attention Augmented Convolutional Networks(ICCV 201 ...

  4. ViT/vit/VIT详解

    参考: Vision Transformer详解: https://blog.csdn.net/qq_37541097/article/details/118242600 目录: x.1 (论文中)模 ...

  5. 3名中学生脚踹流浪老人还发视频炫耀:没教养的人,有多可怕?

    Python实战社群 Java实战社群 长按识别下方二维码,按需求添加 扫码关注添加客服 进Python社群▲ 扫码关注添加客服 进Java社群▲ 作者丨进化君 来源丨每日进化论(jinhualun1 ...

  6. 一失脚为千古恨,再回头一百年人

    zz 人生有很多路要走,可是命运的往往只有一两步.走错了这一步.也许人生的命运就发生了改变,等到你发现却为时已晚,因为时间是不能倒流的.人生有多少错误可犯?又有多少时光可以蹉跎?有多少错误是可以挽救的 ...

  7. 用脚踹?地震火灾中,如何快速打开人脸识别闸机门?

    作者 | 谭婧 责编 | 胡巍巍 2019年6月17日晚,四川宜宾长宁县发生连续地震,多地震感明显,<观察者网>报道"大学生地震时排队刷脸出宿舍一事." 事后,又据&l ...

  8. 光进铜退再掀热潮 布线须冷思考

    2015年随着总理对"提速降费"的三令五申,"光进铜退"的舆论也被再次推到风口浪尖.国务院直接出文要求,在网费明显偏高的城市开展宽带免费提速和降价活动,将具备网 ...

  9. matlab二元方程组,用matlab解一个二元方程组,会的进,得到解再回答

    共回答了20个问题采纳率:75% clear,clc format long f=@(x)[tan(4*3.14*20000*0.03)+2*3.14*20000/x(1)*tan(x(1)*x(2) ...

最新文章

  1. 学运维能不能通过大厂面试,进来试试——面经总结(二)
  2. php 长短字符串转换,将php的数组按照字符串长短进行排序
  3. 电子技术基础三_电子技术基础
  4. 通过LDAP验证Active Directory服务
  5. hadoop历史版本,包括大名鼎鼎的hadoop 0.20.2
  6. linux基础知识总结(四)
  7. 万由nas系统安装MySQL_ESXi安装万由OS(U-NAS 3.0.9)
  8. css下拉菜单代码(用纯css实现下拉菜单)
  9. yocto的hello world
  10. 华为网络设备-NAT实验
  11. 常见鸟的种类及特点_鸟的种类(常见鸟的名字大全)
  12. 生成器(generator)理解
  13. 【优秀课设】基于OpenCV+MediaPipe的手势识别(数字、石头剪刀布等手势识别)
  14. “神一般存在”的印度理工学院到底有多牛?
  15. python怎么画卡通人物_Python绘制可爱的卡通人物 | 【turtle使用】
  16. 对自己未来生活的一些规划
  17. 使用FleaPHP框架构建简单留言本应用
  18. python ccf题解 201903-1 小中大
  19. TypeScript与Haxe:两种截然不同的JS转译工具横向对比
  20. 基于浏览器的交互式Go学习平台 | Gopher Daily (2020.11.14) ʕ◔ϖ◔ʔ

热门文章

  1. rsync下行同步和inotify实时同步部署
  2. 研究生语音识别课程作业记录(三) 非特定人孤立词识别
  3. PS网页设计教程XV——如何在Photoshop中创建一个充满活力的作品集的网页设计
  4. 嵌入式软件工程师—成长笔记#05
  5. 电脑桌面上的控制面板和计算机怎么没有了,控制面板在哪?没有控制面板怎么办...
  6. Oracle Essbase入门系列(一)
  7. 3.9、互斥锁(互斥量)
  8. 新技術讓大數據“看得見”
  9. Redis基本事务的操作
  10. 基于ITIL的医院信息化服务管理实践(客户说)