最近圈里都在聊一件事:七家公司被点名,罪名是同一个动作——“蒸馏”。很多人跑来问我,蒸馏到底偷了什么东西,怎么就能让几家公司一起上了榜。说实话,这个问题的热度,比那七家公司的名字本身更有意思。它说明大模型的能力,第一次被当成了一种可以被“审计”的资产。
我想借这个机会,把蒸馏这件事从头到尾掰开讲清楚:它到底是怎么运作的,所谓“偷走能力”在技术上意味着什么,哪些做法算是在规则边缘反复横跳,哪些其实是正儿八经的常规工程。文章尽量不堆术语,遇到绕不开的地方我会用生活化类比补上。如果你正在做模型压缩、准备微调自己的小模型,或者只是好奇“蒸馏”两个字为什么突然变成敏感词,这篇应该能帮你把前因后果捋顺。
1. 被点名的“蒸馏”:先把这个瓜吃明白
1.1 七家公司为什么会被盯上
先说个结论:那份名单在不同平台上传了好几轮,具体出处有说是行业观察报告,有说是某模型服务商更新的调用监测记录,我这里不替大家去抠原始出处了。真正值得关注的是,“蒸馏”这个动作第一次被放到了聚光灯下。
过去大家聊大模型,注意力都在训练数据、算力规模、参数量这些上游指标上,蒸馏更像一个藏在工程角落里的优化技巧,只在模型压缩的论文里高频出现。但这次点名事件把“蒸馏”和“偷”直接挂钩,意味着一件事:大模型行业进入了一个新阶段,能力本身开始被审计,模型的输出行为可以被追踪、比对,甚至倒推出“你的模型是不是拿了别人的模型当老师”。
从技术角度看,这种审计并不是玄学。教师模型的输出有统计特征,学生模型如果长期围绕教师模型的问题-答案对做拟合,两者在相同输入下的概率分布会高度相似。这种相似性可以做量化比对,相似度过高就足以引起怀疑。说白了,这不是靠“内部举报”,而是靠分布在说话。
1.2 “偷走了什么”为什么这么难回答
这个问题看着简单,答案却让技术人员和规则制定者都很头疼。你拿一个开源模型去微调,那是许可范围内的正常操作;你拿一个商用的模型接口疯狂提问,把问答结果存下来喂给自己的训练集,这大概率属于违约;而蒸馏恰恰踩在两者之间——它通过构造大量输入输出对,把教师模型的能力逐步迁移到另一个模型里,到底算借鉴还是算抄袭,行业里没有统一答案。
难就难在大模型的能力不是一件可以贴标签的实物。它没法定格成一段源码、一个数据库文件或者某几个参数矩阵。蒸馏完成之后,你得到的学生模型可能参数量只有老师的几十分之一,内部结构完全重写,但回答风格、推理偏好、甚至犯错的方式都很像老师。硬要说“偷走了什么”,偷走的其实是行为模式和概率分布,而不是可以上秤称重的东西。这就像一个徒弟跟在师父身边三年,没偷过师父的菜谱,却把师父做菜的火候、调味习惯、装盘偏好都学了个七八成。你很难说徒弟“偷”了哪道具体的菜,但他确实把师父的手艺带走了。
2. 蒸馏到底是怎么“偷”走能力的?技术拆解
2.1 教师-学生框架:logits 里的第二课堂
先放下那些争议,聊聊蒸馏的基本功。几乎所有蒸馏方法都跑不出知识蒸馏教师-学生框架:一个参数量大、已经训练好的教师模型,和一个参数量小、待训练的学生模型。学生模型学的不只是正确答案,还有教师模型在输出每一个 token 或标签时给出的概率分布。
举个直观的例子。假设教师模型做图片分类,输入一张猫的照片,它输出的标签分布不是简单的一个“猫”,而是一串带概率的向量:猫 0.8,狐狸 0.15,狗 0.05。如果只用传统训练方式,学生模型只会看到“正确答案是猫”,然后拼命把猫的概率拉高。但通过蒸馏,学生还能看到“猫和狐狸有点接近,和狗差别更大”这层隐藏信息。
这里有个关键操作叫温度缩放。直接把教师的 0.8、0.15、0.05 当成学习目标,学生很容易学不到小概率背后的差异。把 logits 除以一个温度系数 T(比如 4 或 8),分布会变得“更平”,那些原本只有 0.01 甚至更小的概率也会被放大,学生才有机会观察到教师模型的完整知识结构。蒸馏结束后再把温度调回 1,学生模型的预测照样会变得锐利。
所以说白了,教师模型的门并没有被人撬开,学生通过几千次、几百万次提问,把教师模型在各类输入上的反应模式记录并复刻下来。这就是“黑盒蒸馏”的基本原理,也是很多争议的起点。
2.2 蒸馏“偷”的是参数吗?不是,是决策流形
很多人直觉上以为,蒸馏是把教师模型的权重复制了一份,再塞进小模型里。真实情况完全不是这样。
教师模型的权重通常是几十 GB 的浮点数矩阵,学生模型如果只有几亿参数,物理上根本没有空间装下这些权重。蒸馏真正传递的东西,是教师模型在输入空间里形成的决策流形。所谓流形,你可以把它理解成一个模型对世界结构的“内部地图”:哪些输入是相似的,哪些答案之间挨得近,哪些边界必须严格区分。
学生模型不复制地图本身,它通过反复看教师的输出,自己重建了一张风格相似但路径不同的地图。这也是为什么模型作者很难拿蒸馏产物去做逐位比对:两边权重对不上,代码也对不上,但行为对得上。能力被迁移了,痕迹却很难抓。
用一个生活类比:老面馆的老板收了个徒弟,徒弟没有偷走任何配方文档,每天就在后厨看老板揉面、醒面、配料、试咸淡。几个月后,徒弟独立开的面馆,面条口感、汤头风韵和老板有八成相像,但你要说徒弟偷了哪一张配方纸条,还真找不出来。蒸馏做的事,本质上就是这种“观察学习”。
2.3 那些并不算“偷”的蒸馏
把“蒸馏”一律当成偷窃,是这次舆情里最容易误导人的地方。实际操作中,大量蒸馏工作是在完全正当的框架下进行的,至少可以分成三类。
第一类,基于开源权重蒸馏。你对开源模型本来就有使用和修改权,把教师模型的 logits 导出来训练一个小模型做端侧推理,这是典型的模型压缩,论文里到处都是,没有任何争议。第二类,自蒸馏,也就是用同一个模型自己当自己的老师,把自己更深层的表示教给浅层分支,这是提升模型性能的常规手段,根本涉及不到外部模型。第三类,数据集蒸馏,那是把整个数据集的精华浓缩成少量合成样本,和“参考某个外部模型”更没关系。
真正容易踩线的,是把别人的商用模型服务当成免费教师,通过大规模调用得到的高质量问答对来训练自己的产品。这种行为有没有问题,取决于教师模型的服务条款、数据使用授权和你拿它训练的目的。但至少可以确定,蒸馏方法本身是中性技术,锅不该由算法背。
3. 手把手:一次完整的蒸馏实操
3.1 准备教师与学生模型
前面讲了半天原理,现在直接上手跑一个最小可用的蒸馏流程。我用的是 PyTorch 和 Hugging Face Transformers,日常做 NLP 模型压缩基本就是这套组合。
教师模型我习惯用一个较强的预训练模型,比如bert-base-uncased或者某版本的distilbert-base-uncased。注意,这里有个细节:教师模型最好先在下游任务上做过微调,如果你直接拿一个通用预训练模型当教师,教出来的学生很可能只学会语言建模,不学会任务判断。实操里,我一般先把教师模型在目标数据集上跑一轮微调,得到一份还算靠谱的 logits 缓存,再启动蒸馏。
学生模型的选择更宽松一些,参数量是教师的三分之一到十分之一都可以。我自己常用的是bert-tiny或者albert-base。学生结构不一定要和教师一样,但输入输出接口必须对齐,否则后面对齐 logits 会很痛苦。
3.2 最小蒸馏代码:从 logits 中学习
下面的代码是一个典型的知识蒸馏训练脚本骨架:
import torch import torch.nn as nn import torch.nn.functional as F from transformers import AutoTokenizer, AutoModelForSequenceClassification def soft_target_loss(student_logits, teacher_logits, temperature=4.0): student_soft = F.log_softmax(student_logits / temperature, dim=-1) teacher_soft = F.softmax(teacher_logits / temperature, dim=-1) return F.kl_div(student_soft, teacher_soft, reduction="batchmean") * (temperature ** 2) def hard_target_loss(student_logits, labels): return F.cross_entropy(student_logits, labels) # 教师与学生模型初始化 tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") teacher_model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased") student_model = AutoModelForSequenceClassification.from_pretrained("bert-tiny-uncased") # 前向计算 # inputs, labels 来自 DataLoader with torch.no_grad(): teacher_logits = teacher_model(**inputs).logits student_logits = student_model(**inputs).logits alpha = 0.7 temperature = 4.0 loss = alpha * soft_target_loss(student_logits, teacher_logits, temperature) + \ (1 - alpha) * hard_target_loss(student_logits, labels)这段代码里最容易被忽略的细节是temperature ** 2这个回乘操作。因为 logits 被温度缩放后,梯度会跟着变小,如果不乘回温度的平方,学生模型的学习速度会慢得离谱。很多新手第一次跑蒸馏,loss 怎么都不降,八成就是漏了这一步。
训练循环里还需要把教师模型锁在torch.no_grad()下,不然每次前向都会更新教师的权重,等于让一个正在考试的老师一边答题一边改答案,学生根本没法稳定学习。教师的梯度传播也应该彻底关掉,省显存是次要的,关键是避免训练信号混乱。
3.3 这几个参数,实战中直接影响结果
参数不只是调个数字,它们背后对应着完全不同的训练行为。下面这份总结是我在几个项目里反复调出来的经验值。
| 参数 | 推荐范围 | 作用 | 调参心得 |
|---|---|---|---|
| 温度 T | 2-8,文本任务常用 4 | 控制教师分布被“摊平”的程度 | T 太小,学生只学到最高概率那个标签;T 太大,所有类别的差异被抹平,变成噪声 |
| 软标签权重 alpha | 0.5-0.9 | 平衡“模仿教师”和“拟合真答案”的占比 | 教师很强时 alpha 取高一些;教师本身一般时 alpha 太高反而把错误放大 |
| 教师是否冻结 | 必须冻结 | 确保教师输出稳定 | 不冻结会让训练信号漂移,学生根本抓不住规律 |
| 批大小 | 教师可以离线缓存,学生尽量大 | 软标签对 batch 内的分布更敏感 | 能用 64 就不必用 16,KL 散度在小 batch 下抖动明显 |
| 蒸馏数据集分布 | 必须靠近真实推理分布 | 决定学生学到的流形覆盖范围 | 只拿互联网问答做蒸馏,上线遇到私有格式提问就会崩 |
还有一个值得记住的点:教师模型的 logits 最好先缓存到磁盘上,不要每次迭代都现算。教师模型大,前向推理慢,每轮跑一遍会拖慢训练速度好几倍。把训练集的 logits 离线算好存成.pt文件,训练时直接加载,学生模型只做自己的前向和反向,速度能提升一个量级。
3.4 蒸馏和微调的区别
不少刚开始接触大模型的朋友会把蒸馏和微调混为一谈,这里我列个对比表,一眼就能分清。
| 对比维度 | 模型蒸馏 | 模型微调 |
|---|---|---|
| 学习对象 | 教师模型的概率输出(软标签) | 人工标注的正确标签(硬标签) |
| 目标 | 压缩模型规模、迁移能力 | 适配新任务/新领域 |
| 是否需要大模型做教师 | 需要 | 不需要 |
| 对标注数据的要求 | 需要教师输出,可减少人工标注量 | 需要高质量标注数据 |
| 训练结果 | 行为接近教师,尺寸更小 | 行为接近目标任务,尺寸不变 |
| 典型产出 | 端侧小模型、推理加速模型 | 领域专用接口、业务模型 |
微调像是在同一栋楼里换装潢,结构没变,风格变了;蒸馏是照着原建筑另起一栋小楼,外观相近,但材料、结构完全不同。这两种方法不冲突,实际工业项目里经常先蒸馏再微调:用蒸馏省下模型体积,再在业务数据上微调修正领域偏差。
4. 常见问题排查速查表(我踩过的坑)
4.1 问题一:学生模型一直涨不起来
损失函数半天不降,或者刚降一点就震荡,是蒸馏最常见的翻车现场。我排查的顺序一般固定为:先看校对的 logits 形状对不对,再看温度有没有设得太极端,最后才怀疑学习率。
形状问题最隐蔽。分类任务里很容易忽略教师和学生的输出类别数是否一致,教师如果是多标签输出、学生用的是单标签分类头,KL 散度就会在维度不匹配时报错或者直接算出一个无意义的均值。温度方面,我见过有人把 T 调到 20,结果所有软标签都变成均匀分布,学生除了随机猜什么都学不到。T 调到 0.5,软标签又变得比硬标签还硬,蒸馏的意义就没了一半。
4.2 问题二:学生模型比教师模型还“聪明”
听起来不可思议,但当教师模型本身有偏见过拟合时,学生反而可能通过软标签把教师的“错误直觉”过滤掉。如果训练集同时包含一部分强标签损失,学生偶尔会在个别指标上反超教师。
这种情况不需要恐慌。很多论文里报告过“学生超过教师”的现象,主要原因是硬标签损失让模型直接面向真实标注优化,绕开了教师在部分噪声样本上的错误判断。真正需要警惕的不是“学生超过教师”,而是“学生学了一堆教师胡编的内容”。如果你在蒸馏数据里混入了大量教师自己生成的幻觉文本,学生就会把这些幻觉当成标准答案学走,上线后一本正经地胡说八道。解决办法是给蒸馏样本做一遍置信度过滤,把教师 logits 最高概率低于阈值的样本直接丢掉。
4.3 问题三:蒸馏后泛化能力不升反降
你会看到模型在验证集上和教师几乎一模一样,换到新场景就彻底失灵。这通常不是蒸馏过程出了问题,而是蒸馏数据分布和真实业务分布脱节导致的。
教师是在特定语料上训练的,如果蒸馏数据集只覆盖了几个固定模板,学生学到的流形就是这小小的几块区域。换了个句式、换了种说法,输入落到了流形外面,模型就开始乱猜。处理办法也比较简单:蒸馏数据集里一定要混入一定比例的领域噪声样本、反例样本和边缘样本。哪怕教师回答得不太好,只要分布是真实的,学生就能学到应对不确定性的能力,而不是只在安全区里开无双。
4.4 用评测指标“验货”:别只看准确率
蒸馏项目上线前,我会做三件事。第一,拿一个未参与训练的中性测试集,同时跑教师、学生和基线模型,看准确率和 F1,确认学生没有明显落后。第二,逐样本比对教师和学生输出,算出“行为一致性”比例。如果一致性超过九成,说明蒸馏效果到位;如果只有六成,说明学生学到的只是皮毛。第三,也是最容易被忽视的,随机挑一批教师明显会犯错的样本看学生怎么反应。如果学生完美复刻了教师的错误,那说明蒸馏确实到位了;如果学生改成了一种新的错误方式,那要小心,它学的不是教师的能力,而是自己脑补出来的另一套逻辑。
这三个指标都过了,我才会让模型进测试环境。
5. 从“偷”到“用”:我对蒸馏的看法
5.1 蒸馏不是原罪,违反规则才是
回到开头那个七家公司的话题。我个人在技术层面并不认为“蒸馏”三个字自带原罪,真正需要审视的是蒸馏的边界条件。
你用一个别人明确允许修改的模型做蒸馏,那是高质量工程;你把别人的商用接口当成免费数据工厂,反复调用几百万次生成训练集,再蒸馏出一个竞品,那就算没有触犯技术禁忌,也大概率违反了用户协议。作为工程师,动手前把模型卡、开源许可证、服务条款读一遍,是最基本的职业习惯。尤其是那些提供免费额度的平台,条款里通常写明了“输出不能用于训练同类型竞争模型”之类的限制,别因为没读过就当不存在。
我自己做项目时有一条土规矩:教师模型服务条款说不允许蒸馏,我就不碰;允许微调但不允许模型蒸馏的,我就老老实实微调;明确给了蒸馏许可的,再放开手脚压模型体积。边界清晰了,干活才有底气。
5.2 什么时候我劝你别蒸馏
蒸馏很诱人,但不适合所有场景。如果你的教师模型还在频繁迭代,每周都在更新能力,那学生模型就永远追着老师跑,前一版刚蒸馏完,老师的本领又变了。这时候更适合直接做模型微调,或者干脆等教师稳定之后再压缩。
如果你需要完全独立的技术栈和可解释性,蒸馏也不是好选择。学生模型继承了教师的行为,同时也继承了教师的偏见、幻觉和不可解释性。你没法指着学生模型的某个神经元说,这就是它判断“猫”的原因。在医疗、金融这类高度敏感的场景,黑盒复刻黑盒只会放大责任风险。
还有一个很现实的场景:预算不紧张,推理性能也没到瓶颈。那蒸馏省下来的成本,可能还不够抵消维护两套模型、跟踪行为一致性的人力开销。技术不是越高级越好,合适才是最好的。
5.3 最后再分享一点实操体会
我这两年反复验证过一件事:蒸馏把推理成本打下来真的很简单,难的是搞清楚学生模型到底从教师身上继承了什么、丢掉了什么。你可以在评测集上看到 95% 的行为一致性,但换到真实环境,那 5% 的偏差可能全集中在最关键的少数案例上。
所以在我的项目里,蒸馏从来不是一个“跑完脚本就交付”的环节,它是模型上线的第一道质检门。每次拿到的蒸馏产物,我都会安排一批专人去挑刺,专门找教师和学生在边界样本上的分歧,再决定是补数据继续蒸,还是干脆换个教师。
技术永远在更新,今天聊的七家公司、温度参数、KL 散度,过半年可能又会有新版本。但有一句话随时拿出来都不过时:任何一个被你“偷”来的能力,最后都要由你自己来承担它犯错的后果。想清楚这一层,比争论名单上到底有哪七家,要重要得多。