Zhangzhe's Blog

The projection of my life.

0%

URL

https://arxiv.org/pdf/2007.00649.pdf

TL;DR

  • 传统的集成学习能提高算法效果,但是深度学习多 model 集成学习实在是太慢了

  • 所以作者提出一种深度学习多 group 集成学习,本质是在网络的末尾处使用 分组卷积,分成多路,每一路独立监督,监督得到的 loss 加权求和 作为总 loss,然后反向传播、优化,优点是:理论上不产生额外计算量

  • 加权求和的方式一共有三种,分别是 Group AveragingGroup WaggingGroup Boosting

Algorithm

Group Ensemble 与 Model Ensemble 的区别

group10.png
group1.png

网络结构

网络结构

加权方式

  • Loss=1i=1mWiLiLoss = \frac{1}{\sum_{i=1}^m}W_i*L_iLiL_i 表示第 i 个分支上的损失, WiW_i 表示对应权重

  • Group Averaging

    • Wi=1,     i=1,2,...,mW_i = 1, \ \ \ \ \ i = 1, 2, ..., m
  • Group Wagging

    • WiN(1,δ2),     i=1,2,...,mW_i \sim N(1, \delta^2), \ \ \ \ \ i = 1, 2, ..., m
  • Group Boosting

    • Wi=log(Pi1T),     i=2,3,...,mW_i = -log(\frac{P_{i-1}}{T}), \ \ \ \ \ i = 2, 3, ..., m

      • 其中 T 表示 温度,类似于模型蒸馏时的蒸馏温度

      • Pi1P_{i-1} 表示上一个分支算对的概率

      • 即上一个分支预测的越正确,本分支的权重就越小,这与 boosting 还真的有点像

group2.png

最终效果

  • ImageNet 数据集上,top-1 errorResNeXt-50 降低了 1.83%

  • 几乎不改变 Flopsparams,在几乎所有网络上使用都会有明显提升

  • 在多种任务中都能提高成绩,包括:

    • 目标检测
    • 动作识别
    • 图像识别

Thoughts

  • Group Boosting 这种串行计算 loss 权重的方法确定在 inference 阶段不会影响速度?

  • 为什么在 CIFAR 数据集上,Group Wagging 这种随机方法反而效果好

对比表格

group3.png

group4.png

group5.png

group6.png

group8.png

group7.png

group9.png

URL

https://arxiv.org/pdf/1606.06160.pdf

TL;DR

  • DoReFa-Net是一种神经网络量化方法,包括对weights、activations和gradients进行量化

  • 量化模型的位宽重要性:gradient_bits > activation_bits > weight_bits

Algorithm

算法与实现角度理解:

总体流程

dorefa

quantization of weights (v1) (上图 fωWf_\omega^W )

  • STE (straight-through estimator)

    一种对本身不可微函数(例如四舍五入函数round()、符号函数sign()等)的手动指定微分的方法,量化网络离不开STE

  • 当1 == w_bits时:

    Forward:ro=sign(ri)×EF(ri)Forward: r_o = sign(r_i) \times E_F(|r_i|)

    Backward:cri=croBackward: \frac{\partial c}{\partial r_i} = \frac{\partial c}{\partial r_o}

    其中 EF(ri)E_F(|r_i|) 表示weight每层通道的绝对值均值

  • 当1 < w_bits 时:

    Forwardv1:ro=2quantizek(tanh(ri)2max(tanh(ri))+12)1Forward\\_v1: r_o = 2 quantize_k(\frac{tanh(r_i)}{2max(|tanh(r_i)|)} + \frac{1}{2}) - 1

    Backward:cri=crororiBackward: \frac{\partial c}{\partial r_i} = \frac{\partial c}{\partial r_o} \frac{\partial r_o}{\partial r_i}

    其中 quantize_k()函数是一个量化函数,使用类似四舍五入的方法,将 [0,1,232][0, 1, 2^{32}] 之间离散的数聚类到 [0,1,2wbits][0, 1, 2^{w\\_bits}]

    • quantize_k():

      $ Forward: r_o = \frac{1}{2k-1}round((2k-1)r_i)$

      Backward:cri=croBackward: \frac{\partial c}{\partial r_i} = \frac{\partial c}{\partial r_o}

  • quantization of weights 源码

    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
    def uniform_quantize(k):
    class qfn(torch.autograd.Function):

    @staticmethod
    def forward(ctx, input):
    if k == 32:
    out = input
    elif k == 1:
    out = torch.sign(input)
    else:
    n = float(2 ** k - 1)
    out = torch.round(input * n) / n
    return out

    @staticmethod
    def backward(ctx, grad_output):
    # STE (do nothing in backward)
    grad_input = grad_output.clone()
    return grad_input

    return qfn().apply


    class weight_quantize_fn(nn.Module):
    def __init__(self, w_bit):
    super(weight_quantize_fn, self).__init__()
    assert w_bit <= 8 or w_bit == 32
    self.w_bit = w_bit
    self.uniform_q = uniform_quantize(k=w_bit)

    def forward(self, x):
    if self.w_bit == 32:
    weight_q = x
    elif self.w_bit == 1:
    E = torch.mean(torch.abs(x)).detach()
    weight_q = self.uniform_q(x / E) * E
    else:
    weight = torch.tanh(x)
    max_w = torch.max(torch.abs(weight)).detach()
    weight = weight / 2 / max_w + 0.5
    weight_q = max_w * (2 * self.uniform_q(weight) - 1)
    return weight_q

quantization of activations (上图 fαAf_\alpha^A )

  • 对每层输出量化

    $ f^A_\alpha = quantize_k®$

quantization of gradients (上图 fγGf_\gamma^G )

  • 对梯度量化

    fγk(dr)=2max0(dr)[quantizek[dr2max0(dr)+12+N(k)]12]f^k_\gamma(dr)=2max_0(|dr|)[quantize_k[\frac{dr}{2max_0(|dr|)} + \frac{1}{2} + N(k)] - \frac{1}{2}]

    其中:$ dr $ 表示backward上游回传的梯度, kk 表示 gradient_bits, N(k)N(k) 表示随机均匀噪声N(k)=σ2k1,   σUniform(0.5,0.5)N(k) = \frac{\sigma}{2^k-1},\ \ \ \sigma \sim Uniform(-0.5, 0.5)

  • 由于模型端上inference阶段不需要梯度信息,而大多数模型也不会在端上训练,再加上低比特梯度训练会影响模型精度,所以对梯度的量化大多数情况下并不会使用

其他方面

  • 由于最后一层的输出分布与模型中输出的分布不同,所以为了保证精度,不对模型输出层output做量化(上图step 5, 6)

  • 由于模型第一层的输入与中间层输入分布不同,而且输入通道数较小,weight不量化代价小,所以 第一层的weight做量化

  • 使用多步融合,不保存中间结果会降低模型运行是所需内存

Paper Reading Note

URL

https://arxiv.org/pdf/1912.13200.pdf

TL;DR

  1. 使用-L1距离代替了Conv

  2. 因为-L1距离永远<=0,所以还是保留了bn,另外在梯度回传的时候加了HardTanh

  3. 实际训练很慢,收敛需要的epoch很多

Algorithm

模式匹配方式

  • 卷积本质上是一种模式匹配方式,但是模式匹配方式不是只有卷积,L1距离同样可以衡量模式的相似程度

  • L1距离只使用加法,理论速度比卷积快

卷积的数学表达(输入h*w*c)

  • Y(m,n,t)=i=0dj=0dk=0cinS(X(m+i,n+j,k),F(i,j,k,t))Y(m, n, t) = \sum_{i=0}^d\sum_{j=0}^d\sum_{k=0}^{c_{in}} S(X(m+i,n+j,k) , F(i,j,k,t))

    其中: S(x,y)=x×yS(x, y) = x \times y

-L1距离的数学表达

  • $Y(m, n, t) = -\sum_{i=0}d\sum_{j=0}d\sum_{k=0}^{c_{in}} |X(m+i,n+j,k) - F(i,j,k,t) | $

求导

  • 卷积是乘法和加法,乘法是交换门,也就是说输出对卷积核的梯度可以表示为:

    • Y(m,n,t)F(i,j,k,t)=X(m+i,n+j,k)\frac{\partial Y(m,n,t)}{\partial F(i,j,k,t)} = X(m+i,n+j,k)

    • Y(m,n,t)X(m+i,n+j,k)=F(i,j,k,t)\frac{\partial Y(m,n,t)}{\partial X(m+i,n+j,k)} = F(i,j,k,t)

  • -L1距离求导,由于绝对值函数梯度是分段函数,所以:

    • $ \frac{\partial Y(m,n,t)}{\partial F(i,j,k,t)} = sng(X(m+i,n+j,k) - F(i,j,k,t))$

    • $ \frac{\partial Y(m,n,t)}{\partial X(m+i,n+j,k)} = sng(F(i,j,k,t) - X(m+i,n+j,k))$

    其中: sng(x)={1,x<00,x=01,x>0sng(x) = \left\{\begin{matrix} -1, & x < 0\\ 0, & x = 0\\ 1, & x > 0 \end{matrix}\right.

优化

  • 分段函数求导麻烦,所以输出对卷积核的梯度,直接去掉符号函数,

    $ \frac{\partial Y(m,n,t)}{\partial F(i,j,k,t)} = X(m+i,n+j,k) - F(i,j,k,t)$ ,简单粗暴

  • 对于输出对feature的导数,论文中没有直接去掉符号函数,而是将符号函数换成了HardTanh函数,这个函数用在梯度上的时候还有一个大家熟悉的名字——梯度截断

    HardTanh(x)={1,x>1x,1<x<11,x<1HardTanh(x) = \left\{\begin{matrix} 1, & x > 1\\ x, & -1 < x < 1\\ -1, & x < -1 \end{matrix}\right.

    xx 表示梯度

  • 自此:反向传播时用到的梯度已经变形为:

    $ \frac{\partial Y(m,n,t)}{\partial F(i,j,k,t)} = X(m+i,n+j,k) - F(i,j,k,t)$

    $ \frac{\partial Y(m,n,t)}{\partial X(m+i,n+j,k)} = HardTanh(F(i,j,k,t) - X(m+i,n+j,k))$

  • 学习率:论文中详细的讲解了这个自适应学习率是如何推导出来的,如果想了解建议去看原文,这里直接说结论:

    αl=ηKΔ(Fl)2\alpha_l = \frac{\eta \sqrt K}{||\Delta(F_l)||_2}

    (论文第二版中这个 K\sqrt K 在分母位置,看了源码,还是以第三版为准)

    其中, KK 表示卷积核滤波器中元素的个数, ...2||...||_2 表示矩阵的F范数

优化部分一堆公式不够直观,直接上源码

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
class adder(Function):
@staticmethod
def forward(ctx, W_col, X_col):
ctx.save_for_backward(W_col, X_col)
# -L1距离,其中x_col是使用img2col处理过的
output = -(W_col.unsqueeze(2) - X_col.unsqueeze(0)).abs().sum(1)
return output

@staticmethod
def backward(ctx, grad_output):
W_col, X_col = ctx.saved_tensors
# 对卷积核的梯度,直接去掉了符号函数sgn
grad_W_col = (
(X_col.unsqueeze(0) - W_col.unsqueeze(2)) * grad_output.unsqueeze(1)
).sum(2)
# 这里是乘上推导出的自适应学习率,\eta=0.2
grad_W_col = (
grad_W_col
/ grad_W_col.norm(p=2).clamp(min=1e-12)
* math.sqrt(W_col.size(1) * W_col.size(0))
/ 5
)
# 梯度截断,也就是所谓的HardTanh
grad_X_col = (
-(X_col.unsqueeze(0) - W_col.unsqueeze(2)).clamp(-1, 1)
* grad_output.unsqueeze(1)
).sum(0)

return grad_W_col, grad_X_col

实际运行

  • 真的慢,可能是由于img2col还有求L1距离都是在python中做的,所以训练速度真的好慢啊!

  • 真的难收敛,官方源码中,使用resnet20(conv -> L1)训练CIFAR10,设置的默认epoch次数为400,训练CIFAR10都需要400 epoch

  • 真的不稳定,不像传统Conv网络随着训练次数,网络准确率逐步提高,或者在极小范围内抖动,AdderNet训练过程中,epoch 23结束后在val上准确率可能55%,第epoch 24结束后,val上的准确率直接到了28%,而且不是因为学习率太大导致的,抖动范围真的大…

  • 最终结果:AdderNet-ResNet20CIFAR-10epoch-400 最高正确率:91.67%

Thoughts

  • 虽然这个网络目前问题还很多,而且作者的理论很糙,有些地方简单粗暴,但是这也许是神经网络加速的一种思路

与CNN的对比

add1.png

add2.png

add3.png

add4.png

URL

https://arxiv.org/pdf/1611.10176

TL;DR

  • 提出一种基于DoReFa-Net的RNN量化方式

Algorithm

Dropout

  • 由于 RNN 本质是由多个 FC 组成,所以需要使用 Dropout 将一部分元素随机置0,但在量化神经网络中,0不在 Qk(X+0.5)0.5Q_k(X + 0.5)-0.5 ,所以需要将原始 DoReFa-Net 的量化范围: [0.5,0.5][0,1][-0.5, 0.5]\to [0, 1]

quantization of word embedding weights

  • 初始化

    WUniform(0,1)W\in Uniform(0, 1)

  • quantization

    W=Clip(W,0,1)W = Clip(W, 0, 1)

quantization of GRU

  • standard GRU

    zt=σ(Wz[ht1,xt])z_t = \sigma(W_z \cdot [h_{t-1},x_t])

    rt=σ(Wr[ht1,xt])r_t = \sigma(W_r \cdot [h_{t-1},x_t])

    h~t=tanh(W[rt×ht1,xt])\tilde h_{t}=tanh(W\cdot [r_t \times h_{t-1},x_t])

    $ h_t = (1-z_t)\times h_{t-1}+z_t\times \tilde{h_t}$

    其中:” \cdot “ 表示 matmul,” ×\times “ 表示 hadamard productσ\sigma 表示 sigmoid

  • quantization of GRU

    zt=σ(Wz[ht1,xt])z_t = \sigma(W_z \cdot [h_{t-1},x_t])

    rt=σ(Wr[ht1,xt])r_t = \sigma(W_r \cdot [h_{t-1},x_t])

    h~t=σ(W[Qk(rt×ht1),xt])\tilde h_{t}=\sigma(W\cdot [Q_k(r_t \times h_{t-1}), x_t])

    $ h_t = Q_k((1-z_t)\times h_{t-1}+z_t\times \tilde{h_t})$

    • 改进:
      • tanhsigmoid
      • W,Wz,WrW, W_z, W_r quantize 到 [-1, 1]
      • xtx_t quantize 到 [0, 1]
      • 量化函数 Qk()Q_k()

quantization of LSTM

  • standard LSTM

    ft=σ(Wf[ht1,xt]+bf)f_t = \sigma(W_f\cdot [h_{t-1},x_t]+b_f)

    it=σ(Wi[ht1,xt]+bi)i_t = \sigma(W_i\cdot [h_{t-1},x_t]+b_i)

    Ct~=tanh(WC[ht1,xt]+bi)\tilde{C_t} = tanh(W_C\cdot [h_{t-1},x_t]+b_i)

    Ct=ft×Ct1+it×Ct~C_t=f_t\times C_{t-1}+i_t\times \tilde{C_t}

    ot=σ(Wo[ht1,xt]+bo)o_t = \sigma(W_o\cdot [h_{t-1},x_t]+b_o)

    ht=ot×tanh(Ct)h_t=o_t \times tanh(C_t)

  • quantization of LSTM

    ft=σ(Wf[ht1,xt]+bf)f_t = \sigma(W_f\cdot [h_{t-1},x_t]+b_f)

    it=σ(Wi[ht1,xt]+bi)i_t = \sigma(W_i\cdot [h_{t-1},x_t]+b_i)

    Ct~=tanh(WC[ht1,xt]+bi)\tilde{C_t} = tanh(W_C\cdot [h_{t-1},x_t]+b_i)

    Ct=ft×Ct1+it×Ct~C_t=f_t\times C_{t-1}+i_t\times \tilde{C_t}

    ot=σ(Wo[ht1,xt]+bo)o_t = \sigma(W_o\cdot [h_{t-1},x_t]+b_o)

    ht=Qk(ot×σ(Ct))h_t=Q_k(o_t \times \sigma(C_t))

    • 改进:
      • tanhsigmoid
      • W,Wz,WrW, W_z, W_r quantize[-1, 1]
      • xtx_t quantize 到 [0, 1]
      • 量化函数 Qk()Q_k()

Thoughts

  • 目前芯片上都是将所有的线性操作吸到卷积的 affine_kaffine_b,所以需要解决 tanhsigmoid,一种最简单暴力的方式是分别 clip 到 [-1, 1][0, 1]

URL

https://openaccess.thecvf.com/content_cvpr_2017/papers/Cai_Deep_Learning_With_CVPR_2017_paper.pdf

TL;DR

  • 量化网络的反向传播通常使用STE,造成前后向结果与梯度不匹配,所以通常掉点严重
  • 前向过程中:HWGQ网络使用半波高斯分布量化函数Q代替sign函数
  • 反向过程中:HWGQ网络使用ReLU及其变体代替HardTanh来近似前向量化过程的梯度

Algorithm

  • weight binarization

    I×Wα(IB)I \times W \approx \alpha (I \oplus B)

    其中, BB 表示二值化权重, II 表示输入, αR+\alpha \in \mathbb{R^+} 表示缩放系数, \oplus 表示无系数卷积

  • 常用 activations 量化

    • 常用二值化特征量化函数:sign(x) = 1 if x >=0 else -1
    • 常用二值量化函数的梯度:hardTanh(x) = 1 if abs(x) <= 1 else 0
  • HWGQ activations 量化

    • 前向过程:

      Q(x)={qi,    if  x(ti,ti+1]0,     if  x0Q(x) = \begin{cases} q_i, \ \ \ \ if\ \ x\in (t_i, t_{i+1}] \\ 0, \ \ \ \ \ if \ \ x\le 0 \end{cases}

      qi+1qi=Δ,    iq_{i+1} - q_i = \Delta,\ \ \ \ \forall i

      Δ\Delta 为一个常数的时候, qiq_i 为均匀分布,但 tit_i 不是均匀分布

      其中超参数 Q(x)=argminQEx[(Q(x)x)2]=argminQp(x)(Q(x)x)2dxQ^{\star}(x) = arg\min_Q E_x[(Q(x) - x)^2] = arg\min_Q \int p(x) (Q(x) - x)^2 dx,其中 p(x)p(x) 表示半波高斯分布的概率密度

      • 看了源码发现:
        • 当 f_bits=2 时, q={0.538,1.076,1.614},   t={,0.807,1.345,+}q = \{0.538, 1.076, 1.614\},\ \ \ t=\{-\infty, 0.807, 1.345, +\infty\}
        • 当 f_bits=1 时, $q = {0.453, 1.51},\ \ \ t={-\infty, 0.97, +\infty} $
    • 反向过程:

      • 反向过程有三种方式,分别为:

        • ReLU

          Q~(x)={1,    if  x>00,    otherwise\tilde Q(x) = \begin{cases}1,\ \ \ \ if\ \ x > 0\\0,\ \ \ \ otherwise \end{cases}

        • Clipped ReLU

          Q~(x)={qm,    x>qm1,     if  x(0,qm]0,     otherwise\tilde Q(x) = \begin{cases}q_m,\ \ \ \ x>q_m\\1,\ \ \ \ \ if\ \ x \in (0, q_m]\\0,\ \ \ \ \ otherwise \end{cases}

          源码中,f_bits=2时, qm=1.614q_m = 1.614 ,f_bits=1时, qm=1.515q_m = 1.515

        • Log-tailed ReLU

          Q~(x)={1xτ,    x>qm1,     if  x(0,qm]0,     otherwise\tilde Q(x) = \begin{cases}\frac{1}{x-\tau},\ \ \ \ x>q_m\\1,\ \ \ \ \ if\ \ x \in (0, q_m]\\0,\ \ \ \ \ otherwise \end{cases}

      • 实际发现Clipped ReLU效果最好,复杂度低

Thoughts

  • 算法想法很本质,目的是减小STE带来的前向与反向的不一致

  • 没与DoReFa-Net的效果对比,当比特数较多的时候,round()与使用半波高斯近似那个更好?

图表

deep1.png
deep2.png
deep3.png

URL

http://openaccess.thecvf.com/content_cvpr_2018/papers/Wang_Two-Step_Quantization_for_CVPR_2018_paper.pdf

TL;DR

  • 对于 weight 的量化与对 activation 的量化如果同时学习,模型收敛比较困难,所以分成 code learningtransformation function learning 两个过程进行
  • code learning :先保持 weight 全精度,量化 activation
  • transformation function learning:量化 weight,学习 Al1AlA_{l-1} \to A_l 的映射
  • 最终结果:2-bits activations + 3 值 weights 的 TSQ 只比官方全精度模型准确率低0.5个百分点

Algorithm

传统量化网络

  • 优化过程

    minimize{Wl}   L(ZL,y)minimize_{\{W_l\}}\ \ \ \mathcal L(Z_L, y)

    subject to           W^l=QW(Wl)subject\ to \ \ \ \ \ \ \ \ \ \ \ \hat W_l = Q_W(W_l)

                                Zl^=W^lA^l1\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \hat {Z_l} = \hat W_l \hat A_{l-1}

                                Al=ψ(Zl)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ A_l = \psi (Z_l)

                                A^l=Qn(A),   forl=1,2,...,L\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \hat A_l = Q_n(A),\ \ \ for l=1,2,...,L

  • 难收敛的原因

    • 由于对 QW()Q_W()μLW\mu\frac{\partial L}{\partial W} 难以直接更新到 W^\hat W ,导致 WW 更新缓慢
    • QA()Q_A() 的 STE 会引起梯度高方差

Two-Step Quantization (TSQ)

  • step 1:code learning

    基于 HWGQ,不同点是:

    • weights 保持全精度

    • 引入超参数:稀疏阈值 ϵ0\epsilon \ge 0开源代码ϵ=0.32,δ=0.6487\epsilon = 0.32, \delta = 0.6487

      $ Q_{\epsilon}(x)=\left{\begin{array}{ll}{q_{i}^{\prime}} & {x \in\left(t_{i}^{\prime}, t_{i+1}^{\prime}\right]} \ {0} & {x \leq \epsilon}\end{array}\right.$

    • 引入超参数的目的是使得网络更关注于 high activations

  • step 2:transformation function learning

    我对这个步骤的理解是:使用全精度weights网络蒸馏low-bits weights网络

    minimizeΛ,W^YQϵ(ΛW^X)F2=minimize{αi},{w^iT}iyiTQϵ(αiw^iTX)22\begin{aligned} \underset{\Lambda, \hat{W}}{\operatorname{minimize}} \left\|Y-Q_{\epsilon}(\Lambda \hat{W} X)\right\|_{F}^{2} = \operatorname{minimize}_{\left\{\alpha_{i}\right\},\left\{\hat{w}_{i}^{T}\right\}} \sum_{i}\left\|y_{i}^{T}-Q_{\epsilon}\left(\alpha_{i} \hat{w}_{i}^{T} X\right)\right\|_{2}^{2} \end{aligned}

    其中: αi\alpha_i 表示每个卷积核的缩放因子,XXYY 分别表示 A^l1\hat A_{l-1}A^l\hat A_l用全精度weights得到的量化activations网络蒸馏量化weights量化activations网络

    引入辅助变量 zz 对 transformation function learning 进行分解:

    minimizeα,w,zyQϵ(z)22+λzαXTw^22\underset{\alpha, w, z}{\operatorname{minimize}} \quad\left\|y-Q_{\epsilon}(z)\right\|_{2}^{2}+\lambda\left\|z-\alpha X^{T} \hat{w}\right\|_{2}^{2}

    • Solving α\alpha and w^\hat{w} with zz fixed:

      minimizeα,w^J(α,w^)=zαXTw^22\underset{\alpha, \hat{w}}{\operatorname{minimize}} \quad J(\alpha, \hat{w})=\left\|z-\alpha X^{T} \hat{w}\right\|_{2}^{2}

      J(α,w^)=zTz2αzTXTw^+α2w^TXXTw^J(\alpha, \hat{w})=z^{T} z-2 \alpha z^{T} X^{T} \hat{w}+\alpha^{2} \hat{w}^{T} X X^{T} \hat{w}

      α=zTXTw^w^TXXTw^\alpha^{*}=\frac{z^{T} X^{T} \hat{w}}{\hat{w}^{T} X X^{T} \hat{w}}

      w^=argmaxw^(zTXTw^)2w^TXXTw^\hat{w}^{*}=\underset{\hat{w}}{\operatorname{argmax}} \frac{\left(z^{T} X^{T} \hat{w}\right)^{2}}{\hat{w}^{T} X X^{T} \hat{w}}

    • Solving zz with α\alpha and w^\hat{w} fixed:

      minimizezi(yiQϵ(zi))2+λ(zivi)2\underset{z_{i}}{\operatorname{minimize}} \quad\left(y_{i}-Q_{\epsilon}\left(z_{i}\right)\right)^{2}+\lambda\left(z_{i}-v_{i}\right)^{2}

      czi(0)=min(0,vi){c}{z_{i}^{(0)}=\min \left(0, v_{i}\right)}

      zi(1)=min(M,max(0,λvi+yi1+λ){z_{i}^{(1)}=\min \left(M, \max \left(0, \frac{\lambda v_{i}+y_{i}}{1+\lambda}\right)\right.}

      zi(2)=max(M,vi){z_{i}^{(2)}=\max \left(M, v_{i}\right)}

      • 使用 Optimal TernaryWeights Approximation (OTWA) 初始化 α\alphaW^\hat W

        minα,w^ wαw^22      subject to    α>0,    w^{1,0,+1}m\min_{\alpha, \hat{w}} \ {\|w-\alpha \hat{w}\|_{2}^{2}}\ \ \ \ \ \ subject\ to\ \ \ \ \alpha>0, \ \ \ \ {\hat{w} \in\{-1,0,+1\}^{m}}

        α=wTw^w^Tw^\alpha^{*} =\frac{w^{T} \hat{w}}{\hat{w}^{T} \hat{w}}

        w^=argmaxw^(wTw^)2w^Tw^\hat{w}^{*} =\underset{\hat{w}}{\operatorname{argmax}} \frac{\left(w^{T} \hat{w}\right)^{2}}{\hat{w}^{T} \hat{w}}

        w^j={sign(wj)abs(wj) in top r of abs(w)0 others \hat{w}_{j}=\left\{\begin{array}{ll}{\operatorname{sign}\left(w_{j}\right)} & {\operatorname{abs}\left(w_{j}\right) \text { in top } r \text { of } a b s(w)} \\ {0} & {\text { others }}\end{array}\right.

      • α\alphaw^\hat{w} 初始值的计算过程 (OTWA)

        初始值的计算过程

Thoughts

  • 对 weights 的量化与 activations 的量化拆分是一个容易想到的简化量化问题的方法
  • 把对 weights 的量化转换成一种自蒸馏的方法,与 量化位宽 decay 有相似之处

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
53
54
55
56
57
58
59
60
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)
)