2. 损失函数基础
损失函数,说白了就是模型学习的“指挥棒”。你想想看,神经网络自己又不知道什么是对什么是错,它全靠损失函数给的反馈来调整参数。我刚开始做深度学习那会儿,总觉得损失函数就是个数学公式,随便选一个就行。后来踩了不少坑才明白——选错损失函数,模型根本学不到东西。
损失函数在深度学习中的作用
损失函数的核心作用就三个:
- 衡量差距:告诉模型当前预测值和真实值差了多少
- 提供梯度:反向传播时,损失函数决定了梯度的大小和方向
- 约束行为:不同的损失函数会引导模型学到不同的特征
我个人习惯把损失函数比作“老师”。老师严格,学生就学得认真;老师宽松,学生就容易偷懒。在知识蒸馏里,这个“老师”还不止一个——学生既要听真实标签的“班主任”,又要听教师模型的“特级教师”。
核心观点:损失函数决定了模型“关注什么”和“忽略什么”。选对了,事半功倍;选错了,事倍功半。
分类任务常用损失
交叉熵损失(CrossEntropy Loss)
交叉熵是分类任务中最常用的损失函数。它的原理很简单:预测分布和真实分布越接近,损失越小。
# PyTorch 中的交叉熵损失
import torch
import torch.nn as nn
criterion = nn.CrossEntropyLoss()
# 假设 batch_size=4, num_classes=10
logits = torch.randn(4, 10) # 模型输出
labels = torch.randint(0, 10, (4,)) # 真实标签
loss = criterion(logits, labels)
print(f"CrossEntropy Loss: {loss.item():.4f}")
这里有个细节要注意:nn.CrossEntropyLoss 内部已经包含了 Softmax 操作,所以你不需要在模型输出层再加 Softmax。我曾经见过有人重复加 Softmax,结果梯度消失,模型死活不收敛。
KL 散度(Kullback-Leibler Divergence)
KL 散度衡量的是两个概率分布之间的差异。在知识蒸馏中,它用来让学生模型的输出分布去拟合教师模型的输出分布。
# KL 散度计算示例
def kl_divergence(p, q):
# p: 教师分布, q: 学生分布
return (p * (p.log() - q.log())).sum()
# 实际使用时,通常用 PyTorch 的 nn.KLDivLoss
kl_loss = nn.KLDivLoss(reduction='batchmean')
# 注意:输入需要是 log-probabilities
student_log_probs = torch.log_softmax(student_logits, dim=1)
teacher_probs = torch.softmax(teacher_logits, dim=1)
loss = kl_loss(student_log_probs, teacher_probs)
避坑指南:KL 散度是非对称的。我曾经在项目里把 KL 散度的参数顺序搞反了,结果蒸馏出来的学生模型性能反而下降了。记住:KL(P||Q) 中 P 是目标分布,Q 是待优化分布。
回归任务常用损失
均方误差(MSE)
MSE 是回归任务的老大哥。它对大误差的惩罚特别狠——误差越大,惩罚呈平方增长。
# MSE 计算
mse_loss = nn.MSELoss()
predictions = torch.tensor([2.5, 0.0, 1.8])
targets = torch.tensor([3.0, 0.5, 2.0])
loss = mse_loss(predictions, targets)
print(f"MSE Loss: {loss.item():.4f}")
MSE 的优点是梯度稳定,缺点是容易被异常值带偏。我在做工业缺陷检测时遇到过这种情况:一个离群点就能把整个模型的注意力拉走。
平均绝对误差(MAE)
MAE 对异常值没那么敏感,但它的梯度是常数,收敛速度可能比 MSE 慢。
# MAE 计算
mae_loss = nn.L1Loss()
loss = mae_loss(predictions, targets)
print(f"MAE Loss: {loss.item():.4f}")
| 损失函数 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| MSE | 梯度稳定,收敛快 | 对异常值敏感 | 数据干净、无异常值 |
| MAE | 对异常值鲁棒 | 梯度恒定,收敛慢 | 数据含异常值 |
| Huber Loss | 结合 MSE 和 MAE 优点 | 多了一个超参数 | 不确定数据质量 |
蒸馏损失与传统损失的区别
传统损失和蒸馏损失,本质上是“教答案”和“教思路”的区别。
- 传统损失:直接拿真实标签做监督,模型只学“正确答案是什么”
- 蒸馏损失:拿教师模型的软标签做监督,模型学“教师是怎么思考的”
举个例子:手写数字识别中,真实标签说“这是7”。但教师模型会说“这有90%可能是7,5%可能是1,3%可能是9,2%可能是其他”。这个软分布里包含了类别之间的相似性信息——7和1长得像,7和9也像。学生模型学到这个,泛化能力会更强。
个人经验:我在做图像分类蒸馏时发现,只用 KL 散度做蒸馏损失,学生模型往往学得“太软”,分类边界模糊。后来我加了 0.1 权重的交叉熵损失,效果立刻好了很多。蒸馏损失和传统损失不是二选一,而是互补的。
知识体系总览
这张图把损失函数分成了两大阵营。左边是传统损失,右边是蒸馏损失。你注意看底部说明框——传统损失教的是“答案”,蒸馏损失教的是“思路”。这个区别,就是知识蒸馏的精髓所在。
一句话总结:损失函数是深度学习的“指挥棒”。传统损失告诉模型“答案是什么”,蒸馏损失告诉模型“怎么得到答案”。两者结合,才能训练出又快又准的学生模型。