剪枝分类:结构化 vs 非结构化,通道 vs 层
好,咱们进入正题。模型剪枝听起来高大上,其实核心就一句话:把模型里不重要的参数干掉。但怎么干、干到什么程度,这里面的门道就多了。
我个人习惯把剪枝先分成两大类:结构化剪枝和非结构化剪枝。这俩是根,后面的通道剪枝、层剪枝都是结构化剪枝的具体玩法。
非结构化剪枝:细粒度,但硬件不买账
非结构化剪枝,说白了就是把权重矩阵里那些值很小的单个参数直接置零。你想想看,一个卷积核是 3x3x64 的,里面可能有几百个参数,我把其中 50% 接近零的设成 0,模型精度几乎不掉。
我在项目中第一次试剪枝就是用的这种。当时用 PyTorch 自带的 mask 实现,跑起来很爽,精度掉了不到 0.5%。但一部署到 TensorRT 上就傻眼了——推理速度几乎没变。
| 特性 | 非结构化剪枝 | 结构化剪枝 |
|---|---|---|
| 剪枝粒度 | 单个权重参数 | 整个通道/层/卷积核 |
| 精度保持 | ✅ 较好(细粒度调整) | ⚠️ 一般(粗粒度,容易伤结构) |
| 硬件加速 | ❌ 几乎无(需专用库) | ✅ 直接加速(结构规整) |
| 部署难度 | 高(需稀疏推理框架) | 低(直接导出即可) |
| 压缩率上限 | 高(可到 90%+) | 中(通常 30%-60%) |
结构化剪枝:硬件友好,实战首选
结构化剪枝就不一样了。它直接干掉整个卷积核、整个通道、甚至整层。剪完之后,模型的张量维度变了,比如从 [64, 3, 3, 3] 变成 [32, 3, 3, 3]。硬件一看,哦,通道数减半了,那计算量自然减半。
这才是实战中真正能落地的方案。YOLOv11 这种目标检测模型,部署时对帧率要求极高,你不可能指望每个设备都装 cuSPARSE。所以咱们这门课,重点讲的就是结构化剪枝。
结构化剪枝下面,又分两个主流流派:通道剪枝和层剪枝。
通道剪枝:YOLO 剪枝的主力军
通道剪枝,就是评估每个通道的重要性,把不重要的通道连同它的卷积核一起砍掉。YOLOv11 的 backbone 和 neck 部分,大量使用 C2f 模块,每个模块里都有多个卷积层。通道剪枝在这里特别好使。
怎么评估通道重要性?常见的方法有:
- 基于权重的范数:L1 范数小的通道,贡献低,优先剪。
- 基于 BN 层的 γ 值:BN 层的缩放因子 γ 越小,说明这个通道的输出越接近零,可以剪。这是目前最主流的方法。
- 基于梯度:计算每个通道对 loss 的敏感度,敏感度低的剪掉。
层剪枝:简单粗暴,但容易伤筋动骨
层剪枝就更狠了——直接删除整个网络层。比如 YOLOv11 的 backbone 有 10 个 C2f 模块,我删掉其中 2 个,网络变浅了,计算量自然下降。
但这里有个问题:层与层之间的依赖关系非常强。你删掉一层,后面的层接收到的特征就变了。尤其是 shortcut 连接(残差连接)存在的地方,删层可能导致梯度流断裂。
我记得有一次在 YOLOv8 上试层剪枝,删了第 5 个 C2f 模块,结果 mAP 直接掉了 12 个点。后来花了三天调 fine-tune 策略才勉强拉回来。所以我的建议是:层剪枝只用在冗余度极高的深层网络上,YOLOv11 这种轻量级模型,慎用。
| 对比维度 | 通道剪枝 | 层剪枝 |
|---|---|---|
| 剪枝对象 | 单个通道/卷积核 | 整个网络层(如 C2f 模块) |
| 精度影响 | 较小(可精细控制) | 较大(结构突变) |
| 加速效果 | 线性提升(通道数减少) | 跳跃提升(层数减少) |
| 适用场景 | YOLOv11 全模型 | 超深层模型(如 ResNet-152) |
| 实现复杂度 | 中(需重写 forward) | 低(直接删层) |
一张图看懂剪枝分类
下面这张 SVG 图,把整个剪枝分类体系串起来了。你可以把它当作本章的知识地图。
实战中怎么选?我的建议
说了这么多理论,你肯定想问:做 YOLOv11 剪枝,到底用哪种?
我的答案是:通道剪枝为主,层剪枝为辅,非结构化剪枝暂时放一放。
- 如果你追求精度:用通道剪枝,每次剪 10%-20% 的通道,然后 fine-tune 恢复。迭代 3-5 轮,模型能压缩 40% 左右,精度几乎不掉。
- 如果你追求极致加速:通道剪枝 + 少量层剪枝。比如 backbone 最后两个 C2f 模块冗余度高,可以尝试删掉一个,再配合通道剪枝。
- 如果你只是做学术研究:非结构化剪枝可以试试,但别指望落地。