必须在模型构建前调用tf.keras.mixed_precision.set_global_policy("mixed_float16"),确保计算用float16、权重和优化器状态保float32,并配合LossScaleOptimizer及关键层显式设dtype="float32",否则易出现NaN或溢出。

TensorFlow 2.3+ 原生支持混合精度训练,但必须显式启用 mixed_float16 策略,且模型、损失、优化器需协同适配,否则极易出现 NaN 梯度或数值溢出。
如何正确配置 tf.keras.mixed_precision.Policy
混合精度不是“开个开关就加速”,核心是让计算在 float16 进行,但关键状态(如权重、优化器状态)保留在 float32。必须用 mixed_float16 策略,不能只设 float16:
-
tf.keras.mixed_precision.set_global_policy("mixed_float16")是唯一推荐的全局设置方式; - 若模型中手动创建
tf.Variable,需显式指定dtype=tf.float32(策略不会自动覆盖变量 dtype); - 使用
tf.keras.Model.compile()前必须完成策略设置,否则optimizer初始化会忽略精度逻辑; - 验证策略是否生效:打印
tf.keras.mixed_precision.global_policy(),输出应为Policy(name='mixed_float16')。
为什么 loss 和 optimizer 必须额外处理?
mixed_float16 策略默认不修改 loss 缩放和 optimizer 行为,而 float16 下梯度容易下溢(太小变 0)或上溢(变 inf/NaN),所以必须启用 loss scaling:
- 使用
tf.keras.optimizers.Adam(learning_rate=1e-3)时,要包装成tf.keras.mixed_precision.LossScaleOptimizer; - TF 2.9+ 可直接传
loss_scale="dynamic"给优化器(如Adam(learning_rate=1e-3, loss_scale="dynamic")),但旧版本必须显式包装; - 自定义 loss 函数(如返回标量
tf.Tensor)无需改写,但若内部有tf.reduce_mean等归约操作,确保输入 dtype 不被意外转为float16导致精度丢失; - 验证 loss scaling 是否工作:训练中监控
optimizer.loss_scale,它应在动态调整,而非恒为固定值(如32768.0)。
哪些层/操作容易导致混合精度失效?
不是所有 Keras 层都默认兼容 mixed_float16,尤其涉及数值敏感计算的层:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
-
tf.keras.layers.BatchNormalization默认在float16下更新 running mean/var,易崩溃;必须显式设dtype="float32"(即BatchNormalization(dtype="float32")); -
tf.keras.layers.LayerNormalization同样建议设dtype="float32"; - 自定义
tf.keras.layers.Layer中,若含tf.Variable,务必指定dtype=tf.float32,否则策略不保证其类型; - 使用
tf.image预处理(如tf.image.resize)时,输入若为float16,插值可能不稳定;建议预处理后统一 cast 到float32再进模型。
如何验证混合精度真正生效且稳定?
不能只看训练速度提升,更要看数值行为是否符合预期:
- 检查 GPU 利用率:NVIDIA A100/V100 上,
nvidia-smi显示的Volatile GPU-Util应明显高于纯float32训练; - 用
tf.debugging.check_numerics包裹关键张量(如 loss、gradients),捕获 early NaN; - 对比
model.trainable_variables[0].dtype(应为float32)与model(inputs).dtype(应为float16); - 如果训练几轮后 loss 突然飙升或变为 inf,大概率是 BatchNorm 或 loss scaling 未正确配置,而非硬件问题。
混合精度真正的复杂点不在代码行数,而在于每一处 dtype 的隐式传播——策略不会帮你兜底,每个 Variable、每层 call、每次 cast 都得明确意图。漏掉一个 dtype="float32",就可能让整个训练无声失败。

















