目录

  • 概述
  • 细节
    • 知识蒸馏
    • 更好的参数设置
    • 数据增广
    • 其他策略
  • 简单实现

概述

ViT训练不容易:
1、需要大量的GPU资源
2、预训练数据集没有公开,公开最大的只有ImageNet,预训练效果不好
3、超参数设置不好,训练不出效果
而DeiT大幅提升了ViT的效果,并且训练需要的GPU资源大大下降,使用的数据集也仅仅只是ImageNet,不包括任何其他的数据集。
它做主要就是以下几点:
1、更好的超参数设计
2、多种数据增广
3、知识蒸馏或者直接叫teacher/student

细节

知识蒸馏

概述:简单来说就是用一个已经训练好的、更大的一个老师模型,来训练我们当前要训练的模型。
参数:其中的参数 τ \tau τ被称为temperature,其实描述的就是softmax之前要缩放的倍数(因为softmax操作对于数据的方差很敏感,所以一组数据,经过缩放之后在softmax得到的结果可能与之前完全不同。这个缩放指的就是数据除以这个 τ \tau τ,那么 τ \tau τ越大,得到的数据分布就越平缓)。 λ \lambda λ表示两个loss的比重,注意老师模型是不参与训练的,只有学生模型的参数会更新。
下图是soft-distillation,soft-distillation中老师模型softmax之后的结果与学生模型softmax之后的结果进行损失计算。
而hard-distillation中老师模型最终的预测结果与学生模型最终的预测结果进行损失计算,并且将最后的 λ \lambda λ直接设置为0.5

CNN中就是这么做的,但是DeiT中的做法就有点变化了,就是额外加了一个distill token,这个token的作用和class token的作用一样。但是在训练的时候,class token得到student model的loss,然后distill token和teacher model一起得到一个loss,组成了teacher-student的结构。然后在测试的时候,这两个token的输出加权得到最终的输出,再接最后的分类器得到预测的结果。

更好的参数设置

模型参数初始化的时候,用的是Truncated Normal Distribution而不是常见的Normal Distribution,还有一些列的变化,如

数据增广

常见的做法:Random Erase(随机处理一个区域,常是矩形,处理成一个值,或者每个像素都是随机值), Mixup(将一个patch中的两张图做一个混合,那么对应的输出应该是一个soft-label), Cutmix(对于一个patch中的两张图,将其中一张图的部分拿下来,贴到另一张图中), RandomAug(比较高级的一种方式), Droppath, EMA

其他策略

warmup:操作训练初始阶段的 learning rate,可以使模型参数更快地收敛,并达到更高的精度
Label smoothing:就是将gt的one-hot编码中的1,减去参数 α \alpha α,并将 α \alpha α平分给剩下的label,达到一个类似于soft-label的结果。

droppath:将一个batch中的若干个数据直接抹零

简单实现

主干部分:

import paddle
import paddle.nn as nn
from attention import Attentionclass Mlp(nn.Layer):def __init__(self,embed_dim,mlp_ratio=4.0,dropout=0.):super(Mlp, self).__init__()self.conv1=nn.Linear(embed_dim,int(mlp_ratio*embed_dim))self.conv2=nn.Linear(int(mlp_ratio*embed_dim),embed_dim)self.act=nn.GELU()self.dropout=nn.Dropout(dropout)def forward(self,x):x=self.conv1(x)x=self.act(x)x=self.dropout(x)x = self.conv2(x)x = self.act(x)x = self.dropout(x)return xclass PatchEmbedding(nn.Layer):# img_size是图片大小# patch_size是patch的大小# in_channels是图片的通道数# embed是将patch vector映射到的维度# dropout一般给0 所以写不写其实一样def __init__(self,img_size=224,patch_size=16,in_channels=3,embed_dim=768,dropout=0.):super(PatchEmbedding, self).__init__()self.embed_dim=embed_dimn_patches=(img_size//patch_size)*(img_size//patch_size)self.patch_embed=nn.Conv2D(in_channels=in_channels,out_channels=embed_dim,kernel_size=patch_size,stride=patch_size,bias_attr=False)self.dropout=nn.Dropout(dropout)# class token 除了来自图片的visual token之外额外添加一个相同形状的token# 这个token用于分类# shape是1*embed_dim好理解的 因为visual token的形状也是这样的 但是前面的batch-size? 显然不知道 暂时先设置为1self.class_token=paddle.create_parameter(shape=[1,1,embed_dim],dtype='float32',default_initializer=nn.initializer.Constant(0.))# distill_token和class_token几乎一样self.distill_token = paddle.create_parameter(shape=[1, 1, embed_dim],dtype='float32',default_initializer=nn.initializer.TruncatedNormal(std=.02))# position embedding 给每一个token添加位置信息# +1是因为包括class_token 给他也加上主要是为了方便# shape是(n_patches+1)*embed_dim好理解的 前面的batch-size? 显然不知道 暂时先设置为1self.position_embedding = paddle.create_parameter(shape=[1,n_patches+2 , embed_dim],dtype='float32',default_initializer=nn.initializer.TruncatedNormal(std=.02))def forward(self,x):# x:[N,C,H,W]class_token=self.class_token.expand([x.shape[0],1,self.embed_dim]) # 将batch-size这一维加上distill_token=self.distill_token.expand([x.shape[0],1,self.embed_dim]) # 将batch-size这一维加上# expand操作是将tensor按照指定shape进行拓展x=self.patch_embed(x) # x:[N,embed_dim,H',W'] H'*W'就是划分出的patch的个数x=x.flatten(2) # x:[N,embed_dim,H'*W']x=x.transpose([0,2,1]) # x:[N,H'*W',embed_dim] 或者说是 x:[N,num_patches,embed_dim]x=paddle.concat([class_token,distill_token,x],axis=1) # 将cls_token加上去  x:[N,num_patches+2,embed_dim]x=x+self.position_embeddingx=self.dropout(x)return x
class EncoderLayer(nn.Layer):def __init__(self,embed_dim=768,num_heads=4,qkv_bias=True,mlp_ratio=4.0,dropout=0.):super(EncoderLayer, self).__init__()self.attn_norm=nn.LayerNorm(embed_dim)self.attn=Attention(embed_dim,num_heads,qkv_bias)self.mlp_norm=nn.LayerNorm(embed_dim)self.mlp=Mlp(embed_dim,mlp_ratio)def forward(self,x):h=xx=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.Layer):def __init__(self,embed_dim,depth):super(Encoder, self).__init__()layer_list=[]for i in range(depth):encoder_layer=EncoderLayer()layer_list.append(encoder_layer)self.layers=nn.LayerList(layer_list)self.norm=nn.LayerNorm(embed_dim)def forward(self,x):for layer in self.layers:x=layer(x)x=self.norm(x)return x[:,0],x[:,1]class DeiT(nn.Layer):def __init__(self,image_size=224,patch_size=16,in_channels=3,num_classes=1000,embed_dim=768,depth=3,num_heads=8,mlp_ratio=4,qkv_bias=True,dropout=0.,attention_dropout=0.,droppath=0.):super(DeiT, self).__init__()self.patch_embedding = PatchEmbedding(image_size, patch_size, in_channels, embed_dim)self.encoder=Encoder(embed_dim,depth)self.head=nn.Linear(embed_dim,num_classes)# 另一个distill tokenself.head_distill=nn.Linear(embed_dim,num_classes)def forward(self,x):# x:[N,C,H,W]x=self.patch_embedding(x) # x:[N,H'*W',embed_dim] 或者说是 x:[N,num_patches,embed_dim]x,x_distill=self.encoder(x)x=self.head(x)x_distill=self.head_distill(x_distill)if self.training:return x,x_distillelse:return (x+x_distill)/2 # 取个平均def main():model=DeiT()paddle.summary(model,(4,3,224,224))if __name__ == '__main__':main()

attention部分:

import paddle
import paddle.nn as nnclass Attention(nn.Layer):def __init__(self,embed_dim,num_heads,qkv_bias=False,qk_scale=None,dropout=0.,attention_dropout=0.):super(Attention, self).__init__()self.num_heads=num_headsself.embed_dim=embed_dim# 一般不扩大dim 而是对dim的分割self.head_dim=int(embed_dim/num_heads)# 防止前面除不尽 所以在写一个 而不是直接用embed_dim,他一个一个直观理解就是所有head加起来有多少dimself.all_head_dim=self.head_dim*num_heads# Linear层其实只对最后一维做 所以我们想要处理的 要放到最后去 需要多次的transpose# 所以这一层的weights就是我们讲理论时候的 W^q,w^k,W^v们self.qkv=nn.Linear(in_features=embed_dim,out_features=self.all_head_dim*3) # *3是因为qkv三个self.scale=self.head_dim**-0.5 if qk_scale is None else qk_scaleself.softmax=nn.Softmax() # 沿着最后一个维度做softmax -1 也是默认参数self.proj=nn.Linear(self.all_head_dim,embed_dim)self.dropout=nn.Dropout(dropout)self.attention_dropout=nn.Dropout(attention_dropout)def transpose_multi_head(self,x):# x:[B,num_patches,all_head_dim]new_shape=x.shape[:-1]+[self.num_heads,self.head_dim]x=x.reshape(new_shape)# x:[B,num_patches,num_heads,head_dim]x=x.transpose([0,2,1,3])# x:[B,num_heads,num_patches,head_dim]return xdef forward(self,x):# x:[B,num_patches,embed_dim]B,N,_=x.shapeqkv=self.qkv(x).chunk(3,-1)# qkv是[[B,num_patches,all_head_dim],[B,num_patches,all_head_dim],[B,num_patches,all_head_dim]]q,k,v=map(self.transpose_multi_head,qkv)# q,k,v:[B,num_heads,num_patches,head_dim]atten=paddle.matmul(q,k,transpose_y=True) #做矩阵乘法 相当于是q和k的转置相乘 最后一个维度的head_dim消掉了 api设计就是这样的atten=self.scale*attenatten=self.softmax(atten)atten=self.attention_dropout(atten)# atten:[B,num_heads,num_patches,num_patches] 就是attention-score的metric了out=paddle.matmul(atten,v) # 乘完之后变回来了 [B,num_heads,num_patches,head_dim]out=out.transpose([0,2,1,3])# 变为 [B,num_patches,num_heads,head_dim]了out=out.reshape([B,N,-1]) # [B,num_patches,all_head_dim]了out=self.proj(out)out=self.dropout(out)return out
def main():# model=Attention(embed_dim=96,num_heads=4,qkv_bias=False,qk_scale=None)# # (8,16,96) batch-size是8 num_patches是16 embed_dim是96# paddle.summary(model,(8,16,96))print(paddle.nn.functional.softmax(paddle.to_tensor([1.,20.,400.])))print(paddle.nn.functional.softmax(paddle.to_tensor([0.001,0.02,0.4])))
if __name__ == '__main__':main()

【transformer】DeiT相关推荐

  1. 【Transformer】医学分割领域的应用与扩展(论文阅读)(二) || DETR

    声明:仅学习使用~ 目录 1. Transformer学习 2. DETR 1. Transformer学习 前篇指路:[Transformer]医学分隔领域的应用与扩展(论文阅读)(一) 继续- 关 ...

  2. 【Transformer】一文搞懂Transformer | CV领域中Transformer应用

    目录 阅读本文的基础: 一.发展历史: 二.从上向下的理解Transformer 1.Transformer整体结构简单介绍 2.Transformer中的Self-attention (1)引入 ( ...

  3. 【Transformer】CLS(classification)有什么用?

    CLS]就是classification的意思,可以理解为用于下游的分类任务. 一句话理解:[CSL]就是一个向量,只是不是某一个字的向量,是一个够代表整个文本的的语义特征向量,取出来就可以直接用于分 ...

  4. 【Transformer】《PaLM-E: An Embodied Multimodal Language Model》译读笔记

    <PaLM-E: An Embodied Multimodal Language Model> 摘要 大语言模型已被证明可以执行复杂的任务.不过,要在现实世界中实现通用推理,例如解决机器人 ...

  5. 【transformer】【pytorch】DeiT的数据增强

    1 main中的相关参数 #函数:def get_args_parser(): parser.add_argument('--input-size', default=224, type=int, h ...

  6. 【Transformer】Transformer 中的位置编码 -- ICLR 2021

    引言 Transformer是近年来非常流行的处理序列到序列问题的架构,其self-attention机制允许了长距离的词直接联系,可以使模型更容易学习序列的长距离依赖.由于其优良的可并行性以及可观的 ...

  7. 单目标追踪——【Transformer】Transformer Meets Tracker:Exploiting Temporal Context for Robust Visual Tracking

    目录 文章侧重 网络结构 具体的Encoder和Decoder的结构 模型的推理过程 Transformer这个香饽饽怎么能不用来迁移到目标追踪里呢. 我计划对CVPR2021的3篇将Transfor ...

  8. 【Transformer】HRFormer:High-Resolution Transformer for Dense Prediction

    文章目录 一.背景 二.动机 三.方法 四.效果 论文链接:https://arxiv.org/abs/2110.09408 代码链接:https://github.com/HRNet/HRForme ...

  9. 【Transformer】TNT: Transformer iN Transformer

    文章目录 一.背景 二.动机 三.方法 3.1 Transformer in Transformer 3.2 Network Architecture 四.效果 五.代码 论文链接:https://a ...

最新文章

  1. 驾驶员行为监控系统:需要它来管理车队
  2. 案例分析 陆金所_平安陆金所介绍-关于陆金所-平安陆金所
  3. linux 中php以及nginx的重启命令
  4. Go + Excel 学习 Excelize rows.go
  5. 空间谱专题07:干涉仪仿真思路
  6. MySQL预读失效_华为云MySQL新增“逻辑预读”特性,轻松解决线性预读失效问题...
  7. python之实现从ftp下载文件到本地
  8. Kubernetes NetworkPolicy 工作原理浅析
  9. 練習重繪TreeView控件
  10. 计算机cad运行缓慢怎样处理,旧电脑如何提高CAD运行速度
  11. D3基础 | 条形图
  12. 西湖论剑2021杂项(misc)--YUSA的小秘密
  13. c语言编程题数的平方和,c语言问题:任意输入两个数,求两数的平方之和? , 求一个c语言问题,任意输入两个数,求出这两个数之间的所有水...
  14. react项目中引入的组件在src外从而报错
  15. 治愈系课程教材 第三课
  16. 类似安卓的点9图片,气泡图片调成自己需要的
  17. ESXi/ESX 链路聚合
  18. 使用swiftype实现站内搜索
  19. Amber中的NMR restraint中的一些参数的设置的意义
  20. 项目部署的常用进程管理命令

热门文章

  1. STM32F103系列 TIM1~TIM8库函数初始化代码批量发布干货
  2. CTFd平台二次开(jiao)发(shi)
  3. python dota2数据 3 下载胜负数据
  4. vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析
  5. 你为什么当不了高管?高级经理人和普通管理者有什么区别了?
  6. MySQL基础篇——理解RDBMS
  7. python中xlrd官方_Python中如何用xlrd读取
  8. 浏览器如何在f12中查看接口地址_使用IE浏览器F12开发人员工具提取下载视频地址...
  9. 外贸:这个圣诞节你想好怎么选品了吗
  10. Intel与AMD CPU型号对照