简介:大模型(LLMs)蒸馏面.pdf 是一份聚焦大模型知识蒸馏的面试梳理笔记,主要面向备战 AI 算法岗位的求职者,以及对模型压缩、高效部署感兴趣的工程师和研究者。全篇以问答形式整理知识蒸馏的核心脉络:教师-学生模型如何协作、软标签与温度系数的作用、损失函数如何设计,既涵盖预训练与微调阶段的蒸馏策略,也延伸到模型量化、参数剪枝、低秩分解等压缩加速方法,并拆解知识蒸馏与无监督样本训练的区别。其中对离线蒸馏、半监督蒸馏、自监督蒸馏等策略均有说明,还列举了 FitNets、Hinton 蒸馏、TinyBERT 等经典蒸馏模型,方便读者对照理解不同方法的特点。包体为单个 PDF 文件,容量约 175KB,内容紧凑,适合在手机或电脑上随时翻阅。目前已有 46 人学习下载,既可作为面试前的系统复习资料,也可作为日常查阅的便携知识卡。 从去年第一次跑通大模型蒸馏到现在,我最大的感受是:圈里都在追千亿参数、刷榜单,但真正到了落地环节,能把一个 70B 模型压缩成 7B 还保住 80% 能力的人,才是真正解决实际问题的人。“大模型(LLMs)蒸馏”这六个字,听起来像是一个 PDF 标题或某个课程章节,但背后其实是当前 LLM 工程化最重要的一条捷径。这篇内容我不会给你讲太多花哨理论,直接从蒸馏原理、实操流程、参数设计、避坑经验一步步拆开,尤其适合正在做 LLM 部署、端侧推理、私有化落地,以及想把手头的大模型“变小变快”的工程师和团队参考。
1. 内容整体设计与思路拆解
1.1 大模型蒸馏到底解决什么问题
大模型蒸馏,核心就一句话:把一个大而强的老师模型掌握的知识和决策方式,迁移给一个小而快的学生模型。类比到日常,就像刚入职的实习生跟着带教老师学业务,老师不仅告诉他对错,还告诉他这件事在不同条件下该怎么权衡,最后学生虽然资历浅,但出手的方式已经有老师八成火候。
在 LLM 场景里,这个“知识迁移”比传统 CV 分类任务复杂得多。分类任务里,老师给出的是类别概率分布,比如“猫 0.7、狗 0.2、虎 0.1”,学生照着学就行。但大模型是生成模型,每次生成一个 token 都要输出一个几万维的概率分布,而且还要学会上下文理解、指令跟随、逻辑推理这些能力。所以 LLM 蒸馏不只是让学生模型模仿老师的最终答案,更关键的是模仿老师生成答案过程中的“思考分布”。这也是为什么有人把“蒸馏一本书”这种想法也归到这个领域——本质上都是把文本里的结构知识压缩到更小参数量的模型里。
蒸馏解决的实际问题非常直接:第一,推理成本。大模型跑一次推理,显存占用、算力消耗、响应延迟都随参数规模非线性上涨,而蒸馏后的小模型能把单位请求成本降一个量级。第二,部署门槛。很多场景要求私有化、内网甚至端侧部署,千亿模型根本跑不动,蒸馏到 7B 甚至 1B 级别才有可行性。第三,知识复用。一个已经花了大量成本调教好的大模型,不该只用一次,蒸馏相当于把这个模型的经验沉淀下来,形成可持续分发的能力。
1.2 为什么不是所有场景都适合直接蒸馏
这个问题我踩过坑,必须先说清楚。蒸馏不是“以大变小”的万能钥匙,有几个前置条件不满足,效果会很差。
首先是老师的质量必须可信。如果老师模型本身有严重的幻觉问题或者领域偏科,蒸馏只会把错误知识加倍放大。比如你想蒸馏一个中文医疗模型,老师却在很多药品剂量上胡说,学生学完照样胡说,而且因为小模型容量有限,错误会被固化得更死。
其次是学生容量的问题。1B 的模型再努力,也很难完全继承 70B 模型的所有能力。如果你要蒸馏的方向特别杂——写作、代码、数学、多语种全都要,那小模型根本装不下。正确做法是定向蒸馏,只保留老师在某一个或某几个任务上的能力,比如只蒸馏代码能力或只蒸馏客服对话能力。
最后是数据成本。蒸馏大模型需要准备海量且高质量的输入数据,很多时候还要让老师模型跑一遍推理生成软标签,这个过程中算力消耗并不小。如果只为了省推理时的一点显存,结果训练阶段投入产出不成正比,那就没必要。想清楚“蒸馏后模型到底用在哪里”,比先选哪个蒸馏框架更重要。
2. 核心细节解析与实操要点
2.1 知识蒸馏的三种主流范式
传统知识蒸馏有三条路线,到了 LLM 时代依然适用,但落地方案有些变化。
第一种是离线蒸馏,也叫标准蒸馏。老师模型固定不动,先用它对一批数据做前向推理,把每一层的 logits 或概率分布保存下来,作为学生模型的训练目标。这种方式最可控,也最好调试,缺点是存储开销大。一个 13B 模型跑完 100 万条样本的中间结果,可能占好几个 T 的存储空间。但工程上通常不需要保存全部层,保存最后一层输出分布就够了。
第二种是在线蒸馏。老师和学生一起训练,学生学到的反馈再回传给老师,两者是协同进化的关系。这种模式在持续学习、增量更新场景里更适用,但 LLM 里做在线蒸馏非常贵,因为你等于同时训练两个模型,显存和算力压力都很大,一般小团队不用碰。
第三种是自蒸馏。不用外部老师,模型自己教自己,比如用模型的深层输出指导浅层。这在一些深层 Transformer 结构里有效,但对多数开源 LLM 来说操作复杂度较高,效果不稳定,我建议新手不要一上来就试。
实际做 LLM 蒸馏,90% 的项目最终都选离线蒸馏。原因很简单:老师模型预算可控、学生训练稳定、失败容易排查。
2.2 温度、软标签与损失函数设计
蒸馏里最常听到的两个词是温度和软标签。所谓硬标签,就是“答案就是猫”;软标签则是“猫 0.7、狗 0.2、虎 0.1”这种概率分布。学生不直接学标准答案,而是学老师的概率分布,因为他能从中看到“猫和狗很像”这种暗知识。
为了让这个分布更“软”,需要温度参数 T 对 logits 做缩放。公式上就是把每个 logit 除以 T,然后再做 softmax。T 越大,输出分布越平滑,更多低概率类别的信息被保留;T 越小,分布越尖锐,接近 one-hot。比如取 T=4,原本 “狗” 的概率可能从 0.2 变成 0.25,学生能明显感受到“老师觉得这个选项也不是完全没可能”。
损失函数一般写成三部分叠加:
学生最终损失 = 交叉熵硬标签损失 + α × 教师分布 KL 散度损失 + β × 中间层特征对齐损失
其中,硬标签损失保证学生能学到基础正确性;KL 散度用于让学生在概率分布上贴近老师;隐藏状态对齐损失则是把学生某些中间层输出拉向老师的对应层,让能力迁移得更彻底。α 和 β 是权重,经验上 α 取 0.5 到 0.9 比较常见,β 如果学生模型太小时不要设置过高,否则模型会花大量算力去模仿内部表征,反而忽略最终输出质量。计算 KL 散度时,要记得乘上 T 的平方,因为梯度会被 T 缩放,这个细节忘掉的话,训练时会发现损失忽高忽低,很难调。
LLM 蒸馏里还有一个重点,就是序列级蒸馏。生成任务里模型每个 token 都产生一个分布,如果只拿最后一个 token 做损失,信息太稀薄。实际做法是把整个答案序列的 token 级 logits 都拿来和学生做 KL 对齐,让学生学会老师生成语句时的整体“语气”。这种方法在指令跟随、摘要生成任务里提升明显。
2.3 LLM 蒸馏特有的策略
为了让蒸馏效果贴合大模型场景,还有几个细化策略值得单独说明。
一是教师与学生的词表对齐问题。很多开源模型用的 tokenizer 不同,比如老师是 LLaMA 词表,学生是 Qwen 词表,两边 vocab 不一致,直接算 logits 的 KL 散度会出问题。常见办法是先在共享的 tokenizer 下用映射表对齐,或者不要直接对齐 logits,而是对齐学生模型中每个 token 的概率分布中,被教师 tokenizer 重新映射过的部分。这听起来很绕,操作上就是先把两边文本都转成统一 tokenizer 格式,再做 logits 对齐。新手在这一步容易卡住。
二是隐藏状态对齐。刚才说损失函数里可以加 β 项,但 LLM 层数很多,不需要每一层都对。经验做法是选择老师模型中间某个信息密集层和学生模型对应层做投影对齐,或者干脆只对齐最后一层之前的一层。这里还要提一个细节:加载老师模型时一定要用 eval 模式并且冻结参数,不然 BatchNorm 或 Dropout 的行为差异会污染蒸馏信号。
三是能力定向蒸馏。比如我想蒸馏一个“中文法律问答”能力,就不该盲目拿整个通用语料做蒸馏。最好先让老师模型在目标领域产出一批高质量问答对,再基于这批数据做蒸馏,让学生的能力全部集中在法律场景上。社区里常说的“怎么蒸馏 skill”,本质上就是这个思路——先把目标技能用数据定义清楚,再做知识迁移。
3. 实操过程与核心环节实现
3.1 环境准备与工具选型
做 LLM 蒸馏,不需要从零写一个框架,基于现有生态组合就行。我的常用组合是 HuggingFace Transformers + Datasets + Accelerate + DeepSpeed,老师模型直接用 Transformers 加载,学生模型也走同样接口,蒸馏循环自己写,灵活度高。
如果你想要更省事的抽象封装,可以看一下 TextBrewer 或 torchdistill,它们在 NLP 蒸馏上有不少现成模块。但说实话,LLM 规模太大之后,这些通用蒸馏库适配起来反而麻烦,我最后还是更倾向于自己维护一套几百行的 PyTorch 训练脚本。基于 PEFT/LoRA 把学生模型的一部分参数蒸馏时进行微调,这个思路很常见,能大幅减少显存占用,适合只有一块卡的情况。
硬件方面,如果你想把 7B 老师蒸馏到一个 1B 左右的学生,至少需要一张 24G 显存的显卡。13B 老师蒸馏到 3B 学生,建议 2 张 24G 卡,或者用 DeepSpeed ZeRO-2。老师模型全程用 bfloat16 加载并冻结,学生模型如果是随机初始化则用 bfloat16 混合精度训练。第一次做,建议先从“老师 7B -> 学生 2B”这个规模跑通全流程,成本可控,效果容易观察。
3.2 数据准备与组织
数据是蒸馏效果的天花板。经验上,我会把蒸馏数据集分成三类:
- 通用指令数据:像 Alpaca 格式的指令-回答对,保证学生基本对话能力。
- 领域专属数据:比如代码、法律、医疗,定向补充目标场景数据。
- 反事实与边界样本:这类数据不一定有标准答案,但老师模型能给出合理的概率分布,对学生学会模糊判断很有帮助。
准备方式一般有两种。第一种是直接用现成的公开指令数据集,成本低,但和你的目标场景未必匹配。第二种是让老师模型在领域语料上自主生成问答对,再经过规则过滤和人工抽检,质量更高,但需要设计和迭代 prompt。我常用的比例是领域数据 60%、通用数据 30%、边界样本 10%。如果通用数据占比太高,学生知识面广但不够专;领域数据太高,学生容易过拟合。蒸馏前,老师模型需要对每一条样本做一次前向推理,把 logits 保存下来。内存够就放内存,不够就落盘成.pt文件或者内存映射的 numpy 数组,后续训练时直接读缓存,能省掉每次 forward 老师的开销。
3.3 一个可复现的蒸馏训练流程
下面我给出一个简化但能跑通的流程框架,核心逻辑都在,你替换成自己的模型路径和数据就能测试。
# LLM 离线蒸馏训练循环(伪代码) import torch import torch.nn.functional as F from torch.utils.data import DataLoader from transformers import AutoModelForCausalLM, AutoTokenizer teacher_name = "your_teacher_model_path" student_name = "your_student_model_path" teacher = AutoModelForCausalLM.from_pretrained( teacher_name, torch_dtype=torch.bfloat16 ) teacher.eval() for param in teacher.parameters(): param.requires_grad = False student = AutoModelForCausalLM.from_pretrained(student_name) student.train() optimizer = torch.optim.AdamW(student.parameters(), lr=5e-5) tokenizer = AutoTokenizer.from_pretrained(teacher_name) T = 4.0 # 温度 alpha = 0.7 # 蒸馏损失权重 max_length = 512 for batch in DataLoader(train_dataset, batch_size=8): inputs = tokenizer(batch["text"], truncation=True, max_length=max_length, return_tensors="pt") inputs = {k: v.cuda() for k, v in inputs.items()} with torch.no_grad(): teacher_logits = teacher(**inputs).logits student_logits = student(**inputs).logits # 只在非 padding 位置计算 label_mask = inputs["attention_mask"].unsqueeze(-1) # 硬标签交叉熵 shift_logits = student_logits[:, :-1].contiguous() shift_labels = inputs["input_ids"][:, 1:].contiguous() ce_loss = F.cross_entropy( shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1), ignore_index=tokenizer.pad_token_id ) # 温度软化后的 KL 散度 t_dist = F.softmax(teacher_logits / T, dim=-1) s_dist = F.log_softmax(student_logits / T, dim=-1) kd_loss = F.kl_div( s_dist.view(-1, s_dist.size(-1)), t_dist.view(-1, t_dist.size(-1)), reduction="batchmean", ) * (T ** 2) loss = (1 - alpha) * ce_loss + alpha * kd_loss loss.backward() optimizer.step() optimizer.zero_grad()这段代码里有个容易忽略的地方:teacher_logits 和 student_logits 的序列长度必须一致,因此老师模型和学生模型要用一样的 padding 策略,如果两个模型 tokenizer 不一样,建议统一用老师的 tokenizer 对学生数据进行编码并扩充学生词表。
如果想进一步对齐中间层,可以在循环里取出两个模型的 hidden_states,选择最后一层的前一层加 MSE 损失。但我要提醒,这个增强项对显存和训练时长影响很大,首次跑通建议先不加。
3.4 评测指标与效果对比
蒸馏完不能只看损失下降就认为成功了。我习惯做一组多维评测:
- 困惑度:简单、快速,但只能做基础判断。
- 通用指令任务:测对话、摘要、翻译这些通用能力。
- 领域任务:比如代码补全、法律问答,用目标场景指标单独测。
- 一致性测试:同一问题改写后,看答案是否稳定,这是蒸馏后小模型比较容易出问题的地方。
- 推理速度与显存占用:用相同 prompt 在相同硬件上对比 teacher 和 student 的延迟、吞吐、峰值显存。
可以在项目汇报里用一张表呈现结果,比如:
| 指标 | 原模型 7B | 蒸馏后 2B | 变化 |
|---|---|---|---|
| 指令任务准确率 | 86% | 81% | -5% |
| 领域问答 F1 | 0.82 | 0.78 | -0.04 |
| 首 token 延迟 | 450ms | 220ms | 降 51% |
| 显存占用 | 16GB | 6GB | 降 62% |
这张表传递的核心信息是:小模型用少量精度损失换来了部署规模和速度的巨大收益,这在实际业务里是划算的。
4. 常见问题与排查技巧实录
4.1 常见问题速查表
| 现象 | 可能原因 | 排查与解决建议 |
|---|---|---|
| 学生模型效果比老师差很多 | 数据量少、温度过高或过低、学生模型容量太小 | 增加数据量,调整 T 和 alpha,考虑中间尺寸模型 |
| 训练损失不断下降但评测不涨 | 过拟合蒸馏集 | 加入更多通用数据,增加 eval 频率,检查是否评测集和蒸馏集重叠 |
| KL 损失异常波动 | 温度 T 改变导致梯度尺度变化,未乘 T 平方 | 检查 KL 损失是否乘了 T 平方,学习率降至 1e-5 以下试跑 |
| 显存 OOM | 老师模型 forward 占用过大或 logits 缓存过大 | 冻结老师 + 梯度检查点,改用 batch 内增量缓存,必要时把 logits 落盘 |
| 学生输出同质化,总是说同样的话 | 温度过高导致软标签过于平滑,或数据多样性不足 | 降低温度,增加数据多样性,加入硬标签交叉熵约束 |
| 领域能力提升但通用能力退化 | 领域数据占比过高 | 提高通用指令数据比例,或在蒸馏后用少量通用 SFT 数据做恢复训练 |
4.2 独家避坑技巧
经过多个项目后,我总结出几条百试不爽的经验。
第一条:先把数据搞干净,再调参数。每次蒸馏效果崩坏,90% 是数据问题——重复文本太多、硬标签答案本身有错、指令模板过于单一。我习惯先把数据做 n-gram 去重,再人工抽查 200 条,确认教师回答质量过关,才开始训练。调参只能微调,没法救回垃圾数据。
第二条:温度不是一个“设了就不管”的参数。T=2 到 T=8 这个区间的差异很大。判断温度是否合适的办法,是在训练前拿几条样本,打印老师模型在不同温度下的 top-5 token 概率,观察分布是“有意义地平滑”还是“全部糊成一片”。如果 top-1 和 top-5 概率差异太大,温度偏低;如果 top-5 之间概率几乎没差异,温度偏高。
第三条:不要一开始就上“中间层对齐、在线蒸馏、对抗式训练”这些高阶技巧。我见过很多团队把损失函数写得极复杂,最后难以定位问题。优先用“硬标签 CE + 输出 KL”这个最简组合,把效果基线跑出来,再逐步加复杂度。每一步只改一个变量,才能知道哪个改动真正带来提升。
第四条:学生模型的初始化非常关键。随机初始化的学生模型学得慢,效果容易下不来。我一般从同系列的小尺寸模型 checkpoint 开始,比如老师用 Qwen-7B,学生就用 Qwen-1.5B 作为起点,这样它的底层语言能力已经存在,蒸馏只是在原有能力上对齐老师的风格和知识,收敛速度快很多。
5. 应用场景与部署优化
5.1 端侧与私有化部署场景
蒸馏最大的落地场景就是端侧和私有化部署。现在很多企业要求数据不出内网,模型必须部署在公司机房,而机房给到的算力往往撑不动几十 B 的大模型。蒸馏到 2B 或 4B 级别,再配合 vLLM 或 Ollama 这类推理框架,单卡甚至 CPU 都能跑起来。我自己在项目里用 2B 蒸馏模型配合 Ollama 做私有知识库问答,响应速度从原来大模型的秒级降到 300 毫秒以内,效果基本符合业务预期。
如果你是移动端或嵌入式设备,可以考虑把蒸馏和量化组合使用:先蒸馏到 1B 以下,再做 4bit 量化,最终模型文件可能只有几百 MB。这种组合能把大模型能力真正塞进“口袋里”,而不是永远停留在云端 API。
5.2 蒸馏与微调的配合
很多人会纠结先蒸馏还是先微调。根据我的经验,如果你的目标非常垂直,比如就是做一个“客服意图识别”,那建议先做任务微调再蒸馏。做法是先用一个小模型在任务数据上做 SFT,拿到一个任务基线,再让大模型对同一批任务数据生成软标签,用蒸馏方式把小模型的能力向大模型对齐。这样既能用上大模型的知识,又不至于丢太多任务能力。
如果是通用助手类型的产品,更推荐先做通用蒸馏,蒸馏出一个能力下限不低的小模型,再针对产品需要做少量 SFT。千万不要在蒸馏后完全不微调就直接上生产,小模型对 prompt 格式的敏感度很高,不对齐业务场景会显得“答非所问”。
关于“蒸馏一本书”这类长文本蒸馏,我的看法是可以作为模型个性化知识迁移的延伸。把一本书的内容拆成结构化段落和带问答的样本,让老师模型生成关于这些内容的详细解释和推演,再蒸馏到学生模型里,等于做了一个“读过这本书”的模型。这和大模型蒸馏核心机制是一致的,只是数据形态变成了长文档。如果你手头有大量 PDF 要变成可对话的领域模型,不妨沿着这个思路做一次数据工程改造。
结尾
最后分享一个我在蒸馏项目里最常提醒自己的原则:蒸馏不是把大模型“压缩”成小模型,而是把一个模型的经验“翻译”成另一个模型能学会的语言。翻译得好不好,取决于你对数据的理解,而不是你调参的熟练度。我见过太多团队在最细枝末节的注意力层对齐上花时间,结果数据一团糟,最终模型效果还不如用一个小模型直接 SFT。所以,先跑通最简基线,把数据打磨到挑不出明显问题,再慢慢叠加技巧,这才是做 LLM 蒸馏最务实的路径。希望这篇整理能让你少走几步弯路,也欢迎在实际操作中遇到问题再来交流。
本文还有配套的精品资源,点击获取