知识蒸馏深度解析:从大模型到小模型的智能迁移

7424 字
37 分钟
知识蒸馏深度解析:从大模型到小模型的智能迁移

1. 引言:为什么需要知识蒸馏?#

1.1 大模型的”最后一公里”问题#

GPT-4、Claude、DeepSeek-R1 等大模型在学术 benchmark 上表现出色,但部署到实际应用时面临严峻挑战:

大模型部署的现实困境:
GPT-4 (1.8T 参数):
→ 推理成本: ~$0.03 / 1K tokens
→ 延迟: ~20-60 秒(复杂推理任务)
→ 需要: A100/H100 GPU($30K+/张)
→ 应用场景: 只能是云端 API
Llama-3 70B:
→ 推理成本: ~$0.001 / 1K tokens
→ 延迟: ~2-5 秒
→ 需要: 至少 2×A100 (80GB)
→ 应用场景: 企业级私有部署
Llama-3 8B:
→ 推理成本: ~$0.0001 / 1K tokens
→ 延迟: ~0.5-1 秒
→ 需要: 单卡 4090 (24GB)
→ 应用场景: 个人设备、边缘部署、移动端

知识蒸馏解决的核心问题:如何将大模型(Teacher)的知识迁移到小模型(Student),同时保持大部分能力?

1.2 知识蒸馏的定义#

知识蒸馏(Knowledge Distillation, KD):将复杂教师模型(Teacher)的知识压缩到轻量学生模型(Student)的过程,使学生模型能够在保持高效率的同时达到接近教师模型的性能。

知识蒸馏的直观理解:
Teacher (GPT-4):
输入: "如何用 Python 实现快速排序?"
输出: [概率分布 over 50K tokens]
│ "暗知识"(Dark Knowledge)
│ 除了正确答案,还包含:
│ - 各错误答案的概率分布
│ - 答案之间的相对关系
│ - 模型的"犹豫"程度
Student (Llama-8B):
输入: "如何用 Python 实现快速排序?"
输出: [更紧凑的概率分布]
│ 学习 Teacher 的暗知识
│ 比直接学习"正确答案"学到更多
性能: GPT-4 的 90%+ 能力
成本: 1/100

1.3 知识蒸馏的历史#

知识蒸馏发展时间线:
2015: Hinton et al. 提出 KD
→ Soft Targets, Temperature, Dark Knowledge
→ 目标: 模型压缩(CNN/DNN)
2019: BERT 蒸馏热潮
→ DistilBERT, TinyBERT, MobileBERT, MiniLM
→ 目标: 预训练语言模型压缩
2021-2023: LLM 蒸馏探索
→ GPT-J, LLaMA 蒸馏
→ 目标: 开源社区对标闭源模型
2024-2025: Reasoning 蒸馏
→ DeepSeek-R1 Distill
→ 目标: 将推理能力迁移到小模型

1.4 本系列文章关联#

本文是 LLM 技术系列的延续,与以下文章相关:

文章关联
DeepSeek-R1R1 的蒸馏实验是本文的实践案例
Supervised Fine-TuningSFT 是蒸馏数据生成的基础
RLHFRLHF 生成的数据可用于蒸馏

2. 知识蒸馏理论基础#

2.1 Hinton 的经典框架#

2015 年,Geoffrey Hinton、Jeff Dean 和 Oriol Vinyals 在论文 “Distilling the Knowledge in a Neural Network” 中提出了知识蒸馏的基本框架。

核心思想:让学生模型学习教师模型的软概率分布,而非硬标签。

def hinton_distillation_loss(
student_logits: torch.Tensor, # (B, V) 学生 logits
teacher_logits: torch.Tensor, # (B, V) 教师 logits
hard_labels: torch.Tensor, # (B,) 真实标签
temperature: float = 4.0,
alpha: float = 0.7,
) -> torch.Tensor:
"""
Hinton 经典蒸馏损失。
L = α · L_soft + (1-α) · L_hard
其中:
L_soft = KL_divergence(softmax(T_student/T), softmax(T_teacher/T))
L_hard = CrossEntropy(student_logits, hard_labels)
参数:
T (temperature): 温度越高,分布越平滑,暗知识越明显
α: 软目标和硬目标的权重平衡
"""
# === 1. 软目标损失 ===
# 用高温 T 对 logits 做 softmax,得到软概率分布
soft_student = F.softmax(student_logits / temperature, dim=-1)
soft_teacher = F.softmax(teacher_logits / temperature, dim=-1)
# KL 散度(蒸馏的核心)
# 注意:KL(p||q) = p * (log p - log q)
# 当 T > 1 时,平滑的概率分布让暗知识显现
L_soft = F.kl_div(
torch.log_softmax(student_logits / temperature, dim=-1),
soft_teacher,
reduction='batchmean'
) * (temperature ** 2) # 补偿温度带来的缩放
# === 2. 硬目标损失 ===
# 标准的交叉熵(当学生学会大部分知识后占主导)
L_hard = F.cross_entropy(student_logits, hard_labels)
# === 3. 加权组合 ===
loss = alpha * L_soft + (1 - alpha) * L_hard
return loss

2.2 温度与软概率分布#

温度 TT 是蒸馏的核心超参数。理解它需要理解 softmax 的温度效应:

温度 T 对 softmax 分布的影响:
T = 1.0 (原始 softmax):
logits = [2.0, 1.0, 0.5, -1.0]
probs = [58.1%, 23.7%, 10.7%, 2.4%]
→ 分布尖锐,差异放大
T = 4.0 (蒸馏温度):
logits = [2.0, 1.0, 0.5, -1.0]
probs = [ 28.9%, 24.4%, 22.5%, 18.8%]
→ 分布平滑,暗知识显现
T = 10.0 (极高温度):
logits = [2.0, 1.0, 0.5, -1.0]
probs = [25.8%, 25.1%, 24.7%, 24.0%]
→ 几乎均匀,失去区分度
最佳实践:T ∈ [2, 6],通常是 4.0

为什么高温能提取暗知识?

def demonstrate_dark_knowledge():
"""
展示暗知识的价值。
"""
# 教师模型的 logits(某种分类任务)
teacher_logits = torch.tensor([5.0, 2.0, 1.0, 0.0, -1.0])
print("=== 暗知识示例 ===")
print(f"Logits: {teacher_logits}")
print(f"T=1 probs: {F.softmax(teacher_logits, dim=-1)}")
print(f"T=4 probs: {F.softmax(teacher_logits / 4, dim=-1)}")
# 假设真实标签是类别 0
hard_label = 0
# 低温度:只告诉学生"类别 0 是正确答案"
low_temp = F.softmax(teacher_logits, dim=-1)
# → 概率比: 58.1% / 23.7% ≈ 2.5x
# 高温度:告诉学生"类别 0 比类别 1 好 2.5 倍,比类别 2 好 2.7 倍..."
high_temp = F.softmax(teacher_logits / 4, dim=-1)
# → 概率比: 28.9% / 24.4% ≈ 1.18x
# → 揭示了类别之间的相对关系!
print("\n暗知识的价值:")
print(" 低 T: 只知道正确答案")
print(" 高 T: 知道答案之间的'距离'和'关系'")
print(" → 这些关系是泛化能力的关键!")

2.3 暗知识(Dark Knowledge)#

Hinton 提出的暗知识概念是蒸馏的理论基础。

暗知识的三层含义:
第一层:类别关系
正确答案之外的概率分布包含了:
→ 哪些错误答案"更像"正确答案
→ 哪些错误答案之间"语义相近"
→ 分类边界附近的"模糊地带"
例:区分"猫"和"狗"
→ 狗被误分类为猫的概率 > 误分为汽车
→ 这反映了语义相似性
第二层:置信度校准
教师的概率分布反映了它的置信度:
→ 高置信度:模型很确定
→ 低置信度:模型不确定(接近随机猜测)
→ 学生学习这个校准信息
第三层:泛化先验
教师在训练数据上学到的泛化模式:
→ 什么特征是"猫"的标志
→ 什么特征在不同类别间是共享的
→ 这些通过软分布隐式传递

2.4 蒸馏损失的理论分析#

为什么蒸馏比直接学习硬标签更好?直觉和理论两个层面:

蒸馏的理论优势:
1. 信息密度
硬标签: 1 bit / 样本(只有"对"或"错")
软标签: log(V) bits / 样本(V=词表大小,隐藏了类别关系)
2. 梯度方差
硬标签: 梯度方向噪声大
软标签: 梯度方向更稳定(来自教师的先验)
3. 类别不均衡
硬标签: 少数类样本极少
软标签: 软概率隐式包含了类别分布信息
数学直觉:
交叉熵 H(y, p) = -Σ y_i log p_i
当 y 是 one-hot 时:H = -log p_true
当 p 是软分布时:学生学习更丰富的结构

3. 知识蒸馏的分类体系#

3.1 四种蒸馏类型概览#

根据蒸馏的知识来源,可以分为四类:

┌────────────────────────────────────────────────────────────────┐
│ 知识蒸馏分类体系 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 1. Response Distillation(响应蒸馏) │
│ → 蒸馏教师的输出层/最终预测 │
│ → 最简单、最常用 │
│ │
│ 2. Feature Distillation(特征蒸馏) │
│ → 蒸馏教师的中间层表示(隐藏状态) │
│ → 需要处理维度不匹配问题 │
│ │
│ 3. Representation Distillation(表示蒸馏) │
│ → 蒸馏嵌入层面的知识 │
│ → 比特征蒸馏更底层 │
│ │
│ 4. Relation Distillation(关系蒸馏) │
│ → 蒸馏知识之间的关联/结构 │
│ → 最新研究方向 │
│ │
└────────────────────────────────────────────────────────────────┘

3.2 Response Distillation(响应蒸馏)#

定义:让学生学习教师模型的最终输出(Response/Logits/预测概率)。

class ResponseDistillationLoss(nn.Module):
"""
Response Distillation: 直接蒸馏教师输出。
最简单,适用于分类任务和语言模型。
"""
def __init__(self, temperature=4.0, alpha=0.7):
super().__init__()
self.T = temperature
self.alpha = alpha
def forward(self, student_logits, teacher_logits, hard_labels):
# 软目标
soft_loss = F.kl_div(
F.log_softmax(student_logits / self.T, dim=-1),
F.softmax(teacher_logits / self.T, dim=-1),
reduction='batchmean'
) * (self.T ** 2)
# 硬目标
hard_loss = F.cross_entropy(student_logits, hard_labels)
return self.alpha * soft_loss + (1 - self.alpha) * hard_loss

适用场景

  • 分类任务(BERT 蒸馏)
  • 语言模型(GPT 蒸馏)
  • 生成任务(部分使用)

3.3 Feature Distillation(特征蒸馏)#

定义:让学生学习教师模型的中间层特征表示。

class FeatureDistillationLoss(nn.Module):
"""
Feature Distillation: 蒸馏中间层表示。
需要处理 Teacher 和 Student 维度不匹配的问题。
"""
def __init__(self, hidden_dim_student, hidden_dim_teacher):
super().__init__()
# 适配器:将学生特征映射到与教师相同的维度
self.adapter = nn.Linear(hidden_dim_student, hidden_dim_teacher)
self.projection = nn.Linear(hidden_dim_teacher, hidden_dim_teacher)
def forward(self, student_features, teacher_features):
"""
student_features: (B, L, H_s)
teacher_features: (B, L, H_t)
"""
# 维度适配
student_proj = self.adapter(student_features) # (B, L, H_t)
student_proj = F.gelu(self.projection(student_proj))
# MSELoss / CosineSimilarity / L2
# 选择 1: MSE 损失
mse_loss = F.mse_loss(student_proj, teacher_features)
# 选择 2: Cosine 损失(只关心方向,不关心模长)
cos_loss = 1 - F.cosine_similarity(
student_proj,
teacher_features,
dim=-1
).mean()
# 选择 3: L2 距离
l2_loss = ((student_proj - teacher_features) ** 2).sum(dim=-1).mean()
return mse_loss # 或 cos_loss, l2_loss
class注意力蒸馏:
"""
Attention Distillation (TinyBERT): 蒸馏注意力权重。
让学生学习教师的 Attention 模式。
"""
def attention_distillation_loss(q_student, k_student, v_student,
q_teacher, k_teacher, v_teacher):
# 计算注意力矩阵
A_teacher = F.softmax(
q_teacher @ k_teacher.transpose(-2, -1) / (q_teacher.size(-1) ** 0.5),
dim=-1
)
A_student = F.softmax(
q_student @ k_student.transpose(-2, -1) / (q_student.size(-1) ** 0.5),
dim=-1
)
# 蒸馏注意力权重分布
# KL 散度:学生应学习教师注意了哪些位置
attn_loss = F.kl_div(
torch.log(A_student + 1e-8),
A_teacher,
reduction='batchmean'
)
# Value 蒸馏:学生应学习教师关注的值
value_loss = F.mse_loss(
A_student @ v_student,
A_teacher @ v_teacher
)
return attn_loss + value_loss

3.4 Representation Distillation(表示蒸馏)#

定义:蒸馏嵌入空间的知识,比特征蒸馏更底层。

class RepresentationDistillation(nn.Module):
"""
Representation Distillation: 蒸馏嵌入表示。
方法 1: 直接对齐嵌入
方法 2: 对齐 Gram 矩阵(保留二阶统计量)
方法 3: 对齐跨层表示(layer-to-layer mapping)
"""
def __init__(self, embedding_dim_s, embedding_dim_t):
super().__init__()
self.projection = nn.Linear(embedding_dim_s, embedding_dim_t)
def forward(self, emb_s, emb_t):
proj_s = self.projection(emb_s)
# 方法 1: L2 对齐
l2_loss = ((proj_s - emb_t) ** 2).mean()
# 方法 2: Gram 矩阵对齐(保留二阶统计)
# Gram 矩阵捕获了表示的"风格"
def gram_matrix(x):
B, L, H = x.shape
x_flat = x.reshape(B, H, L)
return x_flat @ x_flat.transpose(-2, -1) / (L * H)
gram_s = gram_matrix(proj_s)
gram_t = gram_matrix(emb_t)
gram_loss = ((gram_s - gram_t) ** 2).mean()
# 方法 3: MID (Mean Inner Product Distance)
# 衡量不同 token 表示之间的相对关系
def mid(x):
x_norm = x / (x.norm(dim=-1, keepdim=True) + 1e-8)
return (x_norm @ x_norm.transpose(-2, -1)).mean()
mid_loss = abs(mid(proj_s) - mid(emb_t))
return l2_loss + gram_loss + 0.1 * mid_loss

3.5 Relation Distillation(关系蒸馏)#

定义:蒸馏知识之间的结构关系,而非单个知识点的表示。

class RelationDistillation(nn.Module):
"""
Relation Distillation: 蒸馏层间/样本间的关系。
核心思想:
Teacher 的知识不仅在于单个表示
还在于表示之间的关系/结构
"""
def forward(self, teacher_layers, student_layers):
"""
蒸馏层间关系:
Teacher 相邻层之间的关系 → Student 相邻层之间的关系
"""
loss = 0.0
for t_layer, s_layer in zip(teacher_layers, student_layers):
# 层间关系矩阵
R_teacher = F.cosine_similarity(
t_layer.unsqueeze(1), t_layer.unsqueeze(0),
dim=-1
)
R_student = F.cosine_similarity(
s_layer.unsqueeze(1), s_layer.unsqueeze(0),
dim=-1
)
# 对齐关系矩阵
loss += F.mse_loss(R_student, R_teacher)
return loss
class PKD_Style(nn.Module):
"""
Patient Knowledge Distillation (Clark et al., 2019):
蒸馏特定层的知识,而非最后一层。
例:蒸馏 BERT 的 [CLS] token 表示和中间层
"""
def patient_distillation(self, student_layers, teacher_layers, patient_k=3):
"""
patient_k: 每 k 层蒸馏一次,避免学生模仿所有层
"""
loss = 0.0
t_idx = 0
for i, s_layer in enumerate(student_layers):
# 选择对应的教师层
t_layer = teacher_layers[t_idx]
# CLS token 表示蒸馏
cls_loss = F.mse_loss(s_layer[:, 0], t_layer[:, 0])
loss += cls_loss
# 每 k 层移动教师指针
if (i + 1) % patient_k == 0:
t_idx += 1
return loss

4. LLM 蒸馏的特殊挑战#

4.1 生成式 LLM vs 分类模型#

蒸馏 BERT/DNN 等分类模型是简单的(Response KD 即可),但蒸馏 LLM 有本质区别:

分类模型蒸馏:
输入: "The cat sat on the [MASK]"
教师输出: P([MASK]=mat) = 0.85
蒸馏: 让学生学习 P([MASK]=mat) = 0.85
LLM 蒸馏:
输入: "How to implement quicksort in Python?"
教师输出: [概率分布 over 50K+ tokens] × seq_len
复杂度:
- 输出空间巨大(50K+ tokens)
- 自回归生成(每个 token 都影响下一个)
- 序列依赖关系
- 开放式生成(没有唯一正确答案)

4.2 LLM 蒸馏的三种范式#

┌────────────────────────────────────────────────────────────────┐
│ LLM 蒸馏三种范式 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 范式 1: 离线蒸馏(Offline Distillation) │
│ Teacher 生成数据 → 学生 SFT │
│ 流程: T(冻结) 生成 → 学生学习 │
│ 优点: 简单、可离线、可复用 │
│ 缺点: 学生无法探索,依赖教师质量 │
│ 代表: DeepSeek-R1 Distill │
│ │
│ 范式 2: 在线蒸馏(Online Distillation) │
│ Teacher 和 Student 同时训练 │
│ 流程: T 和 S 同步更新 → 相互促进 │
│ 优点: 学生可探索、教师可改进 │
│ 缺点: 工程复杂、需平衡 T 和 S 的学习 │
│ 代表: GKD, DISTILLM │
│ │
│ 范式 3: 协作蒸馏(Collaborative Distillation) │
│ 多学生协作蒸馏一个教师 │
│ 流程: 多 S 协作 → 共享知识 │
│ 优点: 学生多样性、可相互学习 │
│ 缺点: 需要多学生架构 │
│ │
└────────────────────────────────────────────────────────────────┘

4.3 挑战一:序列级蒸馏#

class SequenceLevelDistillation:
"""
序列级蒸馏的两种策略:
策略 1: Token-level 蒸馏
让学生学习每个 token 的概率分布
问题: 计算量大(seq_len × vocab_size)
策略 2: Sequence-level 蒸馏
只蒸馏"最终生成的序列"
问题: 学生看不到中间决策的暗知识
"""
def token_level_distillation(self, student_logits, teacher_logits,
temperature=4.0, alpha=0.7):
"""
Token-level: 蒸馏每个位置的分布
"""
T = temperature
seq_len, vocab = teacher_logits.shape
# 软概率
soft_teacher = F.softmax(teacher_logits / T, dim=-1)
soft_student = F.log_softmax(student_logits / T, dim=-1)
# KL 散度(对所有 token 求平均)
token_kl = F.kl_div(soft_student, soft_teacher, reduction='batchmean')
return token_kl * (T ** 2)
def sequence_level_distillation(self, student_ids, teacher_ids,
student_logits, teacher_logits):
"""
Sequence-level: 蒸馏教师采样的序列
问题: 没有学习到"其他正确路径"
"""
# 交叉熵(只看最终序列)
loss = F.cross_entropy(
student_logits.view(-1, student_logits.size(-1)),
teacher_ids.view(-1)
)
# 改进: 加入 beam search 多样性
teacher_beams = teacher_logits.topk(k=5, dim=-1)
# 学生学习 top-k 个候选
return loss
class MiniLLM_Style:
"""
MiniLLM (Gu et al., 2023):
解决 LLM 蒸馏的"teacher collapse"问题。
问题: 学生模仿教师的高概率 token,过度集中
解决: 用 reverse KL 而非 forward KL
"""
def reverse_kl_distillation(self, student_logits, teacher_logits,
temperature=4.0):
"""
Reverse KL: D_KL(teacher || student)
比 forward KL 更适合避免 mode collapse
"""
T = temperature
# Forward KL: D_KL(student || teacher)
# 鼓励学生的高概率 token 也是教师的高概率 token
# Reverse KL: D_KL(teacher || student)
# 鼓励教师的每个高概率 token 都是学生的高概率 token
# → 更保守,避免学生"遗漏"重要 token
# 实践中用最小化 teacher_entropy 作为近似
teacher_probs = F.softmax(teacher_logits / T, dim=-1)
teacher_entropy = -(teacher_probs * torch.log(teacher_probs + 1e-8)).sum(dim=-1).mean()
# 鼓励学生也有低熵(与教师一致)
student_probs = F.softmax(student_logits / T, dim=-1)
student_entropy = -(student_probs * torch.log(student_probs + 1e-8)).sum(dim=-1).mean()
return student_entropy - 0.1 * teacher_entropy # 让学生熵低但不高于教师

4.4 挑战二:教师-学生能力差距#

class GapAwareDistillation:
"""
处理教师-学生能力差距的策略。
问题: 差距太大时,学生无法学到教师的所有知识
解决: 渐进式蒸馏 + 自适应难度
"""
def progressive_distillation(self, teacher, student, dataset, epochs=3):
"""
渐进式蒸馏:
第 1 轮: 大教师 → 小教师 (30B → 13B)
第 2 轮: 小教师 → 更小学生 (13B → 7B)
...
"""
current_teacher = teacher
for stage in range(epochs):
smaller_teacher = compress_model(current_teacher, ratio=0.5)
student = distill(smaller_teacher, student, dataset)
current_teacher = student # 学生成为下一轮的教师
return student
def curriculum_distillation(self, teacher, student, dataset):
"""
课程蒸馏:从易到难
"""
# 按难度排序数据
difficulty_scores = compute_difficulty(dataset, teacher)
sorted_dataset = sort_by_difficulty(dataset, difficulty_scores)
# 从简单数据开始蒸馏
for batch in sorted_dataset:
if current_difficulty(batch) <= current_capacity(student):
distill_single_batch(teacher, student, batch)
def adaptive_distillation(self, teacher, student, batch, step):
"""
自适应蒸馏:根据训练进度调整蒸馏强度
"""
# 早期: 更多蒸馏(学生需要更多教师知识)
# 后期: 更少蒸馏(学生已学会大部分知识)
distillation_weight = max(0.1, 1.0 - step / total_steps)
loss = distillation_weight * kl_divergence(student, teacher, batch)
loss += (1 - distillation_weight) * supervised_loss(student, batch)
return loss

5. LLM 蒸馏核心方法#

5.1 GKD(Generalized Knowledge Distillation)#

GKD(Agarwal et al., 2024)提出了 LLM 蒸馏的通用框架:

class GKD:
"""
GKD: Generalized Knowledge Distillation for LLMs
核心发现:
1. 在线蒸馏(师生同时训练)优于离线蒸馏
2. 学生的策略熵(policy entropy)是关键信号
3. 亚稳态(metastability)问题需要特殊处理
"""
def __init__(self, teacher, student, temperature=4.0, beta=0.5):
self.teacher = teacher
self.student = student
self.T = temperature
self.beta = beta
def gkd_loss(self, prompt_ids, response_ids=None, is_pretraining=False):
"""
GKD 损失函数
"""
# === 1. 学生输出 ===
student_logits = self.student(prompt_ids, response_ids)
# === 2. 教师输出 ===
with torch.no_grad():
teacher_logits = self.teacher(prompt_ids, response_ids)
# === 3. KL 蒸馏损失 ===
soft_student = F.log_softmax(student_logits / self.T, dim=-1)
soft_teacher = F.softmax(teacher_logits / self.T, dim=-1)
kl_loss = F.kl_div(soft_student, soft_teacher, reduction='batchmean') * (self.T ** 2)
# === 4. 策略熵正则(关键) ===
# 鼓励学生保持一定随机性,避免坍缩
student_probs = F.softmax(student_logits, dim=-1)
student_entropy = -(student_probs * torch.log(student_probs + 1e-8)).sum(dim=-1).mean()
# === 5. 亚稳态检测 ===
# 亚稳态:学生的分布与教师相似,但与真实数据偏离
# 检测方法:比较学生和教师在低概率 token 上的差异
metastability = self.detect_metastability(student_logits, teacher_logits)
if is_pretraining:
# 预训练模式:主要用 KL 蒸馏
return kl_loss + 0.01 * student_entropy
else:
# 微调模式:加入亚稳态正则
return kl_loss + 0.01 * student_entropy + 0.1 * metastability
def detect_metastability(self, s_logits, t_logits, threshold=0.1):
"""
检测亚稳态。
亚稳态的信号:
- 师生 KL 散度很小
- 但与真实分布的差异很大
"""
# 师生 KL
kl = F.kl_div(
F.log_softmax(s_logits, dim=-1),
F.softmax(t_logits, dim=-1),
reduction='batchmean'
)
# 如果师生 KL 小但学生熵也小 → 亚稳态
entropy = -(F.softmax(s_logits, dim=-1) * F.log_softmax(s_logits, dim=-1)).sum(dim=-1).mean()
if kl < threshold and entropy < 2.0: # 熵低表示过度自信
return 1.0 # 检测到亚稳态
return 0.0

5.2 MiniLLM(低熵蒸馏)#

MiniLLM (Gu et al., 2023) 针对 LLM 生成式蒸馏的特殊问题提出解决方案:

class MiniLLM:
"""
MiniLLM: Knowledge Distillation from Large Language Models
三个关键改进:
1. Reverse KL Divergence(避免过度泛化)
2. 生成质量学习(学习教师的生成质量)
3. 长度归一化(处理长度偏差)
"""
def __init__(self, teacher, student):
self.teacher = teacher
self.student = student
def reverse_kl_loss(self, student_logits, teacher_logits, T=4.0):
"""
Reverse KL Divergence: D_KL(teacher || student)
Forward KL: 鼓励 student 的 mass 在 teacher mass 的地方
Reverse KL: 鼓励 teacher 的 mass 在 student mass 的地方
效果: Reverse KL 更保守,避免产生 teacher 不认可的低概率 token
"""
T = T
# 教师分布(目标)
p_teacher = F.softmax(teacher_logits / T, dim=-1)
# 学生分布(优化对象)
q_student = F.log_softmax(student_logits / T, dim=-1)
# Reverse KL: E_p[log(p/q)] = E_p[log p] - E_p[log q]
# 实现: -E_p[log q] + const
reverse_kl = -(p_teacher * q_student).sum(dim=-1).mean()
# 添加熵惩罚防止学生过度自信
student_entropy = -(F.softmax(student_logits, dim=-1) *
F.log_softmax(student_logits, dim=-1)).sum(dim=-1).mean()
return reverse_kl - 0.01 * student_entropy
def length_normalized_loss(self, student_logits, teacher_logits,
target_ids, length_penalty=1.0):
"""
长度归一化:解决学生倾向于生成短文本的问题
"""
# 标准交叉熵
ce_loss = F.cross_entropy(
student_logits[:, :-1].reshape(-1, student_logits.size(-1)),
target_ids[:, 1:].reshape(-1),
reduction='mean'
)
# 长度惩罚
seq_len = target_ids.size(1)
length_norm = (length_penalty ** (seq_len ** 0.5)) # Google 的长度惩罚
return ce_loss / length_norm
def minilm_loss(self, prompt_ids, response_ids, T=4.0):
"""
MiniLLM 完整损失
"""
student_logits = self.student(prompt_ids, response_ids)
with torch.no_grad():
teacher_logits = self.teacher(prompt_ids, response_ids)
# Reverse KL
kl_loss = self.reverse_kl_loss(student_logits, teacher_logits, T)
# 生成质量
quality_loss = self.generation_quality_loss(student_logits, response_ids)
return kl_loss + 0.1 * quality_loss

5.3 MiniPLM(渐进式蒸馏)#

class MiniPLM:
"""
MiniPLM: 渐进式蒸馏
核心思想:
1. 多阶段压缩(先压缩宽度,再压缩深度)
2. 中间层对齐
3. 动态温度
"""
def __init__(self, teacher, student_configs):
self.teacher = teacher
self.configs = student_configs # 从大到小的配置列表
def progressive_distill(self, dataset):
"""
多阶段渐进蒸馏
"""
current_teacher = self.teacher
for i, config in enumerate(self.configs):
print(f"Stage {i+1}: {config['hidden_dim']} dims")
# 创建学生
student = create_student(config)
# 蒸馏
student = self.distill(
teacher=current_teacher,
student=student,
dataset=dataset,
stage=i,
)
# 学生成为下一阶段的教师
current_teacher = student
return current_teacher
def distill(self, teacher, student, dataset, stage=0):
"""
单阶段蒸馏
"""
# 动态温度:早期高温度(更多探索),后期低温度(更精确)
T = max(2.0, 6.0 - stage)
# 早期:特征蒸馏为主
# 后期:响应蒸馏为主
feature_weight = max(0.0, 1.0 - stage * 0.3)
response_weight = 1.0 - feature_weight
optimizer = torch.optim.AdamW(student.parameters(), lr=1e-4)
for batch in dataloader(dataset):
s_logits = student(**batch)
with torch.no_grad():
t_logits = teacher(**batch)
t_hidden = teacher.get_hidden_states()
# 响应蒸馏
response_loss = self.response_distillation(s_logits, t_logits, T)
# 特征蒸馏
if feature_weight > 0:
s_hidden = student.get_hidden_states()
feature_loss = self.feature_distillation(s_hidden, t_hidden)
else:
feature_loss = 0
loss = response_weight * response_loss + feature_weight * feature_loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
return student

5.4 SEAL(自进化蒸馏)#

SEAL (Li et al., 2024) 提出了让教师自我改进的蒸馏方法:

class SEAL:
"""
SEAL: Self-Evolving Distillation
核心思想:教师不是固定的,而是随蒸馏过程自我改进
"""
def __init__(self, teacher, student, buffer_size=10000):
self.teacher = teacher
self.student = student
self.memory_buffer = ReplayBuffer(buffer_size)
def self_evolve(self, dataset, rounds=3):
"""
自演化蒸馏
"""
for round_idx in range(rounds):
print(f"Round {round_idx + 1}/{rounds}")
# 1. 用当前教师生成数据
generated_data = self.generate_data(dataset, self.teacher)
# 2. 保存高质量数据到记忆缓冲区
high_quality = self.filter_high_quality(generated_data, self.teacher)
self.memory_buffer.add(high_quality)
# 3. 用记忆缓冲区蒸馏学生
self.distill_student(self.memory_buffer)
# 4. 学生反馈给教师(选择性更新)
self.update_teacher()
return self.student
def generate_data(self, prompts, model, num_samples=4):
"""
为每个 prompt 生成多个回答
"""
data = []
for prompt in prompts:
# 采样多个回答
for _ in range(num_samples):
with torch.no_grad():
response = model.generate(prompt, temperature=0.8)
# 评分
score = self.rate_response(prompt, response, model)
data.append({
"prompt": prompt,
"response": response,
"score": score,
})
return data
def rate_response(self, prompt, response, judge_model):
"""
用教师模型给回答打分
"""
# 方法 1: 困惑度
ppl = compute_perplexity(response, judge_model)
# 方法 2: 奖励模型
reward = reward_model.score(prompt, response)
# 方法 3: 一致性(多次生成的一致性)
# ...
return reward - 0.1 * ppl
def update_teacher(self):
"""
用学生的优秀表现更新教师
如果学生在某些样本上超过教师,则用学生更新教师
"""
# EMA 方式更新教师
with torch.no_grad():
for s_param, t_param in zip(
self.student.parameters(),
self.teacher.parameters()
):
t_param.data.mul_(0.99).add_(s_param.data, alpha=0.01)

6. 蒸馏数据构建#

6.1 数据来源#

class DistillationDataBuilder:
"""
蒸馏数据的四种来源
"""
def build_from_teacher(self, teacher, prompts, strategy='sample'):
"""
方式 1: 教师生成
"""
data = []
for prompt in prompts:
if strategy == 'greedy':
response = teacher.generate(prompt, do_sample=False)
elif strategy == 'sample':
response = teacher.generate(prompt, temperature=0.7, top_p=0.9)
elif strategy == 'beam':
responses = teacher.generate_beam(prompt, num_beams=5)
response = responses[0] # 取最优
data.append({"prompt": prompt, "response": response})
return data
def build_from_preference(self, teacher, prompts, num_samples=8):
"""
方式 2: 偏好对数据
生成多个回答,用教师选出最好的
"""
data = []
for prompt in prompts:
# 生成多个候选
candidates = [
teacher.generate(prompt, temperature=t)
for t in [0.3, 0.5, 0.7, 0.9, 1.1]
]
# 用教师评分
scores = [teacher.score(prompt, c) for c in candidates]
best_idx = scores.index(max(scores))
data.append({
"prompt": prompt,
"chosen": candidates[best_idx],
"rejected": [c for i, c in enumerate(candidates) if i != best_idx],
"teacher_scores": scores,
})
return data
def build_from_reasoning(self, teacher, prompts, reasoning_template):
"""
方式 3: 思维链蒸馏(DeepSeek-R1 风格)
"""
data = []
for prompt in prompts:
# 生成带思维链的回答
response_with_reasoning = teacher.generate(
prompt,
system_prompt=reasoning_template,
max_tokens=4096,
)
# 提取答案
answer = extract_final_answer(response_with_reasoning)
data.append({
"prompt": prompt,
"reasoning": extract_reasoning(response_with_reasoning),
"answer": answer,
})
return data

6.2 数据过滤与质量控制#

class DataFilter:
"""
蒸馏数据过滤
"""
def filter_by_quality(self, data, teacher, threshold=0.8):
"""
过滤低质量数据
"""
filtered = []
for item in data:
prompt = item["prompt"]
response = item["response"]
# 质量评分
score = self.quality_score(prompt, response, teacher)
if score >= threshold:
filtered.append(item)
return filtered
def quality_score(self, prompt, response, teacher):
"""
多维度质量评分
"""
score = 0.0
# 1. 困惑度(越低越好)
ppl = compute_perplexity(response, teacher)
score += max(0, (50 - ppl) / 50) # 归一化
# 2. 长度合理性(不太短不太长)
length = len(response.split())
if 50 < length < 1000:
score += 0.2
# 3. 教师置信度
confidence = teacher.confidence(prompt, response)
score += confidence * 0.3
# 4. 多样性(与已有数据的差异)
diversity = self.compute_diversity(response, self.existing_data)
score += diversity * 0.2
return score
def filter_by_consistency(self, data, teacher, num_eval=3):
"""
一致性过滤:同一 prompt 多次生成应该语义一致
"""
from collections import defaultdict
prompt_to_responses = defaultdict(list)
for item in data:
prompt_to_responses[item["prompt"]].append(item["response"])
consistent_data = []
for prompt, responses in prompt_to_responses.items():
if len(responses) >= 2:
# 计算语义一致性
consistency = self.semantic_consistency(responses, teacher)
if consistency > 0.8:
# 取质量最好的作为蒸馏数据
best_response = max(
responses,
key=lambda r: self.quality_score(prompt, r, teacher)
)
consistent_data.append({
"prompt": prompt,
"response": best_response,
"consistency": consistency,
})
return consistent_data

6.3 数据量与配比#

class DataRatio:
"""
蒸馏数据的量与配比
"""
def compute_required_data(self, teacher_params, student_params):
"""
估算所需数据量
经验公式:
所需数据 ∝ log(教师参数 / 学生参数)
"""
ratio = teacher_params / student_params
# 基础数据量(针对 7B → 1.5B)
base_data = 100000
# 根据规模调整
data = int(base_data * (1 + 0.5 * math.log(ratio)))
return data
def compute_composition(self, task_distribution):
"""
计算数据配比
"""
# 经验配比(DeepSeek-R1 的数据构成)
composition = {
"math_reasoning": 0.20, # 数学推理(最重要)
"code_generation": 0.20, # 代码生成
"logic_reasoning": 0.15, # 逻辑推理
"general_knowledge": 0.20, # 通用知识
"creative_writing": 0.15, # 创意写作
"instruction_following": 0.10, # 指令遵循
}
# 根据学生能力调整
# 如果学生代码能力弱 → 增加代码比例
return composition

7. 最佳实践与调参指南#

7.1 超参数设置#

超参数推荐范围说明
温度 TT2.0 ~ 6.0(通常 4.0)高 T:更多暗知识;低 T:更精确
α\alpha(软硬平衡)0.5 ~ 0.9预训练用高 α;微调用低 α
学生学习率SFT 的 1/2 ~ 1/10学生比教师小很多,需要更小 LR
蒸馏 epochs1 ~ 5数据量大时 1-2 轮足够
batch size教师的一半显存限制
数据量10K ~ 1M(task 依赖)推理任务需要更多数据

7.2 蒸馏策略选择#

def choose_distillation_strategy(
teacher_size: int,
student_size: int,
task_type: str,
compute_budget: str,
) -> dict:
"""
根据情况选择蒸馏策略
"""
ratio = teacher_size / student_size
strategy = {}
# === 1. 选择蒸馏类型 ===
if ratio > 10:
# 大幅压缩 → 需要特征蒸馏
strategy["distillation_type"] = ["response", "feature"]
strategy["feature_layers"] = list(range(0, 32, 4)) # 每 4 层蒸馏
elif ratio > 4:
strategy["distillation_type"] = ["response"]
else:
# 小幅压缩 → 纯响应蒸馏
strategy["distillation_type"] = ["response"]
# === 2. 选择在线/离线 ===
if compute_budget == "high":
strategy["mode"] = "online" # GKD
else:
strategy["mode"] = "offline"
# === 3. 选择 KL 变体 ===
if task_type == "generation":
strategy["kl_variant"] = "reverse_kl" # MiniLLM
else:
strategy["kl_variant"] = "forward_kl"
# === 4. 设置超参数 ===
if ratio > 10:
strategy["temperature"] = 6.0 # 高温度提取更多暗知识
strategy["alpha"] = 0.9
elif ratio > 4:
strategy["temperature"] = 4.0
strategy["alpha"] = 0.7
else:
strategy["temperature"] = 2.0
strategy["alpha"] = 0.5
return strategy

7.3 常见问题与对策#

┌────────────────────────────────────┬──────────────────────────────────────────┐
│ 问题 │ 对策 │
├────────────────────────────────────┼──────────────────────────────────────────┤
│ 学生性能远低于预期 │ ① 检查蒸馏数据质量 ② 渐进蒸馏 ③ 增加数据量 │
│ 学生坍缩(只生成固定内容) │ ① 加入熵正则 ② 用 Reverse KL ③ 增大 T │
│ KL 损失不收敛 │ ① 检查维度匹配 ② 调整 α ③ 预热教师 │
│ 推理能力未迁移 │ ① 用 Reasoning 数据蒸馏 ② 加入 CoT │
│ 生成内容与教师相似度过低 │ ① 减小学生容量差距 ② 多阶段蒸馏 │
│ 特定能力退步 │ ① 增加该能力的数据比例 ② 针对性蒸馏 │
└────────────────────────────────────┴──────────────────────────────────────────┘

7.4 评估指标#

class DistillationEvaluator:
"""
蒸馏效果评估
"""
def evaluate(self, student, reference_student, test_data):
"""
评估蒸馏效果
"""
metrics = {}
# 1. 下游任务性能
metrics["task_accuracy"] = self.task_accuracy(student, test_data)
metrics["task_improvement"] = (
metrics["task_accuracy"] - reference_student["task_accuracy"]
)
# 2. 分布匹配
metrics["kl_divergence"] = self.compute_kl(student, reference_student)
metrics["cosine_similarity"] = self.compute_cosine(student, reference_student)
# 3. 生成质量
metrics["generation_quality"] = self.generation_quality(student, test_data)
# 4. 压缩率
metrics["compression_ratio"] = self.compute_compression(student, reference_student)
metrics["speedup"] = self.compute_speedup(student, reference_student)
return metrics
def compute_compression_summary(self, student, teacher):
"""
压缩效果汇总
"""
teacher_params = count_parameters(teacher)
student_params = count_parameters(student)
compression = teacher_params / student_params
# 估算加速比(简化)
# 实际上加速比取决于架构、量化等
speedup = compression ** 0.7 # 经验公式
return {
"compression_ratio": f"{compression:.1f}x",
"speedup_estimate": f"{speedup:.1f}x",
"teacher_params": f"{teacher_params/1e9:.1f}B",
"student_params": f"{student_params/1e9:.1f}B",
}

8. 高级话题#

8.1 多教师蒸馏#

class MultiTeacherDistillation:
"""
多教师蒸馏:用多个教师指导一个学生
"""
def __init__(self, teachers: list, student):
self.teachers = teachers
self.student = student
def distill(self, batch):
"""
多教师蒸馏
"""
student_logits = self.student(**batch)
total_loss = 0.0
for teacher in self.teachers:
with torch.no_grad():
teacher_logits = teacher(**batch)
# 权重(可学习)
weight = self.teacher_weights[len(self.teachers)]
loss = F.kl_div(
F.log_softmax(student_logits / 4.0, dim=-1),
F.softmax(teacher_logits / 4.0, dim=-1),
reduction='batchmean'
)
total_loss += weight * loss
return total_loss

8.2 蒸馏 + RLHF#

class DistillThenRLHF:
"""
蒸馏 + RLHF 的结合
典型案例:DeepSeek-R1 的蒸馏数据用于 RFT
"""
def __init__(self, teacher, student, reward_model):
self.teacher = teacher
self.student = student
self.reward_model = reward_model
def distill_then_rlhf(self, prompts, epochs=2):
"""
两阶段:先蒸馏,再 RLHF
"""
# === 阶段 1: 蒸馏 ===
print("Stage 1: Knowledge Distillation")
distillation_data = self.generate_distillation_data(prompts)
student = self.distill(self.teacher, self.student, distillation_data)
# === 阶段 2: RLHF 对齐 ===
print("Stage 2: RLHF Alignment")
for epoch in range(epochs):
# GRPO 更新
rollouts = self.generate_rollouts(student, prompts)
rewards = [self.reward_model.score(p, r) for p, r in rollouts]
student = self.grpo_update(student, rollouts, rewards)
return student

9. 核心公式汇总#

9.1 Soft Target Loss(软目标损失)#

Lsoft=KL(softmax(zsT)softmax(ztT))T2\mathcal{L}_{\text{soft}} = \text{KL}\Big(\text{softmax}\big(\frac{z_s}{T}\big)\,\Big\|\,\text{softmax}\big(\frac{z_t}{T}\big)\Big) \cdot T^2

9.2 Hard Target Loss(硬目标损失)#

Lhard=CE(softmax(zs),y)\mathcal{L}_{\text{hard}} = \text{CE}(\text{softmax}(z_s), y)

9.3 总蒸馏损失#

LKD=αLsoft+(1α)Lhard\mathcal{L}_{\text{KD}} = \alpha \cdot \mathcal{L}_{\text{soft}} + (1-\alpha) \cdot \mathcal{L}_{\text{hard}}

9.4 Reverse KL Divergence#

LReverseKL=xpt(x)logps(x)pt(x)=H(pt)+Ept[logps(x)]\mathcal{L}_{\text{ReverseKL}} = -\sum_x p_t(x)\,\log\frac{p_s(x)}{p_t(x)} = -H(p_t) + \mathbb{E}_{p_t}[-\log p_s(x)]

9.5 特征蒸馏(MSE 对齐)#

Lfeature=E[fs(x)ft(x)22]\mathcal{L}_{\text{feature}} = \mathbb{E}\big[\|f_s(x) - f_t(x)\|_2^2\big]

9.6 Attention 蒸馏#

Lattn=KL(AsAt)\mathcal{L}_{\text{attn}} = \text{KL}\big(A_s\,\|\,A_t\big)

10. 总结#

10.1 知识蒸馏的核心洞见#

┌─────────────────────────────────────────────────────────────┐
│ 知识蒸馏的五个核心洞见 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 暗知识比硬标签更有价值 │
│ → Soft targets 包含类别间关系 │
│ → 揭示了模型的"推理路径" │
│ │
│ 2. 温度控制信息的提取量 │
│ → T 高:提取更多暗知识,但噪声也放大 │
│ → T 低:更精确,但失去关系信息 │
│ → 平衡点:T=2~6 │
│ │
│ 3. 蒸馏是知识迁移,而非能力迁移 │
│ → 教师会的,学生不一定能学会 │
│ → 容量差距限制可迁移的知识量 │
│ → 渐进蒸馏是突破容量限制的有效方法 │
│ │
│ 4. 生成式 LLM 蒸馏需要特殊处理 │
│ → Reverse KL 避免 mode collapse │
│ → 在线蒸馏优于离线蒸馏(GKD) │
│ → 策略熵是关键监控指标 │
│ │
│ 5. 数据质量 > 数据数量 │
│ → 用教师筛选高质量蒸馏数据 │
│ → 一致性过滤避免噪声 │
│ → 任务配比影响最终能力分布 │
│ │
└─────────────────────────────────────────────────────────────┘

10.2 方法选择指南#

蒸馏方法选择决策树:
教师 → 学生 压缩比?
├── < 4x
│ └── Response KD + T=4 + α=0.5
├── 4x ~ 10x
│ ├── Response KD + T=4 + α=0.7
│ └── 可选:中间层对齐
└── > 10x
├── 多阶段渐进蒸馏
├── Response + Feature KD
└── GKD 在线模式
任务类型?
├── 分类任务
│ └── 标准 Response KD
├── 生成任务
│ └── MiniLLM (Reverse KL)
├── 推理任务
│ └── Reasoning KD + CoT 数据
└── 多种能力
└── 多教师 + 能力配比调优
推荐阅读
  1. Hinton KD(2015)—— 经典框架,暗知识理论
  2. MiniLLM(Gu et al., 2023)—— LLM 生成蒸馏
  3. GKD(Agarwal et al., 2024)—— 在线蒸馏理论
  4. TinyBERT(Jiao et al., 2020)—— 特征蒸馏
  5. DeepSeek-R1 Distill—— 推理蒸馏最佳实践

参考资料#

  1. Hinton, G., et al. (2015). “Distilling the Knowledge in a Neural Network.” arXiv (NIPS Workshop).
  2. Gu, Y., et al. (2023). “MiniLLM: Knowledge Distillation of Large Language Models.” ICLR.
  3. Agarwal, R., et al. (2024). “Generalized Knowledge Distillation for Language Models.” NeurIPS.
  4. Jiao, X., et al. (2020). “TinyBERT: Distilling BERT for Natural Language Understanding.” EMNLP.
  5. Sanh, V., et al. (2019). “DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter.” NeurIPS Workshop.
  6. Sun, Z., et al. (2019). “MobileBERT: a Compact Task-Agnostic BERT for Resource-Limited Devices.” ACL.
  7. Wang, W., et al. (2024). “MiniPLM: Progressive Knowledge Distillation of Large Language Models.” ICLR.
  8. Li, L., et al. (2024). “SEAL: Self-Evolving LLM Distillation.” arXiv.
  9. Clark, K., et al. (2019). “What Does BERT Look At? An Analysis of BERT’s Attention.” BlackboxNLP.
  10. DeepSeek-AI. (2025). “DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning.” arXiv(R1 蒸馏实验部分).

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

知识蒸馏深度解析:从大模型到小模型的智能迁移
https://aiattnstudio.link/posts/knowledge-distillation/
作者
Federico
发布于
2026-07-16
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author

Federico

AI Research Lab

Hello, I'm Federico.

关于实验室 / About
公告

欢迎来到Federico的个人博客

分类
标签
站点统计
57文章
7分类
404标签