Back to Mvision

pruning 剪枝

CNN/Deep_Compression/pruning/readme.md

latest12.6 KB
Original Source

pruning 剪枝

Caffe-Python-Tutorial/prune.py 剪枝 网络参数统计可视化

深度神经网络剪枝

TensorFlow 剪枝代码 参考

博客

综合记录

剪枝(pruning) 在训练结束后,可以将一些不重要的神经元连接

结构化Pruning,Filter Pruning,梯度Pruning等方法

(可用权重数值大小衡量配合损失函数中的稀疏约束)或整个滤波器去除,
之后进行若干轮微调。实际运行中,神经元连接级别的剪枝会
使结果变得稀疏,
不利于缓存优化和内存访问,有的需要专门设计配套的运行库。
相比之下,滤波器级别的剪枝可直接运行在现有的运行库下,
而滤波器级别的剪枝的关键是如何衡量滤波器的重要程度。
例如,可用卷积结果的稀疏程度、该滤波器对损失函数的影响、
或卷积结果对下一层结果的影响来衡量。

特别地,由于计算稀疏矩阵在CPU和GPU上都有特定的方法,所以前向计算也需要对一些部分进行代码修改。
GPU上计算稀疏需要调用cuSPARSE库,
而CPU上计算稀疏需要mkl_sparse之类的库去优化稀疏矩阵的计算,
否则达不到加速效果.

剪枝方法基本流程如下:
    1. 正常流程训练一个神经网络。以CAFFE为例,就是普普通通地训练出一个caffemodel。
    2. 确定一个需要剪枝的层,一般为全连接层,设定一个裁剪阈值或者比例。
        实现上,通过修改代码加入一个与参数矩阵尺寸一致的mask矩阵。
        mask矩阵中只有0和1,实际上是用于重新训练的网络。
    3. 重新训练微调,参数在计算的时候先乘以该mask,则mask位为1的参数值将继续训练通过BP调整,
       而mask位为0的部分因为输出始终为0则不对后续部分产生影响。
    4. 输出模型参数储存的时候,因为有大量的稀疏,所以需要重新定义储存的数据结构,
       仅储存非零值以及其矩阵位置。重新读取模型参数的时候,就可以还原矩阵。
       

Lecun老爷子的OBD可以将网络中不重要的参数剔除

模型的裁剪方法则比较简单明了,直接在原有的模型上剔除掉不重要的filter,
虽然这种压缩方式比较粗糙,但是神经网络的自适应能力很强,
加上大的模型往往冗余比较多,将一些参数剔除之后,
通过一些retraining的手段可以将由剔除参数而降低的性能恢复回来,
因此只需要挑选一种合适的裁剪手段以及retraining方式,
就能够有效的在已有模型的基础上对其进行很大程度的压缩,是目前使用最普遍的方法。


基于模型裁剪的方法: 

       对以训练好的模型进行裁剪的方法,是目前模型压缩中使用最多的方法,
       通常是寻找一种有效的评判手段,来判断参数的重要性,
       将不重要的connection或者filter进行裁剪来减少模型的冗余。

a. 阈值剪裁**

基于模型裁剪的方法

pruning可以分为三步: 
step1. 正常训练模型得到网络权值; 
step2. 将所有低于一定阈值的权值设为0; 
step3. 重新训练网络中剩下的非零权值。 

截止滤波后的稀疏矩阵:

0很多,直接存储矩阵太浪费,采用CSR 存储方式
记录非零值 以及非零值的 索引,

CSR可以将原始矩阵表达为三部分,即AA,JA,IC 

AA是矩阵A中所有非零元素,长度为a,即非零元素个数; 
JA是矩阵A中每行第一个非零元素在AA中的位置,最后一个元素是非零元素数加1,长度为n+1, n是矩阵A的行数;  
IC是AA中每个元素对应的列号,长度为a。 
所以将一个稀疏矩阵转为CSR表示,需要的空间为2*a+n+1个,同理CSC也是类似。 

caffe-python 剪枝 实例

b.基于量级的裁剪方式 相关论文

基于量级的裁剪方式,用weight值的大小来评判其重要性,
对于一个filter,其中所有weight的绝对值求和,后通过排序来区分重要性。
来作为该filter的评价指标,
将一层中值低的filter裁掉,
可以有效的降低模型的复杂度并且不会给模型的性能带来很大的损失.
https://arxiv.org/pdf/1608.08710.pdf

作者在裁剪的时候同样会考虑每一层对裁剪的敏感程度,作者会单独裁剪每一层来看裁剪后的准确率。
对于裁剪较敏感的层,作者使用更小的裁剪力度,或者跳过这些层不进行裁剪。
目前这种方法是实现起来较为简单的,并且也是非常有效的,
它的思路非常简单,就是认为参数越小则越不重要。

c.统计filter中激活为0的值的数量作为标准 论文参考

作者认为,在大型的深度学习网络中,大部分的神经元的激活都是趋向于零的,
而这些激活为0的神经元是冗余的,将它们剔除可以大大降低模型的大小和运算量,
而不会对模型的性能造成影响,于是作者定义了一个量APoZ(Average Percentage of Zeros)
来衡量每一个filter中激活为0的值的数量,来作为评价一个filter是否重要的标准。

d.基于熵值的剪裁 论文参考

作者认为通过weight值的大小很难判定filter的重要性,
通过这个来裁剪的话有可能裁掉一些有用的filter。
因此作者提出了一种基于熵值的裁剪方式,利用熵值来判定filter的重要性。 
作者将每一层的输出通过一个Global average Pooling,
将feature map 转换为一个长度为c(filter数量)的向量,
对于n张图像可以得到一个n*c的矩阵,
对于每一个filter,将它分为m个bin,统计每个bin的概率pi,
然后计算它的熵值,利用熵值来判定filter的重要性,再对不重要的filter进行裁剪。
第j个feature map熵值的计算方式如下: 

Hj = -sum(pi*log(pi))

在retrain中,作者使用了这样的策略,即每裁剪完一层,通过少数几个迭代来恢复部分的性能,
当所有层都裁剪完之后,再通过较多的迭代来恢复整体的性能,
作者提出,在每一层裁剪过后只使用很少的训练步骤来恢复性能,
能够有效的避免模型进入到局部最优。

e.基于能量效率的裁剪方式 论文参考

作者认为以往的裁剪方法,都没有考虑到模型的带宽以及能量的消耗,
因此无法从能量利用率上最大限度的裁剪模型,因此提出了一种基于能量效率的裁剪方式。
作者指出一个模型中的能量消耗包含两个部分,一部分是计算的能耗,一部分是数据转移的能耗,
在作者之前的一片论文中(与NVIDIA合作,Eyeriss),提出了一种估计硬件能耗的工具,
能够对模型的每一层计算它们的能量消耗。然后将每一层的能量消耗从大到小排序,
对能耗大的层优先进行裁剪,这样能够最大限度的降低模型的能耗,对于需要裁剪的层,
根据weight的大小来选择不重要的进行裁剪,同样的作者也考虑到不正确的裁剪,
因此将裁剪后模型损失最大的weight保留下来。 

每裁剪完一层后,对于该层进行locally的fine-tune,locally的fine-tune,
是在每一层的filter上,使用最小二乘优化的方法来使裁剪后的filter
调整到使得输出与原始输出尽可能的接近。在所有层都裁剪完毕后,
再通过一个global的finetuning来恢复整体的性能。

f.遗传算法思想,随机剪裁,选择效果好的 论文参考

作者认为,既然我无法直观上的判定filter的重要性,
那么就采取一种随机裁剪的方式,然后对于每一种随机方式统计模型的性能,来确定局部最优的裁剪方式。 
这种随机裁剪方式类似于一个随机mask,假设有M个潜在的可裁剪weight,那么一共就有2^M个随机mask。
假设裁剪比例为a,那么每层就会随机选取ML*a个filter,一共随机选取N组组合,
然后对于这N组组合,统计裁剪掉它们之后模型的性能,
然后选取性能最高的那组作为局部最优的裁剪方式。

g.基于icc组内相关来衡量filter的重要性 论文参考

作者发现,在最后一个卷积层中,经过LDA分析发现对于每一个类别,
有很多filter之间的激活是高度不相关的,
因此可以利用这点来剔除大量的只具有少量信息的filter而不影响模型的性能。 
作者在VGG-16上进行实验,VGG-16的conv5_3具有512个filter,
将每一个filter的输出值中的最大值定义为该filter的fire score,
因此对应于每一张图片就具有一个512维的fire向量,当输入一堆图片时,
就可以得到一个N*512的fire矩阵,作者用intra-class correlation来衡量filter的重要性: 

作者这样做的目的是通过只保留对分类任务提取特征判别性最强的filter,来降低模型的冗余。

h.基于神经元激活相关性的重要性判别方法 论文参考

作者认为,如果一层中的某个神经元的激活与上一层的某个神经元的激活有很强的相关性,
那么这个神经元对于后面层的激活具有很强的判别性。
也就是说,如果前后两层中的某对神经元的激活具有较高的相关性,
那么它们之间的连接weight就是非常重要的,而弱的相关性则代表低的重要性。
如果某个神经元可以视为某个特定视觉模式的探测器,那么与它正相关的神经元也提供了这个视觉模式的信息,
而与它负相关的神经元则帮助减少误报。作者还认为,那些相关性很低的神经元对,
它们之间的连接不一定是一点用也没有,它们可能是对于高相关性神经元对的补充。

i.将裁剪问题当做一个组合优化问题 论文参考

作者将裁剪问题当做一个组合优化问题:从众多的权重参数中选择一个最优的组合B,使得被裁剪的模型的代价函数的损失最小.
这类似于Oracle pruning的方式,即通过将每一个weight单独的剔除后看模型损失函数的衰减,
将衰减最少的参数认为是不重要的参数,可以剔除,这也是OBD的思路,但是OBD的方法需要求二阶导数,
实现起来难度较大,而本文提出的Taylor expansion的方法可以很好的解决这个问题.

j. 一种基于Hessian矩阵的网络修剪算法
论文参考

参考博文

OBS算法是一种基于Hessian矩阵的网络修剪算法,首先,构造误差曲面的一个局部模型,分析权值的扰动所造成的影响。 

   通过对误差函数进行Taylor展开 可以得到 与二阶导数 海塞矩阵H相关的一个式子

1.OBS算法的全称为optimal brain surgeon,翻译成中文就是最优外科手术,表面的意思就是该方法是和神经网络过程是分开的。 
2.该方法是一种框架,只要是模型能求出参数的梯度,那么都可用这个方法进行稀疏化。