3. 模型剪枝与知识蒸馏

模型压缩这件事,说白了就是给神经网络「减负」。我在工业现场见过太多这样的场景:算法在服务器上跑得飞快,一搬到 Jetson 或者 RK3588 上,帧率直接掉到个位数。这时候你第一个想到的,往往就是剪枝和蒸馏。

这两个技术,一个做减法,一个做迁移。我习惯把它们放在一起讲,因为在实际项目中,它们经常组合使用。今天我们就把这套组合拳拆开来看。

3.1 剪枝:从「胖模型」到「瘦模型」

剪枝的核心思路很简单:去掉不重要的连接或通道。但怎么定义「不重要」,就分出了两个流派。

3.4.1 非结构化剪枝

非结构化剪枝,是对单个权重下手。低于某个阈值的权重,直接置零。这种方法压缩率很高,但有个致命问题——权重矩阵变得稀疏,普通硬件根本加速不了。

我记得有一次,我把一个 ResNet-50 剪掉了 80% 的参数,模型文件小了很多,但推理速度一点没变。为什么?因为 CPU 和 GPU 对稀疏矩阵的支持很差,你那些零权重占着位置,计算量一点没少。

非结构化剪枝的特点:
  • 压缩率高,可以剪掉 90% 以上的参数
  • 需要专用硬件或稀疏计算库才能加速
  • 通用硬件上几乎没有收益
  • 模型结构不变,只是权重变稀疏
我曾经踩过的坑: 非结构化剪枝后的模型,用 PyTorch 的 torch.nn.utils.prune 导出时,权重掩码会一直保留。如果你不手动 remove(),推理时会有额外的 mask 计算开销,反而更慢。

3.4.2 结构化剪枝

结构化剪枝就实在多了。它直接干掉整个通道、卷积核或者层。模型结构变了,宽度变窄了,但依然是密集矩阵。任何硬件都能直接受益。

我目前在工业项目里,90% 的情况都用结构化剪枝。原因很简单:部署省心。你剪完通道,模型尺寸变小,计算量变小,推理速度直接提升。不需要任何特殊库。

对比维度 非结构化剪枝 结构化剪枝
剪枝粒度 单个权重 通道/卷积核/层
模型结构 不变 变窄/变浅
硬件加速 需专用硬件 通用硬件直接加速
推理速度提升 不明显 明显
精度恢复难度 较容易 较困难

3.4.3 剪枝的完整流程

剪枝不是一步到位的。我一般按这个流程走:

  1. 预训练:先训练一个精度达标的基线模型
  2. 评估重要性:用 L1 范数、BN 层的 gamma 值、或者梯度作为重要性指标
  3. 剪枝:按比例剪掉不重要的通道
  4. 微调:用少量 epoch 恢复精度
  5. 迭代:重复 2-4 步,直到达到目标压缩率

这里有个关键点:不要一次剪太多。我见过有人一次剪掉 50% 的通道,结果精度直接崩了,后面怎么微调都回不来。我习惯每次剪 10%-20%,然后微调 10-20 个 epoch,再继续剪。

我的经验: 用 BN 层的 gamma 值作为重要性指标,效果最稳定。gamma 值小的通道,说明它对后续层的影响小,优先剪掉。

3.2 知识蒸馏:大模型教小模型

剪枝是从模型内部做减法。蒸馏则是让一个大模型(教师)去教一个小模型(学生)。学生模型的结构可以完全独立,不需要和教师有任何关系。

蒸馏的核心,是让学生去模仿教师的「软标签」。软标签就是教师输出的概率分布,它比硬标签(0 或 1)包含更多信息。比如一张图片,硬标签是「猫」,软标签可能是「猫 0.9,狗 0.08,兔子 0.02」。这个分布里,包含了教师对类别相似性的理解。

3.2.1 蒸馏训练流程

标准的蒸馏流程分三步:

  1. 训练教师模型:一个高精度的大模型,可以是 ResNet-101、EfficientNet-B7 等
  2. 生成软标签:用教师模型在训练集上推理,保存 softmax 输出
  3. 训练学生模型:同时用硬标签(真实标签)和软标签(教师输出)来训练

损失函数长这样:

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-5 个点。

3.4 本章知识结构

下面这张图,把剪枝和蒸馏的核心逻辑串起来了。你可以看到,两种技术最终都指向同一个目标:在边缘设备上跑得更快、更省资源。

模型压缩技术全景 模型剪枝 非结构化 结构化 权重置零 通道裁剪 知识蒸馏 教师模型 学生模型 软标签 硬标签 边缘部署:更快、更省、更稳 剪枝做减法,蒸馏做迁移。两者组合使用,效果远大于单独使用。 实际项目中,建议先蒸馏再剪枝,精度损失最小。