Swin Transformer
Swin Transformer 提出ViT具有两个缺点:
1. 没有多尺度特征 ,不能生成多尺度的特征图传给FPN (检测) \ U-Net (分割),从而对于不同大小的物体都能进行良好感知,即只有16 * 16的patch尺寸
2. 全局计算自注意力浪费资源,并且计算复杂度跟图像增加的大小成平方关系
对于以上缺点,Swin Transformer给出了以下的解决方法:
- 将patch size由 16 * 16 变成 4 * 4,从而对小物体,密集预测型任务有更好的性能。
- 不同于ViT—在整张输入图片上进行自注意力计算。Swin Transformer是在窗口内进行自注意力计算的,同时这个窗口又是包含固定数量的patch,每个patch的尺寸也是固定的。由于在图像领域中,同一个物体的不同部位、或语义相似的不同物体大概率会出现在相邻的地方,所以没必要像ViT那样–对整张图进行自注意力操作,其实可以借鉴CNN卷积的局部性的归纳偏置,在一个小的局部窗口内进行自注意力计算,也是差不多够用的。所以Swin Transformer使用了尺寸不一的窗口来避免序列长度过大,从而节约计算资源。
- 不同于ViT— 在每个Transformer Encoder Block上都是做相同尺寸的自注意力操作,得到的也是相同尺寸的特征。Swin Transformer在不同尺寸的窗口内做自注意力操作,从而得到不同尺寸的特征图,也就是多尺度的特征图。
- 在Swin Transformer中,这叫做patch merging。这跟CNN的池化操作很类似,CNN通过池化Pooling,来增大每一个卷积核能看到的感受野,从而使每次池化后的卷积核能够抓住不同尺寸的物体。
- 在Swin Transformer中,灰色的格子叫patch是最小的计算单元(尺寸为4 * 4),红色的格子叫window是中等的计算单元,最小的窗口里有7 * 7个patch,通过将整张图分成不同的窗口,只计算窗口内的自注意力,可以极大程度的减小序列长度,减小计算复杂度。
- shifted window是指:先将左侧图中的分割线往右下移动两个patch,然后将左上角变大后的窗口重新按中心分割成四个窗口,再将分割线往左上移动两个patch,就变成右图的样子。
- shifted window 和 patch merging的好处如下:
- 当我们进行注意力计算的时候,只在Swin Transformer的局部窗口内进行局部自注意力计算,相比ViT的全局窗口来说,可以减少序列长度,节省内存,加快计算。
- 因为自注意力都是只在窗口内进行,所以如果不进行shift,那么某个窗口内的patch就永远无法注意到其他窗口内patch的信息,这就违背了Transformer的初衷—更好的理解上下文,掌握全局信息。但是经过shift之后,比如中间的窗口,就是由之前四个窗口的patch组成的,也就表示中间窗口进行自注意力计算后,可以关注到其他窗口的信息,窗口和窗口之间可以进行交互(Cross-Window Connection)
- 再加上之前的patch merging,那么在不断合并的时候,每个patch可以注意到很多其他窗口的patch信息,即每个patch的感受野会不断增大
- 因此虽然我们计算的是每个窗口内的局部自注意力,但是实际上它近似等于一个全局的自注意力。
- Swin Transformer 的流程图如上:C是跟模型大小相关的超参数,在Swin-Tiny中为96,表示在Swin小模型中每个patch的经过线性投影后的维度为96
- 具体的维度变换如上图:
- 如果输入图片的尺寸为(224 * 224 * 3),经过Patch Partition后,将图片分割成尺寸为4 * 4的patch,那么输入图片的尺寸就变成(56 * 56 * 48),其中 56 = 224 / 4,48 = 4 * 4 * 3,表示输入图片一共有56 * 56 个patch,每个patch的维度为48
- 再经过Linear Embedding,将输入图片投影到一个适合当前模型大小的空间上,即将每个patch的维度增大为96,将(56 * 56 * 48)变成(56 * 56 * 96 ),但是由于56 * 56 = 3136的序列长度(patch数量)太大,Transformer无法接受,所以Swin Transformer Block这里采用在局部窗口计算自注意力的方式,每个窗口只有7 * 7 = 49 个 patch,因此它的序列长度只有49,这是一个Transformer可以接受的长度
- 如果不对Transformer做过多约束的话,那么由于Transformer Block有残差连接,所以Block的输入维度等于输出维度,那么Stage 1的输出维度就是 56 * 56 * 96
- 接下来由于需要构建多尺度的特征,就采用了Patch Merging(类似于卷积中的池化操作),将 56 * 56 * 96 变成了 28 * 28 * 192,即宽高减半,通道数翻倍,跟VGGNet、ResNet相同,即在池化操作之后加入一个 1 * 1 的卷积,使得最后结果的宽高减半,通道数翻倍。
- 同时由于Stage 2的Transformer Block也没有做过多约束,所以Block的输入维度也等于输出维度,那么Stage 2 的输出维度就是 28 * 28 * 192
- Stage 3、Stage 4和Stage 2类似,同理可得,Stage 3输出维度是 14 * 14 * 384,Stage 4输出维度是 7 * 7 * 768
Window的划分
- 相比于ViT在全局计算自注意力,Swin Transformer只在窗口内进行自注意力计算,于是整个特征图就会被分成很多没有重叠的窗口。且窗口的尺寸为(7 * 7),即窗口内部有49个patch,而patch是最小的计算单元。在Stage 1中,如上图,整个特征图的尺寸为56 * 56 * 96,一共分成了8 * 8个窗口,每个窗口的尺寸为7 * 7 * 96
Multi-head Self-Attention 和 Window Multi-head Self-Attention的计算复杂度对比
- 公式如上,其中h * w为当前输入图片一共被分成了多少个patch,C是patch特征的维度,M表示一个窗口的宽或高被分成了多少个patch
- 如果两个矩阵A、B相乘,A的形状为(M * N),B的形状为(N * P),那么相乘的结果C矩阵的形状为(M * P),整个计算的复杂度为O(M * N * P),由于C矩阵的每一个元素都是经过N次乘法,N-1次加法,总共2N-1次基本操作得来的,而对于O()来说常数不重要,所以C矩阵的每一个元素可以看作:需要N次基本操作,那么整个C矩阵就一共需要M * N * P次基本操作,复杂度就为O(M * N * P)
- 公式一的推算过程如下:
- 输入矩阵的尺寸为 h * w * c,乘以 W q 、 W k 、 W v W_q、W_k、W_v Wq、Wk、Wv变换矩阵(形状为c * c)之后,得到q、k、v(形状为h * w * c),所需要的计算复杂度为:O(h * w * c * c * 3) ,注意这里需要转换为二维矩阵后再进行矩阵的乘法,即转换后的形状为(h * w) * c
- q矩阵乘以k矩阵的转置,注意这里需要先转换为二维矩阵,再进行转置相乘,即转换后的形状为(h * w) * c和c * (h * w),得到A矩阵的形状为(h * w) * (h * w),所需要的计算复杂度为:O(h * w * c * h * w)
- 之后A矩阵和V矩阵相乘,得到经过注意力计算后的特征,所需的计算复杂度为O(h * w * h * w * c)
- 最后需要经过一层投射层,即乘以一个形状为c * c的变换矩阵,得到最后
Patch & Window和ViT的计算复杂度对比
- 在Swin Transformer中,不同层级的窗口内部的补丁数量是固定的,补丁内部的像素数量也是固定的,如上图的红色框就是不同的窗口(Window),窗口内部的灰色框就是补丁(Patch)
- 如果输入图像的宽W、高H,增加到原来的两倍,那么输入图像的总面积(总像素数量)就增加到原来的四倍(2H * 2W = 4HW)
- 在ViT中,由于窗口是固定的,且就是整个输入图片,所以当我们将输入图片分割成很多个尺寸为16 * 16 的patch时,如果输入图像的总面积增加到原来的四倍,那么patch的数量也会变成原来的四倍,那么计算复杂度 O ( N 2 ⋅ d ) O( N^2 · d) O(N2⋅d),就变成了 O ( ( 4 N ) 2 ⋅ d ) = O ( 16 N 2 ⋅ d ) O( (4N)^2 · d) = O( 16N^2 · d) O((4N)2⋅d)=O(16N2⋅d),其中d是每个patch的维度,N是patch的数量。因此,对于ViT来说,计算复杂度是跟图像增加的大小成平方关系
- 在Swin Transformer中,由于窗口不是固定的,但是窗口内部的补丁数量是固定的,补丁的尺寸也是固定的,所以当我们将输入图片的总面积增加到原来的四倍,那么只有窗口的数量增加到原来的四倍,那么计算复杂度 O ( M 2 ⋅ N ⋅ d ) O( M^2 · N · d) O(M2⋅N⋅d),就变成了 O ( M 2 ⋅ 4 N ⋅ d ) O( M^2 · 4N · d) O(M2⋅4N⋅d),其中M是每个窗口内补丁的数量,N是窗口的数量,d是每个补丁patch的维度。(虽然每个patch的维度都不一样,这里先不管了)
Patch Merging
- 类似于卷积中的池化操作:我们需要在一个区域中选出一个特征,然后不断移动这个区域,直到走完全图。从而降低特征图分辨率并且不丢失过多的原图信息。
- 在Swin Transformer中,是下采样两倍,即每间隔一个元素就选一个点,由于我们在第一步已经将输入图片分成一个个的patch,所以这里的元素指的是一个patch
- 假设原特征图的尺寸为H * W * C,那么每隔一个元素就选一个点,就等价于将原图按2 * 2的元素分为一个块,如果我们把块内的元素标上序号,那么相同序号的点将组成一个新特征图,尺寸为(H/2 * W/2)。随着原特征图的尺寸增大,那么新特征图尺寸也会增大,但是数量仍然是四个
- 将新特征图按通道拼接在一起后,通道数就变成原特征图的四倍。为了和卷积神经网络保持一致,即池化操作降维之后会使用一个1 * 1的卷积,将通道数翻倍。所以在Patch Merging中经过归一化后,需要经过1 * 1的卷积,来将通道数降低成原特征图的两倍
- 因此最后的结果就是将原特征图的尺寸由H * W * C ,变成H/2 * W/2 * 2C
- 综上:Patch Merging相当于是将空间上的维度换成更多的通道数,来达到卷积中池化操作的降低分辨率的效果
池化
- 如果使用卷积核大小为(1 * 2)具体为 [1 , -1],由于卷积操作对位置很敏感,所以对最左侧的输入进行卷积之后,得到的结果只有一列是1,即边缘会检测不准,如果当图片发生微小改变后,边缘经过卷积都会发生变化。所以卷积对于位置的敏感性不是一个很好的事。因此最好能具有一定程度的平移不变性,即当图片发生微小的改变,卷积结果不会发生改变。
- 所以往往在卷积之后加入池化操作,以上是二维最大池化的示意过程
- 池化通常在卷积之后
- 通过二维最大池化的结果可以看出,池化的操作近似于模糊化,在卷积输出的值附近出现多次同样的值。
- 池化层和卷积层类似,都有填充和步幅,池化的步幅通常等于池化窗口的大小,但是上图的步幅为1
- 但是池化层没有可学习的参数,直接从输入中选取值了
- 池化层的输出通道数等于输入通道数,即在每个输入通道应用池化层来获得相对应的输出通道。(由于卷积层可以改变通道,而池化层往往是跟在卷积层后面,所以池化层就不需要改变通道数了)