最近被问到最多的问题,就是大模型蒸馏。很多团队手里已经有一个大模型,或者一个调好的开源大模型,推理质量不错,但每次调用的延迟和成本都很让人抓狂。于是大家自然想到一条路:用大模型生成数据,去训练一个小模型,把小模型部署到生产环境。方向没问题,学名叫知识蒸馏。但这几年看下来,真正把蒸馏做成的人不多,大量团队卡在数据质量、训练路线选择和评估混乱这三件事上。这篇文章我会把做蒸馏时最关键的选择逻辑、实操细节和踩坑过程完整放出来。如果你正在纠结小模型训练该用SFT还是RL,也担心超长上下文的小参数模型会不会崩,那这篇文章应该能给你一套可以直接参考的答案。
先说清楚一件事:蒸馏不是把大模型的参数复制到小模型里,而是让大模型当老师,生成一批高质量行为样本,用小模型去模仿这些行为。所以整个项目里最重要的东西,不是训练代码,而是训练数据。数据不对,后面所有环节都没得救。
1. 蒸馏到底在治什么病:先看清问题再决定方案
1.1 蒸馏的本质:不是搬参数,是搬行为
很多人把蒸馏想成一种“模型压缩算法”,觉得它和量化、剪枝是一类东西。实际差得很远。量化和剪枝是在已有模型的权重基础上做近似,模型结构不变,知识来源还是原来的权重。蒸馏则是重新训练一个新模型,新模型的结构可以完全不同于老师。举个例子,老师可能是一个几百B参数的稠密模型,学生可以是3B甚至0.5B的模型,结构完全没关系,学生学的是老师“在什么输入下给出什么输出”这种行为模式。
这种“行为迁移”在深度学习里最早可以追溯到Hinton在2015年提出的知识蒸馏,当时核心做法是把老师模型的软标签(soft label)拿过来当训练信号,让学生模型去对齐老师模型的输出分布。到了大模型时代,事情出现了一个重要变化:我们一般很难直接拿到老师模型在词表上的完整概率分布,尤其是用API调用模型的时候,只能拿到采样的文本。所以现在主流的蒸馏方式已经从logits蒸馏变成了数据蒸馏,也就是拿老师生成的文本当监督数据,用SFT或偏好优化训练学生。
这里有个关键点容易忽略:行为迁移不等于完全复刻。老师的回答风格、思考链条、拒绝方式,这些都是可以迁移的知识。但老师的错误观念、过度自信和幻觉也会一并迁移过来。所以蒸馏某种程度上是在做“数据提纯”加“行为克隆”,你没有机会像训练老师那样从头做对齐,你只能尽量选择那些“老师表现得好”的样本进训练集。
1.2 适合蒸馏的场景和不该硬上的场景
不是所有任务都适合蒸馏,动手之前建议先做个判断。从我自己的项目经验看,适合蒸馏的场景通常有几个特征:
- 任务边界明确,比如客服问答、信息抽取、摘要生成、SQL生成、固定格式输出。
- 高并发、低延迟、成本敏感,线上扛不住大模型,需要小模型顶上去。
- 对效果的要求是“不低于原来的80分”,而不是追求满分。
- 数据量可控,领域比较窄,不需要模型具备过于宽泛的世界知识。
反过来,以下场景我建议你别急着做蒸馏:
- 任务需要强推理,比如复杂数学题、多跳逻辑推理。小模型容量有限,老师能算出来的步骤它未必follow得住,蒸馏后效果往往大幅缩水。
- 开放域创作,比如写小说、生成营销文案。这种任务没有标准答案,小模型很容易学成“套路话痨”,反而比大模型更难用。
- 领域知识极其广泛,老师背后的知识库远大于你准备的蒸馏数据覆盖范围。小模型不吃亏才怪。
- 你只有少量数据(几千条),但期望学生达到老师95%的水平,这不现实。蒸馏是一个数据密集工程。
所以精准定位是第一步。你越了解自己的任务边界,后面的数据、训练、评估就越有方向。别把蒸馏当成一个万能按钮,它还是一场工程。
2. 开工前先把三件事定下来
2.1 训练数据从哪来:三种来源与清洗要点
蒸馏训练集一般有三个来源:
第一种是公开数据集。比如各类指令数据集、行业公开QA、知识库问答对。优点是量大且便宜,缺点是普遍和你的业务场景有偏差。直接用公开数据蒸馏,出来的模型泛化能力看起来不错,但上线后经常答非所问。
第二种是用户日志。这是最贴近线上真实分布的来源。把线上用户问题收集下来,用老师模型生成答案,或者直接复用线上表现最好的人工答案。问题在于日志里的隐私信息要去干净,还要做问题去重和难度筛选,不然训练集里全是被问到烂的简单问题,小模型学不到复杂pattern。
第三种是老师模型自举生成。也就是先准备一批种子任务,让老师模型生成答案,最好的方式是先生成初始响应,再让老师“自我批判”或生成多个候选答案,从里面挑选质量最高的样本。这招在蒸馏里非常常用,因为种子可以来自业务问题、公开题目,甚至另一个模型。
清洗环节是大头,必须做的操作包括:
- 精确去重和语义去重。很多seed任务会让学生模型产生非常相似的输出,重复样本太多会造成过拟合。
- 过滤教师幻觉。拿一个验证集让另一位更强的模型打分,把低分样本丢掉。
- 平衡长度和难度。别让训练集里全是“一句话回答”的短样本,要有长文本、结构化输出、中间推理过程。
- 清洗格式。统一prompt格式,统一输出标记,避免模型学到“一会儿中文一会儿英文、一会儿JSON一会儿markdown”的坏习惯。
我自己的经验是:蒸馏数据宁可少而精,不要多而滥。几千条精心清洗的数据,往往比几万条乱来的数据训练出来的模型稳定得多。
2.2 小参数模型训练路线之争:先SFT还是直接RL?
这个问题几乎每次做小模型训练都会碰到,也是现在社区里争论比较多的话题。很多人看到前面一句“要有RL”,就打算直接上PPO,结果训到一半发现reward飘得离谱,甚至把模型训崩。我的结论很简单:对小参数模型,SFT是地基,RL或者更轻量的DPO是装修。永远先做SFT,不要跳过。
为什么?因为小参数模型容量有限,它要先通过SFT学会最基本的“听指令、按格式回答、说人话”。如果连这个基础都没有,直接RL就像让一个还没学会走路的小孩直接去跑马拉松。SFT阶段本质上是在做监督学习,每个训练样本都有明确的输入输出,模型可以快速拟合出稳定的条件分布。这个阶段会决定模型的下限。
RL或偏好优化阶段解决的问题是“怎么回答更符合人的偏好”。比如避免废话、不要过度道歉、遇到敏感问题时简洁拒绝。这些行为很难通过SFT学到位,因为SFT只是学会模仿某一个具体答案,而偏好优化是从“好的回答”和“差的回答”之间学习边界。
在小参数模型上,我更推荐用DPO而不是完整RLHF。原因很实际:
- DPO不需要单独训练奖励模型,省了一整套RL基础设施。
- DPO训练稳定,资源占用低,对超参数没有那么敏感。
- DPO可以直接在SFT模型上基于偏好对微调,非常适合蒸馏数据这种“有参考答案”的场景。
当然,如果你的团队有成熟的RL训练平台,资源和工程能力都在,也可以尝试RLVR(可验证奖励的强化学习),但要准备好接受结果不稳定和调参周期长的风险。我见过不少团队折腾了一个月PPO,最后效果还不如DPO训三天。
可以这样理解:SFT解决“它会不会说”,RL/DPO解决“它说得好不好”。对一个蒸馏出来的小模型,你首先要确保它把老师的大部分能力迁移过来,这个阶段必须用SFT。然后再用偏好优化去修掉那些“虽然学会了但是不讨喜”的行为。
2.3 目标模型与评估基线:别等训练完再补课
很多人做蒸馏,是把数据准备好、模型训完才想到评估。这是个大坑。评估方案应该和最开始的基线一起定义好。
教师模型选谁?一般是质量和速度、成本综合起来最平衡的那个。比如你有预算,可以直接用API大模型当老师;如果想省钱,可以用本地部署的开源模型。但要注意,老师的推理结果本身要有一定质量线,否则“差老师教出更差学生”,错误会层层放大。
评估题目必须覆盖以下维度:
- 指令遵循:能不能按格式回答、按约束完成。
- 效果指标:根据任务定,比如QA的准确率、摘要的ROUGE、代码生成的Pass@k。
- 行为风格:是否有礼貌但不过度、是否简洁、是否拒绝得当。
- 稳定性:同一个问题跑3次,长期上下文是否一致。
- 长文本能力:输入变长以后是否还能保持正确理解。
建议一开始就把这些维度做成一套回归评测集,每次训练迭代都跑一遍。很多团队蒸馏失败不是因为训练不够,而是因为他们只在最后看了一个归一化的“平均分”,完全没看到模型在长文本和边界情况下的崩塌。
3. 完整实战流程:从数据准备到小模型蒸馏一次跑通
3.1 种子任务集的构造思路
模型学习的第一步是给它喂一个覆盖广泛的任务集合。这里有个关键认知:你不是要让小模型成为“所有任务的通才”,而是让它在你的业务范围内够用。所以种子任务集要尽可能覆盖业务内所有输入形态和难度梯度。
实际操作中,我一般会按下面几个维度来构造:
- 输入长度分布。从十几个字的短问题到几千字的长文档都放一些,不要让模型只见过短输入。
- 输出格式分布。有直接文字回答、有JSON、有markdown表格、有代码块。
- 问题类型分布。有事实问答、有开放讨论、有拒绝回答、有修正用户问题的场景。
- 难度分布。简单问题占一部分,需要多步推理或者多轮信息整合的问题也要占一部分。
种子任务的数量尽量在5000条以上,太少会让小模型缺乏泛化空间。如果业务场景很窄,可以把数量降低,但不能低于2000条,否则模型很容易只记住样本而不是学会能力。
3.2 让教师模型产出高质量响应
种子任务有了以后,开始调用老师模型生成答案。这里有几个参数和经验值可以分享:
- Temperature设置在0.3到0.7之间。过高会生成混乱的答案,过低则多样性不足。
- 对同一个问题采样2到4次,取最好的那一条。用另一套规则或打分模型来挑。
- 如果是推理类任务,可以让老师先生成思考过程,再给出最终答案。小模型可以从中学到推理步骤,但推理过程会让训练数据变长,需要控制长度。
- 如果业务里有“必须拒绝”的场景,一定要单独构造一批拒绝样本,不要让模型只会回答、不会拒绝。
生成完的原始数据必须做一次质量过滤。比较实用的做法是先用简单规则过滤(比如空输出、超长冗余、语法混乱),再用一个质量打分模型对剩余样本排序,取前60%-80%作为最终训练集。这比盲目堆数据量有效得多。
另外提醒一句:教师模型本身也会受提示词影响。同一个问题换个prompt写法,输出质量可能差很多。所以蒸馏数据生成阶段要固定一套prompt模板,保持一致性,不然数据里会混入“老师在不同风格下回答”的方差,小模型会学得很困惑。
3.3 用SFT打底:小模型的第一步
数据准备好了,开始训练。现在开源生态对小模型很友好,我一般会用Qwen或Llama系列的1.5B-7B模型当学生底座。不要用太大了,因为如果你有预算用14B,那不如直接用14B开源模型并做量化部署,没必要蒸馏。
SFT阶段我常用的训练配置是这样:
model_name_or_path: Qwen/Qwen2.5-1.5B stage: sft dataset: distilled_data_sample max_length: 4096 batch_size: 4 gradient_accumulation_steps: 8 learning_rate: 2e-5 num_train_epochs: 3 lr_scheduler: cosine warmup_ratio: 0.03 lora_rank: 32 lora_alpha: 64这里有三个点值得注意:
第一,学习率不宜过高。小模型参数少,2e-5到5e-5是一个比较稳的范围。学习率太高,模型容易忘记原有的基础能力,出现“灾难性遗忘”,表现为输出质量反而下降。
第二,epoch数控制在2到4轮就够了。蒸馏数据集的多样性有限,训太多轮模型会背答案,反而丢失泛化能力。如果训练集有2万条以上,1到2轮就够。
第三,上下文长度要贴合业务。如果业务输入经常超过2000字,那就把最大长度设为4096甚至8192。但要注意,小模型超过16K之后训练显存和速度都会急剧上升,需要配合长上下文微调技巧,这点后面会专门说。
训练结束后不要急着上RL。先跑一遍评测,看看哪些问题类型已经达标,哪些还不行。很多行为问题在这个阶段就能通过加数据解决,根本不用走到偏好优化那一步。
3.4 再用DPO/RL精调:把行为上限拉高一点
SFT模型跑通以后,如果有明显的行为问题,比如废话太多、拒绝不当、格式不稳定,那就进偏好优化阶段。对这个需求,我最推荐DPO。
DPO训练需要构造偏好对:对一个输入,准备一个“好的回答”和一个“差的回答”。在蒸馏场景里,这两个回答怎么来?
我的常用方案是:
- 让SFT模型自己生成多个回答。
- 借老师模型或一个更强裁判模型打分排序。
- 分数最高的作为chosen,分数最低的作为rejected。
实操的时候要注意,chosen和rejected不要差得太离谱。如果两个回答一个极好一个极差,DPO会学得很容易,但模型只会改变极端行为,对中间地带没有改善。更好的做法是选“稍好”和“稍差”但都有一定代表性的回答,这样模型能学到细腻的偏好边界。
DPO训练脚本大概是这个风格:
python train.py \ --stage dpo \ --model Qwen/Qwen2.5-1.5B \ --pref_data dpo_train_data.jsonl \ --max_length 4096 \ --learning_rate 1e-6 \ --beta 0.1 \ --num_train_epochs 1DPO里有一个重要超参数beta,它控制对偏好对之间的边际的敏感程度。beta太大,模型更新保守,偏好影响弱;beta太小,模型会被少数偏好对带着跑。我一般从0.1起步,效果不够再加到0.3,不稳定就降到0.05。
这个阶段通常只需要1个epoch,甚至0.5个epoch就够了。原因是SFT已经让小模型学会了基本能力,DPO只是微调行为偏好,不需要大动干戈。如果训练集很大,有时候等loss降低到一定程度就提前保存,不用跑满。
3.5 评估与回归:验证蒸馏结果
训练完成之后,把模型放到一套固定的评测集上跑完整回归。我建议准备三套评测集:
- 训练集内抽查:看模型有没有把老师的行为学到位。
- 同分布但未训练的验证集:看模型有没有泛化能力。
- 边界数据和对抗样本:比如超长文本、极短问题、恶意输入、需要拒绝的场景。
如果只看了前两套,你很难发现模型会不会在长上下文场景突然失忆。蒸馏项目里常见的情况是模型在常规问题上表现得很好,上下文一拉长就崩,尤其在小参数模型上更明显。所以边界评测不是可选项,是必选项。
4. 踩坑实录与常见问题排查
4.1 数据污染、重复与输出“背叛”
数据污染是蒸馏里最隐蔽的问题。我在早期的一个项目里,用线上日志做大模型蒸馏数据,跑出来的小模型效果奇好。后来一检查,发现训练集里混进了一部分测试集的相似题目。模型是“背答案”而不是“会做题”,上线后换一批新问题立刻露馅。
避免方法:严格区分训练集和评测集,对评测集做相似度去重,训练集里的问题和评测集问题的相似度要控制在一个阈值以下。此外,训练集内部也要做去重,尤其是从多个来源拼接的数据,很容易在不知不觉中出现大量重复样本。
另一种“背叛”是模型学会了老师的口头禅和正确格式,但没有学会内容。比如老师每次回答都带“好的,我来帮你”,小模型也学了,甚至每个回答都强制带这句,这就是数据里开头语占比过高导致的。解决办法是清洗数据时把模板化的开头语去掉,或者增加多样的引导语。
4.2 小模型过拟合与“鹦鹉学舌”陷阱
小参数模型很容易进入“背题模式”。它的容量有限,当训练集里重复样本多、任务类型单一时,模型会把输入的关键词直接映射到训练集中的答案,而不是真正理解输入。
“鹦鹉学舌”这个名字很形象,就是模型看起来回答得很顺,语法没问题,但内容完全是训练集里相近样本的复读,而不是针对当前输入的合理回答。
排查方法很简单:拿着训练集里没有出现过的相似问题去问模型,看它能不能给出合理泛化;或者将一个问题换个说法、换个数字,看它是否被带偏。如果一换说法就崩,说明模型在背题。
解决思路:增加数据多样性、降低epoch数、增加dropout、扩大模型容量(比如从1.5B换到3B)。很多时候不是训练不够,而是数据太窄、模型只能死记。
4.3 超长上下文的小参数模型蒸馏难点
现在很多业务场景都需要超长上下文,比如长文档问答、大文件代码理解、长对话总结。蒸馏超长上下文能力比常规蒸馏复杂得多。
先说训练数据。小模型要能处理超长上下文,训练数据里必须有大量超过它上下文长度一半的样本。如果你只训了512长度的样本,然后让学生模型去上8K上下文,它不可能突然学会长依赖关系。这是很多长上下文模型的通病:推理时宣称支持多少K,实际连2K以上都开始丢信息。
数据构造上我推荐分块策略而不是全量拼接。把长文档切成多个有重叠的片段,每个片段带上它在原文档中的位置信息,分别生成问题和答案。这样小模型既能学到局部信息,也能学到上下文位置的关联。再用一小部分“真正超长”的样本做增强,让模型适应真正的长文本输入。
再说模型结构。小模型要在超长上下文上工作,通常需要位置编码扩展。现在开源模型大多使用RoPE,把训练时的位置编码直接外推到更长的位置,效果会迅速衰减。解决方法是对RoPE做缩放或者插值,让位置编码适应更长的位置区间。
训练层面,长上下文最现实的问题是显存和速度。小模型虽然参数少,但注意力计算是输入长度的平方,8K长度的训练序列已经有一定压力。可以用序列打包、FlashAttention、梯度检查点这些手段来缓解。如果还是扛不住,就适当降低最大训练长度,比如用4096训练,推理时再配合位置编码外推,但这需要做充分评测。
4.4 训练超参与策略取舍
最后整理一张我在实践中常用的超参速查表,给大家当起点:
| 参数 | SFT阶段推荐值 | DPO阶段推荐值 | 说明 |
|---|---|---|---|
| 学习率 | 2e-5 ~ 5e-5 | 1e-6 ~ 3e-6 | DPO要更保守,避免破坏SFT学到的行为 |
| epoch | 2 ~ 3 | 0.5 ~ 1 | DPO跑多了反而过拟合偏好对 |
| 最大长度 | 4096(可按业务调整) | 4096 | 必须和业务输入分布匹配 |
| LoRA rank | 16 ~ 64 | 8 ~ 32 | 小模型不一定要全量微调,LoRA够用 |
| beta | 不适用 | 0.05 ~ 0.3 | DPO偏好边界的敏感度 |
| batch size | 尽量大 | 尽量大 | 小模型显存够就大一点 |
| warmup | 0.03 | 0.1 | DPO的更新步数少,warmup要更高比例 |
这些数值不是死规矩,是踩过坑之后的常用起点。如果你的训练集质量和数量足够高,甚至可以跳过DPO。但如果模型行为明显欠拟合用户偏好,DPO依然是性价比最高的修正手段。
另一个经验是:蒸馏完成之后,不要直接把小模型全量替换上线。先灰度一部分流量,观察真实用户反馈和大模型的差异。因为离线评测只能覆盖一部分问题,线上的长尾分布、输入噪声、用户复杂指令,永远比离线评测集复杂得多。
做蒸馏这几年,我自己最大的体会是:这件事拼的不是谁模型调参更花哨,而是谁的数据更干净、评估更贴近真实场景。小模型确实能获得大模型的一部分能力,但老师教得再好,学生也需要时间消化。把数据、路线、测评这三件事老老实实做好,小模型上线跑业务的那天,你才能真正体会到“用小成本换大效果”的踏实感。