CrossEntropy Loss(交叉熵损失)和 NLLLoss(负对数似然损失):数学公式 + 代码示例

CrossEntropy Loss(交叉熵损失)

C:类别总数

\boldsymbol{z} = [z_1,z_2,...,z_C]:模型输出 (未经过softmax)

y \in \{0,1,...,C-1\}:真实类别索引(非one-hot)

3分类,类别 C=3,单样本损失:

\mathcal{L} = -\log\left( \frac{e^{z_y}}{\sum_{i=0}^2 e^{z_i}} \right)

对logits做Softmax归一化得到各类概率

p_i = \text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{k=1}^C e^{z_k}}

单样本交叉熵损失

\mathcal{L}(z,y) = -\log\left(p_y\right) = -\log\left( \frac{e^{z_y}}{\sum_{k=1}^C e^{z_k}} \right)

批量样本(batch size=N)

\mathcal{L}_{batch} = \frac{1}{N}\sum_{n=1}^N \mathcal{L}(z^{(n)}, y^{(n)})

数据设定

3个样本、3类 logits:

  • 样本1:z_1=[2, 1, 0],   真实标签 y_1=0

  • 样本2:z_2=[-1, 3, 0],真实标签 y_2=1

  • 样本3:z_3=[0, -2, 4],真实标签 y_3=2

分步手动计算

样本1

e^2=7.389,\ e^1=2.718,\ e^0=1,

\quad sum=11.107

p_0 = \frac{e^2}{sum} \approx 0.6652

\mathcal{L}_1 = -\log(0.6652) \approx 0.407606

样本2

e^{-1}=0.3679,\ e^3=20.0855,\ e^0=1,

\quad sum=21.4534

p_1 = \frac{e^3}{sum} \approx 0.9362

\mathcal{L}_2 = -\log(0.9362) \approx 0.065884

样本3

e^0=1,\ e^{-2}=0.1353,\ e^4=54.5982,

\quad sum=55.7335

p_2 = \frac{e^4}{sum} \approx 0.9796

\mathcal{L}_3= -\log(0.9796) \approx 0.020581

批量平均损失

\mathcal{L}_{batch} = \frac{0.407606 + 0.065884 + 0.020581}{3} \approx 0.164690

PyTorch 代码验证

import torch
import torch.nn as nn
import torch.nn.functional as F
​
# 3个样本,3分类 logits
logits = torch.tensor([
    [2.0, 1.0, 0.0],    # 样本1
    [-1.0, 3.0, 0.0],   # 样本2
    [0.0, -2.0, 4.0]    # 样本3
])
labels = torch.tensor([0, 1, 2])  # 对应真实类别索引
​
# 交叉熵损失 # 默认 reduction="mean" 输出3个样本损失平均值;
loss_fn = nn.CrossEntropyLoss()
loss = loss_fn(logits, labels)
print("批量平均损失:", loss.item())

# 输出结果:
# 批量平均损失: 0.16468988358974457
# 和手算 0.164690 完全吻合。

​


# 若想输出每个样本单独损失:reduction="none"

loss_fn_none = nn.CrossEntropyLoss(reduction="none")
loss_each = loss_fn_none(logits, labels)
print("每个样本损失:", loss_each)

# 每个样本损失: tensor([0.4076, 0.0659, 0.0206])

NLLLoss(负对数似然损失)

\boldsymbol{z_i} = [z_1,z_2,...,z_C]:模型输出(未经过softmax)

CrossEntropy = LogSoftmax + NLLLoss,一般优先用 CrossEntropy

\text{CrossEntropyLoss}(z,y) = \text{NLLLoss}(\text{LogSoftmax}(z),\; y)

 LogSoftmax =\log(\text{softmax}(z_i))

公式等价转换

\text{CE}(z,y) = -\log\left( \frac{e^{z_y}}{\sum_{k=1}^C e^{z_k}} \right) = -z_y + \log\left(\sum_{k=1}^C e^{z_k}\right)

\text{NLL}(\text{LogSoftmax}(z),y) = -\log p_y = -\log\left(\frac{e^{z_y}}{\sum e^{z_k}}\right)

二者数学完全等价,只是输入预处理不同。

概率  p_i=LogSoftmax (z_i)

损失函数

以单样本 \mathcal L = -\log p_y拆解:

p_y:模型预测真实标签类别的概率, 0 < p_y \le 1

\log p_y:对数压缩概率值域

  1. p_y=1(预测完全正确): \log 1 = 0,损失 \mathcal L=0
  2. p_y\to 0 (完全预测错误):\log p_y \to -\infty,损失 \mathcal L\to +\infty
  3. 负号 - :把最大化对数似然转为最小化损失,适配梯度下降优化

\mathcal{L}_{batch} = \frac{1}{N}\sum_{n=1}^N \mathcal{L}(p^{(i)}, y^{(i)})

import torch
import torch.nn as nn
import torch.nn.functional as F
​
# 3个样本,3分类 logits
logits = torch.tensor([
    [2.0, 1.0, 0.0],    # 样本1
    [-1.0, 3.0, 0.0],   # 样本2
    [0.0, -2.0, 4.0]    # 样本3
])
labels = torch.tensor([0, 1, 2])  # 对应真实类别索引
​
# 交叉熵损失 # 默认 reduction="mean" 输出3个样本损失平均值;
loss_fn = nn.CrossEntropyLoss()
loss = loss_fn(logits, labels)
print("批量平均损失:", loss.item())
​
# 拆分验证 LogSoftmax + NLLLoss
log_sm = F.log_softmax(logits, dim=1)
nll_loss = F.nll_loss(log_sm, labels)
print("手动拆解损失:", nll_loss.item())

# 输出结果:
# 批量平均损失: 0.16468988358974457
# 手动拆解损失: 0.16468988358974457
# 和手算 0.164690 完全吻合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值