4. 剪枝理论基础

剪枝,说白了就是给模型「瘦身」。

YOLOv11 跑在边缘设备上,动不动就几十兆甚至上百兆的参数量,推理延迟根本扛不住。我最早接触剪枝是在一个无人机巡检项目里,模型在 Jetson Nano 上跑不到 10 FPS,客户要求至少 25 FPS。那时候我才意识到,光靠量化远远不够,必须动刀剪枝。

但剪枝不是乱剪。你得先搞清楚:剪什么?怎么剪?剪完会怎样?

4.1 结构化剪枝 vs 非结构化剪枝

这两种剪枝方式,本质区别在于「剪完后的模型结构是否规整」。

对比维度 非结构化剪枝 结构化剪枝
剪枝粒度 单个权重/神经元 通道/卷积核/层
稀疏模式 不规则稀疏 规则稀疏
硬件加速 需要专用硬件/库 通用硬件直接加速
模型体积压缩 高(理论极限高) 中高
推理加速效果 依赖稀疏计算库 立竿见影
部署难度 高(需稀疏推理引擎) 低(直接导出即可)

非结构化剪枝:把权重矩阵里接近 0 的小值直接置 0。模型变成稀疏矩阵,但形状没变。问题在于——大多数推理框架对不规则稀疏的支持很差。我曾经试过用 PyTorch 的 mask 做非结构化剪枝,剪掉 70% 的权重,结果在 ONNX Runtime 上跑,速度几乎没变。为什么?因为稀疏矩阵的存储和计算需要特殊优化,通用引擎不买账。

结构化剪枝:直接砍掉整个通道或卷积核。模型变「窄」了,但结构还是规整的。你想想看,一个 Conv2d 层原来有 64 个输出通道,剪掉 32 个,下一层的输入通道也跟着变。这种剪枝方式,任何推理框架都能直接加速。

核心结论:对于 YOLOv11 这种需要部署到边缘设备的模型,结构化剪枝是更务实的选择。非结构化剪枝更适合学术研究或拥有自研稀疏推理引擎的团队。

4.2 通道剪枝原理

通道剪枝是结构化剪枝里最常用的一种。它的核心思路很简单:找出不重要的通道,连根拔掉。

具体来说,YOLOv11 的每个卷积层都有多个输出通道。每个通道对应一个卷积核,卷积核的权重决定了这个通道对最终输出的贡献。如果某个通道的权重整体都很小,说明它对特征提取没什么用,砍掉它不会对精度造成太大影响。

我习惯把通道剪枝分为三步:

  1. 评估通道重要性:给每个通道打分,分数低的优先剪掉。
  2. 确定剪枝率:决定每层砍掉多少比例的通道。
  3. 重训恢复精度:剪完后微调模型,把精度拉回来。

这里面最关键的是第一步——怎么打分?

4.3 L1 范数与 BN 层权重

打分方法有很多,但最经典、最实用的两个指标是:卷积核的 L1 范数和BN 层的缩放因子 γ。

4.3.1 卷积核 L1 范数

一个卷积核的 L1 范数,就是它所有权重值之和。公式很简单:

L1 = sum(|w_i|)  for i in 1..N

为什么 L1 范数能衡量重要性?

你想想看,如果一个卷积核的权重都很小,那它提取的特征响应肯定也弱。L1 范数大的卷积核,通常对应更强烈的特征模式。我在项目中做过对比实验:按 L1 范数排序剪枝,比随机剪枝的精度损失小 3-5 个百分点。

我的经验:L1 范数适合剪枝浅层卷积层。浅层特征比较通用,冗余度高,L1 范数能有效区分重要和不重要的通道。但到了深层,特征变得抽象,L1 范数的区分度会下降。

4.3.2 BN 层缩放因子 γ

YOLOv11 每个卷积层后面都跟着 BN 层。BN 层的计算公式是:

y = γ * (x - μ) / √(σ² + ε) + β

这里的 γ 就是缩放因子。它控制着这个通道的输出幅度。γ 接近 0 的通道,输出被压缩到很小,说明这个通道对后续层的影响微乎其微。

用 γ 做剪枝指标有个天然优势:训练过程中 γ 会自动学习。你只需要在训练时给 γ 加一个 L1 正则化项,模型自己就会把不重要的通道的 γ 推向 0。

注意:用 γ 剪枝时,一定要在训练时加入稀疏正则化。否则 γ 的分布会很均匀,剪枝效果大打折扣。我曾经踩过这个坑——直接拿预训练模型的 γ 剪枝,剪掉 30% 的通道,mAP 直接掉了 12 个点。

4.4 剪枝率与性能权衡

剪枝率越高,模型越小越快,但精度损失也越大。这个权衡是剪枝的核心矛盾。

我整理了一份 YOLOv11 在 COCO 数据集上的剪枝实验数据,供你参考:

剪枝率 mAP@0.5 下降 推理加速比 模型体积缩减 推荐场景
0%(基线) 0% 1.0x 0% 精度优先
30% 0.5% - 1.0% 1.3x - 1.5x 25% - 30% 通用部署
50% 1.5% - 3.0% 1.8x - 2.2x 45% - 50% 边缘设备
70% 4.0% - 8.0% 2.5x - 3.0x 65% - 70% 极端加速

几点经验:

  • 剪枝率 30% 以内:基本无损,微调 10-20 个 epoch 就能恢复。我一般把这个作为默认剪枝率。
  • 剪枝率 30%-50%:精度会有轻微下降,但通过知识蒸馏或更长时间微调可以弥补。
  • 剪枝率超过 50%:精度损失明显,需要精细调整每层的剪枝比例,不能一刀切。

我的建议:不要追求极限剪枝率。在实际项目中,30%-40% 的剪枝率 + 量化,往往能获得最佳的精度-速度平衡。剪太多,微调成本高,得不偿失。

4.5 本章知识结构图

下面这张图总结了通道剪枝的核心流程和关键决策点:

通道剪枝知识结构图 通道剪枝核心流程 原始 YOLOv11 模型 通道重要性评估 L1 范数(卷积核) BN 层 γ 值 其他指标(可选) 确定剪枝率 & 剪枝 微调 / 知识蒸馏 导出部署 关键权衡 剪枝率 ↑ → 速度 ↑ 剪枝率 ↑ → 精度 ↓ 推荐 30%-40%

4.6 避坑指南

最后分享几个我踩过的坑:

  • 不要对 shortcut 层随意剪枝:YOLOv11 有残差连接,剪掉 shortcut 的某个通道会导致维度不匹配。我一般对 shortcut 层设置剪枝率为 0,或者保持输入输出通道数一致。
  • 剪枝后一定要重训 BN 层:剪完枝,模型结构变了,BN 层的统计量(μ 和 σ)需要重新计算。我习惯先 freeze 卷积层,只训 BN 层 5-10 个 epoch,再全量微调。
  • 逐层剪枝比全局剪枝更稳:全局剪枝(所有层用同一个剪枝率)容易导致某些层被过度剪枝。我推荐逐层设定剪枝率,敏感层少剪,冗余层多剪。

小技巧:用 torch.nn.utils.prune 做快速原型验证,但生产环境建议自己写剪枝逻辑。框架自带的剪枝工具灵活性不够,尤其是处理 YOLOv11 这种多分支结构时。