软标签与硬标签:温度系数T的作用机制、KL散度推导与加权融合策略
蒸馏的核心,说白了就是让「学生模型」模仿「老师模型」的行为。但怎么模仿?直接拿老师预测的类别标签去教?还是把老师脑子里那些「犹豫」和「模糊判断」也一并学过来?
这就引出了我们今天要聊的两个关键概念——硬标签和软标签。我刚开始做蒸馏时,也觉得硬标签就够了,反正最终结果对了就行。后来发现,学生模型学到的泛化能力差了一大截。嗯,这里面的门道,咱们一点点拆开看。
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是学生分布。这个值越小,说明两个分布越接近。
我们来拆解一下:
- P(i) * log(P(i)):这部分是老师分布的熵,与学生无关,可以忽略
- -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. 知识结构图:蒸馏中的标签体系
下面这张图梳理了软标签、硬标签、温度系数和损失函数之间的关系:
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的量级。如果两者差了一个数量级以上,说明权重需要调整。让两个损失的量级大致相当,训练会更稳定。