简介:面向目标检测模型轻量化场景的 YOLOv5 知识蒸馏实战资源,结合教师网络-学生网络迁移范式,帮助有一定 YOLO 基础的学习者掌握蒸馏原理、环境配置与完整训练流程。压缩包共 8 个文件,约 593MB,包含 4 个 zip 资料包、3 个 pt 权重文件与 1 个 Python 处理脚本:zip 内为代码、数据集及说明文档,pt 可用于对比蒸馏前后模型效果,py 用于数据预处理,整体按教程逐步操作即可复现实验。已有 2022 人学习下载。亮点在于不仅提供可直接运行的 YOLOv5 蒸馏源码,还配套 VOC 格式数据、整理好的说明文档与权重文件,读者可对照教师网络与学生网络的训练差异,理解蒸馏 loss 设计与模型轻量化调优思路,并基于现有数据快速迁移到自己的检测任务。
1. 为什么 YOLOv5 要做知识蒸馏:轻量模型不是练不大,是没找对老师
做目标检测落地的工程师几乎都撞过同一堵墙:模型在服务器上跑得挺好,一换到边缘设备就卡成 PPT。YOLOv5s 在 Jetson Nano 上勉强能跑实时,精度却比 YOLOv5x 掉了一截;换成 YOLOv5n 倒是快了,小目标直接漏成筛子。知识蒸馏(Knowledge Distillation)解决的就是这个矛盾——用一个笨重但精准的教师模型,把一个轻量学生模型"教"到尽可能接近教师的精度,而不是让学生从头自己悟。基于 YOLOv5 的知识蒸馏实战源码,本质上就是把这套"教师-学生"训练框架写进 YOLOv5 原本的训练流程里,让蒸馏不是论文里的概念,而是你能跑起来、能调参、能评估收益的工程代码。本文适合两类人:一类是手里有边缘部署需求、想压缩模型但不想牺牲太多精度的算法工程师;另一类是把 YOLOv5 当入门框架、想搞懂蒸馏到底怎么改训练逻辑的学生。
2. 知识蒸馏在 YOLOv5 里的落地形态:从教师选择到损失函数设计
2.1 教师模型选型:不是越大越好,是越像越好
先解决一个最容易被带偏的问题:教师模型是不是直接选 YOLOv5x 就完事?我在项目里试过用 YOLOv5x 蒸馏 YOLOv5s,效果有提升,但提升幅度天花板很明显。原因在于蒸馏的有效性取决于教师和学生之间"能力差"与"结构差"的平衡。结构差太大,学生的特征表达空间根本装不下教师的信息,学到的只是一堆无法消化的高维噪声。
我一般这样选型:教师选比学生大一到两个量级的同族模型。学生是 YOLOv5s,教师选 YOLOv5l 或 YOLOv5x 都行;学生是 YOLOv5n,教师选 YOLOv5s 或 YOLOv5m 就够了。关键指标不是参数量差距,而是教师自身在验证集上的 mAP 要明显高于学生直接训练的 mAP,差值最好在 5 个点以上。如果教师只比学生高两三个点,蒸馏收益会很小,不如直接调学生的超参数。
另一个容易被忽略的点是输入分辨率。YOLOv5 训练时默认 imgsz=640,但如果你的教师模型是用 1280 分辨率训出来的,蒸馏时教师和学生必须用同一个输入尺寸。否则教师输出的 feature map 尺寸和学生完全对不上,蒸馏 loss 都没法算。
2.2 学生模型的三个改造点:预测头、neck、backbone
拿到 YOLOv5 源码后,蒸馏改造不是从零写训练脚本,而是基于原有的train.py做增量修改。需要动三个位置。
第一是 backbone 的输出。YOLOv5 的 backbone 在三个尺度上输出特征,分别是下采样 8 倍、16 倍、32 倍的位置。蒸馏时我们要在这些位置分别取教师的 feature map 和学生的 feature map 算 distance loss。你可以在models/yolo.py的Detect层前面把 backbone 和 neck 的输出引出来,或者在forward函数里加一个返回中间特征的开关。
第二是 neck 部分。YOLOv5 的 PANet 结构会把高层语义信息往下融合,蒸馏时如果只对齐 backbone 特征而忽略 neck,学生的浅层特征可能学到位了,但融合后的信息仍然是乱的。我在实际项目里发现,neck 输出的三个特征图做蒸馏对齐,收益比 backbone 蒸馏更明显,因为检测头的定位和分类直接消费的是 neck 的输出。
第三是预测头。YOLOv5 的检测头输出是(batch, 3*(5+num_classes), grid_h, grid_w)的格式,蒸馏时需要把教师和学生的输出 reshape 成(batch, 3, grid_h, grid_w, 5+num_classes),然后分别取 objectness、box、class logits 来算 loss。这里有一个坑:教师和学生的 grid 尺寸必须一致,如果教师用了更大的输入,grid 尺寸会不同,蒸馏 loss 压根算不了。
2.3 损失函数怎么拼:Logits 蒸馏与 Feature 蒸馏的主次关系
知识蒸馏的损失函数有两类主流做法,YOLOv5 的检测场景下我建议两个都上,但主次要分清。
第一类是 logits 蒸馏,对应分类分支。YOLOv5 的分类头输出的是每个 anchor 在每个类别上的 logits,蒸馏时用 KL Divergence 让学生 logits 逼近教师 logits。温度 T 是这里的核心参数,T 越大,softmax 后的分布越平滑,学生能学到类别之间的相似关系。检测任务里我一般把 T 设在 3 到 7 之间,太低学不到暗知识,太高会把背景类的噪声也放大。
第二类是 feature 蒸馏,对应的是 backbone 和 neck 的中间特征。常用的做法是计算教师和学生 feature map 之间的 L2 loss 或者 L1 loss。但这里有个玄学问题:直接对齐绝对数值会让学生模型训练不稳定,因为教师特征的数值范围和学生完全不一样。业界更稳的做法是先用 attention map 或者归一化把特征变换到同一量纲,再算距离。我在实战里用的是 channel-wise 归一化后再算 MSE,效果比直接算稳得多。
整体 loss 的拼法是:
# 蒸馏总loss = YOLOv5原始loss + 蒸馏loss loss = loss_box + loss_obj + loss_cls + lambda_distill * loss_distilllambda_distill 的取值我在 0.1 到 1.0 之间都试过,最终稳定在 0.5 左右。蒸馏 loss 权重太大,学生会过度拟合教师的预测,反而丢失自己从 ground truth 学到的信息;太小则基本没效果。这个参数值得花时间细调,它比学习率对最终精度的影响更直接。检测头的 box 回归部分不参与蒸馏,因为 box 坐标是连续值回归问题,教师给的信息优势不大,直接让学生学 ground truth 更干净。
3. 把 YOLOv5 改造成支持蒸馏的训练框架:环境准备与代码结构
3.1 环境与依赖:conda 创建环境,避免污染现有项目
YOLOv5 的依赖相对固定,但蒸馏会引入额外的张量操作,建议单独用 conda 建一个环境,避免跟其他项目打架。我用的组合是 Python 3.9 + PyTorch 1.13 + CUDA 11.7,这个组合在 YOLOv5 官方仓库的 requirements 里能直接对齐。如果你用的是更新的 PyTorch 2.x,YOLOv5 也能跑,但某些算子行为可能有细微差异,蒸馏训练时显存占用会更高。
# 创建独立环境并激活 conda create -n yolo_kd python=3.9 -y conda activate yolo_kd # 安装 PyTorch 全家桶 pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117 # 安装 YOLOv5 的官方依赖 pip install -r requirements.txt这里要特别说一下requirements.txt里的版本问题。YOLOv5 官方仓库会定期更新依赖,如果你克隆的是最新版,里面的opencv-python和numpy版本可能和 PyTorch 1.13 有冲突。我踩过一次 numpy 2.0 和 PyTorch 1.13 的兼容性坑,训练到一半直接报_ARRAY_API not found。解决办法是把 numpy 降到 1.24 以下再装其他依赖。
3.2 目录结构:把蒸馏代码放在哪里
拿到 YOLOv5 源码包之后,默认目录结构是models/、utils/、data/、train.py、detect.py这些。我不建议把蒸馏逻辑全部塞进train.py,那样文件会膨胀到没法维护。常见做法是新增一个kd/目录,放三个模块:distill_loss.py、teacher_model.py、distill_utils.py。
yolov5/ ├── kd/ │ ├── __init__.py │ ├── distill_loss.py # 蒸馏loss计算 │ ├── teacher_model.py # 教师模型加载与冻结 │ └── distill_utils.py # 特征对齐、温度控制等工具函数 ├── models/ │ ├── yolo.py # 原模型定义(需微调) │ └── ... ├── train.py # 原训练入口(需微调) └── ...这个结构的好处是隔离清晰,train.py只负责调用蒸馏模块,具体的 loss 计算和特征对齐逻辑都封装在kd/下。如果要换蒸馏策略,比如从 logits 蒸馏换成 relation distillation,只需要改distill_loss.py,不需要动训练入口。
3.3 教师模型加载与冻结推理模式
教师模型加载是蒸馏框架里最容易出问题的一环。很多人直接torch.load教师权重就丢进训练循环,结果发现显存爆了——原因是教师模型也在反向传播,它的梯度也要保留。教师模型在整个训练过程中是只读的,必须冻结它的全部参数,并且在 forward 之外包torch.no_grad()。
def load_teacher_model(weights_path, device, half=True): # 加载教师模型,只推理不求梯度 teacher = torch.load(weights_path, map_location=device) teacher = teacher['model'].float() teacher.eval() # 冻结所有参数,不参与梯度计算 for param in teacher.parameters(): param.requires_grad = False # 半精度推理节省显存 if half: teacher = teacher.half() return teacher逻辑说明:torch.load返回的 checkpoint 是个字典,YOLOv5 的权重文件里model字段存放的是 nn.Module 实例。.eval()必须调用,否则模型里的 BatchNorm 层还在用训练模式,running mean 会继续更新,教师输出的特征分布就不稳定了。half()可以在显存吃紧时把教师模型切成半精度,但要注意蒸馏 loss 计算时要把教师输出转回 float32,否则数值精度不够会导致梯度波动。教师模型放在哪个设备上也有讲究,如果你的显存是 24GB,学生和教师放同一张卡没问题;如果只有 12GB,建议把教师放 CPU 上跑推理,虽然慢一点,但能省下 GPU 显存给学生模型做更大 batch。
4. 基于 YOLOv5 的知识蒸馏实战源码核心:训练主循环改写
4.1 KD Loss 的计算:从 teacher 输出到学生梯度
训练主循环的改写是整套源码的核心。原始 YOLOv5 的train.py里,每个 batch 的流程是:前向学生模型、算 loss、反向传播、更新参数。蒸馏模式下,流程变成:前向教师模型(冻结)、前向学生模型、分别算原始 loss 和蒸馏 loss、合并反向传播。
for batch_idx, (imgs, targets, paths, _) in enumerate(dataloader): imgs = imgs.to(device).float() / 255.0 targets = targets.to(device) # 教师模型前向,不计算梯度 with torch.no_grad(): teacher_outputs = teacher_model(imgs) # 学生模型前向 student_outputs, student_feats = student_model(imgs) # 原始YOLOv5 loss(box + obj + cls) loss_box, loss_obj, loss_cls = compute_yolo_loss(student_outputs, targets) # 蒸馏loss,基于教师和学生的输出 loss_distill = distill_loss( teacher_outputs=teacher_outputs, # 教师logits和特征 student_outputs=student_outputs, # 学生logits和特征 temperature=3.0, lambda_feat=0.5 ) # 合并loss loss = loss_box + loss_obj + loss_cls + 0.5 * loss_distill # 反向传播 loss.backward() optimizer.step() optimizer.zero_grad()这段代码是训练主循环的最小骨架。compute_yolo_loss是 YOLOv5 原始代码里的ComputeLoss类,我直接引用了没有改动。distill_loss是核心函数,负责把教师的输出和学生的输出对应起来算 KL 散度和特征距离。参数说明:temperature=3.0在分类数较少的数据集上偏保守,如果检测类别超过 20 个,可以尝试把温度调到 5.0,让类别分布更平滑;lambda_feat=0.5控制特征蒸馏在总蒸馏 loss 里的占比,如果训练早期 loss 波动太大,可以先把 lambda_feat 降到 0.2 看看稳定性。
student_model(imgs)返回了两个值,这个需要你在models/yolo.py的 forward 里改造。原始 YOLOv5 的 forward 只返回检测输出,拿到后面做 NMS 用。蒸馏训练需要额外的中间特征,我在 forward 里加了一个return_feats的开关,默认是False,训练时置为True就能拿到 backbone 和 neck 的多层特征。
4.2 Feature 蒸馏:用 attention map 对齐特征分布
仅仅对 logits 做 KL 散度,学生学到的是"结果"而非"过程"。YOLOv5 的检测能力高度依赖多尺度特征融合,所以特征层面的蒸馏不能省。我实现了一个 channel-wise attention 对齐的方案,比直接算 MSE 更稳。
def distill_feature_loss(teacher_feats, student_feats, device): """ teacher_feats和student_feats都是列表,包含三个尺度的特征图 每个特征图形状: (batch, channels, grid_h, grid_w) """ total_loss = 0.0 for t_feat, s_feat in zip(teacher_feats, student_feats): # Channel-wise 归一化,消除量纲差异 t_norm = F.normalize(t_feat, p=2, dim=1) s_norm = F.normalize(s_feat, p=2, dim=1) # 计算通道注意力权重 t_attn = torch.mean(torch.abs(t_feat), dim=(2, 3), keepdim=True) s_attn = torch.mean(torch.abs(s_feat), dim=(2, 3), keepdim=True) # 加权后的L2距离 t_weighted = t_norm * t_attn s_weighted = s_norm * s_attn # 逐像素计算MSE loss = F.mse_loss(s_weighted, t_weighted) total_loss += loss return total_loss / len(teacher_feats)逻辑说明:这里做了两步关键处理。第一步是F.normalize把特征图沿 channel 方向归一化,消除教师和学生特征数值范围不一致的问题。如果不做这一步,教师特征中某些通道的值是学生的 10 倍,MSE loss 会被这些通道主导,学生模型只会疯狂拟合大数值通道,其他通道学不到东西。第二步是通道注意力加权,这个 trick 来自实验经验——特征图中响应值大的通道(也就是模型更关注的语义信息)应该在蒸馏里占更高权重,用torch.mean(torch.abs())算出一个通道级别的标量,乘到归一化后的特征上。实践效果是:加入 attention 加权后,蒸馏收敛速度明显加快,早期 loss 震荡也减轻了。
尺度对齐是另一个必须有前瞻性的点。YOLOv5 的 neck 输出三个尺度,如果教师和学生输入尺寸不一样,grid 大小就对不上。我处理的方法是强制蒸馏时教师和学生使用相同输入尺寸,且不启用 mosaic 增强的随机缩放部分。mosaic 会随机改变图像的拼接比例,导致 label 和特征的对应关系在不同 batch 之间漂移,蒸馏 loss 会被这种漂移干扰。我一般在蒸馏训练阶段把mosaic概率从默认的 1.0 降到 0.5,保证特征对齐的稳定性。
4.3 训练参数:温度、权重系数、学习率怎么调
蒸馏训练的参数设定和普通训练有明显差别。下面是我在多个数据集上调过之后认为比较稳的默认起点,适用于 COCO 类别的检测任务。
| 参数 | 建议值 | 说明 |
|---|---|---|
| 温度 T | 3~7 | T 越大类别分布越平滑,暗知识越充分,但 T 过大会放大背景噪声 |
| 蒸馏权重 λ | 0.5 | 超过 1.0 学生会过度拟合教师,低于 0.1 基本无效果 |
| 特征蒸馏权重 λ_feat | 0.2~0.5 | 先小后大,前期 focus 在 logits 蒸馏 |
| 学习率 lr | 0.001 | 比原始训练略低,蒸馏本身能加速收敛 |
| 优化器 | SGD | Adam 在蒸馏场景下容易丢失精度 |
| Batch size | 尽量大 | 蒸馏对 batch 的稳定性更敏感,建议 >= 32 |
| 训练轮数 | 原始训练的 60%~80% | 蒸馏加速收敛,不需要训满 300 epoch |
学习率是这里最反直觉的一个参数。我一开始按原始训练的习惯用 0.01,结果训练 loss 震荡得非常厉害——因为 KL 散度对 logits 的尺度变化很敏感,学习率太大导致学生的 logits 跳动幅度大,蒸馏这部分 loss 根本稳定不下来。降到 0.001 之后整个训练就顺了。这里给一个调参思路:如果你观察到训练早期蒸馏 loss 在下降但原始 yolo loss 在上升,说明学习率太高,学生模型被蒸馏 loss"带偏"了,需要降 lr 或者降 λ。
SGD 和 Adam 的选择也是实践里踩出来的。Adam 在普通 YOLOv5 训练上表现一般,但蒸溜场景下问题更严重——它对 logits 的梯度响应不均衡,容易让学生模型在蒸馏收敛后又跑偏。SGD + momentum 0.937 是 YOLOv5 官方默认组合,在蒸馏模式下表现更稳定。
5. 避坑指南:基于 YOLOv5 的知识蒸馏常见的 5 个翻车现场
5.1 教师模型没冻结,显存直接爆掉
现象:训练刚开始,GPU 显存占用飙到接近满格,但不报错,只是训到第 10 个 batch 左右 OOM,进程被杀。原因:教师模型的参数没有冻结,反向传播时系统要保存教师模型的所有激活值,显存占用比学生模型还高。解决:在加载教师模型后显式遍历所有参数,把requires_grad都置为False,同时在每个 batch 前向时用torch.no_grad()包住教师推理。做了这两步之后,显存占用基本和学生单独训练差不多,只多了教师 forward 的临时缓存。
提示:检查冻结是否生效的一个小技巧是打印教师模型参数梯度的总数,如果
torch.nn.utils.parameters_to_vector(teacher.parameters()).grad不为 None,说明还有参数没冻结。
5.2 温度设太高,学生学到了背景噪声
现象:训练 loss 降得很好,但验证集 mAP 不升反降,而且降的主要是 recall。原因:温度 T 设到了 10 以上,softmax 输出被拉得太平,教师对背景类(通常是 80 个类别里的第 0 类)给出的微小 logits 也被放大成了有效信号,学生疯狂拟合这些噪声,反而忽略了真实目标类别之间的区分。解决:把 T 拉回 3 到 5 的范围,同时调整蒸馏 loss 只对前景类别的 logits 计算 KL 散度,背景类的 logits 直接 mask 掉。我在代码里加了一个foreground_mask参数,根据 ground truth 的 label 把背景位置过滤掉再算蒸馏 loss,效果非常明显。
5.3 小目标不蒸馏:特征对齐后小目标检测反而变差
现象:蒸馏后整体 mAP 涨了,但小目标(面积小于 32x32 像素)的 AP 掉了 2 个点以上。原因:YOLOv5 的三个预测尺度对应下采样 8 倍、16 倍、32 倍的特征图,小目标主要由下采样 8 倍的大特征图负责。但大特征图包含大量背景纹理信息,蒸馏时教师和学生在这些位置的特征差异大,loss 权重大,模型为了拟合教师特征,把更多容量花在了背景纹理上,反而挤占了对小目标的学习。解决:在特征蒸馏时对不同尺度的特征图设置不同的权重,下采样 8 倍的特征权重降低到 0.3,下采样 32 倍的高层语义特征权重提到 0.7。这样学生模型不会把注意力全放在低层纹理上,小目标精度能保住。
5.4 评测指标没对齐:mAP 涨了但 PR 曲线崩了
现象:mAP 从 0.72 涨到 0.76,很兴奋,但换了测试集之后精度暴跌,泛化变差了。原因:蒸馏让学生的输出分布向教师靠拢,而教师的预测置信度普遍偏高,学生学到的不仅是"怎么检测"还有"多自信"。如果只对比 mAP 一个指标,看到的是整体提升,但 PR 曲线其实变得陡峭了——高置信度区域的 precision 上去了,低置信度区域的 recall 掉了。解决:评测时一定要同时看 mAP@0.5 和 mAP@0.5:0.95 两组指标,这两者的差值如果超过 8 个点,说明模型过拟合了教师的置信度分布。我还会额外跑一次 TTA(Test Time Augmentation)做对照,蒸馏后的模型 TTA 提升幅度应该和原始模型差不多,如果提升异常大,说明模型本身的判别力不足,只是学到了教师的"自信感"。
5.5 多卡训练的同步问题:DDP 下蒸馏 loss 不一致
现象:使用torch.distributed跑多卡训练,每张卡的 loss 都不一样,而且差异不是随机噪声,是肉眼可见的系统性偏差。原因:DDP 模式下每张卡独立前向教师模型,但 BatchNorm 的 running mean 是在每张卡上独立更新的,如果不做同步,教师模型在每张卡上的特征分布会逐渐偏离。带 BatchNorm 的 YOLOv5 教师模型在没有同步 BN 的情况下,不同卡输出的 teacher logits 都不一样。解决:在 DDP 初始化里加上sync_bn=True,把教师的 BatchNorm 层同步。但要注意,YOLOv5 的模型结构里有些 BN 层的 num_features 比较大,同步 BN 会额外增加通信开销,训练速度会降 5% 左右,这是值得付的代价。
6. 蒸馏效果验证与进阶玩法:怎么确认这套源码真的有用
6.1 蒸馏前后的同权重评测脚本:排除变量干扰
很多人在蒸馏之后直接用 YOLOv5 官方的val.py跑一遍 mAP 就下结论说"涨了多少",这个流程有漏洞。因为你可能改了训练超参数、数据增强、训练轮数,甚至换了优化器,mAP 的提升可能来自这些改动而不全是蒸馏的功劳。我一般用一个独立的评测脚本,把教师、学生、蒸馏后学生三个模型放在同一个评测配置下跑一模一样的验证集,然后打印对比表。
# 教师模型基准 python val.py --data coco.yaml --weights teacher.pt --img 640 --task val # 学生模型直接训练的基准 python val.py --data coco.yaml --weights student_baseline.pt --img 640 --task val # 学生模型蒸馏后的结果 python val.py --data coco.yaml --weights student_distilled.pt --img 640 --task val评测结果记录三个数:mAP@0.5、mAP@0.5:0.95、推理耗时。推理耗时要在同一个设备上测,最好固定 batch size,前 50 次 warmup 之后取平均。这个对比表能回答三个问题:蒸馏到底涨了多少、涨的点值不值得付训练时间成本、学生蒸馏后和教师的差距还有多大。
6.2 把蒸馏和剪枝、量化串起来:蒸馏是前端不是终点
知识蒸馏不是说做完就完了,它最大的价值是给后续的模型压缩铺路。我常用的链路是:蒸馏 → 剪枝 → 量化。蒸馏先把学生模型的精度拉上去,相当于给模型挖了一层"额外储备";然后做结构剪枝,把一些响应值低的通道剪掉,精度会掉,但蒸馏带来的储备正好弥补这部分的损失;最后做 INT8 量化,精度还会再掉一点,蒸馏储备还能再兜一程。
这条链路里蒸馏的位置特别讲究:它必须放在剪枝之前,不能放在剪枝之后。因为剪枝后的模型结构已经不完整了,再去对齐教师的特征图会出现结构不匹配的问题。我做过一次顺序颠倒的试验,先剪枝再蒸馏,效果很差——剪枝后的通道数和教师对不上,只能做 logits 蒸馏,特征蒸馏完全没法用,最后精度比先蒸馏再剪枝低了 3 个点。
6.3 训练日志的可视化:蒸馏 loss 应该在哪个范围
判断蒸馏是否正常,不能只看 loss 曲线形态,还要看具体数值范围。我总结了一个经验:蒸馏 loss 占总 loss 的比例如果在 10% 到 30% 之间,说明蒸馏的作用是"辅助修正"而不是"主导训练",这是健康的状态。如果蒸馏 loss 占比超过 50%,学生模型基本是被教师"摁着学",它会丧失从 ground truth 学习新信息的能力。
# 在训练循环里记录蒸馏loss占比 distill_ratio = loss_distill.item() / loss.item() if batch_idx % 10 == 0: logger.info(f"Distill ratio: {distill_ratio:.3f}, total loss: {loss.item():.3f}")如果发现 distill_ratio 持续高于 0.5,就把 λ_distill 往下调,或者提高 batch size 让原始 loss 更稳定。如果 distill_ratio 低于 0.05,说明蒸馏已经不起作用了,可以停掉蒸馏,直接用学生模型 finetune 到最后。用 tensorboard 记录 loss 曲线时,记得同时记录 distill_ratio 这个标量,它比单独的 loss 曲线更能反映蒸馏的训练状态。
最后说一个习惯
我跑蒸馏项目至今,最有效的经验不是调参技巧,而是养成"每改一个变量只动一个变量"的强迫症心态。蒸馏涉及教师模型、温度、多个权重系数、学习率、数据增强,任何两个变量同时变动,结果出了问题你都分不清是谁导致的。有一次我把温度从 3 调到 5 的同时改小了 λ_feat,结果 mAP 掉了 2 个点,排查了半天才定位到是 λ_feat 改动的影响,温度根本没造成问题。从那以后我每次只改一个参数,跑完一组对比再动下一个。这套基于 YOLOv5 的知识蒸馏实战源码本身不复杂,复杂的是你对待实验变量的态度,希望帮到你。
本文还有配套的精品资源,点击获取