3、剪枝粒度与策略:细粒度剪枝、向量剪枝、通道剪枝、层剪枝的对比

聊到剪枝,很多人第一反应就是「把不重要的权重砍掉」。但具体怎么砍、砍到什么程度,这里面的门道可深了。我刚开始做模型压缩那会儿,也踩过不少坑——比如一股脑儿用细粒度剪枝,结果模型是变小了,但推理速度一点没快,你说气不气人?

说白了,剪枝的粒度决定了你的收益天花板。咱们今天就把四种主流粒度掰开揉碎了讲清楚。

细粒度剪枝:最精细,但最不实用

细粒度剪枝,也叫非结构化剪枝。它直接对单个权重下手——觉得哪个参数不重要,就把它置零。

举个例子,一个 4×4 的卷积核,剪完后可能变成这样:

原始权重:
[[0.8, 0.1, 0.6, 0.3],
 [0.2, 0.9, 0.4, 0.7],
 [0.5, 0.3, 0.8, 0.2],
 [0.1, 0.6, 0.4, 0.9]]

剪枝后(50%稀疏度):
[[0.8, 0.0, 0.6, 0.0],
 [0.0, 0.9, 0.0, 0.7],
 [0.5, 0.0, 0.8, 0.0],
 [0.0, 0.6, 0.0, 0.9]]

你看,权重变成了稀疏矩阵。理论上压缩率很高,但实际部署时问题就来了——硬件对非结构化稀疏的支持很差。我在RK3588上试过,稀疏度80%的模型,推理速度反而比原始模型慢了30%。为什么?因为CPU和GPU的缓存机制被破坏了,零值和非零值交错存储,根本没法批量跳过。

⚠️ 避坑指南: 我曾经在NVIDIA Jetson上部署过一个细粒度剪枝的YOLOv5模型,稀疏度做到90%,模型文件小了80%。结果一跑推理,帧率从60fps掉到了12fps。后来查资料才发现,TensorRT对非结构化稀疏的支持直到最近几个版本才勉强能用,而且只针对特定架构。

所以我的建议是:除非你的目标硬件明确支持稀疏计算(比如某些专用NPU),否则别碰细粒度剪枝。它更适合学术研究,不适合工程落地。

向量剪枝:折中的选择

向量剪枝,也叫结构化剪枝的初级版。它不再砍单个权重,而是以「向量」为单位——比如卷积核的一行或一列。

你想想看,一个 3×3 的卷积核,如果整行都不重要,直接干掉这一行,剩下的结构还是规整的矩阵。这对硬件就友好多了。

原始卷积核(3×3):
[[w11, w12, w13],
 [w21, w22, w23],
 [w31, w32, w33]]

向量剪枝(剪掉第2行):
[[w11, w12, w13],
 [w31, w32, w33]]

嗯,这里要注意:向量剪枝后,矩阵的维度变了。比如原本 3×3 变成 2×3,后续的矩阵乘法需要重新适配。我在项目中遇到过这种情况——剪完枝后,下一层的输入通道数对不上,还得手动调整网络结构。

向量剪枝的压缩率介于细粒度和通道剪枝之间。我个人习惯用它来做中间层的微调——比如某个层剪掉20%的向量,精度损失控制在1%以内,推理速度能提升15%左右。性价比还不错。

通道剪枝:工程界的首选

通道剪枝,说白了就是直接砍掉整个通道(也就是整个特征图)。这是目前工业界最常用的方法,没有之一。

为什么?因为通道剪枝后的模型结构是完全规整的——你砍掉一个通道,对应的卷积核也砍掉一层,整个网络还是标准的矩阵运算。硬件跑起来毫无压力。

剪枝粒度 结构规整性 硬件友好度 压缩率 精度损失
细粒度 极差
向量剪枝
通道剪枝 中高
层剪枝 最好 最好

通道剪枝的核心在于如何评估通道的重要性。常用的方法有:

  • 基于权重大小:L1/L2范数小的通道直接砍掉。简单粗暴,但效果还行。
  • 基于激活值:统计每个通道的输出激活值,零值多的通道说明贡献小。
  • 基于BN层参数:利用Batch Normalization的缩放因子γ,γ小的通道可以剪掉。这是目前最主流的方法。
💡 实战技巧: 我在做MobileNetV3的通道剪枝时,发现BN层的γ值分布特别有意思——大部分通道的γ集中在0.1以下,只有少数通道的γ大于1。这时候设置一个阈值(比如0.5),直接剪掉γ小于阈值的通道,精度几乎不掉。

层剪枝:最暴力,但最有效

层剪枝,就是直接删掉整个网络层。比如ResNet有50层,你觉得后面20层没啥用,直接砍掉。

听起来很粗暴对吧?但有时候它确实管用。我记得有一次做语音识别模型的部署,原始模型有12层Transformer,我试着剪掉最后2层,精度只掉了0.3%,但推理速度提升了20%。

层剪枝的难点在于:你很难判断哪些层是冗余的。我常用的方法是:

  1. 逐层计算「贡献度」——把某一层的输出替换为恒等映射,看精度变化。
  2. 如果精度下降小于0.5%,说明这层可有可无。
  3. 从后往前剪,因为深层网络的特征更抽象,冗余度更高。
⚠️ 注意: 层剪枝不适合所有网络。像MobileNet这种轻量级网络,每层都很重要,剪掉一层可能直接崩掉。我建议只在超过50层的深度网络上尝试层剪枝。

四种策略怎么选?

说实话,没有银弹。我一般按这个思路来选:

  • 目标硬件是GPU/CPU:首选通道剪枝,次选向量剪枝。别碰细粒度。
  • 目标硬件是专用NPU:先查文档,看它支持哪种稀疏格式。有些NPU支持2:4结构化稀疏,这时候细粒度剪枝反而香。
  • 模型特别深(100层+):先做层剪枝,砍掉明显冗余的层,再做通道剪枝微调。
  • 精度要求极高:用细粒度剪枝做微调,但只做最后阶段的压缩,别一开始就用。

最后说一句:剪枝不是一锤子买卖。我习惯的做法是「迭代式剪枝」——每次剪掉10%的参数,然后微调恢复精度,再剪10%,直到精度开始明显下降。这样比一次性剪到位稳定得多。

📌 我的经验: 剪枝后的微调学习率要调小,一般是原始训练学习率的1/10。我曾经用原始学习率微调剪枝后的模型,结果loss直接飞了,白白浪费了两天时间。