启用FP16混合精度训练时loss突变为NaN/inf,主因是中间激活层溢出;需用torch.autograd.set_detect_anomaly(True)定位异常层,或手动插入torch.isfinite().all()断言检查高危算子输出,结合Polygraphy二分法定位问题层,并核查输入数据与权重初始化是否超出FP16安全范围。

显卡在深度学习模型训练中启用FP16混合精度后,loss突然跳变为NaN或inf,验证集准确率断崖下跌,但错误不报在前向传播末尾,也不出现在loss.backward()第一行——这意味着溢出发生在中间激活层,必须逐层拦截检查才能准确定位。
用torch.autograd.detect_anomaly快速触发异常位置
在训练脚本最开头插入torch.autograd.set_detect_anomaly(True),让PyTorch在反向传播时对每一步梯度计算做合法性校验。
这一步会显著拖慢训练(约3倍),所以仅限调试阶段启用;正式训练前务必删掉或注释该行。
启动训练后,一旦某层输出含inf/NaN,框架会立即抛出完整traceback,精确到attn_scores = q @ k.t()这一行,而不是笼统提示“loss is nan”。
手动插入.isfinite()断言定位具体张量
方法一:在forward函数返回前加全局检查
assert torch.isfinite(x).all(), f"NaN/inf in output {x.shape}"
方法二:聚焦高危算子,对注意力分数立即检查
attn_scores = q @ k.t() / math.sqrt(d_k)<br>if not torch.isfinite(attn_scores).all():<br> print("ATTN SCORES MAX:", attn_scores.max().item())<br> raise RuntimeError("Inf detected in attention scores")
注意:不要只检查.mean()或.sum(),它们可能掩盖局部inf;必须用.all()确保每个元素合法。
用Polygraphy对ONNX导出模型做逐层精度比对
第一步:将训练好的PyTorch模型导出为ONNX格式,确保opset_version=17且export_params=True。
第二步:用Polygraphy运行FP16推理并标记所有层输出
polygraphy run model.onnx --trt --fp16 --trt-outputs mark all --onnx-outputs mark all --rtol 1e-2 --atol 1e-2
第三步:启用二分法定位问题层范围
polygraphy debug precision model.onnx --fp16 --check
该命令会自动关闭一半层的FP16精度,反复对比输出差异,最终收敛到唯一导致偏差的层。如果输出显示Layer 'encoder.layer.3.attention.self.query' diverges,就说明问题锁定在该层权重或输入上。
【关键前提】导出ONNX时必须禁用torch.no_grad()以外的所有优化,否则中间层不可见。
检查输入数据与初始化权重是否越界
第一步:统计训练数据像素值或token embedding范数分布
print("Input max:", x.max().item(), "min:", x.min().item())
第二步:检查Linear层权重初始化是否过大
for name, param in model.named_parameters():<br> if "weight" in name and param.dim() > 1:<br> print(name, param.abs().max().item())
FP16安全阈值是65504,但实际建议控制在
第三步:对大动态范围输入做预归一化
若图像输入最大值达255,直接送入FP16网络易在第一个Conv层后就溢出;应先执行x = x / 255.0再转FP16。

















