4.1 先学后教与边学边教:离线与在线蒸馏


文档摘要

4.1 先学后教与边学边教:离线与在线蒸馏 本节摘要:离线与在线蒸馏是教法组织的两大基本形态:教师是预先练成的现成师傅,还是与学生同堂训练的"同学兼师傅"。本节对比两种形态的损失结构、资源开销与适用前提,给出深度互学习的最小实现——它是在线蒸馏最有代表性的门派,也是"没有教师可用"时的解法之一。 第四章开篇。前三章的蒸馏都默认教师现成:先有老师傅,后有学生。本节松开这个假设,回答课堂组织的第一个问题——教学在时间上怎么排。 两种课堂组织 离线蒸馏:教师预先训练完毕、参数冻结,学生训练全程只做一次教师前向。软目标可以在线打分(教师陪跑)也可以离线打分(预存输出,见 2.1),但无论哪种,教师自身都不再更新。它的优势是训练稳定、可并行、教师可以反复复用;

4.1 先学后教与边学边教:离线与在线蒸馏

本节摘要:离线与在线蒸馏是教法组织的两大基本形态:教师是预先练成的现成师傅,还是与学生同堂训练的"同学兼师傅"。本节对比两种形态的损失结构、资源开销与适用前提,给出深度互学习的最小实现——它是在线蒸馏最有代表性的门派,也是"没有教师可用"时的解法之一。

第四章开篇。前三章的蒸馏都默认教师现成:先有老师傅,后有学生。本节松开这个假设,回答课堂组织的第一个问题——教学在时间上怎么排。

两种课堂组织

离线蒸馏:教师预先训练完毕、参数冻结,学生训练全程只做一次教师前向。软目标可以在线打分(教师陪跑)也可以离线打分(预存输出,见 2.1),但无论哪种,教师自身都不再更新。它的优势是训练稳定、可并行、教师可以反复复用;隐含的上限是——学生的天花板被这位固定的师傅封住。

在线蒸馏:没有现成师傅。训练现场同时有多个模型在学,它们互为师生、共同进步。最经典的形态是深度互学习:两个结构不同的网络从随机初始化同时开训,各自的总损失里都含一项"向对方当前输出学习"的 KL。谁在某一步更强,谁就暂时当师傅;互相拉扯着,两个学生的终局往往都好于各自单练。

import torch import torch.nn.functional as F def mutual_learning_step(net_a, net_b, x, y, T=4.0, beta=0.5): """深度互学习的一步:两个网络互为师生。 beta 是向同伴学习的权重,其余是常规硬标签损失。""" la, lb = net_a(x), net_b(x) loss_a = F.cross_entropy(la, y) loss_b = F.cross_entropy(lb, y) # 各自向"对方当前的软化输出"学习;detach 切断对侧的梯度 peer_a = F.kl_div( F.log_softmax(la / T, dim=-1), F.softmax(lb.detach() / T, dim=-1), reduction="batchmean") * T * T peer_b = F.kl_div( F.log_softmax(lb / T, dim=-1), F.softmax(la.detach() / T, dim=-1), reduction="batchmean") * T * T total_a = (1 - beta) * loss_a + beta * peer_a total_b = (1 - beta) * loss_b + beta * peer_b total_a.backward() total_b.backward() return total_a.item(), total_b.item() # 输出说明:返回两个网络的损失标量。 # 关键在 detach():a 学 b 的当前输出,但这个学习行为不能 # 反过来把 b 往"迁就 a"的方向拽——否则两个网络互相躺平。

在线形态讲完,离线形态还有一个工程上很值钱的变体:预打分。教师对整个训练集的软化输出只需算一次、存成缓存,之后学生训练的每个轮次都直接读缓存——教师前向从"每步都跑"降到"全程一次",多轮实验时训练开销几乎与普通训练持平。

# 离线蒸馏的预打分:教师输出算一次,缓存后反复复用 import torch import torch.nn.functional as F @torch.no_grad() def precompute_teacher_logits(teacher, loader, path_stub): teacher.eval() saved = 0 for x, _y in loader: logits = teacher(x) # 只存 logits,不存软化分布 torch.save(logits.cpu(), f"{path_stub}_{saved:06d}.pt") saved += 1 return saved # 返回缓存分片数 def distill_from_cache(student, x_batch, y_batch, t_logits, T=4.0, alpha=0.9): s_logits = student(x_batch) # 学生的当前批前向 soft = F.kl_div(F.log_softmax(s_logits / T, dim=-1), F.softmax(t_logits / T, dim=-1), reduction="batchmean") * T * T hard = F.cross_entropy(s_logits, y_batch) return alpha * soft + (1 - alpha) * hard # 总损失:软硬两项配比 # 输出示例:10 万样本的教师预打分在单卡上约 25 分钟; # 之后学生每轮训练读缓存,教师前向开销降为 0。 # 注意两点:一是存 logits 而非 softmax 结果——温度要换时不必重算; # 二是数据增强要在打分前决定,增强策略一变,缓存全部作废。

预打分把离线蒸馏的成本结构改写了:教师越大、实验轮数越多,省得越多。反过来说,它也是离线与在线选择的一个隐性砝码——在线蒸馏没有缓存可用,教师(或同伴)输出每步都得现算。

怎么选

维度 离线蒸馏 在线蒸馏
教师来源 必须有现成强教师 不需要,同堂互教
训练期开销 教师可预存输出,增量小 所有成员全程在训
稳定性 高,教师输出固定 中,早期同伴输出噪声大
精度上限 受固定教师封顶 可能超过单教师上限
工程复杂度 中,需协调多个优化器

一句话决策:有强教师就用离线(简单稳定收益高),没有教师但训练预算管够才考虑在线。实践中九成生产蒸馏是离线的,在线蒸馏的价值更多在"从零起家的团队怎么起步"和"多模型互相成就"这两个场景。

一条完整案例:没有教师时的冷启动

背景。 某初创团队做工业缺陷分类,数据私有且规模中等(约 20 万张),没有大模型可用,直接训练的最大网络只到 91.4%,业务需要 93%。

操作。 走在线蒸馏:同堂放两个结构互补的网络——一个宽而浅的卷积网、一个窄而深的残差网,各自硬标签损失加向同伴学习的 KL(beta 0.5,温度 4),同步训练 120 轮。训练中期做了一次调度调整:前 40 轮 beta 从 0.1 线性升到 0.5,让两人在各自站稳之后再开始互相影响。

结果。 两个终局网络分别达到 92.8 与 93.1,均超过单练的 91.4;随后把 93.1 的网络当教师做一轮离线蒸馏,教出部署用的轻量学生 92.6,达标上线。

解读。 这条案例展示了在线蒸馏的标准用法:它不是离线蒸馏的替代,而是"无教师冷启动"的垫脚石——先用互学习把现有数据榨出超出单模型的成绩,再把最强者转任教师走成熟的离线路线。beta 预热的细节也值得记:训练早期同伴输出接近随机猜,直接高权重互学会让两个网络把彼此的噪声当真话。

变式。 同堂成员从两个扩展到三四个(多分支或多结构)是常见变体,收益边际递减、协调成本递增;另一种打法是"自榕式"快照集成——同一个网络在训练路上的多个历史快照互为师生,省掉多模型的显存开销,这条思路在 4.3 的自蒸馏里展开。

⚠️ 常见坑:在线蒸馏去掉 detach。两个网络的梯度互相穿透后,损失会找到"互相迁就"的捷径——双方输出趋同、谁都不少学但谁也没多学,表现为互学损失迅速归零而精度停滞。

💡 关键直觉:离线蒸馏是"拜师",在线蒸馏是"同学互助"。没有师傅时,两个同学互相讲题,往往都能比自己闷头学考得好——但别忘了最后还是要有一个人出师带新人。

本节要点回顾

  • 基本二分:教师冻结预先练成是离线;多模型同堂互为师生是在线。
  • 深度互学习:两人各自硬损失加向同伴软化输出学习的 KL,detach 切断对侧梯度是命门。
  • 选择依据:有强教师选离线;无教师且预算充足才上线;beta 早期预热防互学噪声。
  • 冷启动套路:在线互学习练出最强者,转任教师走离线蒸馏出部署学生。
  • 上限差异:离线受固定教师封顶,在线可能超越任何单成员的初始上限。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U