极智AI | 谈谈模型剪枝之通道剪枝策略

1,223 阅读9分钟

欢迎关注我的公众号 [极智视界],获取我的更多笔记分享

  大家好,我是极智视界。本文主要聊一聊模型剪枝之通道剪枝相关实践分享。

  模型剪枝是模型推理加速的重要实现技术手段,模型剪枝往往期望达到不怎么掉精度的同时又能使模型更加轻量化。

  下面咱们开始。

1 剪枝思想

  这里使用通道剪枝策略,主要是对那些不会影响最终输入维度和输出维度的模块进行通道剪枝,同时为了适应深度学习某些层对输入维度具有严格限制的特性,这里的策略是对那些具有前后维度处理的层不进行通道剪枝。

1.1 主要思想

  基于 YOLOV3 模型的结构特点,有大量的 卷积层 和 BN层 进行直连,利用 BN层 的 weights 系数来判断对应通道的重要性,移除掉不重要通道,从而可以减少前面一层卷积层的参数;若后面也是卷积层,那么也将减少后面那层卷积的参数。这里讨论的通道剪枝的基本模块是一个 CBL块,其网络结构示意图如下:

1.2 BN层 选择策略

  深度学习网络中有着各种基于维度处理的层,有的层对输入维度进行了严格的使用规范。比如 shortcut(深度学习中一种防止信息丢失或者梯度消失的结构) 这类的层,这类层往往包含了多个输入,当对其中一个输入的模块进行通道剪枝,而其他的输入的模块不进行维度配合,那么shortcut就会出现维度不匹配的问题。因此,简化剪枝流程,不对所有的 CBL模块进行剪枝操作,只对那些不需要进行维度配合的 CBL模块 进行剪枝。在 YOLOV3 模型中,不进行剪枝操作的 CBL模块主要是如下的情况:

1.3 举例说明

  以一个简单的例子解释一下,剪枝到某个 BN层 的通道对卷积核的参数有影响。因为一个 BN层 的某个通道被剪枝后,必然会影响前后的两个卷积层的卷积核参数。如下所示,假设前面卷积层的卷积核大小为 3x3,输入为 2通道,输出为 4通道;后面的卷积层的卷积核大小为 3x3,输入到 4通道,输出为 2通道,那么对 BN层 的第2个通道剪枝,剪枝的情况将会如下图所示,其中绿表示要被剪枝的通道,红表示对应通道剪去后需移除的核参数:

2 剪枝流程图

  通道剪枝流程图如下:

2.1 正常训练和稀疏训练

2.1.1 正常训练

  具体的训练结果和曲线,其中左边的二行三列曲线表示的是加了损失增益的对应损失,如下:

  正常训练前的BN层的权重分布情况,总共112层BN层,服从一个在以0.749为中心的正态分布,如下:

  正常训练过程中BN层的weigths的总体分布随着训练次数迭代情况如下:

  正常训练后的BN层的权重分布情况如下:

2.1.2 稀疏训练

  稀疏训练的目的是让学习到的大部分网络权重靠近零,因为深度学习网络中权重跟输入的关系都是相乘相加的关系,如果网络模型中的大部分权重都是零值或者靠近零值,移除掉这部分对最终的网络输出精度影响小。

  稀疏训练不止用于剪枝中,是一种通用的模型压缩方法,因为即使不进行剪枝,对于与零值的运算在速度上更加快速和在存储上都会更加轻量。稀疏训练使用的原理是:使用L1范数把数非零权重个数的操作作为损失函数,权重的L1范数越大,表示越多的非零权重,将该L1损失组合之前的目标检测任务的损失变成最终的损失函数。总之,就是利用了L1范数损失的置零效应,增加约束,从而稀疏化参量。其损失函数表达式如下:

  在程序中并不是直接使用这个组合的损失,而是在需要进行稀疏训练的权重的梯度上进行累加 L1范数 的梯度,代码如下:

for idx in prune_idx:
    bn_module = module_list[idx][1]
    bn_module.weight.grad.data.add_(s * torch.sign(bn_module.weight.data))  # L1

  稀疏训练的曲线图如下:

  稀疏训练前 BN层 的 weights分布 情况如下:

  稀疏训练过程中 1-52次 迭代,BN层 的weigths总体分布变化情况如下:

  稀疏训练过程中 52-120次 迭代,BN层 的weigths总体分布变化情况如下:

  稀疏训练后 BN层 的权重分布情况如下:

3 基于时间序列预测的提前通道剪枝

  通过稀疏训练和正常训练的各项参数对比,我们知道了稀疏训练和正常训练两者各有自己的优缺点。迭代次数一致时,两者训练时间基本是相同;正常训练有着在剪枝前有着不错的精度性能,但是一旦遭遇剪枝,那么就会损失大部分的精度性能,需要经过多次的微调才能够达到可接受的精度。相比之下,稀疏训练能够做到直接通道剪枝而只是牺牲了一点精度损失,但是训练精度不高。通过比较两者的优缺点,意识到是否能进行通道剪枝后能够在更少地微调以及有着合适的精度。

  通过查看稀疏训练中 BN层 的weights分布情况随着训练次数的迭代而进行有规律的变化,而且随着迭代次数增加,最后的分布情况是趋向于稳定的。这表示 BN层权重的总体分布,跟迭代次数是成时间序列的依赖关系。

  种种现象表明,提前剪枝或许是可行的。如果在训练早期预测了 BN层 的weights的未来分布情况,就可以提前剪掉不重要的通道,然后经过一定的次数的迭代应该可以获得较好性能指标,能够在正常训练和稀疏训练中取得平衡。

3.1 设计思路和实验

  为了能够预测到 BN层 的weights的在后面的训练过程中的数值,需要把训练迭代次数当作时间,而 weights 的数值是一个随着迭代次数变化的时间序列。提前剪枝的目的是为了能够一次剪枝然后,经过后续的训练迭代取得不错的精度性能,同时也能减少了训练的时间成本。使用稀疏训练来得到前期weights变化情况,依次作为预测的数据依赖。但在剪枝后,为了提升精度,就需要使用正常训练,这样能够让剪枝后的模型没有 L1范数 约束,且精度相较于稀疏训练后的剪枝更加高。

3.1.1设计流程图

  基于时间序列预测的提前通道剪枝的流程图如下:

3.1.2 关键点设计

  整个设计方案中,关键在于如何用时序预测模型来进行权重预测。在这个方案中,关于时间序列预测模型的建立,需要考虑这两个方面:BN层 的weights预测值之间是否有着相互关系,是否是统计概念上的不相关;我们应该选择怎样的时间序列预测算法来预测,未来的预测值是否有一个不错的分布让通道剪枝有效。

  一般而言,该网络的 BN层 的weights之间不可能是统计不相关,采用统计相关的时间序列预测模型,该预测模型基本是带有学习参数的,就 YOLOV3 模型而言,可剪枝的 BN层 的weights个数有13376个,即使是使用最简单的线性预测,也必须学习一个行列数分别是 13376 和 13376 x (n>=1) 的矩阵,这对于正在进行训练的程序而言可能会造成硬件资源不够的现象。另外还有一个难点在于,时间序列预测模型需带参训练,但时间序列样本数太少,预测模型预测结果说服力不强。

  所以在验证该方案时,对 BN层 的weights数据的假设是统计不相关,这样就可以单独对每个 weights 进行预测。且这样,时间序列预测模块对于硬件资源的占用也就不会过高。

  基于时间成本,时间序列预测模块使用了简单移动平均法进行实验,具体的参数配置如下:

3.1.3 实验结果

  损失对比图,其中最上面粉色的为稀疏训练,中间绿色的为提前通道剪枝训练,下面蓝色的是正常训练:

  精度对比图如下,其中下面粉色的为稀疏训练,中间绿色的为提前通道剪枝训练,上面蓝色的是正常训练:

  结果分析:通过进行提前通道剪枝的做法,我们平衡了精度和模型参量规模,不仅加速训练过程,同时实现了一次性剪枝的精度高于单纯的稀疏正则化方法。最终达到的性能指标如下所示:

  如上数据表明,基于时间序列预测的提前通道剪枝能够实现预测通道的效果是非常好的。这种方法只需要一次剪枝,剪枝后的正常训练相当于进行了微调,不但降低了时间成本,而且相对于稀疏训练后剪枝不微调的精度结果更加好。


  好了,以上分享了模型剪枝之通道剪枝相关策略和实践,希望我的分享能对你的学习有一点帮助。


 【公众号传送】

《【模型推理】谈谈模型剪枝之通道剪枝策略》


logo_show.gif