1.4 师傅、徒弟与传承课:蒸馏三要素 本节摘要:一次蒸馏开工前,只需要问清三件事:师傅是谁(教师模型)、徒弟是谁(学生模型)、传承课用什么教材(软目标与训练数据)。本节把三要素的构成、常见形态与检查清单一次讲透,作为全册后续所有教法的"开工底座"。概念上承 1.2 的定义,向下为第二章"老师傅知道什么"搭好舞台。 前两节讲了为什么和值不值,本节回到零件层面:把一次蒸馏拆成三个部件,逐件验收。第二章要讲的"知识形态",其实就是传承课这门课的三种教材;第四章要讲的教法变体,都是三要素的排列组合。把本节的清单带在身上,后面读起来会轻松很多。 要素一:教师模型——师傅是谁 教师不一定是参数最大的那个模型,而是"对这个任务判断最有分寸"的模型。
本节摘要:一次蒸馏开工前,只需要问清三件事:师傅是谁(教师模型)、徒弟是谁(学生模型)、传承课用什么教材(软目标与训练数据)。本节把三要素的构成、常见形态与检查清单一次讲透,作为全册后续所有教法的"开工底座"。概念上承 1.2 的定义,向下为第二章"老师傅知道什么"搭好舞台。
前两节讲了为什么和值不值,本节回到零件层面:把一次蒸馏拆成三个部件,逐件验收。第二章要讲的"知识形态",其实就是传承课这门课的三种教材;第四章要讲的教法变体,都是三要素的排列组合。把本节的清单带在身上,后面读起来会轻松很多。
教师不一定是参数最大的那个模型,而是"对这个任务判断最有分寸"的模型。挑选时看三样:
# 检查教师输出的校准程度:看 top-1 置信度与实际准确率是否匹配 import torch import torch.nn.functional as F def confidence_buckets(teacher, loader, n_bins=5): teacher.eval() buckets = [[] for _ in range(n_bins)] # 每个桶存"是否答对" with torch.no_grad(): for x, y in loader: p = F.softmax(teacher(x), dim=-1) conf, pred = p.max(dim=-1) for c, ok in zip(conf, pred.eq(y)): b = min(int(c * n_bins), n_bins - 1) buckets[b].append(ok.item()) for i, b in enumerate(buckets): if b: print(f"置信度区间 [{i/n_bins:.1f},{(i+1)/n_bins:.1f}): " f"样本数 {len(b)}, 实际正确率 {sum(b)/len(b):.3f}") # 健康的教师:高置信桶的实际正确率应接近置信度本身。 # 若 0.8-1.0 桶的正确率远低于 0.9,说明教师过度自信,软目标质量存疑。 # 输出示例: # 置信度区间 [0.0,0.2): 样本数 312, 实际正确率 0.051 # 置信度区间 [0.2,0.4): 样本数 448, 实际正确率 0.279 # 置信度区间 [0.4,0.6): 样本数 731, 实际正确率 0.512 # 置信度区间 [0.6,0.8): 样本数 1102, 实际正确率 0.741 # 置信度区间 [0.8,1.0): 样本数 8407, 实际正确率 0.934
学生的设计自由度比教师大,但不是随便挑个小网络。三个约束按优先级排:
传承课的教材由两部分构成:训练数据本身,和教师在这些数据上的输出。第二部分就是 1.2 讲的软目标,工程上分两路走:
# 离线打分:把教师的软化输出预存成文件,训练时不再驮着教师 import torch import torch.nn.functional as F @torch.no_grad() def dump_teacher_logits(teacher, loader, out_path): teacher.eval() chunks = [] for x, _ in loader: # 标签不需要,但要保序 logits = teacher(x) soft = F.softmax(logits / 4.0, dim=-1) # 训练温度在这里就定好 chunks.append(soft.cpu()) torch.save(torch.cat(chunks), out_path) # 训练循环里按索引读取对应软目标即可, # 显存占用从"教师+学生"降到"学生", # 对于教师很大的场景(如数 GB 权重)这是决定性的差别。 # 输出说明:out_path 保存形状为 N x C 的张量;注意 loader 不得打乱顺序。
背景。 某团队要把线下门店的客流属性识别模型从服务器搬到边缘盒子,立项一份蒸馏任务。盒子预算:权重 40 MB 以内,单帧 80 毫秒。
操作。 按三要素逐项验收。教师:选用现有服务端模型(该任务测试集 92.5%),跑一遍校准检查,高置信桶实际正确率 0.934,校准尚可,通过。学生:按算力表初选三个候选结构,参数量分别约 300 万、800 万、1500 万,先各做一轮短程直接训练摸底。传承课:门店数据 40 万张,隐私原因不能离开门店内网,决定在线打分——教师部署在内网训练机上与学生同批前向。
结果。 短程摸底显示 300 万参数的候选学不动(直接训练准确率 83.1%,且加软目标后提升不足一个点),800 万参数版本加软目标后从 86.7% 提到 88.9%,1500 万版本 89.7%。800 万版本权重 int8 量化后约 29 MB,单帧实测 61 毫秒,双双达标,选定它为正式学生。
解读。 这次验收验证了三件事:容量鸿沟真实存在(300 万版本"喂不进")、部署算力表是硬约束(1500 万版本精度虽高但量化后 55 MB 超预算)、打分方式由数据出域限制决定。注意 300 万版本若配合第四章的层深度对齐或多教师手段仍有救,此处先按朴素方案定案。
变式。 若数据能出域,离线打分能省一半训练显存;若教师校准检查不通过,先对教师做温度缩放校准再当师傅;若三个候选全部学不动,回到 6.1 重新设计学生结构,而不是硬调温度。
⚠️ 常见坑:开工前忘查教师的输出校准。一个过度自信的教师,其软目标在高温下也提不出有区分度的暗知识,学生会把整个训练周期浪费在模仿一个"只会说肯定"的师傅上。
💡 关键直觉:三要素里最容易被草率对待的是学生模型。教师是现成的,教材是现成的,学生结构却常常"随手挑一个小的"——而容量鸿沟与部署预算这两个最常见的翻车点,都埋在学生设计里。