一、核心思想:通过概率分布惩罚错误
交叉熵损失的本质是:
比较模型预测的概率分布 vs 真实标签的概率分布,惩罚两者之间的差异。
例如:
- 真实标签:图像 0 → 文本 0(独热编码 [1, 0, 0, ...])
- 模型预测:[0.1, 0.2, 0.3, 0.4, ...](预测文本 0 的概率仅 0.1)
此时损失会很大,因为预测分布与真实分布差异大。
二、分步解析交叉熵惩罚机制
1. 相似度矩阵 → 概率分布
假设 sim_i2t 是一个 [3, 6] 的矩阵(3 个图像 × 6 个文本):
# 示例相似度矩阵(简化版,仅展示对角线高相似度)
sim_i2t = torch.tensor([[5.0, 1.0, 1.0, 1.0, 1.0, 1.0], # 图像0 → 文本0是正样本[1.0, 5.0, 1.0, 1.0, 1.0, 1.0], # 图像1 → 文本1是正样本[1.0, 1.0, 5.0, 1.0, 1.0, 1.0] # 图像2 → 文本2是正样本
])
通过 softmax 将相似度转换为概率分布:
probs = F.softmax(sim_i2t, dim=1) # 对每行做softmax
print(probs)
输出结果:
tensor([[0.94, 0.02, 0.02, 0.02, 0.02, 0.02], # 预测文本0概率最高(正确)[0.02, 0.94, 0.02, 0.02, 0.02, 0.02], # 预测文本1概率最高(正确)[0.02, 0.02, 0.94, 0.02, 0.02, 0.02] # 预测文本2概率最高(正确)
])
2. 真实标签的概率分布
假设 targets = [0, 1, 2],转换为独热编码:
# 独热编码(简化版,仅展示核心逻辑)
one_hot = torch.zeros_like(probs)
for i, t in enumerate(targets):one_hot[i, t] = 1.0print(one_hot)
输出结果:
tensor([[1.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 图像0的正样本是文本0[0.0, 1.0, 0.0, 0.0, 0.0, 0.0], # 图像1的正样本是文本1[0.0, 0.0, 1.0, 0.0, 0.0, 0.0] # 图像2的正样本是文本2
])
3. 计算交叉熵损失
交叉熵损失公式:
对于上述例子:
- 图像 0 的损失:-log(0.94) ≈ 0.06
- 图像 1 的损失:-log(0.94) ≈ 0.06
- 图像 2 的损失:-log(0.94) ≈ 0.06
平均损失:(0.06 + 0.06 + 0.06) / 3 ≈ 0.06
实际函数内部:
# 1. 对预测值应用softmax,转换为概率分布
probs = F.softmax(sim_i2t, dim=1)# 2. 对每个样本,取出目标类别对应的概率
# 例如:
# - 第0个样本的目标类别是0,取出probs[0, 0]
# - 第1个样本的目标类别是1,取出probs[1, 1]
# - 第2个样本的目标类别是2,取出probs[2, 2]
target_probs = probs[torch.arange(len(targets)), targets]# 3. 计算负对数似然
nll = -torch.log(target_probs)# 4. 求平均值得到最终损失
loss = nll.mean()
三、标签平滑如何调整惩罚
标签平滑(label_smoothing=0.1)会将:
- 正样本的概率从 1.0 调整为 0.9
- 负样本的概率从 0.0 调整为 0.1 / (类别数-1)
例如,对于图像 0(正样本是文本 0):
- 原始标签:[1.0, 0.0, 0.0, 0.0, 0.0, 0.0]
- 平滑后标签:[0.9, 0.02, 0.02, 0.02, 0.02, 0.02]
此时损失计算变为:
实际函数内部:当使用label_smoothing=0.1时,函数内部会将目标概率分布从严格的独热编码调整为平滑分布:
def cross_entropy_with_label_smoothing(logits, targets, smoothing=0.1):num_classes = logits.size(1)# 计算平滑后的目标分布# - 正样本概率: 1.0 - smoothing + (smoothing / num_classes)# - 负样本概率: smoothing / num_classessmooth_targets = torch.full_like(logits, smoothing / (num_classes - 1))smooth_targets[torch.arange(len(targets)), targets] = 1.0 - smoothing + (smoothing / num_classes)# 对预测值应用log_softmaxlog_probs = F.log_softmax(logits, dim=1)# 计算交叉熵(等价于F.kl_div(log_probs, smooth_targets))loss = (-smooth_targets * log_probs).sum(dim=1).mean()return loss
四、惩罚机制可视化
假设模型预测错误(图像 0 预测文本 1 的概率最高):
# 错误预测的情况
bad_probs = torch.tensor([[0.1, 0.8, 0.05, 0.05, 0.0, 0.0], # 错误:预测文本1概率最高[0.02, 0.94, 0.02, 0.02, 0.02, 0.0], # 正确[0.02, 0.02, 0.94, 0.02, 0.02, 0.0] # 正确
])# 计算交叉熵损失(无标签平滑)
loss = -torch.log(bad_probs[0, 0]) # 图像0的损失:-log(0.1) ≈ 2.3
print(f"错误预测的损失: {loss.item():.4f}") # 损失远大于正确预测的0.06
输出结果:
错误预测的损失: 2.3026
五、总结
交叉熵损失的惩罚机制是:
- 对正样本:预测概率越低,惩罚越大(损失呈对数增长)
- 对负样本:预测概率越高,惩罚越大
- 标签平滑:减轻对极端预测的惩罚,防止过拟合
通过这种方式,模型被强制学习到:
- 正样本对的相似度要尽可能高
- 负样本对的相似度要尽可能低
这就是对比学习中 “拉近正样本、推远负样本” 的核心实现方式!