3. 剪枝粒度与策略:细粒度、向量、通道、层剪枝
剪枝这活儿,说白了就是给模型“瘦身”。但瘦身也有不同玩法——你是想剪掉几根头发,还是直接砍掉一条胳膊?不同的剪枝粒度,带来的效果和代价天差地别。今天我就把这四种主流策略掰开揉碎了讲,顺便聊聊我在项目里踩过的坑。
3.1 细粒度剪枝:最精细的“微雕”
细粒度剪枝,也叫非结构化剪枝。它针对的是单个权重参数。只要某个权重值接近0,或者对输出贡献很小,就把它置为0。
优点:
- 压缩率极高。理论上你可以剪掉90%甚至更多的参数,模型精度下降可控。
- 灵活性最大。每个权重独立判断,想剪哪就剪哪。
缺点:
- 硬件不友好。剪完后权重矩阵变得稀疏且不规则,普通CPU/GPU没法加速。你需要专门的稀疏矩阵计算库,比如NVIDIA的cuSPARSE。
- 实际推理速度提升有限。我试过在Jetson Nano上跑细粒度剪枝后的模型,理论计算量少了80%,但实际帧率只提升了不到20%。为什么?因为稀疏矩阵的访存模式太乱了,缓存命中率低得可怜。
我的经验:细粒度剪枝适合学术研究或存储受限的场景。如果你想把模型部署到嵌入式设备上跑实时推理,我建议你慎重。我曾经在一个语音唤醒项目里用了细粒度剪枝,模型文件确实小了,但一跑起来CPU占用率反而更高了——因为解压缩稀疏格式的开销比计算本身还大。
3.2 向量剪枝:折中的“条状切割”
向量剪枝是细粒度和粗粒度之间的过渡。它不再剪单个权重,而是剪掉一整行或一整列。比如在卷积核里,你剪掉某个输出通道对应的整列权重。
优点:
- 比细粒度更规整。剪完后权重矩阵变成规则的带状或块状稀疏,硬件能利用SIMD指令加速。
- 压缩率依然可观。通常能剪掉50%-70%的参数。
缺点:
- 精度损失比细粒度大。因为你是“一刀切”,可能把一些重要的权重也连带剪掉了。
- 实现稍复杂。你需要设计合理的向量分组策略,比如按行、按列还是按块。
避坑指南:我曾经在剪枝一个ResNet-18时,尝试按输出通道的L2范数排序,然后剪掉范数最小的那几列。结果发现中间层的精度掉得特别快。后来我改成按层单独设置剪枝率,浅层少剪、深层多剪,效果才稳住。记住:不同层对剪枝的敏感度完全不同。
3.3 通道剪枝:最实用的“瘦身术”
通道剪枝是目前工业界最常用的方法。它直接删除整个卷积核或整个特征图通道。说白了,就是砍掉一整条“生产线”。
优点:
- 硬件极度友好。剪完后网络结构不变,只是通道数变少了。任何推理框架都能直接加速,不需要特殊优化。
- 推理速度提升明显。我在RK3588上做过测试,通道剪枝50%后,模型推理速度直接翻倍。
缺点:
- 精度损失相对较大。毕竟你是一次性删除一整组权重。
- 需要重新训练微调。剪完后的模型通常需要几个epoch的fine-tune才能恢复精度。
注意:通道剪枝有个隐藏陷阱——剪枝后的模型如果还要做量化,精度可能会雪崩。我有个项目就是先通道剪枝再INT8量化,结果精度从98%掉到了82%。后来我改成先量化再剪枝,或者剪枝后做QAT(量化感知训练),才把精度拉回来。
3.4 层剪枝:最暴力的“截肢”
层剪枝最简单粗暴——直接删除整个网络层。比如ResNet里删掉几个残差块,或者Transformer里删掉几层注意力。
优点:
- 加速效果最明显。删掉一层,这层的所有计算都省了。
- 实现最简单。改一下网络配置文件就行。
缺点:
- 精度损失最大。网络深度直接影响特征提取能力,删层很容易导致欠拟合。
- 适用范围窄。只有冗余度高的深层网络才适合,比如超过50层的ResNet或12层以上的Transformer。
我个人很少用层剪枝。除非模型真的太大,比如有个客户要求把BERT-base塞进手机里,我才会考虑删掉最后2-3层Transformer。但删完之后必须做知识蒸馏来补偿精度。
3.5 四种策略对比
| 策略 | 压缩率 | 硬件加速 | 精度保留 | 实现难度 | 推荐场景 |
|---|---|---|---|---|---|
| 细粒度剪枝 | 极高 | 差 | 好 | 中 | 学术研究、存储受限 |
| 向量剪枝 | 高 | 中 | 中 | 中高 | 需要平衡压缩和速度 |
| 通道剪枝 | 中高 | 好 | 中 | 低 | 嵌入式部署、工业落地 |
| 层剪枝 | 中 | 极好 | 差 | 低 | 超深层网络、模型过大 |
3.6 核心逻辑图
下面这张图展示了四种剪枝粒度的关系。从细到粗,精度损失递增,但硬件加速效果也递增。你想想看,这其实是个trade-off——你要在精度和速度之间找到自己的平衡点。
嗯,这里要注意:没有银弹。每种策略都有它的用武之地。我个人最常用的是通道剪枝,因为它对嵌入式部署最友好。但如果你在做学术研究,想探索模型压缩的极限,细粒度剪枝依然是不可绕过的基础。
总结一下:剪枝粒度越细,精度保留越好,但硬件加速越差。反之亦然。实际项目中,我建议你先用通道剪枝砍掉30%-50%的通道,如果精度还能接受,再考虑要不要上更细粒度的剪枝。别一上来就搞细粒度,否则你可能会像我一样,花了两周调稀疏矩阵库,最后发现还不如直接换个小模型。