
张一鸣为什么反对蒸馏这个问题最近被反复提起。很多人第一反应是“蒸馏不是提升效率的吗为什么会有人反对”但如果你真正做过模型压缩、知识迁移或端侧部署就会明白蒸馏从来不是一个单纯的技术选项它背后是“能力来源、数据价值、成本结构和创新路线”的取舍。本文不讨论任何具体企业或个人的经营决策只从技术角度回答一个更本质的问题知识蒸馏到底在做什么它有什么不可忽视的代价以及在哪些场景下“反对蒸馏”其实是理性的工程判断。这篇文章会分四层展开。第一层把知识蒸馏的原理讲清楚包括软标签、温度系数、师生模型这些核心概念第二层用一个最小可运行的 PyTorch 示例带你把一次完整蒸馏跑通第三层拆解“反对蒸馏”的技术理由包括信息损耗、数据偏差、评测陷阱和创新惰性第四层给出可落地的工程建议告诉你在什么场景该用蒸馏什么场景应该谨慎。如果你正在做模型压缩、端侧部署或者只是好奇“为什么一项主流技术会引发争议”这篇文章值得读完。1. “反对蒸馏”背后到底在争论什么模型蒸馏也叫知识蒸馏Knowledge Distillation最早由 Hinton 等人在 2015 年系统提出。它的基本思路很简单用一个已经训练好的大模型教师模型指导一个小模型学生模型训练让学生在尽量接近教师输出的同时也学习训练数据中的真实标签。这个技术在过去几年几乎成了模型压缩的标配。BERT 蒸馏成 TinyBERTGPT 系列蒸馏成小参数模型图像模型蒸馏成移动端可用的轻量网络这些都是工业界反复验证过的路径。按理说一个能够降低推理成本、帮助模型上手机的技术应该被大力欢迎才对。但争议恰恰出在“蒸馏”这个动作上。有人反对蒸馏不是反对压缩本身而是反对一种“路径依赖”如果整个行业都习惯于蒸馏大模型那么小模型就永远是大模型的影子失去了自己从数据中学习新知识的能力。更现实的问题是蒸馏依赖教师模型的输出而教师模型本身可能有幻觉、偏见和错误。学生模型不只会学到教师的“知识”也会学到教师的“毛病”。所以当你听到“反对蒸馏”这类说法时不要把它理解成“蒸馏没用”而要理解成“蒸馏在这个场景下不划算”或者“蒸馏带来的损失大于收益”。这本账怎么算才是技术人真正需要关心的事。2. 知识蒸馏的核心原理软标签、温度与师生模型要理解蒸馏的争议先要理解蒸馏的技术细节。知识蒸馏的核心不是让 Student 直接复读 Teacher 的预测结果而是让 Student 学习 Teacher 输出的概率分布。这里的关键是“软标签”Soft Label。普通分类任务使用独热编码One-hot作为标签比如一张图片是猫标签就是[1, 0, 0]模型只知道正确答案是第 0 类。但教师模型会输出一个概率分布比如[0.85, 0.10, 0.05]它告诉学生这张图有 85% 概率是猫但还有 10% 像狗、5% 像兔子。这种“像什么但又不完全像”的信息就是教师模型从海量数据中总结出来的知识。温度系数Temperature则是控制软标签“软”的程度。Hinton 在论文中提出训练学生模型时要先用温度 T 对教师模型的 logits 做平滑处理公式如下[ q_i \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)} ]当 T 较大时概率分布更平滑类别之间的差异被缩小模型更容易学到“类别间的相似关系”当 T1 时就是普通的 Softmax当 T 趋近于 0 时分布退化成接近独热编码。训练学生模型时通常会使用较高的温度这样才能把教师的“暗知识”传递出来。所以蒸馏损失通常由两部分组成一部分是学生模型与真实标签之间的交叉熵另一部分是学生模型与教师模型在高温下的软标签之间的 KL 散度。用一个权重参数调节两者的比例。你可能听到过“蒸馏一本书的 skill”“蒸馏知识库”这类工程化名词。它们并不完全等同于 Hinton 的经典知识蒸馏更接近“把大模型在处理某个任务时的思考流程或答复风格转换为可复用的提示词模板、Agent Skill 或小模型能力”。这类做法在工程上很常见但它们的理论基础仍然是“让一个模型模仿另一个模型的行为”所以争议点也和经典蒸馏高度重合。3. 蒸馏不是只有一种常见蒸馏路线与工程化变体知识蒸馏在工程上已经演化出多种路线不同的路线对应不同的成本结构。理解这些变体有助于判断“反对蒸馏”的人到底在反对什么。第一类是离线蒸馏Offline Distillation。教师模型先在大规模数据上完成训练然后冻结权重只用于生成软标签再训练学生模型。这是最经典、最稳定的路线。优点是训练过程简单可控缺点是教师模型已经定型学生模型的天花板受限于教师模型。如果你蒸馏一个大模型教师模型的一次前向推理成本甚至会高于学生模型的训练成本。第二类是在线蒸馏Online Distillation。教师模型和学生模型同时训练教师模型并不是预先训练好的而是在训练过程中和学生模型一起更新。自蒸馏Self-Distillation就是在模型深层指导学生模型自身浅层学习本质上是让模型自己教自己。这类方法的优势是不需要预先训练大模型节省了训练成本但训练稳定性更难控制。第三类是特征蒸馏Feature-based Distillation。它不只是让学生模型学习教师模型的输出概率还让学生模型学习教师模型的中间层特征。典型实现是一致性约束比如让两个模型的中间层特征向量尽量对齐。这类方法在图像分类、目标检测、语义分割等任务中非常常用性能往往优于纯输出蒸馏但实现复杂度也更高。从工程角度看还有一类更接近“行为克隆”的蒸馏也就是用大模型生成大量输入-输出对再用小模型去拟合。这类做法在 Agent 领域尤其常见先用一个强模型写出某个任务的完整过程和结果再用小模型微调或构建 Prompt 库。它的好处是快速、便宜、可以针对特定领域定制但坏处是你获得的能力上限基本等于你用来生成数据的那个大模型的能力上限。如果教师模型本身没有解决某个问题的能力学生模型无论如何都学不会。这些变体说明了一件事蒸馏不是一个单一操作而是一系列“用已有模型指导新模型”的方法。讨论“要不要蒸馏”之前必须先明确说的是哪一种蒸馏。4. 最小可运行示例用 PyTorch 完成一次蒸馏纸上谈兵没有意义。下面用一个最小示例演示完整蒸馏流程。我们会用 PyTorch 在 MNIST 数据集上做一次标准的知识蒸馏先训练一个较大的教师模型再用教师模型的软标签训练一个小学生模型。为了控制篇幅这里只展示核心代码。你需要提前安装好torch、torchvision和tqdm版本以你本机环境为准本文不锁定具体版本。整体流程分为四步准备数据、定义模型、训练教师模型、蒸馏训练学生模型。4.1 准备数据集和数据加载器import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torchvision import datasets, transforms from torch.utils.data import DataLoader transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) test_dataset datasets.MNIST(root./data, trainFalse, downloadTrue, transformtransform) train_loader DataLoader(train_dataset, batch_size128, shuffleTrue) test_loader DataLoader(test_dataset, batch_size256, shuffleFalse)这段代码使用 MNIST 数据集完成训练与测试数据的加载。root./data表示数据集会下载到当前目录下的data文件夹。4.2 定义教师模型和学生模型class TeacherModel(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(28 * 28, 1200) self.fc2 nn.Linear(1200, 1200) self.fc3 nn.Linear(1200, 10) def forward(self, x): x x.view(x.size(0), -1) x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) return self.fc3(x) class StudentModel(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(28 * 28, 400) self.fc2 nn.Linear(400, 10) def forward(self, x): x x.view(x.size(0), -1) x F.relu(self.fc1(x)) return self.fc2(x)这里故意让教师模型远大于学生模型以模拟真实场景中“大模型教小模型”的设定。4.3 训练教师模型device torch.device(cuda if torch.cuda.is_available() else cpu) teacher TeacherModel().to(device) optimizer_t optim.Adam(teacher.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() def train_model(model, optimizer, epochs3): model.train() for epoch in range(epochs): total_loss 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch 1}, Loss: {total_loss / len(train_loader):.4f}) train_model(teacher, optimizer_t, epochs3)教师模型训练 3 个 epoch 后测试集精度一般可以达到 98% 以上。4.4 蒸馏训练学生模型这部分是关键。我们同时计算学生模型与真实标签的交叉熵以及学生模型与教师模型在温度 T 下的软标签之间的 KL 散度。def distillation_loss(student_outputs, teacher_outputs, labels, T4.0, alpha0.7): ce_loss F.cross_entropy(student_outputs, labels) soft_teacher F.log_softmax(student_outputs / T, dim1) soft_targets F.softmax(teacher_outputs / T, dim1) kl_loss F.kl_div(soft_teacher, soft_targets, reductionbatchmean) * (T * T) return alpha * ce_loss (1 - alpha) * kl_loss student StudentModel().to(device) optimizer_s optim.Adam(student.parameters(), lr1e-3) teacher.eval() student.train() T 4.0 for epoch in range(3): total_loss 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer_s.zero_grad() with torch.no_grad(): teacher_outputs teacher(images) student_outputs student(images) loss distillation_loss(student_outputs, teacher_outputs, labels, T, alpha0.7) loss.backward() optimizer_s.step() total_loss loss.item() print(fDistill Epoch {epoch 1}, Loss: {total_loss / len(train_loader):.4f})注意kl_loss乘了T * T这是 Hinton 论文中的做法。因为 soft target 被温度 T 缩放后梯度幅度会减小乘以 T² 可以抵消这种影响让 KL 损失的梯度和普通交叉熵处于同一量级。4.5 测试学生模型准确率def evaluate(model): model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) preds outputs.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) return correct / total teacher_acc evaluate(teacher) student_acc evaluate(student) print(fTeacher Acc: {teacher_acc:.4f}) print(fStudent Acc after distillation: {student_acc:.4f})这就是一个完整的最小蒸馏示例。5. 运行结果与效果验证运行上述代码你会得到类似下面的输出格式Epoch 1, Loss: 0.2341 Epoch 2, Loss: 0.1125 Epoch 3, Loss: 0.0887 Distill Epoch 1, Loss: 0.4521 Distill Epoch 2, Loss: 0.3108 Distill Epoch 3, Loss: 0.2456 Teacher Acc: 0.9821 Student Acc after distillation: 0.9723注意具体数值会因 PyTorch 版本、CUDA 环境和随机种子不同而波动以上只是用于说明预期趋势的格式示例。效果验证的关键不是学生模型比教师模型更准而是比较两个指标。第一学生模型在蒸馏后的精度是否明显高于“学生模型从头直接训练”的精度。你可以把distillation_loss替换成普通交叉熵重新训练一个学生模型对比两者在测试集上的准确率。如果蒸馏后的学生模型更高说明教师模型的软标签确实提供了有效信息。第二学生模型参数量是否明显小于教师模型。在上面的示例中教师模型参数量大约 1.2M学生模型只有 0.4M 左右推理速度更快、内存占用更少。如果蒸馏后学生模型精度反而下降明显优先检查以下几点教师模型是否过拟合、温度 T 是否过大或过小、alpha权重是否合理、软标签的计算逻辑是否正确。最常见的问题是把F.log_softmax和F.softmax的顺序写反导致 KL 散度计算错误。6. 为什么有人反对蒸馏争议点与适用边界现在可以正面回答标题提到的“反对蒸馏”了。从技术角度看蒸馏至少存在五个争议点。第一信息损耗是客观存在的。教师模型输出的概率分布已经是对原始数据的压缩学生模型再去拟合这个压缩结果等于在压缩结果上再做一次有损压缩。两个模型的表达能力差异越大信息损耗就越明显。蒸馏后的学生模型可能在测试集上表现不错但在分布外数据上会暴露盲区。第二教师模型的错误会被继承甚至放大。教师模型在某个类别上的误判会以软标签的形式传递给所有训练样本。如果教师模型对某些长尾样本的置信度很高但预测错误学生模型会把这种错误当成“知识”学习。更隐蔽的是教师模型的种族、性别、地域偏见也会通过软标签进入学生模型而且清洗难度更大。第三蒸馏会掩盖数据价值。如果一个团队只依赖教师模型的输出做蒸馏而不去管理原始训练数据那么学生模型的能力上限就被锁死了。更严重的是当教师模型基于的数据集存在版权问题时蒸馏后的模型是否构成“衍生作品”在很多法律框架下并没有清晰结论。第四评测指标会骗人。在标准测试集上蒸馏学生模型往往表现优秀但在真实业务场景中你可能发现它“变得笨”了。原因在于测试集和真实数据分布不一致而蒸馏过程会进一步强化学生模型对教师模型偏好的拟合。如果一个指标完全来自蒸馏数据上的准确率它只能证明“学生像教师”不能证明“学生足够好”。第五组织层面的路径依赖。当蒸馏成为默认选项团队可能不再思考“是否需要训练一个更大的模型”“是否需要设计更好的数据管道”“是否需要改进模型架构”。蒸馏降低了短期成本但也可能降低了创新的可能性。这可能是“反对蒸馏”最深层的原因。那么什么场景适合蒸馏端侧推理、低延迟服务、模型压缩到特定硬件、保持一致行为风格的 Agent 快照、冷启动阶段的小模型热启动这些都是蒸馏的合理场景。什么场景不适合蒸馏数据分布与教师模型训练数据差异很大的新领域、对长尾样本有强需求的业务、需要模型具备教师模型完全不具备的新能力的任务。在这些场景里优先考虑用原始数据训练小模型或者用强模型生成增强数据后微调小模型而不是直接做行为克隆式的蒸馏。7. 常见问题与排查思路问题现象可能原因排查方式解决方案学生模型精度远远低于教师模型教师模型过拟合或数据泄漏检查教师模型在验证集上的表现重新训练教师模型或换成更稳定的教师模型蒸馏损失下降但测试精度不升KL 损失权重过大学生模型过分拟合软标签检查 loss 中 ce_loss 和 kl_loss 的比例尝试减小 alpha 或降低温度 T温度 T 调高后训练不稳定温度过高导致梯度过于平滑观察训练曲线是否震荡从 T2~4 开始调参不要一开始就设到 10教师模型推理速度太慢大模型前向推理开销大统计教师模型单批推理耗时预先离线生成软标签并保存为 .npy 或 .pt 文件蒸馏后模型对某些类别失灵教师模型在该类别上置信度分布混乱分析教师模型的混淆矩阵针对该类别补充真实标签数据降低对软标签的依赖软标签计算报维度错误忘记对 logits 做温度缩放或使用了错误的 softmax 函数检查 KL 散度输入的维度是否匹配统一使用 F.log_softmax(outputs/T) 和 F.softmax(teacher/T)保存软标签文件后硬盘被占满软标签以 float32 保存占空间大查看文件大小改用 float16 或按类别分桶保存控制副本数量8. 最佳实践与工程建议基于上面的原理和争议我给出几条经过验证的工程建议。第一先问自己“要蒸馏的是什么”。如果你只需要一个小模型完成固定分类任务经典知识蒸馏就够了如果你需要一个小模型复现大模型的交互风格和任务流程行为克隆式蒸馏更适用如果你希望小模型学到中间层特征则需要特征蒸馏。不要把所有压缩需求都叫“蒸馏”。第二不要把教师模型的输出当作“标准答案”。更稳妥的方式是混合训练一部分 batch 使用教师模型的软标签另一部分 batch 使用真实标签通过动态权重控制两者比例。这样即使教师模型某个样本出错学生模型也有机会从真实标签中纠正。第三软标签预先算好再训练。教师模型非常大时在蒸馏过程中实时前向传播会浪费大量显存和算力。更好的做法是用教师模型对训练集推理一次把软标签保存为压缩格式然后学生模型训练时直接读取。第四做好分布外数据验证。不能只看蒸馏后的模型在测试集上的准确率要额外准备一份与教师模型训练分布差异明显的验证集观察学生模型和教师模型各自的退化程度。如果学生模型在分布外数据上退化特别严重说明蒸馏过程丢失了重要的泛化能力。第五建立回滚机制。在模型发布流程中蒸馏学生模型应该和普通模型一样做灰度发布同时保留前一个线上版本作为回滚目标。蒸馏模型的一个隐藏风险是它在离线评测指标上看起来没问题但线上某些 prompt 或样本会被它以一种奇怪的方式处理。这类问题通常在线下很难发现只能靠灰度流量逐步暴露。第六对“蒸馏 Skill”这类工程化做法要建立质量门槛。用大模型生成 Skill 指导文档、用大模型生成微调数据本身没问题但你要评估生成结果的质量和覆盖率。业界比较稳妥的做法是抽样对比随机抽取一定比例的大模型生成结果由人工或规则引擎做质量评估不达标的生成结果直接丢弃而不是全部塞进训练集。9. 总结蒸馏是工具不是信仰回到开头的标题。“张一鸣为什么反对蒸馏”如果去掉故事性的部分这个问题真正想问的是为什么有人不愿意把“用大模型教小模型”当成默认选项因为蒸馏的本质是用一个模型的判断去替代另一个模型的学习。它高效、低成本、能快速得到可用模型但它也天然地带有信息损耗、误差继承和创新惰性。一个合格的工程师不应该无条件拥抱蒸馏也不应该盲目反对蒸馏而是应该清楚每个方案在成本、质量、安全和创新四个维度上的取舍。如果你今天的任务是把一个 7B 模型压缩到 2B 并部署到端侧蒸馏依然是目前最实用的技术路径之一放心用。如果你今天的任务是在一个新领域从零构建模型能力而你的训练数据本来就不充足那么先别急着蒸馏先把数据管道和评测基线做起来。文章中的代码示例你可以在本地直接运行修改数据集和模型结构后也可以迁移到自己的任务上。建议收藏备用在真正做模型压缩和蒸馏方案选型的时候再回来对照这篇的争议点和实践建议。