GradScaler 报错“Found dtype Float but expected Half”是因为模型、输入、损失未显式转为 torch.float16,却直接调用 step();它需配合 autocast 使用,不自动转换 dtype,仅缩放梯度并防下溢。

为什么训练时 GradScaler 报 RuntimeError: Found dtype Float but expected Half
这是最典型的误用信号:模型、输入、损失全没转成 torch.float16,却直接用 GradScaler 调 step()。PyTorch 的混合精度不是“自动帮你转”,而是要求你显式把前向计算切到 half,GradScaler 只负责缩放梯度、避免下溢,并在反向后恢复精度。
关键点:GradScaler 本身不改变模型 dtype,它只配合 autocast 使用;单独用它会因梯度类型和参数类型不匹配而崩溃。
- 必须搭配
torch.cuda.amp.autocast()上下文管理器做前向 - 模型和输入需提前调用
.half()(仅限 CUDA)或使用to(torch.float16) -
optimizer.step()前必须先调scaler.step(optimizer),不能直接调原生step() - 每次迭代末尾必须调
scaler.update(),否则缩放因子不会自适应调整
GradScaler 的 init_scale 和 growth_interval 怎么设才不炸梯度
默认 init_scale=65536.0(即 2¹⁶)是为大多数 CV 模型设计的起点,但太大会导致首次反向就 inf,太小又容易 0.0 梯度——这取决于你模型初始权重的数值范围和 loss magnitude。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 如果训练初期频繁触发
scaler.step()返回None(说明梯度已失效),立刻降低init_scale,比如试16384.0或8192.0 -
growth_interval默认是 2000,意味着每 2000 步无溢出才提升 scale;对小 batch 或 unstable loss(如 GAN、RL),可设为100~500加快收敛 - 观察
scaler.get_scale()输出:稳定训练时它应在几百到几万之间浮动;长期卡在1.0说明持续溢出,需检查 loss 计算或梯度裁剪
混合精度下 optimizer.zero_grad() 还要不要加 set_to_none=True
要,而且更推荐加。混合精度训练中梯度张量生命周期变短(autocast + scaler 会新建 half grad),不设 set_to_none=True 容易让旧的 full-precision grad 缓存残留,引发显存缓慢增长或 grad is None 错误。
正确写法:
optimizer.zero_grad(set_to_none=True)
注意:set_to_none=True 要求你在所有梯度访问逻辑(比如手动 param.grad.clone())之前完成,否则会报 AttributeError: 'NoneType' object has no attribute 'clone'。
验证 GradScaler 是否真起作用的三个硬指标
别只看 loss 下降——混合精度是否生效,得看底层行为:
- 显存占用下降:对比纯
float32训练,应减少约 20%~30%(模型参数 + 激活值 + 梯度都减半) - GPU 利用率上升:
nvidia-smi中GPU-Util峰值从 60% 提升到 85%+,说明计算瓶颈缓解 -
scaler.get_scale()动态变化:运行中打印它,应该有升有降,而不是恒为初始值或一路跌到 1.0
最容易被忽略的是:scaler.step() 成功时返回 None 表示跳过更新,这不是错误,而是保护机制——但如果你没检查返回值就继续跑后续逻辑(比如 log grad norm),可能拿到空值或旧值。


















