nn.CrossEntropyLoss内部做了Softmax是因为它需计算负对数似然,而NLLLoss要求输入为log-probabilities,故将LogSoftmax与NLLLoss融合为一步,兼顾数值稳定与效率;手动添加Softmax会导致双softmax、梯度崩坏、loss飙升或nan。

nn.CrossEntropyLoss 为什么内部做了 Softmax?
因为它要算的是「负对数似然」,而 NLLLoss 的输入必须是 log-probabilities;nn.CrossEntropyLoss 把 LogSoftmax 和 NLLLoss 合并成一步,既省代码又保数值稳定。
你传进去的 logits(未归一化的原始输出)会被先减去最大值、再指数、归一、取对数——这一整套操作在底层用 fused kernel 实现,比你自己调 torch.softmax + torch.log + nn.NLLLoss 快,也更不容易下溢或上溢。
手动加 Softmax 会出什么错?
模型输出变「双 softmax」:一次是你显式写的 torch.softmax(x, dim=1),另一次是 nn.CrossEntropyLoss 内部再做一遍。结果就是概率被过度挤压,梯度崩坏,loss 突然飙升甚至 nan。
- 典型错误写法:
x = self.fc(x)→x = torch.softmax(x, dim=1)→loss = criterion(x, target) - 正确写法:
x = self.fc(x)→loss = criterion(x, target)(中间不插任何激活) - 验证方法:打印
x.max()和x.min(),如果已经是 [0,1] 区间且和接近 1,说明你多做了 softmax
什么时候真需要显式 Softmax?
只在推理(inference)阶段,你需要把模型输出解释为「人类可读的概率」——比如展示「猫:73.6%,狗:24.5%,鸟:1.9%」这种结果。
立即学习“Python免费学习笔记(深入)”;
- 训练时永远不要:因为
nn.CrossEntropyLoss不吃概率,它吃 logits - 验证/测试时才用:
probs = torch.softmax(logits, dim=1),然后probs.argmax(dim=1)取预测类别 - 注意:
torch.softmax默认不带inplace=True,不会改原 tensor,但别在 loss 计算路径里调它
Logits 和 probabilities 混用的坑
最容易被忽略的是:nn.CrossEntropyLoss 的 target 必须是 class index(如 tensor([0, 2, 1])),不是 one-hot;而如果你误把概率当 logits 传进去,loss 值会异常小(比如 0.001),但梯度几乎为零——模型根本不学。
- 检查维度:
logits.shape应该是(N, C),target.shape是(N,),且target中每个值 ∈ [0, C-1] - 别用
torch.nn.functional.one_hot转 target,nn.CrossEntropyLoss不接受 one-hot 标签 - 如果数据加载器返回的是 one-hot label,得在送入 loss 前用
target.argmax(dim=1)


















