模型蒸馏(Knowledge Distillation)是让小模型通过”模仿”大模型输出的软标签来学习其泛化能力的技术。当 7B 甚至更大参数的模型推理成本压垮预算时,知识蒸馏能把核心能力迁移到 0.5B~3B 的小模型上,在精度损失可控的前提下把延迟和显存砍掉一个数量级。本文从原理到代码,带你跑通一条可落地的蒸馏流水线。
一、知识蒸馏到底在蒸馏什么
传统监督学习只给学生模型硬标签(猫/狗),信息量有限。知识蒸馏让教师模型(大模型)在带温度的 softmax 上输出类别概率分布——比如”猫 0.70 / 狗 0.25 / 狐狸 0.05″,这种暗知识(dark knowledge)暴露了类别间的相似性结构。学生模型同时拟合硬标签的交叉熵与教师软标签的 KL 散度,温度 T 越高分布越平滑、暗知识越丰富,但噪声也会放大。
举个直观例子:判别”狼”和”狗”,硬标签只告诉学生这是狗;但教师模型在软标签里同时给出”猫 0.01 / 狐狸 0.20″——它其实学到了”狗和狐狸形态接近”这一层语义关系。学生拿到这层信号后泛化更稳,尤其在样本稀少的长尾类别上,效果差距会被放大。这也是蒸馏在小组数据上往往比从头训练更抗过拟合的原因。
二、什么场景该上蒸馏
蒸馏不是免费的午餐,它要训一个学生模型、要数据和算力。适合它的典型场景有四个:
用一组数字感受差距:一条 7B 模型在 A10 上约 18ms/请求、显存占用 16GB;蒸馏出 1.5B 学生后,延迟常能压到 4ms 以内、显存 3GB 出头,单机吞吐提升 3~4 倍。对日均千万级请求的在线服务,按月算推理账单往往能省掉一大半——这正是蒸馏在成本敏感业务里被广泛采用的直接动因。
- 边缘 / 端侧部署:手机、IoT 设备根本跑不动 7B,蒸馏出 0.5B~1.5B 才落得地。
- 高 QPS 在线服务:客服分类、意图识别这类任务成本敏感,小模型单位推理成本可降一个数量级。
- 垂直窄任务:某类工单分类不需要通用大模型的全量能力,蒸馏更聚焦。
- 作为压缩链路一环:先用蒸馏拿小模型,再用 GGUF 量化部署 进一步瘦身,必要时 LoRA 微调 补领域。
三、三种主流蒸馏范式
按”知识载体”不同,蒸馏大致分三类,选型时先想清楚要迁移哪一层信息:
| 范式 | 知识载体 | 代表做法 | 适合 |
|---|---|---|---|
| 响应蒸馏(软标签) | 输出层概率 | 学生拟合教师 softmax(T) | 分类 / 生成通用基线 |
| 特征蒸馏 | 中间层表征 | FitNet / 注意力迁移 | 视觉 / Encoder 模型 |
| 关系蒸馏 | 样本间结构 | RKD / 多教师集成 | 结构化数据、多教师 |
四、实战一:文本分类的响应蒸馏
最易上手的是响应蒸馏。第一步用教师模型对训练集产出带概率的软标签:
from transformers import pipeline
# 教师模型:返回完整概率分布(关键:return_all_scores + top_k=None)
teacher = pipeline("text-classification", model="bert-base-chinese",
device=0, top_k=None)
out = teacher(texts, return_all_scores=True)
# out[i] = [{"label": "体育", "score": 0.70}, ...]
soft_labels = [[o["score"] for o in row] for row in out]
第二步定义组合损失:硬标签交叉熵保证任务正确,软标签 KL 散度迁移暗知识。温度 T 同时作用于师生两端,抵消 softmax 缩放:
import torch, torch.nn as nn, torch.nn.functional as F
T = 4.0
kl = nn.KLDivLoss(reduction="batchmean")
ce = nn.CrossEntropyLoss()
def distill_loss(logits_s, logits_t, hard_labels, alpha=0.7):
soft_s = F.log_softmax(logits_s / T, dim=1)
soft_t = F.softmax(logits_t / T, dim=1).detach()
return alpha * (T * T) * kl(soft_s, soft_t) + (1 - alpha) * ce(logits_s, hard_labels)
训练循环与一般 PyTorch 无异,只是每个 batch 同时拿到教师软标签与学生硬标签。建议先用 1% 数据小步验证损失下降,再全量跑。
五、实战二:用大模型造数据做指令蒸馏
对生成式 LLM,”蒸馏”常等价于用强模型造高质量训练数据,再教小模型。借助本地 Ollama 跑一个 14B 教师,把种子问题扩写成指令数据:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
seed_q = ["如何重置 MySQL 主从复制?", "Docker 容器如何限制内存?"]
for q in seed_q:
r = client.chat.completions.create(
model="qwen2.5:14b",
messages=[{"role": "user",
"content": f"把下面问题改写成 5 种不同问法,并给出标准答案:
{q}"}],
)
# 用结构化输出约定解析:见 https://fsdata.site/?p=451
# 解析后写入 train.jsonl,作为小模型 SFT 数据
拿到 jsonl 后用 LoRA 在小模型上微调即可。这条路的精髓是:教师负责”想清楚”,学生负责”跑得快”,训练数据质量直接决定学生上限。
数据规模上别迷信”越多越好”。指令蒸馏里,几百到几千条高质量、去重的 (指令, 答案) 往往比几万条噪声数据更有效;关键是覆盖任务边界样本(难例、歧义例、拒答例)。建议先造 500 条做小实验,看学生能否复现教师 80% 以上的正确率,再决定是否扩量。
六、蒸馏 vs 量化 vs 微调
三者常被混为一谈,落地时其实处在不同层级:
| 手段 | 改什么 | 精度损失 | 成本 | 适用 |
|---|---|---|---|---|
| 量化 | 权重精度 FP16→INT8/4 | 低~中 | 极低 | 推理加速、显存瘦身 |
| 蒸馏 | 训练更小的学生 | 中 | 中(需数据/算力) | 长期降成本、端侧 |
| 微调 / LoRA | 适配任务 | 低 | 中 | 领域适配 |
七、落地踩坑清单
- 温度 T 不是越大越好:T 过高会让噪声淹没信号,分类任务 T=2~6 最常见,先网格搜。
- 数据分布要对齐:教师软标签来自哪批数据,学生就学哪批;分布漂移会让暗知识失效。
- 评估别只看准确率:生成任务看 BLEU/ROUGE 不够,务必人工抽查 + 任务级指标。
- 学生容量有下限:太小的学生装不下教师能力,先用不同参数量做容量实验。
- 与部署协同:蒸馏后的小模型用 vLLM 部署 再叠加量化,成本进一步下探。
还有两个常被忽略的点:一是教师选型,并非越大的教师越好,与学生任务分布越接近的中等教师往往迁移效率更高,且推理教师软标签更省钱;二是别过度依赖单一指标,分类看准确率+F1,生成看人工抽样一致性,最好建一个固定评估集,每次蒸馏都跑同一套回归,避免”感觉变好了”式的主观判断。
八、小结
知识蒸馏不是”模型缩小”的银弹,而是成本与精度的工程权衡。它和量化、微调构成模型压缩三件套:先用蒸馏拿小模型,再量化上生产,必要时 LoRA 微调补领域。落地时从明确任务指标和教师数据开始,小步验证,别一上来就全量训练。更多 RAG 与检索增强思路可参考 RAG 进阶实战。




