资讯详情

ViT (Visual Transformer)

Acknowledge

论文名称: An Image Is Worth 16x16 Words: Transformers For Image Recognition At Scale 原论文对应源码:https://github.com/google-research/vision_transformer PyTorch实现代码: pytorch_classification/vision_transformer Tensorflow2实现代码:tensorflow_classification/vision_transformer 在bilibili上的视频讲解:https://www.bilibili.com/video/BV1Jh411Y7WQ 博客讲解:https://blog.csdn.net/qq_37541097/article/details/118242600

本文以霹雳为基础Wz讲解的Transformer,在此基础上,推荐阅读原版。

引言

Transformer最初的提议是针对的NLP领域,在NLP该领域取得了巨大的成功。这篇论文也受到它的启发,试图Transformer应用到CV领域。通过文章的实验,给出的最佳模型是ImageNet1K上能够达到88.55%的准确率(先在Google自家的JFT对数据集进行预训练),说明Transformer在CV领域确实有效,效果惊人。

在这里插入图片描述

2. 模型详解

本文主要以作者为主ResNet、ViT(纯Transformer模型)以及Hybrid(卷积和Transformer对比三个模型的混合模型),本博文将简要讨论Hybrid模型。

2.1 Vision Transformer模型概况

下图是原论文给出的关于Vision Transformer(ViT)模型框架。模型由三个模块组成:

  • Linear Projection of Flattened Patches(Embedding层,又称嵌入层)
  • Transformer Encoder(编码层,图右侧有更详细的结构)
  • MLP Head(多层感知机头,用于最终分类的层结构)

以上ViT模型架构。网络工作流程如下:

  1. 首先输入一张图片,分成一个接一个patch。
  2. 然后对每一个patch输入到Linear Projection of Flattened Patches层(也就是Embedding层)。通过这个Embedding层后,我们可以得到一个接一个的向量。这里的向量通常被称为token。每个patch通过Embedding层都会得到一个token。
  3. 在这一系列token前面加一个新的token(专门用于分类class token)。
  4. 让每一个token考虑到位置信息,每一个都需要考虑token添加位置信息,即Position Embedding。对应图中0、1、2、3、4、5、6、7、8、9。
  5. 将Embedding完的token输入到Transformer Encoder中。Transformer Encoder对应右边的图片。Visual Transformer中,将Transformer Encoder重复堆叠 L L L 次。
  6. 因为我们的网络是用来分类的,所以我们只需要嵌入的网络class token取出来即可。
  7. 将class token送入MLP Head得到最终的分类结果。

2.2 Embedding层结构详解

对于标准的Transformer模块,需要输入token(向量)序列,即二维矩阵[num_token, token_dim],如下图,token0-9对应于向量,以ViT-B以/16为例,每一个token768的向量长度。

ViT-B/16中:

  • ViT: Visual Transformer
  • B: Base
  • 16: Patch Size, 16×16

2.2.1 Patch Embedding层

对图像数据而言,其数据格式为 [ H , W , C ] [H, W, C] [H,W,C] 显然不是三维矩阵Transformer想要的。所以需要先通过一个。Embedding层来改变数据。

如下图所示,首先将图片按给定大小分成一堆Patches。以ViT-B以/16为例,输入图片( 224 × 224 224\times 224 224×224 )按照 16 × 16 16\times 16 16×16 大小的Patch进行划分,划分后会得到 ( 224 / 16 ) 2 = 14 × 14 = 196 (224 / 16)^2=14\times 14 = 196 (224/16)2=14×14=196 个Patches。接着通过线性映射(Linear Projection)将每个Patch映射到一维向量中,以ViT-B/16为例,每个patch的shape为 [ 16 , 16 , 3 ] [16, 16, 3] [16,16,3] ,通过映射得到一个长度为768的向量(后面都直接称为token)。整体过程为:

[ 224 , 224 , 3 ] I m a g e s → 196 p a t c h   n u m × [ 16 , 16 , 3 ] p a t c h p a t c h e s → 196 p a t c h   n u m × [ 768 ] t o k e n t o k e n s \underset{\mathrm{Images}}{[224, 224, 3]} → \underset{\mathrm{patches}}{\underset{\mathrm{patch \ num}}{196} \times \underset{\mathrm{patch}}{[16, 16, 3]}} → \underset{\mathrm{tokens}}{ \underset{\mathrm{patch \ num}}{196} \times \underset{\mathrm{token}}{[768]} } Images[224,224,3]​→patchespatch num196​×patch[16,16,3]​​→tokenspatch num196​×token[768]​​

Note: , , ,

  1. : 根据Patch Size得到的单独的patch,上面示例中的 [ P a t c h   S i z e , P a t c h   S i z e , C h a n n e l ] = [ 16 , 16 , 3 ] \mathrm{[Patch \ Size, Patch \ Size, Channel]=[16, 16, 3]} [Patch Size,Patch Size,Channel]=[16,16,3] 就是patch

  2. : [patch总个数,patch],就是上面的 [ 196 , 16 , 16 , 3 ] = [ ( 224 / 16 ) 2 , 16 , 16 , 3 ] [196, 16, 16, 3] = \mathrm{[(224 / 16)^2, 16, 16, 3]} [196,16,16,3]=[(224/16)2,16,16,3]

  3. : 一维向量,就是单独一个patch映射得到的一维向量,上面示例中的 [ 768 ] [768] [768] 就是token

  4. : [patch个数, token],就是上面的 [ 196 , 768 ] [196, 768] [196,768]


的区别:其实展平都是一样的,只不过是用不同的shape表示不同的含义罢了 顾名思义,patch意思为小块,我们就将其理解为是特征图,所以是 [ 16 , 16 , 3 ] [16, 16, 3] [16,16,3],而token这里我们理解为是一维序列就行,所以它的形状理所当然是 [ 16 × 16 × 3 ] = [ 768 ] [16 \times 16 \times 3] = [768] [16×16×3]=[768]

在代码实现中,这个过程是直接通过一个卷积层来实现。 以ViT-B/16为例,直接使用nn.conv2d(in_channels=224, out_channels=768, kernel=(16, 16), stride=16)来实现,即:

[ 224 , 224 , 3 ] I m a g e s → [ 14 , 14 p a t c h   n u m , 768 t o k e n ] p a t c h e s / t o k e n s \underset{\mathrm{Images}}{[224, 224, 3]} → \underset{\mathrm{patches/tokens}}{[\underset{\mathrm{patch \ num}}{14, 14}, \underset{\mathrm{token}}{768}]} Images[224,224,3]​→patches/tokens[patch num14,14​,token768​]​

输入输出就不用说了,这里比较巧妙的是kernel=(16, 16),其实明白卷积是怎么运算的,也很好理解。

然后把 H , W H, W H,W 两个维度展平即可。 [ 14 , 14 , 768 ] → [ 196 , 768 ] t o k e n s [14, 14, 768] → \underset{\mathrm{tokens}}{[196, 768]} [14,14,768]→tokens[196,768]​,此时正好变成了一个二维矩阵,正是Transformer想要的。

[ 224 , 224 , 3 ] I m a g e s → [ 14 , 14 , 768 ] p a t c h e s → [ 196 , 768 ] t o k e n s \underset{\mathrm{Images}}{[224, 224, 3]} → \underset{\mathrm{patches}}{[14, 14, 768]} → \underset{\mathrm{tokens}}{[196, 768]} Images[224,224,3]​→patches[14,14,768]​→tokens[196,768]​

2.2.2 Class Embedding 层

注意:在输入Transformer Encoder之前需要加上

  • [class]token → 接下来会说的
  • Position Embedding → 2.2.3会说

在原论文中,作者说参考BERT,在刚刚得到的一堆tokens中插入数据格式和其他token一样都是一个向量

以ViT-B/16为例,,与之前从图片中生成的tokens [ 196 , 768 ] [196, 768] [196,768] 拼接在一起,即:

c o n c a t ( [ 1 , 768 ] ; [ 196 , 768 ] ) → [ 197 , 768 ] \mathrm{concat}([1, 768]; [196, 768]) \rightarrow [197, 768] concat([1,768];[196,768])→[197,768]

代码实现为:

nn.cat([class]tokent, token) 

[ 196 , 768 ] ⟶ C o n c a t ( t o k e n s , [ c l a s s ] t o k e n ) ⟶ C o n c a t ( [ 196 , 768 ] , [ 1 , 768 ] ) ⟶ [ 197 , 768 ] [196, 768] \longrightarrow \mathrm{Concat(tokens, [class]token)} \\ {\longrightarrow} \mathrm{Concat([196, 768], [1, 768])}\\ \longrightarrow [197, 768] [196,768]⟶Concat(tokens,[class]token)⟶Concat([196,768],[1,768])⟶[197,768]

2.2.3 Position Embedding层

然后关于Position Embedding(就是之前Transformer中讲到的Positional Encoding),这里的(具体为),是直接叠加在tokens上的( ⊕ \oplus ⊕),所以shape要一样。

意思是说,前面的类别编码是一个trainable params,这里的位置编码也是一个trainable params,都是需要学习才能使得网络比较好的work。

以ViT-B/16为例,刚刚拼接[class]token(类别序列)后tokens的shape是 [ 197 , 768 ] [197, 768] [197,768] ,那么这里的Position Embedding的shape也是 [ 197 , 768 ] [197, 768] [197,768]。

  • 与《Transformer Is All You Need》不同,ViT作者没有使用固定的function去做Position Embedding,而是使用可训练的Position Embedding。
  • 一般ViT都是可训练的Position Embedding

2.2.3.1 Position Embedding有效性说明

对于Position Embedding作者也有做一系列对比试验,,对比不使用Position Embedding准确率提升了大概3个点,和2D Pos. Emb.比起来没太大差别。

  • No Pos. Emb.: 不使用位置编码
  • 1-D Pos. Emb.: 使用一维位置编码
  • 2-D Pos. Emb.: 使用二维位置编码
  • Rel Pos. Emb.: 使用相对位置编码
  • “The differences in how to encode spatial information is less important”
  • 位置编码很重要,但如何进行位置编码不是那么重要😂

论文展示了训练得到的位置编码的的余弦相似度的热力图。这里的Patch Size为 32 × 32 32 \times 32 32×32 ,即一张图片可以被划分为 224 / 32 × 224 / 32 = 7 × 7 224 / 32 \times 224 / 32 = 7 \times 7 224/32×224/32=7×7 个patch,每个patch的shape为: [ 32 , 32 , 3 ] [32, 32, 3] [32,32,3],共 7 × 7 = 49 7 \times 7 = 49 7×7=49 个,我们可以对每个patch进行线性映射得到所需要的token [ 32 × 32 × 3 ] = [ 3072 ] [32 \times 32 \times 3] = [3072] [32×32×3]=[3072],即

[ 224 , 224 , 3 ] I m a g e s → 49 p a t c h   n u m × [ 32 , 32 , 3 ] p a t c h → 49 p a t c h   n u m × [ 3072 ] t o k e n \underset{\mathrm{Images}}{[224, 224, 3]} \rightarrow \underset{\mathrm{patch \ num}}{49} \times \underset{\mathrm{patch}}{[32, 32, 3]} \rightarrow \underset{\mathrm{patch \ num}}{49} \times \underset{\mathrm{token}}{[3072]} Images[224,224,3]​→patch num49​×patch[32,32,3]​→patch num49​×token[3072]​

即一张图片被切分为49个patch,对每个patch进行变换后得到shape为 [ 3072 ] [3072] [3072] 的token,即tokens的shape为 [ 49 , 3072 ] [49, 3072] [49,3

标签: fci连接器10075025

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台