13. 蒸馏、继续预训练与模型合并:把能力搬到合适的模型上
前面几篇一直在让资料助手变得更好:SFT 和 LoRA 教它按公司口径回答,偏好对齐教它不乱承诺,强化学习教它算对加班费。现在它跑在 8B 模型上,效果满意,新问题又来了:一是太贵,团队想要一个 1.5B 的版本,部署在更便宜的卡上,承接大部分简单问题;二是“条款格式”和“加班费计算”是两次分别训练的结果,线上维护两个模型很麻烦,能不能合成一个。
这一篇讲解决这两个问题的三种手段:蒸馏把大模型的能力迁到小模型上;继续预训练在这条链路里的位置简短回顾;模型合并不再训练,直接在权重上做加减。读完应能判断:逐 token 蒸馏和序列级蒸馏各需要什么条件,蒸馏数据要怎样过滤和评估,用别人模型的输出做训练要查哪些条款,以及模型合并为什么只对同一底座有效。
为什么要一个更小的模型
先看推理侧的账。按 BF16 存储:
text
8B 教师:80.3 亿参数 × 2 字节 ≈ 16.1 GB
1.5B 学生(Qwen2.5-1.5B-Instruct):15.4 亿 × 2 字节 ≈ 3.1 GB推理系列讲过,生成阶段每出一个 token 都要把全部权重读一遍,速度主要受显存带宽限制(见GPU 与 Roofline)。权重小到约五分之一,同一张卡上每个 token 的读取量也约是五分之一,还能腾出显存给更多并发。
直接拿 SFT 数据训练 1.5B 当然也可以,但人工写的标准回答通常只有几百到几千条,小模型从这么少的样例里学不全。蒸馏的思路是:让已经调好的 8B 当老师,给大量问题写回答、甚至给出每个词的概率,学生从老师那里拿到比人工标注多得多、也更细的训练信号。
逐 token 蒸馏:学老师的“犹豫”
最早的蒸馏形式来自 Hinton 等人 2015 年的论文。训练语料只告诉模型正确答案是哪一个,这叫硬标签;老师模型对每个候选都给出概率,这叫软标签。软标签里藏着额外的信息:
看图的下半部分。“年假需提前三个”后面,硬标签只说是“工作日”;老师则认为“工作日”0.80、“自然日”0.12、“小时”0.05、“月”0.03。学生从中能学到,“自然日”虽然不对,但比“月”更接近。这类“错得有多近”的信息,硬标签里完全没有。
老师通常很自信,软标签过于尖锐,次要候选的概率接近 0,信息量不大。Hinton 的办法是用温度 T 把分布变平:
text
p_i = exp(z_i / T) ÷ Σ_j exp(z_j / T)
z_i:老师对第 i 个候选的原始分数(logit)
T = 1 是正常的 softmax;T 越大分布越平
示意:T = 1 时 0.80、0.12、0.05、0.03
T = 2 时 0.55、0.21、0.14、0.11(相当于对 T=1 的概率开平方再归一化)训练学生时,损失由两部分组成:一部分是对硬标签的普通交叉熵,另一部分是学生在同样温度下的分布与老师分布之间的差距(KL 散度)。软目标部分的梯度会随温度按 1/T² 缩小,所以要乘以 T² 把它补回来,否则调温度时两部分的比重就变了。
逐 token 蒸馏有两个硬条件。第一,要拿得到老师每个位置的完整概率分布,开源权重可以,商业 API 通常最多返回前几个候选的概率。第二,老师和学生的词表要一致,否则两边的“第 i 个候选”根本不是同一个东西。资料助手的老师 Llama-3.1-8B 词表 128,256,学生 Qwen2.5-1.5B 词表 151,665,“工作日”在两边的切法都可能不同,不能直接逐位置对齐。
序列级蒸馏:直接学老师写出来的回答
另一条路只要老师的文字输出:让老师对一批输入生成完整回答,再把这些回答当作 SFT 数据训练学生。这叫序列级蒸馏,最早由 Kim 和 Rush 在 2016 年用于机器翻译,他们最好的学生比老师快约 10 倍,翻译质量损失很小,用贪心解码时比不蒸馏的同尺寸模型高 4.2 BLEU。
它对词表没有要求,也不需要老师的内部概率,所以是今天最常用的做法。DeepSeek-R1 的蒸馏模型就是这样做的:用 R1 整理了约 80 万条样本(约 60 万条推理、20 万条非推理),对 Qwen2.5-Math-1.5B、Qwen2.5-Math-7B、Qwen2.5-14B、Qwen2.5-32B、Llama-3.1-8B、Llama-3.3-70B-Instruct 这些底座只做 SFT,不做强化学习。论文还做了一个对比:在 Qwen2.5-32B 上直接做大规模强化学习,效果明显不如从 R1 蒸馏出来的同尺寸模型。对小模型,先从强老师那里蒸馏,通常比自己从头探索更划算。
资料助手的蒸馏流程
回到图的上半部分。资料助手的蒸馏按五步走:
- 准备问题集:从脱敏后的员工提问日志里取真实问法,再做改写扩充到数千条,刻意覆盖跨条款的问题和“制度里没写”的问题。留出一部分作为评测集,这部分永远不进生成和训练。
- 老师生成:用已经调好的 8B 资料助手,带上检索到的制度片段回答每个问题,每题采样几份不同的回答。
- 程序过滤:老师也会错,不过滤就会被学生原样学走。用程序检查:回答里引用的条款编号必须出现在检索片段中;格式符合要求;检索片段里没有答案时必须说“不知道”而不是编一个。每题留一份通过检查的回答,都不通过就丢弃或交给人工。
- 学生 SFT:学生 Qwen2.5-1.5B-Instruct(Apache 2.0 许可,28 层,32K 上下文)的输入同样包含检索片段,只对回答部分算损失。训练方法和第 09 篇、第 10 篇一样,全参或 LoRA 都可以。
- 同卷评估:在留出的评测集上同时跑三个模型:老师、学生原版、蒸馏后的学生。比较条款编号准确率、该拒答时是否拒答、回答是否与检索片段一致,以及每次回答的延迟和成本。
评估结果决定怎样用这个学生,而不是“蒸馏完就替换”。常见的结局是:学生在单条款查询上接近老师,在“工龄满 9 年零 11 个月、当年入职、跨年请假”这类多步计算上明显变差。这时可以按问题类型路由:简单查询给 1.5B,涉及计算或多条款的转给 8B。评估发现的薄弱题型,再补问题、让老师重新生成,进入下一轮(图中的虚线)。
只在和训练问题相似的题上评估,会得到过于乐观的结论。评测集要包含训练时没见过的问法和新修订的制度条款,才能看出学生是真的学会了查片段回答,还是只背下了常见问题的答案。
在线策略蒸馏
序列级蒸馏有一个隐患:学生训练时看到的永远是老师写的完美前文,上线后却要在自己写出的、可能已经有错的前文上继续写,训练和使用的分布不一致。在线策略蒸馏(on-policy distillation)让学生自己生成回答,老师在学生生成的每个位置给出它认为的概率分布,学生据此修正,Google 2023 年提出的 GKD 是代表方法。学生能学到“自己犯错之后该怎样接”,代价是训练时老师要在线计算,而且逐位置比较分布,通常也要求两边词表一致。
用别人模型的输出训练:先查条款
蒸馏数据来自另一个模型,这个模型的许可证和服务条款就成了约束。下面是要核对的项目,具体能否使用需要法务按条款原文和所在地区判断:
- 开源模型许可证:Llama 3.1 社区许可证允许用其输出训练其他模型,模型卡明确列出了合成数据和蒸馏用途;但用 Llama 材料或其输出训练、改进并对外分发或提供的模型,名称开头要加“Llama”,并在相关页面显著标注“Built with Llama”。月活超过 7 亿的公司还需另行申请许可。
- 学生底座的许可证:Qwen2.5-1.5B-Instruct 是 Apache 2.0;同一系列的不同尺寸许可证可能不同(例如 Qwen2.5-3B-Instruct 标注的是 qwen-research 许可),要逐个核对。
- 商业 API 的服务条款:很多商业模型的条款对“用输出开发与之竞争的模型”有限制,以使用时的条款版本为准。
- 混合来源:DeepSeek-R1 本身是 MIT 许可、允许蒸馏,但它基于 Llama 的蒸馏版仍受 Llama 许可证约束。数据从哪个模型来、学生基于哪个底座,两边都要查。
把老师模型名称、版本、许可证版本和生成日期记进数据集的说明里,后面审计时才说得清。
继续预训练在这条链路的哪里
第 07 篇讲过继续预训练:用领域语料以预训练目标再训一段,适合有数十亿 token 级领域语料或要补一门语言的情况,要用学习率重新预热和回放通用数据控制遗忘。放到整条链路里,它的位置在 SFT 之前:先继续预训练让底座熟悉领域文本,再做 SFT、偏好对齐,最后需要小模型时再蒸馏。蒸馏生成的数据在形式上是 SFT 数据,进入的是 SFT 这一步。资料助手的语料规模达不到继续预训练的门槛,所以它的链路里没有这一步。
模型合并:不训练,直接在权重上做加减
第二个问题:团队在同一个 8B 底座上训了两次,A 学会了带条款编号的回答格式,B 学会了加班费计算。能不能不再训练,直接把两者合成一个模型?
权重平均与任务向量
最简单的合并是对权重取平均。2022 年的 model soups 研究发现,从同一个预训练模型出发、用不同超参数微调出的多个模型,把权重平均起来往往能达到甚至超过其中最好的单个模型;论文里效果最稳的是“贪心汤”,即按验证集成绩逐个尝试加入、只保留让成绩变好的模型,把所有模型不加挑选地平均有时反而更差。合并后仍是一个模型,推理没有额外开销。原因是这些模型落在损失曲面上同一个低谷里,平均后的点还在谷底附近。
把这个思路推广到不同任务,就是任务向量(task vector):微调后的权重减去底座权重,得到“这次微调改了什么”。
text
τA = W_A − W0 (条款格式这次微调的改动)
τB = W_B − W0 (加班费计算这次微调的改动)
W_merged = W0 + λ × (τA + τB)
λ:缩放系数,取几个值分别合并,用评测结果挑选任务向量还能做减法:把某个任务向量取负加回底座,可以削弱对应的行为,同时对其他任务影响较小。2022 年提出任务向量的论文演示了加法获得多任务能力、减法“遗忘”某种行为,以及类比式的组合。
冲突怎么办:TIES 与 DARE
两个任务向量直接相加或平均,会在一些参数上互相干扰。看图右边 6 个参数的示意数值:p3 上 A 改了 +0.5,B 改了 −0.7,直接平均成 −0.1,两边的改动几乎都丢了。
TIES-Merging(2023)分三步处理:
- 修剪:每个任务向量只保留变化最大的一部分参数,其余归零。示意里每个向量只留绝对值最大的 3 个,A 的 −0.1、+0.02、+0.3 被清掉。
- 选符号:每个参数上把各向量的值相加,按总和的正负选定方向。p3 上 +0.5 − 0.7 = −0.2,选负。
- 只平均同号的值:p3 只保留 B 的 −0.7;p1 两边都是正的,平均为 +0.7;p5 两边都是负的,平均为 −0.55。
DARE(2023)是另一种预处理:随机丢掉任务向量里比例为 p 的参数,剩下的乘以 1/(1−p) 补回整体幅度。论文发现 SFT 改动的数值通常很小(大多在 0.002 以内)且高度冗余,丢掉 90% 甚至 99% 能力基本不变,稀疏之后再合并,参数之间的冲突就少了。
LoRA 本身就是任务向量
第 10 篇的 LoRA 训练的是 ΔW = (α/r) × B × A,这个 ΔW 正是一个任务向量。合并 LoRA 有两种层次:一是把单个适配器加回底座,W' = W0 + (α/r)BA,得到一个不需要额外适配器的完整模型;二是把两个适配器的 ΔW 按权重相加,相当于上面的任务向量合并,同样可以先用 TIES 或 DARE 处理。如果底座是量化的,合并时要先反量化,再算加法,会引入额外误差,合并后要和未合并的版本对比输出。
为什么只能合并同一底座的模型
合并能成立,前提是两个模型的“第 i 个参数”是同一个东西。同一个底座微调出来的模型,参数只是在原位置上挪了一点,位置的含义没变,所以可以相加。两个独立预训练的模型即使结构完全相同,内部的神经元排列和分工也完全不同,同一位置的参数毫无对应关系,相加只会得到噪声。Llama 和 Qwen 这样词表、层数都不同的模型,连形状都对不上。
所以合并只适用于“同一个底座、各自微调”的模型。资料助手的 A 和 B 都基于同一个 Llama-3.1-8B-Instruct,满足条件;想把 8B 的 LoRA 合到 1.5B 的学生上,则不可能,那要靠蒸馏。
合并之后一定要评估
合并是一个没有训练过程的操作,没有损失曲线可看,只能靠评估判断好坏。合并后至少跑三组评测:A 的任务(条款编号准确率)、B 的任务(加班费计算正确率)、通用能力(检查有没有整体变差)。扫几个 λ 值,挑三组都可接受的那个。
失败的情况并不少见。比如合并后加班费算得对了,但回答里的条款编号开始缺失,说明两个任务向量在关键参数上冲突。这时的退路有两个:线上保持一个底座、按请求加载不同 LoRA 适配器;或者把 A、B 两份训练数据混在一起重新训一次。后者成本高一些,往往是更可靠的做法。
蒸馏让小模型学大模型的输出:逐 token 蒸馏学老师的概率分布,需要相同词表和完整的 logit;序列级蒸馏只学老师写出的文字,最常用,但老师的错误要靠程序过滤挡住,学生要在没见过的问题上评估,使用别人模型的输出前要核对许可证和服务条款。继续预训练放在 SFT 之前,只在领域语料足够大时才值得。模型合并在同一底座的任务向量上做加减,TIES 和 DARE 用来减少参数冲突,合并后必须分任务评估。到这里,大语言模型的训练链路告一段落,下一部分换到声音:语音识别模型怎样把声音变成文字,见14. 语音识别怎样训练。
