知识蒸馏的三大范式:从模仿到超越

知识蒸馏听起来很玄乎,说白了就是让一个小模型(学生)去模仿一个大模型(老师)的行为。但模仿也有不同的层次——你是只抄答案,还是连解题思路一起学?这就引出了蒸馏的三大范式。

我个人习惯把这三类蒸馏比作三种教学方式:Logit蒸馏是直接看学生的考卷和老师的考卷哪里不一样;特征蒸馏是盯着学生的解题步骤,看中间哪一步写错了;关系蒸馏则是让学生学会老师处理多个问题时的整体思路。下面我们逐个拆解。

Logit蒸馏:最经典的“软标签”教学

Logit蒸馏是Hinton在2015年提出的原始方法。它的核心思想很简单:学生模型不仅要学习真实标签(硬标签),还要学习老师模型输出的概率分布(软标签)。

为什么会这样?因为硬标签只告诉学生“这是猫”,但软标签会告诉学生“这有80%像猫,15%像狗,5%像兔子”。这种额外的信息量,能让学生学到类别之间的相似性。

核心公式:蒸馏损失 = α × KL散度(学生软输出 || 老师软输出) + (1-α) × 交叉熵(学生硬输出 || 真实标签)

其中α是平衡系数,通常取0.7~0.9。

我在项目中遇到过一个问题:直接用原始概率做KL散度,学生根本学不动。因为老师输出的概率分布太“尖锐”了——比如正确类别的概率是0.99,其他类别接近0。这时候学生看到的软标签和硬标签几乎没区别。

温度系数T:让软标签真正“软”起来

温度系数T就是用来解决上面这个问题的。它通过一个简单的数学变换,把概率分布“软化”:

# 带温度的Softmax
def softmax_with_temperature(logits, T):
    # T > 1 时分布变平滑,T < 1 时分布变尖锐
    exp_logits = np.exp(logits / T)
    return exp_logits / np.sum(exp_logits)

# 举个例子
logits = np.array([5.0, 2.0, 1.0])
print("T=1:", softmax_with_temperature(logits, 1))
print("T=5:", softmax_with_temperature(logits, 5))
print("T=10:", softmax_with_temperature(logits, 10))

输出结果会让你直观感受到T的作用:

温度T 类别A概率 类别B概率 类别C概率 分布特点
1 0.936 0.046 0.018 尖锐,几乎只有正确类
5 0.524 0.289 0.187 平滑,类别间关系清晰
10 0.386 0.314 0.300 非常平滑,接近均匀分布

我的经验:T值一般取2~8之间。T太小(<2)软标签不够软,T太大(>10)会丢失类别间的区分度。我习惯先试T=4,然后根据学生收敛速度微调。

KL散度损失:衡量两个分布的“距离”

KL散度(Kullback-Leibler divergence)是蒸馏中最常用的损失函数。它衡量的是两个概率分布之间的差异:

# KL散度的PyTorch实现
import torch
import torch.nn.functional as F

def kl_div_loss(student_logits, teacher_logits, T):
    # 先除以温度,再计算KL散度
    student_soft = F.log_softmax(student_logits / T, dim=1)
    teacher_soft = F.softmax(teacher_logits / T, dim=1)
    
    # KL散度 = sum(teacher * log(teacher/student))
    kl_loss = F.kl_div(student_soft, teacher_soft, reduction='batchmean')
    
    # 乘以T^2以补偿梯度缩放
    return kl_loss * (T ** 2)

嗯,这里要注意:代码里最后乘了T²。为什么?因为温度缩放后梯度会变小,乘T²是为了让梯度大小和T=1时保持一致。这个细节我踩过坑——第一次写蒸馏代码时忘了乘T²,结果学生怎么训都训不好。

特征蒸馏:盯着中间层学

Logit蒸馏只关注最终输出,但特征蒸馏会让学生去模仿老师中间层的特征图。这就像学画画时,老师不仅看你的成品,还检查你的每一笔素描。

特征蒸馏的损失函数通常写成:

L_feat = || f_s(x) - f_t(x) ||²

其中f_s和f_t分别是学生和老师在某个中间层的特征输出。但这里有个坑:学生和老师的特征维度通常不一样,需要加一个适配层(Adaptation Layer)来对齐。

我曾经踩过的坑:特征蒸馏时直接拿学生最后一层卷积去对齐老师的倒数第二层,结果维度不匹配报错。后来我加了一个1×1卷积做适配层,才把问题解决。另外,特征蒸馏的权重不要设太大,我一般设0.1~0.3,否则学生模型会过度关注中间特征而忽略最终分类。

关系蒸馏:学关系,不学特征

关系蒸馏是相对较新的范式。它不要求学生和老师的特征图一模一样,而是要求学生能学到老师模型中样本之间的关系。

举个例子:老师模型认为样本A和样本B很相似,样本A和样本C差异很大。关系蒸馏就让学生也学会这种相似性关系。常用的关系蒸馏损失包括:

  • 距离关系:学生和老师的样本对距离分布一致
  • 角度关系:学生和老师的样本对角度分布一致
  • 图关系:用图结构表示样本间关系,让学生模仿整张图

关系蒸馏的好处是:它不依赖特征图的具体数值,所以对模型结构差异的容忍度更高。我在做跨架构蒸馏(比如从Transformer蒸馏到CNN)时,关系蒸馏的效果往往比特征蒸馏好。

三大范式对比:什么时候用哪个?

蒸馏范式 核心思想 适用场景 我的建议
Logit蒸馏 模仿最终输出分布 通用场景,模型结构相近 作为基线方法,先跑通
特征蒸馏 模仿中间层特征 学生和老师结构相似 适合CNN蒸馏,注意维度对齐
关系蒸馏 模仿样本间关系 跨架构蒸馏,结构差异大 Transformer→CNN时优先考虑

你想想看,实际项目中很少有人只用一种蒸馏范式。我通常的做法是:先用Logit蒸馏打个底,然后根据模型结构差异决定是否加特征蒸馏或关系蒸馏。三种范式可以组合使用,但要注意权重分配——我一般设Logit蒸馏权重为1,特征蒸馏0.2,关系蒸馏0.1。

知识蒸馏三大范式对比 知识蒸馏 Logit蒸馏 特征蒸馏 关系蒸馏 核心要素 温度系数T KL散度损失 核心要素 适配层(1×1卷积) L2损失 核心要素 距离/角度/图关系 对比损失 适用:通用场景 结构相近的模型 适用:CNN蒸馏 同架构迁移 适用:跨架构蒸馏 Transformer→CNN 实际项目:三种范式组合使用,效果更佳

最后说一句:别被这些理论吓到。你先把Logit蒸馏跑通,理解温度系数和KL散度怎么调,后面再慢慢加特征蒸馏和关系蒸馏。我在实际项目中,80%的场景只用Logit蒸馏就能拿到不错的收益。剩下的20%才需要上更复杂的方案。