pytorch中fp16需配合gradscaler和autocast,而bf16无需scaler、仅限支持设备且部分算子不兼容;autocast可显式指定dtype,但bf16下误用gradscaler会导致nan。

PyTorch中FP16和BF16的启用方式完全不同
FP16(half)和BF16(bfloat16)在PyTorch里不是靠改一个dtype参数就能互换的。FP16需要手动管理缩放(scaler)、避免下溢/溢出,而BF16在支持的硬件(如Ampere+ GPU)上可直接用torch.bfloat16且无需scaler——但仅限部分算子和设备。
常见错误是直接把model.half()换成model.to(torch.bfloat16)就以为切换成功,结果报错RuntimeError: "addmm" not implemented for 'BFloat16',这是因为某些算子(如某些CUDA kernel)尚未支持BF16。
- FP16推荐走
torch.cuda.amp.autocast+GradScaler组合,自动处理cast和梯度缩放 - BF16必须确认设备支持:
torch.cuda.is_bf16_supported()返回True才安全启用 - 混合精度训练中,BF16通常只用于模型权重和前向/反向计算,optimizer state仍用FP32(PyTorch默认如此)
autocast上下文里如何控制FP16/BF16行为
torch.cuda.amp.autocast默认启用FP16,但可通过dtype参数显式指定为torch.bfloat16——前提是GPU支持且CUDA版本≥11.0。
注意:即使设置了autocast(dtype=torch.bfloat16),部分操作(如torch.nn.functional.cross_entropy输入logits)仍可能回退到FP32,这是autocast的保守策略,不是bug。
- FP16模式:
with torch.cuda.amp.autocast():(等价于autocast(dtype=torch.float16)) - BF16模式:
with torch.cuda.amp.autocast(dtype=torch.bfloat16): - 禁用autocast时,
.to(torch.bfloat16)需手动对模型、输入、loss全部cast,极易出错,不建议
GradScaler只对FP16有效,BF16不能用
torch.cuda.amp.GradScaler专为FP16设计,用来防止梯度下溢。BF16动态范围与FP32一致,天生不易下溢,因此GradScaler在BF16下不生效,调用scaler.scale(loss).backward()会静默失败或报错scale factor must be > 0。
如果你看到BF16训练中loss突变为NaN,大概率是因为误加了GradScaler——它根本不该出现在BF16流程里。
- FP16训练必须配
GradScaler,且scaler.step(optimizer)和scaler.update()缺一不可 - BF16训练直接用
loss.backward(),后续optimizer.step()即可 - 混合使用时(比如部分模块FP16、部分BF16),需按dtype分组管理scaler,极复杂,生产环境应避免
验证实际使用的精度类型
别信代码里写的dtype,要查运行时tensor的真实类型。尤其在autocast内部,tensor dtype可能被临时改变,但模型参数本身未必变。
最容易忽略的是:model.parameters()的dtype和forward过程中中间tensor的dtype可能不一致。例如模型用torch.bfloat16加载,但autocast设成FP16,中间激活就会是FP16。
- 查参数dtype:
next(model.parameters()).dtype - 查某层输出dtype:
print(x.dtype)放在forward里关键位置 - 查autocast是否生效:
torch.is_autocast_enabled()和torch.is_autocast_cpu_enabled() - BF16训练中若发现某个tensor仍是FP32,大概率是autocast未覆盖该op(如自定义C++扩展),需手动cast
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











