模型剪枝:去掉冗余参数,给模型瘦身
模型剪枝去掉冗余参数给模型瘦身模型里有很多冗余的通道和参数去掉了也不怎么影响精度。模型剪枝就是干这个的——剪掉不重要的通道模型变小、速度变快精度损失还可控。这篇讲剪枝的基本原理、常用方法、以及实际操作中的经验。大家好我是黒漂技术佬。神经网络有个特点过参数化。一个训练好的模型里很多参数其实是「冗余」的——去掉了也不怎么影响精度。剪枝就是把这些冗余的部分去掉让模型更小更快。这篇讲剪枝的原理、方法、实操步骤以及我实际项目中的经验。一、剪枝是什么直观理解模型就像一棵大树枝繁叶茂但很多枝条其实没用。剪枝就是把不重要的枝条剪掉树还是那棵树但更精干了。剪枝的粒度粒度剪什么效果部署难度结构化剪枝通道级整个卷积通道压缩明显加速直接低模型结构变了但还是标准算子非结构化剪枝权重级单个权重压缩率高高需要稀疏推理支持层剪枝整个层粗暴低工业界主流是结构化通道剪枝因为剪完模型还是标准的卷积层所有推理框架都支持直接就能用。非结构化剪枝压缩率更高但大部分推理框架不支持稀疏计算实际跑起来没加速。通道剪枝的原理每个卷积层有 N 个输出通道卷积核个数每个通道的重要性不一样。判断重要性的常见方法BN 层的 γ 系数γ 小的通道输出接近 0不重要权重的 L1/L2 范数权重绝对值小的通道贡献小梯度信息对损失影响小的通道不重要最常用的是BN 剪枝简单有效YOLO 系列用得最多。二、BN 层剪枝原理BN 层回顾Batch Normalizationx_normalized (x - mean) / sqrt(var ε) y γ * x_normalized βγ缩放系数和 β偏移是可学习的参数。如果某个通道的 γ 很小说明这个通道的输出对后续影响很小——因为乘了个接近 0 的系数输出几乎是常数。剪枝流程稀疏训练在损失函数里加 γ 的 L1 正则让不重要的通道 γ 趋近于 0剪枝设定阈值把 γ 小于阈值的通道剪掉微调剪完模型精度会掉再训几轮恢复精度稀疏训练的损失总损失 原损失 λ * sum(|γ|)λ 是稀疏系数越大 γ 越容易被压到 0剪得越狠。三、剪枝的一般步骤第一步训一个 baseline 模型先正常训练得到一个精度不错的模型。不要直接拿随机初始化的模型剪。第二步稀疏训练在 baseline 基础上加 L1 稀疏正则再训几十轮让 BN 的 γ 分化——重要的通道 γ 大不重要的趋近于 0。# 伪代码稀疏训练forepochinrange(sparse_epochs):forbatchindataloader:lossmodel(batch)# 加 BN 的 γ L1 正则bn_gamma0forlayerinmodel.modules():ifisinstance(layer,nn.BatchNorm2d):bn_gammatorch.sum(torch.abs(layer.weight))total_losslosssparse_lambda*bn_gamma total_loss.backward()optimizer.step()sparse_lambda 一般取 0.001 - 0.01越大剪得越狠。第三步分析 γ 分布稀疏训练完看一下所有 BN 层 γ 的分布如果大部分 γ 都接近 0 → 稀疏过头了精度掉太多如果 γ 都很大 → 稀疏不够剪不了多少理想状态明显的两极分化一部分接近 0一部分比较大第四步剪枝设定剪枝比例比如 30%、50%把 γ 最小的那部分通道剪掉。注意不是所有层都能剪有些层比如最后检测头要保留shortcut 连接的层要一起剪不然维度对不上通道数最好是 8/16/32 的倍数硬件推理更高效第五步微调Fine-tune剪完模型精度会掉再正常训练几十轮恢复。剪枝比例不大的话微调几轮就能恢复甚至超过原精度剪得狠就要多训几轮。四、YOLO 剪枝实操工具推荐YOLOv5 剪枝有很多开源实现搜yolov5 pruningYOLOv8 剪枝ultralytics 官方还没内置社区有实现TorchPrune通用的 PyTorch 剪枝库MMRazor商汤开源的模型压缩工具箱功能全典型的剪枝配置# 剪枝配置示例prune:sparse_epochs:50# 稀疏训练轮数sparse_lambda:0.005# 稀疏系数prune_ratio:0.4# 剪枝比例 40%finetune_epochs:50# 微调轮数skip_layers:[head]# 不剪的层检测头剪枝比例怎么选剪枝比例速度提升精度损失适用场景20-30%10-20%几乎不掉保守优化精度优先40-50%30-50%掉 1-2 个点平衡方案推荐60-70%50-70%掉 3-5 个点速度优先需要好的微调80%可能更快掉很多极端场景不推荐建议从 30-40% 开始试精度掉得不多再加大比例。我的经验售货柜 YOLOv8n 模型剪了 40% 通道模型大小3.2M → 2.1MRK3588 推理速度30fps → 38fpsmAP95.6% → 94.8%掉 0.8 个点微调后回到 95.3%整体很划算精度几乎没掉速度快了 25%五、剪枝的收益和局限收益模型更小方便部署、OTA 升级包小推理更快通道少了计算量小了内存占用低特征图占的内存少了功耗更低计算少了NPU/CPU 更省电局限不是所有模型都能剪很多本身就很精简的模型比如 MobileNet冗余少剪不动多少剪得太狠精度掉得多需要权衡需要重新训练稀疏训练 微调时间成本加速比不等于剪枝比剪了 40% 通道速度不一定快 40%还有内存、调度等开销什么时候值得做✅ 模型还有冗余速度还不够快✅ 有训练资源能做稀疏训练和微调✅ 对模型大小和推理速度有严格要求❌ 模型已经很小很快了再剪收益不大❌ 没训练数据或训练资源没法微调六、剪枝 vs 量化 vs 换小模型这三个都是模型压缩手段经常被拿来比方法压缩比精度损失速度提升难度推荐度换小模型nano2-4x可能较大2-4x低⭐⭐⭐⭐⭐ 首选INT8 量化4x体积1-2%2-4x中⭐⭐⭐⭐⭐ 必做通道剪枝 40%~1.5x1%20-40%高⭐⭐⭐ 可选知识蒸馏无压缩涨点无中⭐⭐⭐⭐ 推荐我的建议优先级先选合适大小的模型nano 起步不够再升做量化收益大难度中等必做加蒸馏白嫖精度配合小模型剪枝前三个都做了还不够快再考虑剪枝剪枝是锦上添花的手段不是首选。量化和模型选型的收益更大也更简单。七、剪枝常见的坑坑 1剪枝比例太大精度崩了上来就剪 70%精度掉十几个点微调也救不回来。解决从小比例开始试逐步加大。坑 2shortcut 层没处理好残差连接的两个分支通道数要一致只剪一边维度就对不上了。解决shortcut 连接的层绑定剪要剪一起剪。坑 3剪完 NPU 反而没加速有些硬件对特定通道数比如 16 的倍数优化更好剪完通道数变成了奇怪的数反而变慢。解决剪枝时对齐到 8/16/32 的倍数。坑 4稀疏训练的 λ 没调好λ 太大所有 γ 都压没了模型精度崩λ 太小γ 都很大剪不动。解决先试几个 λ看 γ 分布再定。坑 5只剪 backbone 不剪检测头检测头也有冗余但很多人不敢剪。其实检测头也能剪注意别剪太狠。八、本篇小结通道剪枝去掉不重要的卷积通道模型更小更快BN 剪枝最常用通过 γ 系数判断通道重要性流程baseline 训练 → 稀疏训练 → 剪枝 → 微调恢复剪枝比例推荐 30-50%从小比例开始试收益速度快 20-40%精度掉 0-2 个点优先级选模型 量化 蒸馏 剪枝注意shortcut 绑定剪、通道数对齐硬件优化下一篇讲模型量化原理FP32 转 INT8 是怎么回事为什么能加速精度为什么会掉。我是黒漂技术佬。