3. 知识蒸馏(Knowledge Distillation)

知识蒸馏这个概念,说白了就是「以大带小」。你训练一个又大又准的教师网络,让它教一个轻量级的学生网络。学生学到的不是原始标签,而是教师对数据的「理解方式」。我在落地项目中试过很多次,蒸馏后的学生模型,精度能追平大模型90%以上,参数量却只有十分之一。

核心思想: 教师网络输出软标签(Soft Label),包含类别间的相似度信息;学生网络同时学习硬标签(Ground Truth)和软标签,从而获得更强的泛化能力。

3.1 教师-学生网络架构

先看架构。教师网络通常是一个预训练好的大模型,比如YOLOv8-Large。学生网络是你真正要部署的小模型,比如YOLOv8-Nano。训练时,教师网络冻结权重,只做前向推理;学生网络正常训练,同时接收两个监督信号。

教师-学生蒸馏架构图 教师网络(大模型) 学生网络(小模型) 输入图像 软标签(Logits) 学生预测 蒸馏损失(KL散度 + MSE) 硬标签损失(交叉熵) 总损失 = α·蒸馏损失 + β·硬标签损失

教师网络输出的是「软标签」——经过温度系数缩放后的概率分布。举个例子,一张猫的图片,硬标签是[1,0,0],但软标签可能是[0.85, 0.10, 0.05]。这0.10和0.05告诉学生:猫和狗有点像,但和汽车完全不像。这种「暗知识」是蒸馏的精髓。

温度系数 T: T越大,软标签分布越平滑,类别间关系越明显。我一般从T=3开始调,T=1时退化为硬标签,T>10时信息过于模糊。

3.2 Logit蒸馏与Feature蒸馏

蒸馏不是只有一种玩法。我把它分成两派:Logit蒸馏和Feature蒸馏。前者关注输出层,后者关注中间层。

蒸馏类型 监督信号 损失函数 适用场景
Logit蒸馏 教师网络输出层的软标签 KL散度 + 交叉熵 分类头、检测头
Feature蒸馏 教师网络中间层的特征图 MSE、L2、注意力对齐 骨干网络、Neck层
混合蒸馏 输出层 + 中间层 KL散度 + MSE + 交叉熵 YOLO全模型蒸馏

Logit蒸馏实现简单,计算量小。你只需要在训练时让教师网络做一次前向,然后把软标签和学生输出算KL散度。我在项目中试过,光靠Logit蒸馏,学生模型精度能提升2-3个点。

Feature蒸馏更精细。教师网络中间层的特征图包含丰富的空间信息,学生网络在这些层上对齐,能学到更好的特征表达。但要注意——教师和学生网络的通道数、分辨率可能不一样,需要加一个适配层(Adaptation Layer)做维度对齐。

避坑指南: 我曾经在Feature蒸馏时直接让1x1卷积做适配层,结果学生网络学到的全是噪声。后来改成3x3卷积+BN+ReLU,效果才上来。适配层的设计很关键,别偷懒。

3.3 YOLO蒸馏实战案例

拿YOLOv8来举例。教师用YOLOv8-Large,学生用YOLOv8-Nano。蒸馏策略我选混合蒸馏——检测头用Logit蒸馏,骨干网络用Feature蒸馏。

具体步骤:

  1. 准备教师模型: 加载预训练的YOLOv8-Large权重,冻结所有参数。
  2. 定义蒸馏损失: 检测头输出用KL散度,骨干网络特征图用MSE。
  3. 设置温度系数: 我习惯T=4,软标签分布平滑度适中。
  4. 联合训练: 学生网络正常训练,同时最小化蒸馏损失和硬标签损失。
  5. 微调: 蒸馏训练完成后,去掉教师网络,只用硬标签再微调几轮。
# 伪代码:YOLO蒸馏训练核心逻辑
teacher_model = load_model('yolov8l.pt')
teacher_model.eval()
for param in teacher_model.parameters():
    param.requires_grad = False

student_model = load_model('yolov8n.pt')
student_model.train()

for images, targets in dataloader:
    # 教师前向
    with torch.no_grad():
        teacher_logits, teacher_features = teacher_model(images)
    
    # 学生前向
    student_logits, student_features = student_model(images)
    
    # 蒸馏损失
    loss_kd = kl_divergence(
        log_softmax(student_logits / T),
        softmax(teacher_logits / T)
    ) * (T ** 2)
    
    # 特征损失
    loss_feat = mse_loss(student_features[-1], teacher_features[-1])
    
    # 硬标签损失
    loss_hard = cross_entropy(student_logits, targets)
    
    # 总损失
    loss = alpha * (loss_kd + loss_feat) + beta * loss_hard
    loss.backward()
    optimizer.step()

蒸馏后的效果对比:

模型 mAP@0.5 参数量 推理速度(T4)
YOLOv8-Large(教师) 53.7% 43.6M 2.1ms
YOLOv8-Nano(未蒸馏) 37.3% 3.2M 0.6ms
YOLOv8-Nano(蒸馏后) 41.8% 3.2M 0.6ms

看到没?蒸馏后mAP提升了4.5个点,参数量和推理速度完全不变。这就是蒸馏的价值——白捡的精度。

我的经验: 蒸馏训练时,学习率要调低一点,我一般用原始训练学习率的0.1倍。另外,α和β的权重我习惯α=0.7、β=0.3,蒸馏损失占大头。

最后说一个坑。Feature蒸馏时,教师和学生的特征图分辨率可能不同。YOLOv8的骨干网络有5个下采样阶段,我一般选最后两个阶段做特征对齐。分辨率不一致时,用双线性插值上采样或下采样对齐,别用Adaptive Pooling——它会丢失空间信息。

嗯,蒸馏这部分就讲到这里。记住一句话:蒸馏不是玄学,是工程。选对策略、调好参数,小模型也能有大模型的「灵魂」。