虽然均方误差 (MSE) 和平均绝对误差 (MAE) 很适合目标是预测连续值的回归问题,但它们不是分类任务的理想选择。分类任务预测离散类别(例如,'猫' 与 '狗'、'垃圾邮件' 与 '非垃圾邮件'、数字 '0' 到 '9')。对于分类问题,神经网络的输出通常被解释为对所有可能类别的概率分布。例如,对于一个数字分类器,输出可能是 [0.05, 0.01, 0.8, 0.04, ..., 0.1],表示输入图像是数字 '2' 的概率为 80%。衡量分类误差涉及比较预测的概率分布与真实分布。真实分布通常表示为“独热”向量,其中正确类别概率为 1,所有其他类别概率为 0。例如,如果真实数字是 '2',真实分布是 [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]。交叉熵是分类问题的标准损失函数,因为它衡量两个概率分布之间的差异。较低的交叉熵值表明预测分布更接近真实分布。实践中常用的交叉熵损失主要有两种变体:二元交叉熵和类别交叉熵。二元交叉熵 (BCE)二元交叉熵(也称为对数损失)用于二元分类任务,其中只有两个可能的输出类别(例如,0 或 1、真或假、垃圾邮件或非垃圾邮件)。通常,在二元分类网络中,输出层由单个神经元组成,并使用 Sigmoid 激活函数。Sigmoid 函数将输出压缩到 0 到 1 之间,这可以解释为输入属于正类别(类别 1)的概率。我们将此预测概率记作 $p$。则输入属于负类别(类别 0)的概率是 $1-p$。令 $y$ 为真实标签,其中 $y=1$ 表示正类别, $y=0$ 表示负类别。对于单个训练样本,二元交叉熵损失计算如下:$$ L_{BCE} = - [y \log(p) + (1 - y) \log(1 - p)] $$我们来分解一下:如果真实标签 $y=1$:第二项 $(1-y)\log(1-p)$ 变为零。损失简化为 $L_{BCE} = - \log(p)$。为了使损失最小化,网络需要使 $p$ 尽可能接近 1。当 $p \to 1$ 时, $\log(p) \to 0$,损失趋近于 0。如果网络预测正确类别的概率 $p$ 很低,则 $\log(p)$ 成为一个较大的负数, $-\log(p)$ 成为一个较大的正损失。如果真实标签 $y=0$:第一项 $y\log(p)$ 变为零。损失简化为 $L_{BCE} = - \log(1 - p)$。为了使损失最小化,网络需要使 $1-p$ 尽可能接近 1,这意味着使 $p$ 尽可能接近 0。当 $p \to 0$ 时, $1-p \to 1$, $\log(1-p) \to 0$,损失趋近于 0。此公式有效地对模型施加更重的惩罚,特别是当预测错误但置信度很高时。{ "data": [ { "x": [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.99], "y": [4.605, 2.303, 1.609, 1.204, 0.916, 0.693, 0.511, 0.357, 0.223, 0.105, 0.01], "type": "scatter", "mode": "lines", "line": {"color": "#228be6"} } ], "layout": { "xaxis": { "title": "真实类别 (y=1) 的预测概率 (p)" }, "yaxis": { "title": "损失 (-log(p))", "range": [0, 5] }, "title": "二元交叉熵损失(当 y=1 时)", "margin": { "l": 50, "r": 20, "t": 40, "b": 50 }, "width": 600, "height": 350 } }当正确正类别的预测概率趋近于 0 时,损失急剧增加。当真实类别为 0 时, $-\log(1-p)$ 也有类似的曲线。在 PyTorch 中,通常使用 torch.nn.BCELoss,或者更常用的是 torch.nn.BCEWithLogitsLoss。后者在数值上更稳定,因为它将 Sigmoid 层和 BCE 损失计算合并到一步完成,通过运用数学技巧避免当概率非常接近 0 或 1 时潜在的浮点问题。import torch import torch.nn as nn # 二元分类的示例设置 # 假设模型输出是原始对数几率 (Sigmoid 之前) model_output = torch.randn(5, 1) # 5 个样本,每个样本 1 个输出对数几率 true_labels = torch.randint(0, 2, (5, 1)).float() # 5 个标签(0 或 1) # 使用 BCEWithLogitsLoss(推荐) loss_fn = nn.BCEWithLogitsLoss() loss = loss_fn(model_output, true_labels) print(f"带 Logits 的 BCE 损失: {loss.item()}") # 或者,先应用 Sigmoid,然后使用 BCELoss(稳定性较差) # predicted_probs = torch.sigmoid(model_output) # loss_fn_bce = nn.BCELoss() # loss_bce = loss_fn_bce(predicted_probs, true_labels) # print(f"BCE 损失: {loss_bce.item()}")类别交叉熵类别交叉熵用于多类别分类任务,其中有两个以上可能的输出类别(例如,对手写数字 0-9 进行分类,识别图像中不同类型的物体)。在多类别分类中,网络的最后一层通常为每个类别设置一个神经元,并应用 Softmax 激活函数。Softmax 将每个类别的原始输出(对数几率)转换为概率分布,其中每个概率在 0 到 1 之间,且所有概率之和为 1。令 $C$ 为类别数量。网络输出一个预测概率向量 $\mathbf{p} = [p_1, p_2, ..., p_C]$,其中 $p_i$ 是类别 $i$ 的预测概率。真实标签通常表示为独热编码向量 $\mathbf{y} = [y_1, y_2, ..., y_C]$,其中如果 $i$ 是真实类别,则 $y_i=1$,否则 $y_i=0$。对于单个训练样本,类别交叉熵损失计算如下:$$ L_{CCE} = - \sum_{i=1}^{C} y_i \log(p_i) $$由于真实标签向量 $\mathbf{y}$ 中只有一个元素是 1(假设真实类别 $k$ 的 $y_k=1$),而所有其他元素都是 0,因此求和简化为:$$ L_{CCE} = - y_k \log(p_k) = - \log(p_k) $$损失就是针对正确类别的预测概率的负对数。为了使损失最小化,网络必须学会将尽可能高的概率分配给每个输入样本的真实类别。在 PyTorch 中,实现此功能的标准方式是使用 torch.nn.CrossEntropyLoss。此模块便捷地结合了 Softmax 激活函数和类别交叉熵计算(具体来说,它计算 LogSoftmax,然后是 NLLLoss - 负对数似然损失,这与类别交叉熵等效,但在数值上通常更稳定)。它需要模型的原始输出(对数几率)以及作为类别索引(例如 0, 1, 2, ...)的真实标签,而不是独热编码向量。import torch import torch.nn as nn # 多类别分类的示例设置 # 假设模型输出是原始对数几率 (Softmax 之前) # 5 个样本,10 个类别(例如,MNIST 数字) model_output_logits = torch.randn(5, 10) # 作为类别索引的真实标签(0 到 9) true_labels_indices = torch.randint(0, 10, (5,)) # 使用 CrossEntropyLoss(结合了 Softmax 和 NLLLoss) loss_fn = nn.CrossEntropyLoss() loss = loss_fn(model_output_logits, true_labels_indices) print(f"类别交叉熵损失: {loss.item()}") # 用于演示的手动计算(稳定性较差): # softmax = nn.Softmax(dim=1) # predicted_probs = softmax(model_output_logits) # nll_loss_fn = nn.NLLLoss() # NLLLoss 需要对数概率 # loss_nll = nll_loss_fn(torch.log(predicted_probs + 1e-9), true_labels_indices) # 为了稳定性添加一个小的 epsilon # print(f"手动 NLL 损失(近似值): {loss_nll.item()}")使用交叉熵使损失函数与分类目标直接对齐:即最大化分配给正确类别的概率。它的数学性质通常也会带来更稳定高效的训练,与在分类问题中使用像 MSE 这样的回归损失相比。了解如何选择和应用正确的交叉熵变体是训练有效分类模型的基础组成部分。