Swin Transformer论文阅读笔记

论文题目:Swin Transformer: Hierarchical Vision Transformer using Shifted Windows(使用移动窗口的层级式的Vision transformer)
论文地址:https://arxiv.org/abs/2103.14030

摘要

这篇论文提出了一个新的 Vision Transformer 叫做 Swin Transformer,它可以被用来作为一个计算机视觉领域一个通用的骨干网络。但是直接把Transformer从 NLP 用到 Vision 是有一些挑战的,这个挑战主要来自于两个方面:一个就是尺度上的问题。因为比如说现在有一张街景的图片,里面有很多车和行人,里面的物体都大大小小,那这时候代表同样一个语义的词,比如说行人或者汽车就有非常不同的尺寸,这种现象在 NLP 中就没有。另外一个挑战是图像的 resolution太大了,如果要以像素点作为基本单位的话,序列的长度就变得高不可攀,所以说之前的工作要么就是用后续的特征图来当做Transformer的输入,要么就是把图片打成 patch 减少这个图片的 resolution,要么就是把图片画成一个一个的小窗口,然后在窗口里面去做自注意力,所有的这些方法都是为了减少序列长度。
基于这两个挑战,本文的作者就提出了 hierarchical Transformer,它的特征是通过一种叫做移动窗口的方式学来的。
因为 Swin Transformer 拥有了像卷积神经网络一样分层的结构,有了这种多尺度的特征,所以它很容易使用到下游任务里,所以在这篇论文里,作者不光是在 ImageNet-1K 上做了实验,而且达到了非常好的准确度87.3;而且还在密集预测型的任务上,比如说物体检测、物体分割上取得了很好的成绩,比如说在 COCO 上刷到58.7的 AP,比之前最好的方法高了2.7个点;然后在语义分割上,ADE上 也刷到了53.5,比之前最好的方法高了3.2个点。

引言

Swin Transformer 借鉴了很多卷积神经网络的设计理念以及先验知识:

比如说为了减少序列的长度、降低计算复杂度,Swin Transformer采取了在小窗口之内算自注意力,而不是像 ViT 一样在整图上算自注意力,这样只要窗口大小是固定的,自注意力的计算复杂度就是固定的,整张图的计算复杂度就会跟图片的大小而成的线性增长关系,就是说图片增大了 x 倍,窗口数量也增大了 x 倍,计算复杂度也就乘以 x,而不是乘以 x 的平方
这个就算是利用了卷积神经网络里的 Locality 的 Inductive bias,就是利用了局部性的先验知识,同一个物体的不同部位或者语义相近的不同物体还是大概率会出现在相连的地方,所以即使是在一个 Local,一个小范围的窗口算自注意力也是差不多够用的,全局计算自注意力对于视觉任务来说,其实是有点浪费资源的
另外一个挑战是如何生成多尺寸的特征,卷积神经网络为什么会有多尺寸的特征?主要是因为有 Pooling (池化)这个操作,池化能够增大每一个卷积核能看到的感受野,从而使得每次池化过后的特征抓住物体的不同尺寸,所以类似的 ,Swin Transformer也提出来了一个类似于池化的操作叫做 patch merging,就是把相邻的小 patch 合成一个大 patch,这样合并出来的这一个大patch其实就能看到之前四个小patch看到的内容,它的感受野就增大了,同时也能抓住多尺寸的特征。

shift操作

Swin Transformer一个关键的设计因素是移动窗口的操作,如下图所示:
在这里插入图片描述
如果在 Transformer 第 L 层把输入或者特征图分成小窗口的话,就会有效的降低序列长度,从而减少计算复杂度。图中每一个灰色的小 patch 是最基本的元素单元,也就是图一中4*4的 patch;每个红色的框是一个中型的计算单元,也就是一个窗口。在 Swin Transformer 这篇论文里,一个小窗口里面默认有七七四十九个小patch的。如果用一个大的蓝色的正方形来描述整体的特征图,其实 shift 操作就是往右下角的方向整体移了两个 patch。然后在新的特征图里把它再次分成四方格。然后在新的特征图里把它再次分成四方格。
这样的好处是窗口与窗口之间可以进行互动,因为如果按照原来的方式,就是没有 shift,这些窗口之间都是不重叠的,如果每次自注意力的操作都在小的窗口里头进行了,每个窗口里的 patch 就永远无法注意到别的窗口里的 patch 的信息,这就达不到使用 Transformer 的初衷。
因为Transformer的初衷就是更好的理解上下文,如果窗口都是不重叠的,那自注意力真的就变成孤立自注意力,就没有全局建模的能力
但如果加上 shift 的操作,每个 patch 原来只能跟它所在的窗口里的别的 patch 进行交互,但是 shift 之后,这个 patch就可以跟新的窗口里的别的 patch就进行交互了,而这个新的窗口里所有的 patch 其实来自于上一层别的窗口里的 patch,这也就是作者说的能起到 cross-window connection,就是窗口和窗口之间可以交互了
再配合上之后提出的 patch merging,合并到 Transformer 最后几层的时候,每一个 patch 本身的感受野就已经很大了,就已经能看到大部分图片了,然后再加上移动窗口的操作,它所谓的窗口内的局部注意力其实也就变相的等于是一个全局的自注意力操作了,这样就是既省内存,效果也好。

方法

模型总览图如下图所示:
在这里插入图片描述

假设说有一张2242243(ImageNet 标准尺寸)的输入图片:
第一步就是像 ViT 那样把图片打成 patch,在 Swin Transformer 这篇论文里,它的 patch size 是44,而不是像 ViT 一样1616,所以说它经过 patch partition 打成 patch 之后,得到图片的尺寸是565648,56就是224/4,因为 patch size 是4,向量的维度48,因为443,3 是图片的 RGB 通道。
打完了 patch ,接下来就要做 Linear Embedding,也就是说要把向量的维度变成一个预先设置好的值,就是 Transformer 能够接受的值,在 Swin Transformer 的论文里把这个超参数设为 c,对于 Swin tiny 网络来说,也就是上图中画的网络总览图,它的 c 是96,所以经历完 Linear Embedding 之后,输入的尺寸就变成了565696,前面的5656就会拉直变成3136,变成了序列长度,后面的96就变成了每一个token向量的维度,其实 Patch Partition 和 Linear Embedding 就相当于是 ViT 里的Patch Projection 操作,而在代码里也是用一次卷积操作就完成了。
第一部分跟 ViT 其实还是没有区别的,但紧接着区别就来了,首先序列长度是3136,对于 ViT 来说,用 patch size 16
16,它的序列长度就只有196,是相对短很多的,这里的3136就太长了,是目前来说Transformer不能接受的序列长度,所以 Swin Transformer 就引入了基于窗口的自注意力计算,每个窗口按照默认来说,都只有七七四十九个 patch,所以说序列长度就只有49就相当小了,这样就解决了计算复杂度的问题,所以也就是说, stage1中的swin transformer block 是基于窗口计算自注意力的,现在暂时先把 transformer block当成是一个黑盒,只关注输入和输出的维度,对于 Transformer 来说,如果不对它做更多约束的,Transformer输入的序列长度是多少,输出的序列长度也是多少,它的输入输出的尺寸是不变的,所以说在 stage1 中经过两层Swin Transformer block 之后,输出还是565696,到这其实 Swin Transformer的第一个阶段就走完了,也就是先过一个 Patch Projection 层,然后再过一些 Swin Transformer block,接下来如果想要有多尺寸的特征信息,就要构建一个层级式的transformer,也就是说需要一个像卷积神经网络里一样,有一个类似于池化的操作。

窗口划分举例
原图片会被平均的分成一些没有重叠的窗口,拿第一层之前的输入来举例,它的尺寸就是565696,也就说有一个维度是5656张量,然后把它切成一些不重叠的方格。每一个方格就是一个窗口,但是这个窗口并不是最小的计算单元,最小的计算单元其实还是之前的那个 patch,也就意味着每一个小窗口里其实还有 m * m 个 patch,在 Swin Transformer 这篇论文里一般 m 默认为7,也就是说,一个小方格里有七七四十九个小 patch,现在所有自注意力的计算都是在这些小窗口里完成的,就是说序列长度永远都是七七四十九,原来大的整体特征图到底里面会有多少个窗口呢?其实也就是每条边56/7就8个窗口,也就是说一共会有88等于64个窗口,就是说会在这64个窗口里分别去算它们的自注意力。

基于窗口的自注意力模式的计算复杂度
在这里插入图片描述
如果现在有一个输入,自注意力首先把它变成 q k v 三个向量,这个过程其实就是原来的向量分别乘了三个系数矩阵一旦得到 query 和 k 之后,它们就会相乘,最后得到 attention,也就是自注意力的矩阵,有了自注意力之后,就会和 value 做一次乘法,也就相当于是做了一次加权
最后因为是多头自注意力,所以最后还会有一个 projection layer,这个投射层会把向量的维度投射到我们想要的维度。
如果这些向量都加上它们该有的维度,也就是说刚开始输入是 hwc。首先,to_q_k_v()函数相当于是用一个 hwc 的向量乘以一个 cc 的系数矩阵,最后得到了 hwc。所以每一个计算的复杂度是 hwc^ 2,因为有三次操作,所以是三倍的 hwc^ 2, 然后,算自注意力就是 hwc乘以 k 的转置,也就是 chw,所以得到了 hwhw,这个计算复杂度就是(hw)^2c
接下来,自注意力矩阵和value的乘积的计算复杂度还是 (hw) ^ 2c,所以现在就成了2*(hw)^ 2c,最后一步,投射层也就是hwc乘以 cc 变成了 hwc ,它的计算复杂度就又是 hwc^2
最后合并起来就是最后的公式(1)
基于窗口的自注意力计算复杂度又是如何得到的呢?
因为在每个窗口里算的还是多头自注意力,所以可以直接套用公式(1),只不过高度和宽度变化了,现在高度和宽度不再是 h * w,而是变成窗口有多大了,也就是 M
M,也就是说现在 h 变成了 M,w 也是 M,它的序列长度只有 M * M 这么大,所以当把 M 值带入到公式(1)之后,就得到计算复杂度是4 * M^2 * c^2 + 2 * M^4 * c,这个就是在一个窗口里算多头自注意力所需要的计算复杂度,那我们现在一共有 h/M * w/M 个窗口,现在用这么多个窗口乘以每个窗口所需要的计算复杂度就能得到公式(2)了,对比公式(1)和公式(2),虽然这两个公式前面这两项是一样的,只有后面从 (hw)^2变成了 M^2 * h * w,看起来好像差别不大,但其实如果仔细带入数字进去计算就会发现,计算复杂的差距是相当巨大的,因为这里的 hw 如果是56*56的话, M^2 其实只有49,所以是相差了几十甚至上百倍的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值