查看: 267|回复: 0

PyTorch多分类交叉熵损失CrossEntropyLoss用法

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
背景与核心结论
在 PyTorch 分类任务中,CrossEntropyLoss 常被误认为只是套用交叉熵公式。原文指出,torch.nn.CrossEntropyLoss 实际把三个步骤合在一起:先对输出矢量 [z1, z2, …, zK] 做 softmax,使概率和为 1;再对 softmax 结果取 log;最后结合真实标签计算损失,自动加上负号,并按默认方式做算术平均。因此模型最后一层不需要再加激活函数,用 nn.Linear 直接输出即可。

如果模型最后一层已经加了激活函数,并且也计算了 log(预测值),则可以用 torch.nn.NLLLoss() 完成上述第 3 步。原文示例中 target 使用 [0,1,2] 这类类别索引;原文虽提到 one-hot,但以代码实际传入形式为准,避免把类别索引和 one-hot 混用。

多分类 CrossEntropyLoss 的计算过程
原文用 3×3 输入演示 softmax、log 和按标签取值的计算逻辑,代码如下:
  1. import torch
  2. input = torch.randn(3, 3)
  3. soft_input = torch.nn.Softmax(dim=0)
  4. print(soft_input(input))
  5. # tensor([[0.7284, 0.7364, 0.3343],
  6. #         [0.1565, 0.0365, 0.0408],
  7. #         [0.1150, 0.2270, 0.6250]])
  8. print(torch.log(soft_input(input)))
  9. # tensor([[-0.3168, -0.3059, -1.0958],
  10. #         [-1.8546, -3.3093, -3.1995],
  11. #         [-2.1625, -1.4827, -0.4701]])
  12. # 标签为[0,1,2]时,第一行取第0个、第二行取第1个、第三行取第2个
  13. print((0.3168 + 3.3093 + 0.4701) / 3)
  14. # 1.3654000000000002
  15. loss = torch.nn.NLLLoss()
  16. target = torch.tensor([0, 1, 2])
  17. print(loss(input, target))
  18. # tensor(0.1365)
复制代码
按原文手工计算,取 log 后对应位置为 0.3168、3.3093、0.4701,去掉负号后求平均得到 1.3654。但原文最后一行 loss(input, target) 传入的是原始 input,而不是 torch.log(soft_input(input)),所以输出 0.1365 与手工值 1.3654 不一致。NLLLoss 的输入应是 log 概率,不会自动做 softmax 和 log;复现时应把 log 概率作为 NLLLoss 输入。这个差异也说明 CrossEntropyLoss 的封装步骤不能拆错。

示例中 Softmax 的 dim=0 是按列归一化,实际使用时 dim 要与类别所在维度一致。CrossEntropyLoss 默认会对样本损失做平均,原文特别提醒其计算中包含负号和算术平均。

二分类 BCELoss 的用法与参数
二分类常用 torch.nn.BCELoss()。原文示例先对输入做 Sigmoid,再计算 BCELoss 并反向传播:
  1. m = nn.Sigmoid()
  2. loss = nn.BCELoss()
  3. input = torch.randn(3, requires_grad=True)
  4. target = torch.empty(3).random_(2)
  5. output = loss(m(input), target)
  6. output.backward()
复制代码
原文强烈建议使用 BCELoss 时在模型最后一层加 Sigmoid 层。BCELoss 的参数签名为:
  1. torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
复制代码
weight 是可选的 Tensor,用于给每个 batch element 的损失做手工重缩放,若传入则大小应为 nbatch;size_average 和 reduce 已废弃,由 reduction 控制;reduction 可取 'none'、'mean'、'sum',分别表示不归约、求和后除以元素数、求和。原文说明当出现 log 0 时,该项损失按 -100 处理,这类数值边界需要特别注意。

BCELoss 的输入输出规定为:Input 为 (N, *),Target 与输入同形状;Output 为标量,若 reduction 为 'none',则输出与输入同形状。

BCEWithLogitsLoss 的数值处理
为避免 BCELoss 中 log 0 无法计算的局限,原文建议使用 torch.nn.BCEWithLogitsLoss(),它会在计算时自动给 prediction 加 Sigmoid。其参数签名为:
  1. torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)
复制代码
用法与 BCELoss 相同,额外提供 pos_weight。实际排查时,要先确认模型输出是概率还是 logits:BCELoss 需要先 Sigmoid,BCEWithLogitsLoss 接收 logits,不应再手工加 Sigmoid。

适用场景与排查要点
多分类优先使用 CrossEntropyLoss,模型最后一层不要加激活函数;若已有 log 概率,则用 NLLLoss。二分类使用 BCELoss 时要配 Sigmoid,关注 log 0;需要更稳的数值处理时用 BCEWithLogitsLoss。排查损失异常时,重点检查最后一层是否有重复激活、target 是类别索引还是 one-hot、NLLLoss 输入是否为 log 概率、reduction 设置是否符合预期,以及输入和 target 形状是否匹配。

以上内容围绕原文的代码逻辑、参数含义和计算过程进行重组,保留了 CrossEntropyLoss、NLLLoss、BCELoss、BCEWithLogitsLoss 的关键事实,并补充了原文示例中手工值与 NLLLoss 调用输入不一致的说明。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-20 14:12 , Processed in 0.024574 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部