Zhangzhe's Blog

The projection of my life.

0%

URL

https://arxiv.org/pdf/2009.04759.pdf

TL;DR

  • 本文将常见的激活函数分为两大类,基于 Maxout 和基于 Smooth maximum
  • 基于 Maxout 的主要是 XXXReLU 家族
  • 基于 smooth maximum 的本文命名为 activate or not 家族,著名的 Swish 在 β=1\beta=1 时就是 ACON-A

Dataset/Algorithm/Model/Experiment Detail

Smooth maximumn

  • Sβ(x1,...,xn)=i=1nxi×eβxi=1neβxS_{\beta}(x_1,...,x_n) = \frac{\sum_{i=1}^nx_i \times e^{\beta x}}{\sum_{i=1}^n e^{\beta x}} ,当 β,Sβmax\beta \rightarrow \infty, S_\beta \rightarrow max ,当 β0,Sβmean\beta \rightarrow 0, S_\beta \rightarrow mean
  • 当 n = 2 时, Sβ(ηa(x),ηb(x))=(ηa(x)ηb(x))×σ[β(ηa(x)ηb(x))]+ηb(x)S_\beta(\eta_a(x),\eta_b(x)) = (\eta_a(x)-\eta_b(x))\times\sigma[\beta(\eta_a(x)-\eta_b(x))]+\eta_b(x) ,其中: σ\sigma 表示 Sigmoid, η\eta 表示 per channle 的线性函数
    acon1.png

Meta-ACON

  • ACON 中的 β\beta 从一个 learnable parameter 变成一个 network,ACON -> Meta-ACON,这里的 network 与 SENet 中的 channel-scale 用到的两层 fc 结构相同

代码 (ACON-C 和 Meta-ACON-C)

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
import torch
from torch import nn
class AconC(nn.Module):
r"""ACON activation (activate or not).
# AconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is a learnable parameter
# according to "Activate or Not: Learning Customized Activation" <https://arxiv.org/pdf/2009.04759.pdf>.
"""
def __init__(self, width):
super().__init__()
self.p1 = nn.Parameter(torch.randn(1, width, 1, 1))
self.p2 = nn.Parameter(torch.randn(1, width, 1, 1))
self.beta = nn.Parameter(torch.ones(1, width, 1, 1))
def forward(self, x):
return (self.p1 * x - self.p2 * x) * torch.sigmoid(
self.beta * (self.p1 * x - self.p2 * x)
) + self.p2 * x
class MetaAconC(nn.Module):
r"""ACON activation (activate or not).
# MetaAconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is generated by a small network
# according to "Activate or Not: Learning Customized Activation" <https://arxiv.org/pdf/2009.04759.pdf>.
"""
def __init__(self, width, r=16):
super().__init__()
self.fc1 = nn.Conv2d(
width, max(r, width // r), kernel_size=1, stride=1, bias=True
)
self.bn1 = nn.BatchNorm2d(max(r, width // r))
self.fc2 = nn.Conv2d(
max(r, width // r), width, kernel_size=1, stride=1, bias=True
)
self.bn2 = nn.BatchNorm2d(width)
self.p1 = nn.Parameter(torch.randn(1, width, 1, 1))
self.p2 = nn.Parameter(torch.randn(1, width, 1, 1))
def forward(self, x):
beta = torch.sigmoid(
self.bn2(
self.fc2(
self.bn1(
self.fc1(
x.mean(dim=2, keepdims=True).mean(dim=3, keepdims=True)
)
)
)
)
)
return (self.p1 * x - self.p2 * x) * torch.sigmoid(
beta * (self.p1 * x - self.p2 * x)
) + self.p2 * x

效果

acon2.png

Thoughts

  • 是一种动态上下界的几乎函数,并很 general 的解释了 Smooth maximum 机制的作用

URL

https://arxiv.org/pdf/2104.13371.pdf

TL;DR

  • BasicVSRPropagationAlignment 组件进行了加强,达到了新的 SOTA
  • Propagation 的加强主要是把 BasicVSR 使用的单层双向传播结构改成了 多层 + 双向 + 二阶传播
  • Alignment 的加强主要是把 BasicVSR 使用的基于光流估计的对齐方式改成了基于光流估计指导的可变形卷积对齐,光流估计 + warp 的对齐方式似乎比可变形卷积要弱,可变形卷积单独训练容易崩,二者结合效果更好

Dataset/Algorithm/Model/Experiment Detail

Propagation

  • 加入了二阶(跳帧)传播
    1.png

Alignment

2.png
3.png
使用一个比较复杂的对齐结构(论文中对齐过程的数学表达式写的很清楚),通过 光流估计 + Warp + 残差 生成 DCN 的 offset 和 mask,再通过 DCN 对齐

最终结果

4.png

Thoughts

  • 目前还没有开源代码,但是从结构上看 BasicVSR++ 集成了 BasicVSR 的大部分结构 + IConVSR 的 Propagation 级联结构 + EDVR 的可变形卷积特征对齐
  • mmediting repo 中还是有不少可以借鉴的结构,比如 DCN bloack 这些对齐结构就很 make sense

URL

https://arxiv.org/pdf/2012.02181.pdf

TL;DR

  • 将 VSR 任务所需的网络结构拆分成四个部分,分别是 PropagationAlignmentAggregationUpsampling,并对常见的 VSR 模型进行了总结
  • 只使用常见结构没有使用奇怪操作建立了 BasicVSR 结构和针对 BasicVSR 的改进版 IconVSR,达到了当时的 SOTA
  • 代码开源在:mmediting

BasicVSR

对常见的 VSR 模型结构的总结

bvsr1.png

BasicVSR 总体结构

bvsr2.png

Propagation

  • 从重建后 PSNR 角度证明了 双向传播结构比单向传播结构和局部不传播结构更好

Alignment

  • 同样从重建后 PSNR 角度证明了 VSR 任务中,特征对齐比图像对齐和不对齐效果更好,相较于特征对齐,图像对齐重建后平均 PSNR 降低 0.17dB,不对齐重建后平均 PSNR 降低 1.19dB
  • 本文的对齐模块使用基于光流对齐的方式,即: 双向视频光流估计 + Warp feature + 残差,光流估计网络用的 SPyNet

Aggregation

  • 双向传播 + 双向对齐后的 feature 直接 concat

Upsampling

  • PixelShuffle

IconVSR

bvsr3.png

在 BasicVSR 的基础上增强了 PropagationAggregation 两个部分:

  • Aggregation 部分加入了一个 Information-Refill 结构,对关键帧额外做一次图像级的前后帧信息融合,从代码上看是用 EDVR 做前后帧信息融合
  • Propagation 是将之前的 Bidirectional Propagation 变成 Coupled Bidirectional Propagation,具体来说就是将 BasicVSR 中的独立的双向传播变成了串联的双向传播

最终结果

bvsr4.png

Thoughts

  • 将 VSR 任务的模型结构解耦,每个部分单独做 Ablation experiment,感觉作为一个 VSR 任务的 baseline 是不错的

URL

https://arxiv.org/pdf/2004.02803.pdf

TL;DR

  1. Deformable 3D 简称 D3D,是 C3DDeformable
  2. 在视频超分中引入了可变形3D卷积,看上去很合理
  3. 使用的超分网络结构很简单,与 VDSR 有一点相似

Dataset/Algorithm/Model/Experiment Detail

Algorithm

1. D3D:3D 卷积的可变形版本(或者说可变性卷积的3D版)

deform1.png
可变形2D卷积全过程(图来自论文Deformable Convolutional Networks
deform2.png
可变形3D卷积全过程 (图来自本论文)您这也太像了吧

C3D计算过程:


$y(p_0) = \sum_{n=1}^Nw(p_n)*x(p_0 + p_n)$
其中 $p_0$ 表示卷积核中心所在feature上的位置, $p_n$ 表示卷积核到中心偏移,由于是C3D所以, $N=27$ , $p_n = \{(-1,-1,-1), (-1,-1,0),...,(1,1,0),(1,1,1)\}$ ### D3D计算过程
$y(p_0) = \sum_{n=1}^Nw(p_n)*x(p_0 + p_n + \Delta p_n)$
$p_n$ 表示到卷积核到原始位置的偏移, $p_n + \Delta p_n$ 表示卷积核到中心偏移 ## 2. 超分网络结构 ![deform3.png](https://i.loli.net/2021/09/04/qNWlp6C1T4X8uw3.png) **图中’Conv‘表示C3D,3x3 Conv表示2D Conv** 输入:连续 3 / 5 / 7 frames ## Thoughts 1. 对比了C3D和D3D的效果,说明D3D还是有点用的 2. 与[EDVR](https://arxiv.org/pdf/1905.02716.pdf)对比可知,即使D3D单个算子再强大,网络设计也十分重要! 3. 速度好慢,flops好大 4. 脑补[C3D](https://ieeexplore.ieee.org/document/7410867)的[DConv](https://openaccess.thecvf.com/content_ICCV_2017/papers/Dai_Deformable_Convolutional_Networks_ICCV_2017_paper.pdf)作者会由衷的说一句:优(jiu)秀(zhe?) ## 3. 算法效果对比 ![deform4.png](https://i.loli.net/2021/09/04/ZBLQHNac3ixsXuk.png) ![deform5.png](https://i.loli.net/2021/09/04/rvE5inczPky7uTQ.png) 输入帧数与算法效果的关系: ![deform6.png](https://i.loli.net/2021/09/04/WUAOTtvZhLYzDXu.png) ![deform7.png](https://i.loli.net/2021/09/04/g9zBL8kYeuIWR5U.png) ![deform8.png](https://i.loli.net/2021/09/04/9uJvpgMiqDU1WHR.png) 为什么不和[paperswithcode](https://paperswithcode.com/sota/video-super-resolution-on-vid4-4x-upscaling)里的VSR模型比一比? **Vid4数据集上:** ![deform9.png](https://i.loli.net/2021/09/04/jJkKzqVPmxDH5hA.png) 论文中对比结果巧妙的避开了前五(截图于 2020-08-01),~~优秀~~

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 时:
    Forward_v1: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,2w_bits][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
    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
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ϵ(x)={qix(ti,ti+1]0xϵQ_{\epsilon}(x)=\left\{\begin{array}{ll}{q_{i}^{\prime}} & {x \in(t_{i}^{\prime}, t_{i+1}^{\prime}]} \\ {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 有相似之处