常见模型剪枝方法
- Low-rank Decomposition:低秩分解。将权值矩阵分解为低秩矩阵
- Weight Quantization:权值量化。HashNet 提出去量化网络的权值。在训练之前,网络的权值被hash到不同的组,并且组内的权重是共享的。这样只有共享的权重和hash indices需要被储存,可以节省很大一部分存储空间。然而,这些技术既不能节省运行时的内存,又不能节省推理时间,因为在推理期间,共享的权重需要被恢复到原来的位置(相当于还多了个解码的过程)
- Weight Pruning / Sparsifying:权重剪枝/稀疏化。即在训练网络中剪枝掉不重要的连接关系,这样的话网络中的权重大多数变成了0,可以使用一种稀疏的模式存储模型。然而,这些方法需要专门的稀疏矩阵运算库或硬件来做加速,运行时的内存占用节省非常有限,因为产生的激活值仍然是密集的。
- Structured Pruning / Sparsifying:结构剪枝/稀疏化。即对网络结构的(channels,neurons等)一部分做剪枝或稀疏化,而不是对个别权重,因此它们不太需要特别的库来实现推理加速和运行时的内存节省。我们的网络瘦身就属于这个类别,完全不需要其他的特定库支撑。
- Neural Architecture Learning:神经结构学习。最先进的模型比如VGG、GoogleNet、ResNet等通常都是由专家设计的,也有一些在自动学习网络结构方面的探索。"Neural architecture search with reinforcement learning"和"Designing neural network architectures using reinforcement learning"这两篇工作提出了使用强化学习自动学习神经网络结构,这些方法的搜索空间非常大,因此常常需要从上百个模型中辨别好坏。
稀疏化可以在不同级别实现(weight-level,kernel-level,channel-level,layer-level)。细粒度的稀疏化(weight-level)由最高的灵活性和泛化性能,也能获得更高的压缩比率,但是它通常需要特殊的软硬件加速器才能在稀疏模型上快速推理。相反,粗粒度 layer-level稀疏化不需要特殊的包做推理加速,但是它灵活性上不如细粒度。事实上,只有深度足够深(超过50层),移除某些层才会很高效。相比之下,channel-wise稀疏化在灵活性和实现上做了一个平衡,它可以被应用到任何典型的CNN或者全连接层(将每个神经元看作一个通道),由此得到的网络本质上也是一个瘦的网络,可以在卷积CNN平台上快速推理。
挑战:实现通道稀疏化需要将和一个通道有关联的所有输入和输出的连接都剪掉,但是对于已经预训练好的一个模型来说,不太可能做大这点,因此这个对已经预训练好的模型做通道剪枝效率不高,比如对预训练好的ResNet做通道剪枝,在不损伤准确率的情况下,只能减少10%的参数量。"Learning structured sparsity in deep neural networks"这篇文章中通过将稀疏正则化强加到训练目标函数中,具体来讲,就是采用group Lasso来使得所有filters(卷积核)的同一个通道在训练时同时趋于0。然而,这个方法需要额外计算新引入的和所有filters(卷积核)有关的梯度项,这是一个问题。
channel-level 的剪枝是每一个channel都引入一个缩放因子
γ
\gamma
γ,然后与通道的输出相乘。接着联合训练网络权重和这些缩放因子,最后将小缩放因子的通道直接剪除,微调剪枝后的网络。这种方法的目标函数定义为
L
=
∑
(
x
,
y
)
l
(
f
(
x
,
W
)
,
y
)
+
λ
∑
γ
∈
Γ
g
(
γ
)
L=\sum_{(x, y)} l(f(x, W), y)+\lambda \sum_{\gamma \in \Gamma} g(\gamma)
L=(x,y)∑l(f(x,W),y)+λγ∈Γ∑g(γ)
(x,y)是训练输入和目标,W是网络中可训练参数,第一项是CNN网络的训练损失函数,g(.)是在缩放因子上的惩罚项,
λ
\lambda
λ 是两项的平衡因子。在我们的实验中,我们选择g(s)=|s|,即L1-正则化,被广泛地用于稀疏化.
剪掉一个通道的本质是要剪掉所有与这个通道相关的输入和输出连接关系,我们可以直接获得一个窄的网络(Figure 1),而不需要借用任何特殊的稀疏计算包。缩放因子扮演的是通道选择的角色,因为我们缩放因子的正则项和权重损失函数联合优化,网络自动鉴别不重要的通道,然后移除掉,几乎不影响网络的泛化性能。
利用BN层的缩放因子:BN层已经被大多数现代CNN网络采用,作为一种标准的方法来使得网络快速收敛并获得更好的性能。BN归一化激活值的方法给了我们设计一种简单高效的方法的灵感,即与channel-wise缩放因子的结合。BN层使用mini-batch的统计特性对内部激活值归一化。
z
i
n
z_{i n}
zin和
z
o
u
t
z_{ out}
zout分别是BN层的输入和输出.
z
^
=
z
i
n
−
μ
B
σ
B
2
+
ϵ
;
z
out
=
γ
z
^
+
β
\hat{z}=\frac{z_{i n}-\mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^{2}+\epsilon}} ; \quad z_{\text {out}}=\gamma \hat{z}+\beta
z^=σB2+ϵzin−μB;zout=γz^+β通常的做法是在卷积层之后插入一个BN层,引入channel-wise的缩放/平移参数。因此,我们直接将BN层的
λ
\lambda
λ参数作为我们网络剪枝的缩放因子。事实上,这也可能是我们能够学习到的最有意义的做通道剪枝的缩放因子。
通道剪枝和微调:引入缩放因子正则项之后,我们得到的模型中许多缩放因子都会趋于0。然后我们剪掉接近零的缩放因子对应的通道,这里说的再清楚一点,假设经过卷积之后的feature map 的维度为
h
×
w
×
c
\mathrm{h} \times \mathrm{w} \times \mathrm{c}
h×w×c
c为通道数,将其送入BN层会得到归一化之后的特征图,c个feature map中的每一个都对应一组
λ
\lambda
λ和
γ
\gamma
γ.前面所说的剪掉小的
γ
\gamma
γ对应的通道实质上就是直接剪掉这个feature map对应的卷积核。至于什么样的
γ
\gamma
γ算小,这个取决于我们为整个网络所有层设置的一个全局阈值,它被定义为所有缩放因子值的一个比例,比如我们将剪掉整个网络中70%的通道,那么我们先对缩放因子的绝对值排个序,然后取从小到大排序的缩放因子中70%的位置的缩放因子为阈值,通过这样做,我们就可以得到一个较少参数、运行时占内存小、低计算量的紧凑网络。
Muti-pass方案:我们可以将我们提出的方法从单阶段的学习方案扩展到多阶段,what meaning?通俗来讲,就是多次进行“网络瘦身”,这样可以得到一个更紧凑的模型。
处理跨层的连接和预激活结构:上面介绍的网络瘦身过程可以直接用到大多数比较简单的CNN结构,如AlexNet、VGGNet等。但是对于有跨层连接和预激活设计的网络ResNet和DenseNet等,又应该如何使用这个方法呢?对于这些网络,每一层的输出会作为后续多个层的输入,且其BN层是在卷积层之前,在这种情况下,稀疏化是在层的输入末端得到的,一个层选择性的接受所有通道的子集去做下一步的卷积运算。为了在测试时节省参数和计算量,我们需要放置一个通道选择层鉴别出重要的通道。