2. 软标签与硬标签

蒸馏的核心,说白了就是让「学生模型」模仿「老师模型」的行为。但怎么模仿?直接拿老师预测的类别标签去教?还是把老师脑子里那些「犹豫」和「模糊判断」也一并学过来?

这就引出了我们今天要聊的两个关键概念——硬标签和软标签。我刚开始做蒸馏时,也觉得硬标签就够了,反正最终结果对了就行。后来发现,学生模型学到的泛化能力差了一大截。嗯,这里面的门道,咱们一点点拆开看。

1. 硬标签 vs 软标签:到底差在哪?

先看个直观的例子。假设老师模型在做情感分类,输入「这部电影还不错」,输出概率分布如下:

类别 正面 中性 负面
硬标签 1 0 0
软标签 0.85 0.12 0.03

硬标签只告诉你「答案是正面」。但软标签透露了更多信息——老师其实觉得它「有点接近中性」,只是更偏向正面。这种「类间相似度」信息,对学生模型来说非常宝贵。

核心观点:硬标签传递的是「答案」,软标签传递的是「思考过程」。蒸馏之所以有效,很大程度上是因为学生学到了老师对模糊样本的判断模式。

我在项目中遇到过这样一个场景:用硬标签蒸馏一个BERT-small模型,在测试集上准确率还行,但一到对抗样本或OOD数据上就崩了。换成软标签后,鲁棒性明显提升。说白了,软标签让模型学会了「不确定的时候该怎么犹豫」,而不是盲目自信。

2. 温度系数T:让软标签「显形」的关键

你可能会问:老师模型输出的概率分布,很多时候太「尖锐」了。比如上面那个例子,正面0.85,中性0.12,负面0.03。这个分布虽然比硬标签信息多,但类间差异还是太大,学生很难学到「中性其实也有一定可能性」这种微妙信息。

这时候就需要温度系数T登场了。

它的作用机制很简单——在Softmax之前,先把logits除以T,再算概率:

q_i = exp(z_i / T) / Σ_j exp(z_j / T)
  • T = 1:标准的Softmax,分布不变
  • T > 1:分布变「平滑」,小概率类的权重被放大
  • T → ∞:所有类别概率趋近相等,完全均匀分布
  • T → 0:趋近于one-hot,也就是硬标签

举个例子,假设logits是 [10, 5, 0]:

T值 正面概率 中性概率 负面概率
T=1 0.993 0.007 0.000
T=5 0.731 0.244 0.025
T=10 0.576 0.350 0.074

看到没?T=1时,负面类几乎被忽略。但T=10时,三个类别的概率分布变得「有得聊」了。学生模型就能从中学到「正面和中性比较接近,负面离得远」这种结构信息。

我的经验:T值一般设置在2~8之间。T太小,软标签接近硬标签,蒸馏效果有限;T太大,分布太平坦,噪声太多。我习惯先试T=4,然后根据验证集表现微调。

3. 软标签损失函数:KL散度的推导

蒸馏时,我们让学生模型的软输出(经过高温T软化后)去拟合老师模型的软输出。衡量两个分布差异的指标,就是KL散度。

KL散度的公式长这样:

KL(P || Q) = Σ_i P(i) * log(P(i) / Q(i))

其中P是老师分布,Q是学生分布。这个值越小,说明两个分布越接近。

我们来拆解一下:

  1. P(i) * log(P(i)):这部分是老师分布的熵,与学生无关,可以忽略
  2. -P(i) * log(Q(i)):这部分是交叉熵,让学生分布去拟合老师分布

所以蒸馏损失函数可以写成:

L_distill = KL(P_teacher || Q_student)
           = Σ_i P_teacher(i) * log(P_teacher(i) / Q_student(i))
           = - Σ_i P_teacher(i) * log(Q_student(i)) + H(P_teacher)

实际训练时,我们忽略常数项H(P_teacher),直接用交叉熵形式计算:

L_distill = - Σ_i P_teacher(i) * log(Q_student(i))

注意,这里的P_teacher和Q_student都是在相同温度T下计算出来的软概率。

容易踩的坑:我曾经犯过一个错误——老师用T=4软化,学生用T=1计算。结果两个分布尺度不匹配,损失值忽大忽小,模型怎么都训不收敛。记住,蒸馏时老师和学生必须用同一个T。

4. 硬标签与软标签的加权融合策略

只用软标签蒸馏,学生模型可能会过于「平滑」,在最终预测时不够果断。所以实践中,我们通常把硬标签损失和软标签损失加权融合:

L_total = α * L_hard + (1 - α) * L_distill

其中:

  • L_hard:学生输出(T=1)与真实硬标签的交叉熵
  • L_distill:学生输出(T>1)与老师输出(T>1)的KL散度
  • α:融合权重,一般在0.1~0.5之间

为什么需要硬标签?你想想看,老师模型也不是完美的。如果老师在某些样本上犯了错,学生完全跟着老师学,就会继承这些错误。硬标签相当于给了学生一个「纠偏」的锚点。

我个人的习惯是:

  • 初期α设大一点(0.3~0.5),让学生先学会基本分类能力
  • 后期逐渐减小α(0.1~0.2),让学生更多模仿老师的「思考方式」
  • 如果老师模型很强(准确率95%+),α可以设小一些

一个实用的调参建议:先固定T=4,α=0.3跑一轮。观察验证集上L_hard和L_distill的变化趋势。如果L_distill降得很快但L_hard不动,说明α太大,学生只顾模仿老师,忽略了真实标签。反过来,如果L_hard降得快但L_distill不降,说明α太小,蒸馏没起作用。

5. 知识结构图:蒸馏中的标签体系

下面这张图梳理了软标签、硬标签、温度系数和损失函数之间的关系:

蒸馏标签体系与损失计算流程 老师模型 学生模型 logits (z_i) logits (z_i) 温度 T 软标签 P_teacher 软标签 Q_student KL散度损失 L_distill = KL(P || Q) 硬标签 (one-hot) 学生输出 (T=1) 交叉熵损失 L_hard = CE(y_true, y_pred) L_total = α·L_hard + (1-α)·L_distill 反向传播更新学生模型参数 T控制分布平滑度 α平衡硬/软标签权重

6. 实战中的几个关键点

最后,分享几个我在实际项目中积累的经验:

  • T和α要联动调:T越大,软标签越平滑,L_distill的梯度信号越弱。这时候可以适当增大(1-α),让蒸馏损失占比更高。
  • 注意数值稳定性:KL散度计算时,如果Q(i)接近0,log(Q(i))会趋向负无穷。我习惯在log里面加一个很小的epsilon(比如1e-8)。
  • 老师模型也要调T:不是所有老师模型都适合用同一个T。如果老师本身就很「自信」(logits差异极大),需要更大的T来软化。
  • 别忘了温度回退:推理时,学生模型要用T=1做预测,而不是训练时的T。这个我见过有人搞混过,训练时T=4效果很好,结果推理时也用了T=4,输出分布太平滑,准确率直接掉5个点。

一个小技巧:如果你不确定T和α怎么设,可以先用T=4, α=0.3跑一个epoch,然后打印出L_hard和L_distill的量级。如果两者差了一个数量级以上,说明权重需要调整。让两个损失的量级大致相当,训练会更稳定。