Zhangzhe's Blog

The projection of my life.

0%

URL

https://arxiv.org/pdf/2006.12030.pdf

TL;DR

  • 传统过参数化网络,在训练阶段能提高收敛速度并提高算法表现,但在推理阶段速度会变慢
  • 本文提出一种过参数化卷积 DO-Conv,在训练阶段有过参数化的优点——收敛速度快、算法表现好,在推理阶段将 DO-Conv 转化为标准卷积,不会带来任何额外耗时
  • DO-Conv 实际上是在训练阶段,将标准卷积拆分成标准卷积和Depthwise卷积的叠加;在推理阶段之前,将拆分后的标准卷积和Depthwise卷积再合并为一个标准卷积

Algorithm

何为过参数化?

  • 过参数化可以理解为:通过使用更多的参数,增大模型的假设空间,增加模型的表达能力(线性与非线性),从而加速训练甚至提高算法表现

标准卷积与Depthwise卷积的数学表示:

  • O=W×PO = W \times P
    • WRCout×(M×N)×CinW \in \mathbb{R}^{C_{out} \times (M \times N) \times C_{in}} ,表示 kernels,每一个 kernel.shape==(Cin,M,N)kernel.shape == (C_{in}, M, N) ,一共 CoutC_{out}kernel
    • PR(M×N)×CinP \in \mathbb{R}^{(M \times N) \times C_{in}} ,表示输入 feature mapkernel 覆盖的 patch,每一个 patch.shape==(Cin,M,N)patch.shape == (C_{in}, M, N)
    • ORCoutO \in \mathbb{R}^{C_{out}} ,表示卷积结果
      do1.png

Depthwise 卷积的数学表示:

  • O=WPO = W \circ P
    • WR(M×N)×Dmul×CinW \in \mathbb{R}^{(M \times N) \times D_{mul} \times C_{in}} ,表示 kernels,每一个 kernel.shape==(M,N)kernel.shape == ( M, N) ,每组(每个通道为一组) DmulD_{mul}kernel,一共 CinC_{in}
    • PR(M×N)×CinP \in \mathbb{R}^{(M \times N) \times C_{in}} ,表示输入 feature mapkernel 覆盖(所有通道)的 patch,每一个 patch.shape==(Cin,M,N)patch.shape == (C_{in}, M, N)
    • ORDmul×CinO \in \mathbb{R}^{D_{mul} \times C_{in}} ,表示卷积结果
      do2.png

DO-Conv 的数学表示:

  • O=(D,W)PO = (D, W) \star P
  • feature compositionO=W×(DP)O = W \times (D \circ P)
  • kernel compositionO=(DTW)×PO = (D^T \circ W ) \times P
    • WRCout×Dmul×CinW \in \mathbb{R}^{C_{out}\times D_{mul}\times C_{in}} ,表示 kernels_w,每一个 kernelw.shape== ( Cin,Dmul)kernel_w.shape ==\text{ ( }C_{in}, D_{mul}) ,一共CoutC_{out}kernel_w
    • DR(M×N)×Dmul×CinD \in \mathbb{R}^{(M\times N)\times D_{mul}\times C_{in}} ,表示 kernels_d,每一个 kerneld.shape==(M,N)kernel_d.shape == ( M, N) ,每组(每个通道为一组) DmulD_{mul}kernel_d,一共 CinC_{in}
    • PR(M×N)×CinP \in \mathbb{R}^{(M\times N)\times C_{in}} ,表示输入 feature mapkernel 覆盖(所有通道)的 patch,每一个 patch.shape==(Cin,M,N)patch.shape == (C_{in}, M, N)
    • ORCoutO \in \mathbb{R}^{C_{out}} ,表示卷积结果
      do3.png

这与Depthwise Separable 卷积有什么区别?

  • 相同点:
    • feature composition 可以看做是先对 feature mapDepthwise Conv ,再做标准卷积
  • 区别:
    • Depthwise Separable Conv 是`DO-Conv Dmul=1D_{mul} = 1 的特殊情况

DO-Conv 怎么能保证参数量比标准卷积多呢?

  • 标准卷积卷积核:
    • W1RCout×(M×N)×CinW_1 \in \mathbb{R}^{C_{out}\times (M\times N)\times C_{in}}
  • DO-Conv 卷积核:
    • W2RCout×Dmul×CinW_2 \in \mathbb{R}^{C_{out}\times D_{mul}\times C_{in}}
    • DR(M×N)×Dmul×CinD \in \mathbb{R}^{(M\times N)\times D_{mul}\times C_{in}}
  • Dmul=(M×N)D_{mul} = (M \times N) 时, W2=W1W2 = W1 ,此时 $ D + W_2 > W_1$ ,DO-Conv 拥有更多的参数量
  • 所以规定: Dmul(M×N)D_{mul} \ge (M \times N)

怎么能保证推理阶段不增加耗时呢?

  • 训练阶段 WD 都是可优化参数,所以模型会保存 WD
  • 推理阶段之前,使用 kernel compositionWD 处理, W=DTWW' = D^T \circ W ,然后就使用 WW' 去做标准卷积, W.shape==W1.shapeW'.shape == W_1.shape ,所以 inference 阶段不会增加任何耗时

DO-Conv 分组卷积 / Depthwise卷积

  • O=(D,W)PO = (D, W) \odot P
  • feature compositionO=W(DP)O = W \circ (D \circ P)
  • kernel compositionO=(DTWT)TPO = (D^T \circ W^T )^T \circ P

Thoughts

  • a = a1 * a2,能不能从数学角度证明学习 a1 * a2 比直接学习 a 更容易,效果更好?
  • 芯片上常常不支持 1 * 1 Conv,能否将输入的 (N2×Cin,H,W)(N^2 \times C_{in}, H, W) 使用 PixelShuffle 运算 reshape 成为 (Cin,N×H,N×W)(C_{in}, N \times H, N \times W) ,再使用 kernel.shape==(Cin,N,N),stride=Nkernel.shape == (C_{in},N, N), stride = N 的标准卷积去算?

DO-Conv 网络的实际表现

  • 在所有用到卷积的地方,无脑替换为 DO-Conv 基本都能涨点
    do4.png
    do5.png
    do6.png
    do7.png
    do8.png
    do9.png

URL

https://arxiv.org/pdf/2007.06191.pdf

TL;DR

  • 本文提出了一种新颖的卷积方式——PSConv,参数量与计算量都不变的情况下可以提高网络特征提取能力
  • PSConv的本质是一种dilated系数周期性变化的空洞卷积,周期性变化发生在 CinC_{in}CoutC_{out} 两个“正交”坐标系中

Algorithm

什么是PSConv

  • 标准卷积:
    Hc,x,y=k=1Cini=K12K12j=K12K12Gc,k,i,jFk,x+i,y+jH_{c,x,y} = \sum_{k=1}^{C_{in}}\sum_{i=-\frac{K-1}{2}}^{\frac{K-1}{2}}\sum_{j=-\frac{K-1}{2}}^{\frac{K-1}{2}} G_{c,k,i,j}F_{k,x+i,y+j}
    其中,F表示输入feature, FRCin×H×WF\in\mathbb{R}^{C_{in}\times H\times W} ,G表示kernel, GRCout×Cin×K×KG\in\mathbb{R}^{C_{out}\times C_{in}\times K\times K} ,H表示一个kernel卷积输出 HR1×H×WH\in\mathbb R^{1\times H\times W} ,i,j分表表示kernel中的点距离kernel中心在H于W方向上的偏移
  • 空洞卷积:
    Hc,x,y=k=1Cini=K12K12j=K12K12Gc,k,i,jFk,x+id,y+jdH_{c,x,y} = \sum_{k=1}^{C_{in}}\sum_{i=-\frac{K-1}{2}}^{\frac{K-1}{2}}\sum_{j=-\frac{K-1}{2}}^{\frac{K-1}{2}} G_{c,k,i,j}F_{k,x+id,y+jd}
    其中:id表示 偏移 i * 空洞系数 d
  • PSConv:
    Hc,x,y=k=1Cini=K12K12j=K12K12Gc,k,i,jFk,x+iD(c,k),y+jD(c,k)H_{c,x,y} = \sum_{k=1}^{C_{in}}\sum_{i=-\frac{K-1}{2}}^{\frac{K-1}{2}}\sum_{j=-\frac{K-1}{2}}^{\frac{K-1}{2}} G_{c,k,i,j}F_{k,x+iD(c,k),y+jD(c,k)}
    其中:i * D(c, k)表示 偏移 i * 空洞系数 D(c, k),D是空洞系数矩阵DRCout×CinD\in\mathbb R^{C_{out}\times C_{in}}D(i, j) 表示 第 i 个 kernel 第 j channel 的空洞系数 d

空洞系数矩阵D设计的基本法

  • D矩阵的一行表示一个kernel,kernel 的通道数为 CinC_{in}
  • PSConv 是将这 CinC_{in} 通道分为 P 个周期,每个周期的长度为 t=CinPt = \lceil \frac{C_{in}}{P}\rceil
  • 每个周期 t 个空洞系数 d={d1,d2,...,dt}d = \{d_1, d_2,...,d_t\} ,实验证明当 t=4,d={1,2,1,4}t=4,d=\{1,2,1,4\} 效果最好
  • 另外D矩阵的列上也要具有周期性,方法是让每一行相对与上一行偏移 1个相位(一个周期 t 个相位)

效果

  • 涨点,替换什么什么就涨点,原因是使用一种类似周期性金字塔kernel的方法,得到了非常有想象力的感受野
  • 虽然不增加参数量与计算量,但是train与inference都会变慢,所以作者附录中给出了一种优化实现方式,是将每个周期的相同相位合并到一起使用group Conv,然后再shuffle index,与ShuffleNet一个意思
  • 优化后确实会变快,可以将PS-ResNet-50/101在inference速度提升到与标准ResNet-50/101基本一致
  • 消融实验对比了只在 CinC_{in} 或者 只在CoutC_{out} 上周期性变化,发现哪个轴上周期变化都是必要的
  • 当 t = 1时,D中只会包含一种元素,此时PSconv退化为标准空洞卷积
  • PSConv 可以加入 group 参数,每个 group 内单独周期变化

Thoughts

  • 不增加计算量和参数量,且文章附录中提出经过优化实现可以将PS-ResNet-50/101在inference速度提升到与标准ResNet-50/101基本一致。
  • 如果在硬件设计中,预留了dilated Conv接口,并借鉴文中的优化实现方式,使用分组卷积+index shuffle减小filter通道间dilated不同的问题,这种白送的点也挺香的

图表

  • 标准卷积与PSConv (可以带group参数)
    ps1.png
  • PS group Conv
    ps2.png
  • 优化实现
    ps3.png
  • 搜索 t 与 d
    ps4.png
  • 横纵坐标周期消融实验
    ps5.png
  • 涨点涨点涨点…
    ps8.png
    ps9.png
    ps7.png
    ps6.png

URL

https://arxiv.org/pdf/2103.13425.pdf

TL;DR

  • 重参数化训练:模型的搜索空间更大,收敛更快,点更高,inference 前把多个算子拍成一个,inference 速度和未重参数化模型一样(白给的涨点)
  • 之前的重参数化工作大多都只有特定的一种或几种重参数模式,本文的重参数模式更丰富
  • repVGG 就是 DBB 的一种应用

Dataset/Algorithm/Model/Experiment Detail

dbb
以上六种训练时结构,在 inference 阶段都可拍成一个 Conv,且严格等价,具体的数学推导可以看原文

Thoughts

  • 这种工作我比较熟,毕竟和 neuwizard 也差不多嘛

URL

https://arxiv.org/pdf/2105.01601.pdf

TL;DR

  • 作者认为 CNN 与 Transformer 对视觉任务有利,但不是必要,MLP 也能在视觉任务中做的很好
  • 效果比 VIT 略差,但速度快

Algorithm

作者认为的 CV 的本质

  • 局部的特征提取 + 全局特征相关性的建模
  • CNN 天生对局部的特征提取很擅长,全局特征相关性的分析需要靠下采样 + 堆叠层来增大感受野完成
  • Transformer 与 CNN 相反,Transformation 很擅长全局特征相关性建模(关系矩阵),但局部特征提取比较弱,所以通常需要在超大数据集上预训练来获得局部先验
  • MLP 也是擅长全局特征相关性建模,对于不擅长的局部特征提取,使用 dim shuffle 交换 h*w 和 c,将全局特征提取变成针对 channel 的局部特征提取

网络结构

lm1

  • lecun 嘲讽
    lecun 嘲讽

Thoughts

  • 虽然 lecun 说的没错,但我想作者的意思是 MLP-mixer 架构不依赖于传统的 Conv 3*3 结构而是使用 matmul + dim shuffle 一样可以做到 局部特征提取 + 全局依赖
  • Lecun 的嘲讽翻译过来就是:“MLP 的本质是 Conv,Matmul 的本质是 Conv1d with 1*1 kernel”,这明显是抬杠…

实现代码(非伪代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from torch import nn
from functools import partial
from einops.layers.torch import Rearrange, Reduce
class PreNormResidual(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.fn = fn
self.norm = nn.LayerNorm(dim)
def forward(self, x):
return self.fn(self.norm(x)) + x
def FeedForward(dim, expansion_factor=4, dropout=0.0, dense=nn.Linear):
return nn.Sequential(
dense(dim, dim * expansion_factor),
nn.GELU(),
nn.Dropout(dropout),
dense(dim * expansion_factor, dim),
nn.Dropout(dropout),
)
def MLPMixer(
*,
image_size,
channels,
patch_size,
dim,
depth,
num_classes,
expansion_factor=4,
dropout=0.0
):
assert (image_size % patch_size) == 0, "image must be divisible by patch size"
num_patches = (image_size // patch_size) ** 2
chan_first, chan_last = partial(nn.Conv1d, kernel_size=1), nn.Linear
return nn.Sequential(
Rearrange(
"b c (h p1) (w p2) -> b (h w) (p1 p2 c)", p1=patch_size, p2=patch_size
),
nn.Linear((patch_size ** 2) * channels, dim),
*[
nn.Sequential(
PreNormResidual(
dim, FeedForward(num_patches, expansion_factor, dropout, chan_first)
),
PreNormResidual(
dim, FeedForward(dim, expansion_factor, dropout, chan_last)
),
)
for _ in range(depth)
],
nn.LayerNorm(dim),
Reduce("b n c -> b c", "mean"),
nn.Linear(dim, num_classes)
)

URL

https://arxiv.org/pdf/2105.01883.pdf

TL;DR

  • 本文提出一种 MLP 的重参数结构,训练过程中在 MLP 中构建多个 Conv branch,Inference 过程中,将多个 Conv branch 吸收到 MLP 中,不额外增加 MLP 的推理速度

Algorithm

总体结构

repmlp1

结构分解

  • Global Perceptron
    为了减小 MLP 恐怖的参数量,作者提出将 (N,C,H,W)(N,C,H,W) 的 feature map 通过:
    (N,C,H,W)(N,C,Hh,h,Ww,w)(N,Hh,Ww,C,h,w)(N×H×Wh×w,C,h,w)(N,C,H,W) \rightarrow (N, C, \frac{H}{h}, h,\frac{W}{w},w) \rightarrow (N, \frac{H}{h},\frac{W}{w},C,h,w) \rightarrow (\frac{N\times H\times W}{h\times w}, C, h,w)
    这种 Reshape -> Transpose -> Reshape 的方式缩小分辨率,这种方法的问题在于 patch 之间的关联关系被打破,所以需要一个作用于 Global 的 patch 权重,类似于 patch 版的 SENet,不同之处在于这里没有使用 SENetPer-channel Scale 而是只用了 Per-channel Bias
  • Partion Perceptron
    FC 本体,由于 MLP 的参数过于恐怖,所以这里的 FC 需要二次降参数,做法是 Group FC
  • Local Perceptron
    使用了多个 branch 的 Conv,提取 Global Perceptron 的输出 feature map,目的是局部特征提取,弥补 MLP 局部特征提取的短板,也就是题目的来源

Inference

  • Inference 时,重参数主要是两个方面:
    • FC 吸 BN
    • FC 吸 Conv

结果

  • 从消融实验来看,三种 Perceptron 中除了 Partion Perceptron 作为 FC 本体无法消融之外,重要程度:local Perceptron > Global Perceptron
  • 和 Wide ConvNet 对比 Flops 较小但精度较低,所以需要和 Conv 结合使用,结合方式是:
    repmlp2

Thoughts

  • 重参数过程中 Global Perceptron 只吸了一个 BN,感觉有点亏,而且 Global Perceptron 这个分支的存在让整个 RepMLP 变得没有像 RepVGG 一样优雅
  • 如果 Global Perceptron 的作用是建立 patch 之间的关联,可不可以把 (N,C,H,W)(N×H×Wh×w,C,h,w)(N,C,H,W) \rightarrow (\frac{N\times H\times W}{h\times w}, C, h,w) 这个过程变成类似 PixelShuffle upscale_factor < 1 的剪 patch 方法(可能效率会比文中方法低),每个 patch 信息分布相似,无需建立 patch 间的全局关系,是否可以省掉 Global Perceptron 分支,那样的话,Inference 过程就只有一个 FC 了

URL

https://arxiv.org/pdf/1904.04971.pdf

TL;DR

  • 一种动态卷积网络,每个 Conv 有多组 Weight 被称为多个 Expert,使用与 SENet 相似的方法产生每个 Expert 的权重向量,加权多个 Expert 产生最终的 Weight

Dataset/Algorithm/Model/Experiment Detail

结构

condconv

Thoughts

  • 因为 WeightNet 看了这篇,感觉和 SENet 差别太小了,SENet 是对输入 feature map 使用 GAP + FCs + Sigmoid 产生权重向量,权重向量作用于 Conv Weight 的 CinC_{in} 维度上;CondConv 使用同样的方法产生权重向量,权重向量作用于 Conv Weights 上。

pytorch 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import functools
import torch
from torch import nn
import torch.nn.functional as F
from torch.nn.modules.conv import _ConvNd
from torch.nn.modules.utils import _pair
from torch.nn.parameter import Parameter
class _routing(nn.Module):
def __init__(self, in_channels, num_experts, dropout_rate):
super(_routing, self).__init__()
self.dropout = nn.Dropout(dropout_rate)
self.fc = nn.Linear(in_channels, num_experts)
def forward(self, x):
x = torch.flatten(x)
x = self.dropout(x)
x = self.fc(x)
return F.sigmoid(x)
class CondConv2D(_ConvNd):
r"""Learn specialized convolutional kernels for each example.
As described in the paper
`CondConv: Conditionally Parameterized Convolutions for Efficient Inference`_ ,
conditionally parameterized convolutions (CondConv),
which challenge the paradigm of static convolutional kernels
by computing convolutional kernels as a function of the input.
Args:
in_channels (int): Number of channels in the input image
out_channels (int): Number of channels produced by the convolution
kernel_size (int or tuple): Size of the convolving kernel
stride (int or tuple, optional): Stride of the convolution. Default: 1
padding (int or tuple, optional): Zero-padding added to both sides of the input. Default: 0
padding_mode (string, optional): ``'zeros'``, ``'reflect'``, ``'replicate'`` or ``'circular'``. Default: ``'zeros'``
dilation (int or tuple, optional): Spacing between kernel elements. Default: 1
groups (int, optional): Number of blocked connections from input channels to output channels. Default: 1
bias (bool, optional): If ``True``, adds a learnable bias to the output. Default: ``True``
num_experts (int): Number of experts per layer
Shape:
- Input: :math:`(N, C_{in}, H_{in}, W_{in})`
- Output: :math:`(N, C_{out}, H_{out}, W_{out})` where
.. math::
H_{out} = \left\lfloor\frac{H_{in} + 2 \times \text{padding}[0] - \text{dilation}[0]
\times (\text{kernel\_size}[0] - 1) - 1}{\text{stride}[0]} + 1\right\rfloor
.. math::
W_{out} = \left\lfloor\frac{W_{in} + 2 \times \text{padding}[1] - \text{dilation}[1]
\times (\text{kernel\_size}[1] - 1) - 1}{\text{stride}[1]} + 1\right\rfloor
Attributes:
weight (Tensor): the learnable weights of the module of shape
:math:`(\text{out\_channels}, \frac{\text{in\_channels}}{\text{groups}},`
:math:`\text{kernel\_size[0]}, \text{kernel\_size[1]})`.
The values of these weights are sampled from
:math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})` where
:math:`k = \frac{groups}{C_\text{in} * \prod_{i=0}^{1}\text{kernel\_size}[i]}`
bias (Tensor): the learnable bias of the module of shape (out_channels). If :attr:`bias` is ``True``,
then the values of these weights are
sampled from :math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})` where
:math:`k = \frac{groups}{C_\text{in} * \prod_{i=0}^{1}\text{kernel\_size}[i]}`
.. _CondConv: Conditionally Parameterized Convolutions for Efficient Inference:
https://arxiv.org/abs/1904.04971
"""
def __init__(
self,
in_channels,
out_channels,
kernel_size,
stride=1,
padding=0,
dilation=1,
groups=1,
bias=True,
padding_mode="zeros",
num_experts=3,
dropout_rate=0.2,
):
kernel_size = _pair(kernel_size)
stride = _pair(stride)
padding = _pair(padding)
dilation = _pair(dilation)
super(CondConv2D, self).__init__(
in_channels,
out_channels,
kernel_size,
stride,
padding,
dilation,
False,
_pair(0),
groups,
bias,
padding_mode,
)
self._avg_pooling = functools.partial(F.adaptive_avg_pool2d, output_size=(1, 1))
self._routing_fn = _routing(in_channels, num_experts, dropout_rate)
self.weight = Parameter(
torch.Tensor(num_experts, out_channels, in_channels // groups, *kernel_size)
)
self.reset_parameters()
def _conv_forward(self, input, weight):
if self.padding_mode != "zeros":
return F.conv2d(
F.pad(input, self._padding_repeated_twice, mode=self.padding_mode),
weight,
self.bias,
self.stride,
_pair(0),
self.dilation,
self.groups,
)
return F.conv2d(
input,
weight,
self.bias,
self.stride,
self.padding,
self.dilation,
self.groups,
)
def forward(self, inputs):
b, _, _, _ = inputs.size()
res = []
for input in inputs:
input = input.unsqueeze(0)
pooled_inputs = self._avg_pooling(input)
routing_weights = self._routing_fn(pooled_inputs)
kernels = torch.sum(
routing_weights[:, None, None, None, None] * self.weight, 0
)
out = self._conv_forward(input, kernels)
res.append(out)
return torch.cat(res, dim=0)

URL

https://arxiv.org/pdf/2006.15102.pdf

TL;DR

  1. ULSAM 是一个超轻量级的子空间注意力网络,适合用在轻量级的网络中,例如 MobileNetShuffleNet
  2. 适合用在图像细粒度分类任务中,能减少大约 13%Flops 和大约 25%params,在 ImageNet - 1K 和 其他三个细粒度分类数据集上 Top1 error 分别降低 0.27%1%
  3. SENet 有点类似,SENetC 维度上添加注意力,ULSAMHW 维度上添加注意力

Algorithm

网络结构

ul1.png

  • 将输入 tensor F 按照通道分为 g 组:CHW --> gGHW,$F =
    [F_1,F_2,…,F_g] $ ,每一组 FnF_n 被称为一个子空间
  • 对每个子空间 FnF_n 进行如下运算:
    • Depth-wise Conv(kernel_size = 1)
    • MaxPool2d(kernel_size = 3, stride = 1, padding = 1), 这一步可以获得感受野同时减小方差
    • Point-wise Conv(kernel_size = 1), kernels = 1
    • softmax
    • out = x + x * softmax
  • 将所有子空间的结果 concat 作为输出

公式表示

dwn=DW11(Fn)dw_n = {DW}^{1*1}(F_n)
maxpooln=maxpool33,1(dwn)maxpool_n = {maxpool}^{3*3, 1}(dw_n)
pwn=PW1(maxpooln)pw_n = {PW}^1(maxpool_n)
An=softmax(pwn)A_{n} = softmax(pw_n)
F^n=(AnFn)Fn\hat F_n = (A_n \otimes F_n) \oplus F_n
F^=concat([F^1,F^2,...,F^g])\hat F = concat([\hat F_1,\hat F_2,...,\hat F_g])

源码表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import torch
import torch.nn as nn
torch.set_default_tensor_type(torch.cuda.FloatTensor)
class SubSpace(nn.Module):
"""
Subspace class.
...
Attributes
----------
nin : int
number of input feature volume.
Methods
-------
__init__(nin)
initialize method.
forward(x)
forward pass.
"""
def __init__(self, nin):
super(SubSpace, self).__init__()
self.conv_dws = nn.Conv2d(
nin, nin, kernel_size=1, stride=1, padding=0, groups=nin
)
self.bn_dws = nn.BatchNorm2d(nin, momentum=0.9)
self.relu_dws = nn.ReLU(inplace=False)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
self.conv_point = nn.Conv2d(
nin, 1, kernel_size=1, stride=1, padding=0, groups=1
)
self.bn_point = nn.BatchNorm2d(1, momentum=0.9)
self.relu_point = nn.ReLU(inplace=False)
self.softmax = nn.Softmax(dim=2)
def forward(self, x):
out = self.conv_dws(x)
out = self.bn_dws(out)
out = self.relu_dws(out)
out = self.maxpool(x)
out = self.conv_point(out)
out = self.bn_point(out)
out = self.relu_point(out)
m, n, p, q = out.shape
out = self.softmax(out.view(m, n, -1))
out = out.view(m, n, p, q)
out = out.expand(x.shape[0], x.shape[1], x.shape[2], x.shape[3])
out = torch.mul(out, x)
out = out + x
return out

Grad-CAM++ 热力图

  • ULSAM 加入到 MobileNet v1v2 之后,模型的 focus 能力更好
    ul2.jpeg

Thoughts

  • 虽然 Flopsparams 减小或者几乎不变,但引入了很多 element-wise 运算,估计速度会慢
  • SENet 使用 sigmoid 来处理权重,而 ULSAM 使用 HW 维度上 softmax 处理权重,所以需要使用残差结构

网络表现

  • 通过控制变量实验,验证子空间数量 g 和替换位置 pos 对模型表现的影响

对比实验

ul3.png
ul4.png
ul5.png
ul6.png

URL

https://arxiv.org/pdf/1903.06586.pdf

TL;DR

  • SKNet 给 N 个不同感受野分支的 feature 通道赋予权重,结合了 Attention to channelselect kernel

SKNet网络结构

sk1.png

数学表达

XRH×W×CF~U~RH×W×CX\in\mathbb R^{H'\times W' \times C'} \overset{\tilde F}{\longrightarrow} \tilde U \in \mathbb R^{H\times W\times C}
XRH×W×CF^U^RH×W×CX\in\mathbb R^{H'\times W' \times C'} \overset{\hat F}{\longrightarrow} \hat U \in \mathbb R^{H\times W\times C}
U=U~+U^U=\tilde U + \hat U
sc=Fgp(Uc)=1H×Wi=1Hj=1WUc(i,j)s_c = F_{gp}(U_c) = \frac{1}{H\times W}\sum_{i=1}^H\sum_{j=1}^W U_c(i, j)
z=Ffc(s)=δ(β(Ws)),    WRd×C,    d=max(Cr,L)z = F_{fc}(s) = \delta(\beta (Ws)),\ \ \ \ W\in\mathbb R^{d\times C},\ \ \ \ d = max(\frac{C}{r}, L)
ac=eAczeAcz+eBcz,  bc=eBczeAcz+eBcz,    Ac,BcR1×da_c = \frac{e^{A_cz}}{e^{A_cz} + e^{B_cz}},\ \ b_c = \frac{e^{B_cz}}{e^{A_cz} + e^{B_cz}},\ \ \ \ A_c,B_c\in\mathbb R^{1\times d}
Vc=ac.U~c+bc.U^c,    VcRH×WV_c = a_c . \tilde U_c + b_c . \hat U_c,\ \ \ \ V_c\in\mathbb R^{H\times W}

SKNet实验结果

  • ImageNet
    sk2
    sk3
    sk4
  • other
    sk5

Thoughts

  • SENetSKNet 属于 Attention to channelULSAM 属于 Attention to HW,两个合起来是否可以替代 Non-local——在 THW上的 Attention

URL

https://arxiv.org/pdf/1709.01507.pdf

TL;DR

  • SENet 给每个通道赋予权重,Attention to Channel

Algorithm

se1

数学表达

zc=Fsq(uc)=1H×Wi=1Hj=1Wuc(i,j),     zRCz_c = F_{sq}(u_c) = \frac{1}{H \times W}\sum_{i=1}^H\sum_{j=1}^W u_c(i, j),\ \ \ \ \ z \in \mathbb R^C
s=Fex(z,W)=σ(g(z,W))=σ(W2δ(W1z)),   W1RCr×C,   W2RC×Crs = F_{ex}(z, W) = \sigma(g(z, W)) = \sigma(W_2\delta(W_1z)), \ \ \ W_1 \in \mathbb R^{\frac{C}{r}\times C},\ \ \ W_2 \in \mathbb R^{C \times \frac{C}{r}}
X~c=Fscale(uc,sc)=scuc,    XRC\tilde X_c = F_{scale}(u_c, s_c) = s_cu_c,\ \ \ \ X \in \mathbb R^C

SENet实验结果

  • ImageNet
    se2
    se3
  • other
    se4

Thoughts

  • SENetSKNet 属于 Attention to channelULSAM 属于 Attention to HW,两个合起来是否可以替代 Non-local——在 THW上的 Attention

URL

https://arxiv.org/pdf/2007.11823.pdf

TL;DR

  • 一种动态产生 Conv 权重的方法,统一了 SENet 和 CondConv 等动态卷积算法

Dataset/Algorithm/Model/Experiment Detail

结构

weightnet.png

  • 使用 GAP + FC + Sigmoid + Group_FC + Reshape 把 (N,Cin,H,W)(N, C_{in}, H, W) 的输入 feature map 变成 (Cout,Cin,K,K)(C_{out}, C_{in}, K, K) 的 kernel,再与 feature map 做 Conv

megengine 实现

WeightNet

Thoughts

  • 使用 Group_FC 产生 Weight 是 make sense 的,毕竟下一层 Conv 会做通道间融合
  • 比 CondConv 和 SENet 在结构上要激进不少,原理上是把 CondConv 和 SENet 对 Conv Weight 的初始化往前提到了 Group FC 中,去掉了人为设计