Swin Transformer 张量级别的讲解

395 阅读3分钟

Swin Transformer 一种旨在设计通用的backbone,却在应用了屡屡刷屏

 自从2017年Google Brain 提出了 Transformer之后,便掀起了一股研究transformer的热潮,随着BERT在NLP领域的频繁刷屏,Transformer也开始在CV领域大展身手,从ViTa到DETR,卷积和Transformer之间的结合应用变得越来越紧密。有关以Transformer为backbone的研究也在如火如潮的进行当中。

Swin Transformer提出了Transformer应用在CV领域中的最需要克服的两点区别:
  • 1: scale,图像中的内容不像NLP中是一个一个的词向量,里面的内容是有不同的大小的。目前大部分的序列转换都是以某个特定大小的图像块或者图像特征块作为一个序列的基本单元,这容易导致Transformer不能够很好的适应各种大小不一的物体。
  • 2 higher resolution 和计算成本的扩张一个256*256的照片维度就高达65536维度,Transformer的原版处理的序列也就是几百几千维,即使通过卷积的方式进行了降维,由于Transformer的二次方计算复杂度那也是一个特别需要时间和内存的任务。   Swin Transformer用下列的方式解决了上述的问题: Swin Transformer的结构

下面我会用程序运行流程中各种输入维度和操作的详细信息来讲解Transformer的执行过程和各种维度变化

SwinTransformer整体的框架:

in:(B,C,H,W)out:(B,embed_dim2stage1)in:(B,C,H,W) \quad out:(B,embed\_dim*2^{stage-1})

  • 1.首先输入一个批量的图片数据:(B,C,H,W)(B,C,H,W)
  • 2.进行块嵌入维度变为(B,HWp_s2,embed_dim)其中p_s表示块大小,块是这个图像处理中的最小单位(B,\cfrac{HW}{p\_s^2},embed\_dim)其中p\_s表示块大小,块是这个图像处理中的最小单位
  • 3.进行ape(绝对位置嵌入)+Dropout
  • 4.进行basic_layer的循环也就是上图中的四个stage阶段,注意每一个stage的输入为(B,HWp_s2,embed_dim)(B,\cfrac{HW}{p\_s^2},embed\_dim)输出为(B,HWp_s2/4,embed_dim2)(B,\cfrac{HW}{p\_s^2}/4,embed\_dim*2),这里为了统一处理,假设每一个stage的输入都是(B,L,C)(B,L,C)
  • 5.(B,L,C)>(B,C,L)>AdaptivePool1d(B,C,1)>(B,C)(B,L,C)-> (B,C,L)-> AdaptivePool1d(B,C,1) -> (B,C)主要是通过池化消除掉了L维度,形成最终的C个特征,这C个特征就是最终提出出来的特征。C=embed_dim2stage1C = embed\_dim*2^{stage-1}
Basic_Layer(也就是图中的state的框架):

in:(B,L,C)out:(B,L/4,2C)in:(B,L,C) \quad out:(B,L/4,2C)

  • 循环depthSwinTransformerBlock,这个过程保持维度
  • 进行一次patch_merging(即down_sample)(>(B,L/4,2C)(-> (B,L/4,2*C)
SwinTransformerBlock:

in:(B,L,C)out:(B,L,C)每一个Block包括两个连续的Block,一个带SW,一个不带in:(B,L,C)\quad out:(B,L,C)每一个Block包括两个连续的Block,一个带SW,一个不带

    • 第一个带滑动窗口的SwinTransformerBlock,包含超参数窗口尺寸window_size=7
    • 记录点:out1out_1=x
    • (B,L,C)>norm>(B,H,W,C)(B,L,C) -> norm->(B,H,W,C)
    • 进行斜上方循环Roll(shiftsize)Roll(shift_size)操作,其中shiftsize=win_size//2其中shift、_size=win\_size//2
    • window_partition操作>(BHWw_s2,w_s,w_s,C)>(BnW,w_s2,C)-> (\cfrac {BHW}{w\_s^2},w\_s,w\_s,C)->(B*nW,w\_s^2,C)
    • 进行一次WindowAttention操作
    • (>B,H,W,C)>反向Roll(shiftsize)(-> B,H,W,C)->反向Roll(shift_size)
    • 记录点:out2=x=x+out1out_2=x=x+out1
    • >LayerNorm(out2)>MLP->LayerNorm(out_2)->MLP
    • out=x+out2out = x+out2
    • 第二个不带滑动窗口的SwinTransformerBlock
    • 待续
WindowAttention:

in:(BnW,w_s2,C)out:(BnW,w_s2,C)in:(B*nW,w\_s^2,C)\quad out:(B*nW,w\_s^2,C)

  • >QKV操作Linear(BnW,w_s2,3C)>(3,BnW,nh,w_s2,C/n_h)->QKV操作Linear(B*nW,w\_s^2,3C)->(3,B*nW,n_h,w\_s^2,C/n\_h)
  • Q,K,V瓜分第一个维度即dimQ=dimK=dimV:(BnW,n_h,w_s2,C/n_h)Q,K,V瓜分第一个维度即dimQ=dimK=dimV:(B*nW,n\_h,w\_s^2,C/n\_h)
  • Q**scale*,scale是一个放缩系数
  • atten=Q@Katten:(BnW,n_h,w_s2,w_h2atten = Q @ K atten:(B*nW,n\_h,w\_s^2,w\_h^2
  • atten=atten+mask+rps(relativepositionbias)atten = atten+mask+rps(relative position bias)
  • x=softmax(atten,1)@Vx:(BnW,nh,w_2,C/n_h)x = softmax(atten,-1) @ V x:(B*nW,n_h,w\_^2,C/n\_h)
  • >(BnW,w_s2,C)-> (B*nW,w\_s^2,C)
  • drop(project(x))维度不变drop(project(x))维度不变
PatchEmbedding:

in:标准图像输入(B,C,H,W)out:(B,HW/p_s2,embed_dim)=(B,nP,embed_dim)in:标准图像输入(B,C,H,W)\quad out:(B,HW/p\_s^2,embed\_dim)=(B,nP,embed\_dim)

  • 首先>Conv2d(C,embeddim,k=patch_size,stride=patchsize):(B,embeddim,H/p_s,w/p_s)->Conv2d(C,embed_dim,k=patch\_size,stride=patch_size):(B,embed_dim,H/p\_s,w/p\_s)
  • >(B,HW/p_s2,embed_dim)>Drouout:(B,n_P,embed_dim)->(B,HW/p\_s^2,embed\_dim)->Drouout:(B,n\_P,embed\_dim)
window_partion

in:(B,H,W,C)out:(BHW/w_s2,ws,ws,C)=(BnW,w_s2,w_s2,C)in:(B,H,W,C)\quad out:(BHW/w\_s^2,w_s,w_s,C)=(B*nW,w\_s^2,w\_s^2,C)

aps (absolute position embedding)

 用来和PatchEmbedding相加,P_E后的维度为(B,n_P,C)(B,n\_P,C),aps的维度为(1,n_P,C)(1,n\_P,C)是一个可学习的Param

relative_pos_bias

 用来和注意力矩阵相加,注意力矩阵的维度是(nWB,n_h,w_s2w_s2)(nW*B,n\_h,w\_s^2,w\_s^2)rpb的维度是(n_head,w_s2,w_s2)(n\_head,w\_s^2,w\_s^2)

  • 作者注册了一个维度为((2wH1)(2wW1),n_heads)((2*wH-1)(2*wW-1),n\_heads)可学习张量rpb,还注册了一个buff:rpi(relative_pos_index)(WhWw,WhWw)(Wh*Ww,Wh*Ww)并且这个buff张量中的数字大小都在0(2wH1)(2wW1)0-(2*wH-1)(2*wW-1)之间,然后调用的时候rpb[rpi.flatten(),:]>(n_head,w_s2,w_s2)rpb[rpi.flatten(),:]->(n\_head,w\_s^2,w\_s^2)
mask

 作者通过使用滑动窗口来实现了全局注意力,但是之前不是一个window中的后来和其它窗口混在一起,相互直接就别算注意力了,那么就应该注意力矩阵中把这些窗口中之前不在一个窗口的patch相互注意力填充的很小。

一般的对于注意力的计算流程

 首先说明一下自注意力对于向量来说,这里计为(N,C)其输出应该是和输入是一样的。

  • 首先需要计算一个q,k,v*,假设对于每一个向量的q,k,v维度都是d**,因此需要进行一次线性变换让(N,C)* (C,3d) ->3 个(N,d)。
  • 然后得到了q:(N,d)和k(N,d),v(N,d)对一个需要qi需要和所有的kj做点积得到关于q_i需要 和所有的k_j做点积得到关于因此一共有N*N的式子:oi,j=kqi,kkj,ko_{i,j}=\sum_{k}q_{i,k}*k_{j,k}用 爱因斯坦表法就是einsum("i k , j k -> i j",q,k)
  • 刚刚得到的注意力矩阵**attn:(N,Ni)*表示了第i个向量关于所有的向量的注意力大小,可式子oi=kvkattni,ko_{i} =\sum_{k}v_k*attn_{i,k}.第二个位置不变即oi,j=kvk,jattni,ko_{i,j} =\sum_{k}v_{k,j}*attn_{i,k},即einsum(k j , i k -> i,j)