3. 模型剪枝与知识蒸馏
模型压缩这件事,说白了就是给神经网络「减负」。我在工业现场见过太多这样的场景:算法在服务器上跑得飞快,一搬到 Jetson 或者 RK3588 上,帧率直接掉到个位数。这时候你第一个想到的,往往就是剪枝和蒸馏。
这两个技术,一个做减法,一个做迁移。我习惯把它们放在一起讲,因为在实际项目中,它们经常组合使用。今天我们就把这套组合拳拆开来看。
3.1 剪枝:从「胖模型」到「瘦模型」
剪枝的核心思路很简单:去掉不重要的连接或通道。但怎么定义「不重要」,就分出了两个流派。
3.4.1 非结构化剪枝
非结构化剪枝,是对单个权重下手。低于某个阈值的权重,直接置零。这种方法压缩率很高,但有个致命问题——权重矩阵变得稀疏,普通硬件根本加速不了。
我记得有一次,我把一个 ResNet-50 剪掉了 80% 的参数,模型文件小了很多,但推理速度一点没变。为什么?因为 CPU 和 GPU 对稀疏矩阵的支持很差,你那些零权重占着位置,计算量一点没少。
- 压缩率高,可以剪掉 90% 以上的参数
- 需要专用硬件或稀疏计算库才能加速
- 通用硬件上几乎没有收益
- 模型结构不变,只是权重变稀疏
torch.nn.utils.prune 导出时,权重掩码会一直保留。如果你不手动 remove(),推理时会有额外的 mask 计算开销,反而更慢。
3.4.2 结构化剪枝
结构化剪枝就实在多了。它直接干掉整个通道、卷积核或者层。模型结构变了,宽度变窄了,但依然是密集矩阵。任何硬件都能直接受益。
我目前在工业项目里,90% 的情况都用结构化剪枝。原因很简单:部署省心。你剪完通道,模型尺寸变小,计算量变小,推理速度直接提升。不需要任何特殊库。
| 对比维度 | 非结构化剪枝 | 结构化剪枝 |
|---|---|---|
| 剪枝粒度 | 单个权重 | 通道/卷积核/层 |
| 模型结构 | 不变 | 变窄/变浅 |
| 硬件加速 | 需专用硬件 | 通用硬件直接加速 |
| 推理速度提升 | 不明显 | 明显 |
| 精度恢复难度 | 较容易 | 较困难 |
3.4.3 剪枝的完整流程
剪枝不是一步到位的。我一般按这个流程走:
- 预训练:先训练一个精度达标的基线模型
- 评估重要性:用 L1 范数、BN 层的 gamma 值、或者梯度作为重要性指标
- 剪枝:按比例剪掉不重要的通道
- 微调:用少量 epoch 恢复精度
- 迭代:重复 2-4 步,直到达到目标压缩率
这里有个关键点:不要一次剪太多。我见过有人一次剪掉 50% 的通道,结果精度直接崩了,后面怎么微调都回不来。我习惯每次剪 10%-20%,然后微调 10-20 个 epoch,再继续剪。
3.2 知识蒸馏:大模型教小模型
剪枝是从模型内部做减法。蒸馏则是让一个大模型(教师)去教一个小模型(学生)。学生模型的结构可以完全独立,不需要和教师有任何关系。
蒸馏的核心,是让学生去模仿教师的「软标签」。软标签就是教师输出的概率分布,它比硬标签(0 或 1)包含更多信息。比如一张图片,硬标签是「猫」,软标签可能是「猫 0.9,狗 0.08,兔子 0.02」。这个分布里,包含了教师对类别相似性的理解。
3.2.1 蒸馏训练流程
标准的蒸馏流程分三步:
- 训练教师模型:一个高精度的大模型,可以是 ResNet-101、EfficientNet-B7 等
- 生成软标签:用教师模型在训练集上推理,保存 softmax 输出
- 训练学生模型:同时用硬标签(真实标签)和软标签(教师输出)来训练
损失函数长这样:
loss = α * KL_divergence(student_soft, teacher_soft) + (1-α) * CrossEntropy(student_hard, true_label)
其中 α 控制软标签的权重,我一般设 0.7。温度 T 控制 softmax 的平滑程度,T 越大,软标签越平滑,信息越丰富。我习惯用 T=4 或 T=5。
- 温度 T:控制软标签的平滑度,通常 3-8
- 权重 α:软标签损失的权重,通常 0.5-0.9
- 学生模型:可以是教师模型的缩小版,也可以是完全不同的轻量网络
3.2.2 蒸馏 vs 剪枝:怎么选?
这两个技术不是互斥的。我经常把它们组合使用:
- 先蒸馏,再剪枝:先用大模型蒸馏一个小模型,再对小模型做结构化剪枝
- 先剪枝,再蒸馏:先对教师模型剪枝,再用剪枝后的模型做蒸馏
- 联合训练:在剪枝过程中,同时用蒸馏损失来指导训练
我个人习惯第一种。先用蒸馏把知识从大模型迁移到小模型,再通过剪枝进一步压缩。这样每一步的精度损失都比较小,最终模型也更容易调优。
3.3 实际部署收益:数字说话
光说理论没用,我们看一组我在实际项目中测出来的数据。模型是 YOLOv5s,部署在 Jetson Orin NX 上,输入 640x640:
| 模型版本 | 参数量 | 计算量 (GFLOPs) | 推理速度 (FPS) | mAP@0.5 |
|---|---|---|---|---|
| 基线 (YOLOv5s) | 7.2M | 16.5 | 62 | 0.723 |
| 结构化剪枝 30% | 4.1M | 9.8 | 98 | 0.711 |
| 蒸馏 (教师 YOLOv5m) | 7.2M | 16.5 | 62 | 0.738 |
| 蒸馏 + 剪枝 30% | 4.1M | 9.8 | 98 | 0.729 |
看到没?蒸馏 + 剪枝的组合,在参数量减少 43%、速度提升 58% 的情况下,mAP 反而比基线高了 0.6 个点。这就是压缩的魅力——不是简单的牺牲精度换速度,而是通过知识迁移,让模型在更小的体积下学到更多。
3.4 本章知识结构
下面这张图,把剪枝和蒸馏的核心逻辑串起来了。你可以看到,两种技术最终都指向同一个目标:在边缘设备上跑得更快、更省资源。