Vision Transformer
Transformer原本是用在NLP上的模型,直到Vision Transformer的出现,transformer开始了在视觉领域的应用。
论文:An Image is Worth 16x16 Words:Transformer for Image Recognition at Scale
Split Image into Patches
Vision Transformer(以下简称ViT)要求将图片划分为大小相同的Patches
在上面的划分中,patches没有重叠。当然patches也可以有重叠。划分的patches越多计算量越大
在划分的过程中,需要指定两个超参数:patch size、stride
# patch embedding的代码
import torch
from torch import nn
class PatchEmbedding(nn.Module):
def __init__(self,patch_size=16,img_size=224,in_channel=3,embedd_dim=768)
self.patch_size=(patch_size,patch_size)
self.img_size=(img_size.img_size)
self.patch_embedd=nn.Conv2d(in_channel,embedd_dim,kernel_size=patch_size,stride=patch_size)
def forword(self,x)
B,C,H,W=x.shape # b,c,fh,hw
x=self.patch_embedd(x) # b,c,gh,gw
x=x.flatten(2).transpose(1,2) # b,gh*gw,c
return x
Vectorization
接下来的是向量化,即将张量拉伸成向量
如果每一个patches都是d1xd2xd3的张量,那拉伸后的张量为d1d2d3x1
线性变换
设图片被划分成了n个向量,记作x1…xn。
首先用全连接层对向量x作线性变换,z1=Wx1+b。注意:此处的全连接层不使用激活函数,因此这里的全连接层只是个线性函数。W,b参数需要学习得到
这里的全连接层都共用一套参数,即W和b相同
位置编码
还需要对每个patches做位置编码,因为图片被划分为n块,那么位置就是1-n之间的整数
然后将得到的位置编码的向量加到z向量上
为什么要用position encoding?
如下图所示,如果将patch打乱,此时如果不进行位置编码,那么在transformer看来这两张图就是一样的,最后得到的输出也一样,这显然不合理。
我们需要transformer知道这两张图不一样,所以我们要对patch进行编码
编码之后,若我们在进行patch位置的变换,这时候transformer的输出就不一样了。实验证明,若不加位置编码,最后的结果会掉3个百分点。
# positional_embedding
class PositionalEmbedding1D(nn.Module):
def __init__(self,seq_len,dim):
super.__init__()
self.pos_embedding=nn.Parameter(torch.zeros(1,seq_len,dim))
def forward(self,x):
return x+self.pos_embedding
Transformer Encoder Network
加入位置编码后,z向量便即包含n个小块的内容信息,又包含了位置信息。此外,我们用[CLS]代表分类,对这个符号作embedding,得到z0。把z0-zn输入到多头注意力层,紧接着接上全连接层,以多头注意力层和全连接层连接在一起并多次叠加就是Tranformer的Encoder部分的网络,当然,想要累积多少层都可以。
输出
将Encoder部分的输出记为c0-cn。c1-cn没有用,我们只用关注c0。可以将c0看作是从图片中提取的特征向量,用作分类任务
将c0输入softmax分类器,分类器输出向量p,向量p表示分类的结果,p的大小是类别的数量,如果有1000个类别,那么p就是1000维的向量。
参考论文:An Image is Worth 16x16 Words:Transformer for Image Recognition at Scale