剪枝方向文献笔记:
写在笔记之前的话
博主是一名刚刚进入大三的本科生,正在做一项关于模型压缩的科研项目。之前一直是在ipad在notebility上做笔记,但实在无法忍受自己丑陋的书写和繁琐的latex公式插入,于是尝试用csdn自带的markdown编辑器进行笔记记录。
以公开博客形式记录笔记一是分享知识,二是希望各位读者(希望有读者吧哈哈哈哈哈)能够指出笔记中的错误,相互探讨。
文章1:Dynamic Channel Pruning: Feature Boosting and Suppression
摘要:
问题:想要提升深度卷积神经网络的精度,势必会引起计算量和内存的增加。
基于事实:本文利用卷积核输出的特征图的重要性是高度依赖于输入数据的。
解决措施:feature boosting and suppression(FBS),预测下一层卷积通道的显著性,在程序运行时跳过不显著的通道。不像静态剪枝一样彻底剪掉不重要的通道,动态剪枝是保留所有通道,并在程序运行过程中动态跳过不重要的通道。另外,用了FBS方法的网络仍然是用传统的SGD进行训练,所以FBS适配于各种sota CNN模型。
得到结果:在VGG16上减小五倍计算量,在ResNet18上减小两倍计算量,并在top-5 accuracy上只损失了0.6%的精度。
存在的问题:
基于显著性的(静态)剪枝有三个主要问题:
- 删除的通道是永久消失的,对于一些数据较为复杂的输入(difficult input)无法达到很好的精度。因为可靠的通道已经被永久剪掉了。
- 需要精心设计需要剪枝的通道,不然无法有效减少计算资源。
- 神经元的重要性(或者说 显著性)并不是静态的,而且神经元的重要性很大程度上依赖于输入数据。(这也是本文最重要的假设,立足点)
下面这张图就展示了这个“假设”,每个channel neuron的输出是非常不同的。图a、b下面的数字代表加上激活函数之前channel中的最大值。图c则是前20个channel激活后的最大值的分布。(虽然我觉得这个证据有点不大合适…)
这里提到了channel neuron,也没做太多解释,我猜测就是某个卷积核的某个channel。
同样,动态剪枝也存在一些问题:
- 之前有方法通过强化学习来实现动态剪枝,但在训练过程中要消耗非常多的运算资源。
- 很多动态剪枝的方法都是通过强化学习的方式来实现的,因为其中的 on/off decisions 就是”阀门的开关“,是不可微的,梯度下降法在这里是用不了的。(文章也提到了一些可微的方法)
具体实现:
就是动态剪枝。在网络工作时,考虑前一层卷积层的稀疏性,只“使能”其中的一部分通道,忽略相关性比较小的通道。
本文提出的方法为FBS,feature boosting and suppression,动态地增强或抑制卷积层的输出通道。直观上可以理解为图像数据的信息流通过一个”阀门“来控制。阀门的开关是通过上一层的特征图来预测的。
动态剪枝部署的公式很简单:
f
^
(
x
,
⋯
)
=
f
(
x
,
θ
,
⋯
)
⋅
π
(
x
,
ϕ
,
⋯
)
\hat{f}(\mathrm{x}, \cdots)=f(\mathrm{x}, \theta, \cdots) \cdot \pi(\mathrm{x}, \phi, \cdots)
f^(x,⋯)=f(x,θ,⋯)⋅π(x,ϕ,⋯)其中
f
(
x
,
θ
,
⋯
)
f(\mathrm{x}, \theta, \cdots)
f(x,θ,⋯)为卷积操作,
x
\mathrm{x}
x为input data,
θ
\theta
θ和
ϕ
\phi
ϕ为权重,
π
(
x
,
ϕ
,
⋯
)
\pi(\mathrm{x}, \phi, \cdots)
π(x,ϕ,⋯)就类似于一个mask,为0的地方就相当于被剪掉的地方。动态剪枝的主要核心指出在于对
π
(
x
,
ϕ
,
⋯
)
\pi(\mathrm{x}, \phi, \cdots)
π(x,ϕ,⋯)的设计。本文的mask并不是施加在features或是filters的参数上,而是加在BN上的。 最终的卷积公式如下:
f
^
l
(
x
l
−
1
)
=
(
π
l
(
x
l
−
1
)
⋅
(
norm
(
conv
l
(
x
l
−
1
,
θ
l
)
)
+
β
l
)
)
+
\hat{f}_{l}\left(\mathrm{x}_{l-1}\right)=\left(\pi_{l}\left(\mathrm{x}_{l-1}\right) \cdot\left(\operatorname{norm}\left(\operatorname{conv}_{l}\left(\mathrm{x}_{l-1}, \theta_{l}\right)\right)+\boldsymbol{\beta}_{l}\right)\right)_{+}
f^l(xl−1)=(πl(xl−1)⋅(norm(convl(xl−1,θl))+βl))+
其中,
π
l
(
x
l
−
1
)
=
wta
⌈
d
C
l
⌉
(
g
l
(
x
l
−
1
)
)
\pi_{l}\left(\mathrm{x}_{l-1}\right)=\operatorname{wta}_{\left\lceil d C_{l}\right\rceil}\left(g_{l}\left(\mathrm{x}_{l-1}\right)\right)
πl(xl−1)=wta⌈dCl⌉(gl(xl−1))
wta
⌈
d
C
l
⌉
(
z
)
\operatorname{wta}_{\left\lceil d C_{l}\right\rceil} \left(\mathrm{z}\right)
wta⌈dCl⌉(z)函数返回的是
z
\mathrm{z}
z 的前
⌈
d
C
l
⌉
\lceil dC_l\rceil
⌈dCl⌉大的“比较列表”,前
⌈
d
C
l
⌉
\lceil dC_l\rceil
⌈dCl⌉大的为1,其余为0。它的计算量很小,相比起卷积运算可以忽略。
d
∈
[
0
,
1
]
d \in [0,1]
d∈[0,1]体现了速度和精度之间的权衡关系。但是,上面这个公式里面又引出了一个新函数,
g
l
(
x
l
−
1
)
g_{l}\left(\mathrm{x}_{l-1}\right)
gl(xl−1)。这个公式很简单,由以下两个公式构成:
ss
(
x
l
−
1
)
=
1
H
W
[
s
(
x
l
−
1
[
1
]
)
s
(
x
l
−
1
[
2
]
)
⋯
s
(
x
l
−
1
[
C
]
)
]
\operatorname{ss}\left(\mathbf{x}_{l-1}\right)=\frac{1}{H W}\left[\mathrm{~s}\left(\mathbf{x}_{l-1}^{[1]}\right) s\left(\mathbf{x}_{l-1}^{[2]}\right) \cdots \mathrm{s}\left(\mathbf{x}_{l-1}^{[C]}\right)\right]
ss(xl−1)=HW1[ s(xl−1[1])s(xl−1[2])⋯s(xl−1[C])]
g
l
(
x
l
−
1
)
=
(
ss
(
x
l
−
1
)
ϕ
l
+
ρ
l
)
+
g_{l}\left(\mathrm{x}_{l-1}\right)=\left(\operatorname{ss}\left(\mathrm{x}_{l-1}\right) \phi_{l}+\rho_{l}\right)_{+}
gl(xl−1)=(ss(xl−1)ϕl+ρl)+
第一个公式表示将一个
R
C
×
H
×
W
\mathbb{R}^{C \times H \times W}
RC×H×W的feature map通过全局池化压缩成一个
R
C
\mathbb{R}^{C}
RC的向量。第二个公式就是再将其做一些线性变换。总而言之,上面三个式子就是把每个channel给全局池化了,然后得到的值大的channel被保存下来,值小的被删掉。(这不是扯淡吗…还能这么比较啊,比较rank也比这么暴力的比法要靠谱啊…)
本文中,训练的lasso项也就是:
λ
∑
l
=
1
L
E
x
[
∥
g
l
(
x
l
−
1
)
∥
1
]
\lambda \sum_{l=1}^{L} \mathbb{E}_{\mathbf{x}}\left[\left\|g_{l}\left(\mathbf{x}_{l-1}\right)\right\|_{1}\right]
λ∑l=1LEx[∥gl(xl−1)∥1]
个人想法:
疑惑不只一点点。文中提到好多东西都是不理解的,如3.2节的第二段,讲到了很多现有方法的缺陷,3.3的第一节,讲了把mask放在BN的好处,都是没有理解的部分。
另外,最最最疑惑的一点是(不知道是不是自己理解错了),它居然直接把这个input feature map的全局池化后的值,当作重要性评判标准,这不是扯淡吗? 如果一个feature map全是被点亮的,那是不是说明它就非常重要?但讲道理它应该是完全没包含任何信息啊…还有感觉这个方法如果真这么简单的话为什么之前没人想到啊,思路简单,训练计算量又少,获得的性能还是sota…
文章2:Runtime Neural Pruning
摘要:
提出了一个新的简直框架——Runtime Neural Pruning (RNP),利用马尔可夫决策过程和强化学习来实现动态剪枝。
RNP中包含两个子网络,主CNN网络和决策网络。通过决策网络输出的Q值来对主CNN网络中的卷积核进行动态剪枝。
本文最核心的一个公式:
min
K
i
+
1
,
h
E
F
i
[
L
c
l
s
(
c
o
n
v
(
F
i
,
K
[
h
(
F
i
)
]
)
)
+
L
p
n
t
(
h
(
F
i
)
)
]
\min _{K_{i+1},h}\mathbb{E}_{F_{i}}\left[ L_{cls}\left( conv\left( F_i,K\left[ h\left( F_i\right) \right] \right) \right) +L_{pnt}\left( h\left( F_{i}\right) \right) \right]
Ki+1,hminEFi[Lcls(conv(Fi,K[h(Fi)]))+Lpnt(h(Fi))]
其中,
L
c
l
s
L_{cls}
Lcls是分类任务的损失函数;
L
p
n
t
L_{pnt}
Lpnt是代表速度和精度tradeoff的惩罚项;
h
(
F
i
)
h(F_i)
h(Fi)是条件剪枝单元(conditional pruning unit),提供根据输入特征图得到的需要剪枝的卷积核的索引列表;
K
[
⋅
]
K[·]
K[⋅]是索引操作;
c
o
n
v
(
x
1
,
x
2
)
conv(x1,x2)
conv(x1,x2)是输入特征图
x
1
x_1
x1和卷积核
x
2
x_2
x2的卷积操作。
存在的问题:
如果将它看成一个简单的优化问题,那它的算法复杂度为
O
(
∏
i
=
1
m
n
m
)
\mathcal{O}(\prod^{m}_{i=1} n_m)
O(∏i=1mnm),
n
m
n_m
nm是第
m
m
m层的channel数。这是一个np-hard问题,(是不是就是遍历?)如果网络过深,像vgg或是ResNet,是解不出来的。为了解决这个问题,本文提出了两个主要创新点:
1. 将主CNN的剪枝视为马尔可夫决策过程,利用强化学习训练决策网络。
2. 重新定义剪枝行为,来减少决策的数量。
具体实现:
决策网络主要是由:编码器-RNN-译码器,这样的结构组成。而整体实现过程,是通过layers-by-layers的顺序来的。
State:
利用全局池化,将一个
F
i
F_i
Fi的特征图浓缩为一个
p
F
i
p_{F_i}
pFi的向量,它的长度为
F
i
F_i
Fi的通道数
n
i
n_i
ni。因为不同layer的通道数是不一样的,为了把它转化为一个定长的码,本文使用了编码器
E
(
p
F
i
)
E\left(p_{F_i}\right)
E(pFi)来将他们转化为一个定长的编码(这个编码器是一个全连接层)。
另外,编码器以一个自底向上的方式与RNN链接在一起,它们产生了一个隐藏编码
R
(
E
(
p
F
i
)
)
R\left(E\left(p_{F_i}\right)\right)
R(E(pFi)),该编码就是强化学习的state information,可以为解码器(也是一个全连接层)算出
Q
Q
Q值。
Action:
每一个剪枝的action被定义在一个逐步递增的方式中进行。(这句话怎么翻译都不大准确,原文是:The actions for each pruning are defined in a incremental way)
将输出的特征图分为
k
k
k组,从
F
1
′
F^{\prime}_1
F1′ 到
F
k
′
F^{\prime}_k
Fk′ ,如果
k
k
k 为
n
i
n_i
ni,就相当于没分组。action
a
i
a_i
ai,定义为计算特征图组
F
1
′
F^{\prime}_1
F1′,
F
2
′
F^{\prime}_2
F2′,…,
F
i
′
F^{\prime}_i
Fi′的收益。(
i
=
1
,
2
,
.
.
.
,
k
i=1,2,...,k
i=1,2,...,k) 另外,因为
F
1
′
F^{\prime}_1
F1′总是被计算的,所以我们认为
F
1
′
F^{\prime}_1
F1′是“基组”。因为我们没有第一层卷积层的输入特征图,所以第一层不被纳入剪枝范围内,所以一共有
m
−
1
m-1
m−1个actions需要执行。
Reward:
第 t t t 步,action a i a_i ai的Reward被定义为:
r
t
(
a
i
)
=
{
−
α
L
c
l
s
+
(
i
−
1
)
×
p
,
i
f
i
n
f
e
r
e
n
c
e
t
e
r
m
i
n
a
t
e
s
(
t
=
m
−
1
)
(
i
−
1
)
×
p
,
o
t
h
e
r
w
i
s
e
(
t
<
m
−
1
)
r_{t}\left( a_{i}\right) =\begin{cases}-\alpha L_{cls}+\left( i-1\right) \times p, \quad if \; inference \; terminates\;\left( t=m-1\right) \\ \left( i-1\right) \times p,\quad otherwise\left( t<m-1\right) \end{cases}
rt(ai)={−αLcls+(i−1)×p,ifinferenceterminates(t=m−1)(i−1)×p,otherwise(t<m−1)
其中,
p
p
p是需要手动设置的惩罚系数。用损失函数乘上一个系数
−
α
-\alpha
−α 来当作奖励,当模型训练得越好,损失函数就越小,奖励值也就越大。对于
t
=
1
,
2
,
.
.
.
m
−
2
t = 1,2,...m-2
t=1,2,...m−2来说,
r
t
(
a
i
)
r_{t}\left( a_{i}\right)
rt(ai)仅仅是关于计算量的函数,因为这些中间层根本没有什么损失函数可言,当
t
=
m
−
1
t=m-1
t=m−1,即最后一层时,才会加上损失函数。
马尔可夫决策过程最重要的一点是找到每一个state的最优的action,也就是说找到一个最优的policy。本文利用Q-learning的方法,定义
Q
(
a
i
,
s
t
)
Q(a_i,s_t)
Q(ai,st)为采取action
a
i
a_i
ai在state
s
t
s_t
st的价值期望。所以policy定义为
π
=
arg max
a
i
Q
(
a
i
,
s
t
)
\pi = \argmax_{a_i} Q(a_i,s_t)
π=aiargmaxQ(ai,st). 因此,行动价值函数可以被写为:
Q
(
s
t
,
a
i
)
=
max
π
E
[
r
t
+
γ
r
t
+
1
+
γ
2
r
t
+
1
+
.
.
.
∣
π
]
Q(s_t,a_i)=\max_{\pi} \mathbb{E}\left[r_t+\gamma r_{t+1}+\gamma ^{2}r_{t+1}+...|\pi\right]
Q(st,ai)=πmaxE[rt+γrt+1+γ2rt+1+...∣π]
另外,本文使用决策网络来近似估计
Q
Q
Q值为
Q
∗
Q^*
Q∗,因为所有解码器都共享参数并输出一个长度为
k
k
k的向量,每个向量都代表着
Q
∗
Q^*
Q∗的值。(本质上就是用RNN来代替一Q-table,但Deep Q-learning也没看懂…用RNN估计是为了用来强调layer和layer之间的关系。)
至此,前面的那个np-hard的优化问题就简化成为了:
min
θ
L
r
e
=
E
[
r
(
s
t
,
a
i
)
+
γ
max
a
i
Q
(
s
t
+
1
,
a
i
)
−
Q
(
s
t
,
a
i
)
]
2
\min _{\theta}L_{re} = \mathbb{E}\left[ r(s_t,a_i) + \gamma\max_{a_i}Q(s_{t+1},a_i)-Q(s_t,a_i)\right]^2
θminLre=E[r(st,ai)+γaimaxQ(st+1,ai)−Q(st,ai)]2
这个优化问题利用MSE作为判断标准(为了和决策网络统一)。其中,
θ
\theta
θ 为决策网络的权重。为了训练得到
θ
\theta
θ,本文采取了
ξ
−
g
r
e
e
d
y
\xi-greedy
ξ−greedy算法来选择需要采取的action。
具体算法:
以迭代的方式进行训练。先将主CNN网络随机剪枝后训练当成初始化过程;再将主CNN网络作为环境,训练决策网;再对主CNN进行微调。如此反复。
实验效果和分析:
本文还做了一个直观的小实验。三分类问题:男性面孔、女性面孔、背景样本。直觉上来说,分辨男女性面孔的任务相比起分辨背景更加困难,RNP理应对面孔的图像分类上分配更多资源。最后实验结果也验证了这一想法,再保证百分之九十以上准确率的情况下,RNP在人脸图像分辨的任务上花的计算量比分辨背景的计算量大得多。
最终,在ImageNet上用VGG-16,效果如下图,加速十倍还是能够获得很好的效果:
个人想法:
因为暑期一个线上项目是关于强化学习的,自己做的学校项目又是剪枝,就看了一下利用强化学习剪枝的方法。这是我看的第1.5篇利用强化学习剪枝的文献,第0.5篇是韩松老师的AMC,但当时没看懂,想先看看其他利用强化学习的剪枝方法,就看到了这篇。但看的过程还是非常艰难,一边看一边查。之前学习过马尔科夫链,以为本文用的方法就是马尔科夫链,然而强化学习的马尔可夫决策过程和马尔科夫链简直天差地别。通过其他人的博客了解了马尔科夫决策过程的基础,才稍微了解了这篇文章的大体想法。另外,Deep Q-learning的方法也没学透,如果以后真的要用到强化学习还要好好学一下。
但个人还是对一些步骤有些不解和怀疑的,比如本文(还有下一篇文章FBS的也是)
Q
∗
\sout{Q^*}
Q∗来近似
Q
\sout{Q}
Q的方法还有那个层层考虑贪婪的算法是不是靠谱(因为我直觉感觉对神经网络进行贪婪地层层剪枝必定会落入一个很垃圾的局部最优)?都是值得以后学习到更多知识后回头再来看看的。
另外,这篇文章的动态剪枝方法只适用于卷积层,是对卷积层的层层剪枝,并没有涉及到全连接层的剪枝,文章中的实验部分对VGG16甚至是直接抛弃掉了一层全连接层。