PyTorch训练中损失突变为nan主因是数值不稳定,如梯度爆炸致inf,继而log(0)、除零等产nan;需检查输入/标签非法值,如负标签用于CrossEntropyLoss或像素超[0,1]时用BCELoss。

为什么训练中突然出现 nan 损失值
PyTorch 中损失变成 nan 通常不是单一原因,而是数值不稳定在某个环节爆发的结果。最常见的是梯度爆炸导致权重更新后产生无穷大(inf),后续计算(如 log(0)、除零、exp(large_number))直接产出 nan。它往往在训练几轮后才出现,说明问题藏在数据、模型结构或优化过程的隐性路径里。
检查输入数据和标签是否含非法值
这是最快能排除的环节。哪怕只有一条样本的标签为负数(对 nn.CrossEntropyLoss)、或图像像素超出 [0, 1] 且用了 nn.BCELoss,都可能触发 nan。
- 用
torch.isnan(x).any()和torch.isinf(x).any()分别检查input、target张量 - 对图像数据,加一句
assert x.min() >= 0 and x.max() (若已归一化) - 对分类标签,确认
target.dtype == torch.long且值在[0, num_classes-1]范围内 - 使用
torch.utils.data.DataLoader时,若自定义了collate_fn,务必检查是否意外引入None或空张量
启用梯度异常检测并定位出问题的层
PyTorch 提供了 torch.autograd.set_detect_anomaly(True),它会让反向传播在遇到 nan 或 inf 时抛出带完整调用栈的错误,而不是静默污染后续计算。
- 在训练循环前加
torch.autograd.set_detect_anomaly(True)(仅调试用,会显著降速) - 配合
with torch.autograd.detect_anomaly():包裹loss.backward(),可缩小检测范围 - 报错位置通常指向某层输出(如
Linear后、Softmax前),这时重点检查该层输入是否已含nan,以及权重初始化是否过大(比如nn.Linear(1024, 1024)未用torch.nn.init.kaiming_normal_) - 注意:
BatchNorm在 batch size = 1 时方差为 0,会导致除零 →nan;训练时应避免单样本 batch
损失函数与激活函数的组合陷阱
某些函数天然敏感,单独看没问题,但链式调用就崩。典型例子是 nn.LogSoftmax + nn.NLLLoss 被误写成 nn.Softmax + nn.CrossEntropyLoss,后者内部已包含 log,重复取 log 会放大数值误差。
立即学习“Python免费学习笔记(深入)”;
-
nn.CrossEntropyLoss要求原始 logits 输入,不能先过Softmax;否则小数取 log → 大负数 →exp()回 logits 时溢出 -
nn.BCEWithLogitsLoss是安全选择,它把Sigmoid + BCELoss合并在一个 numerically stable 的 kernel 里;若手动拆开,Sigmoid输出接近 0/1 时,BCELoss计算log(0)就炸 - 自定义 loss 里慎用
torch.log、torch.sqrt、torch.pow,务必加 epsilon(如torch.log(x + 1e-8))或用torch.clamp(x, min=1e-8)
nan 出现在分布式训练(DDP)或混合精度(torch.cuda.amp)中——梯度同步或缩放过程中的舍入误差会被放大,这时光靠单机调试不够,得结合 torch.nn.utils.clip_grad_norm_ 和更保守的 scale_factor 来压住火苗。


















