
大模型训练几乎都用混合精度——大部分计算用低精度fp16/bf16归一化和 loss 用 fp32。但 fp16 和 bf16 选哪个不是随便选的。在昇腾NPU上bf16 训练更省心fp16 训练更快但需要更多调参。fp16 vs bf16 硬件特性维度fp16bf16指数位5 bit8 bit尾数位10 bit7 bit数值范围±65504±3.4×10^38精度~3 位十进制~2 位十进制Cube 单元算力310 TFLOPS310 TFLOPS两者算力一样差别在数值范围和精度。bf16 范围跟 fp32 一样大不容易溢出。fp16 精度更高但容易溢出。Loss Scalingfp16 的必要操作fp16 的最大值 65504梯度很容易超出这个范围。Loss Scaling 在 backward 前把 loss 放大backward 后把梯度缩小回来fromtorch_npu.npuimportamp modelMyModel().to(npu:0)optimizertorch.optim.AdamW(model.parameters())scaleramp.GradScaler(init_scale65536)# fp16 必须用 GradScalerfordataindataloader:optimizer.zero_grad()withamp.autocast(dtypetorch.float16):# fp16 混合精度lossmodel(data)scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()GradScaler 的init_scale需要调。太大导致梯度溢出NaN太小等于没 scaling。通常从 65536 开始遇到 NaN 就减半。bf16 不需要 Loss Scaling——它的范围够大梯度不会溢出# bf16 训练不需要 GradScalerfordataindataloader:optimizer.zero_grad()withamp.autocast(dtypetorch.bfloat16):# bf16 混合精度lossmodel(data)loss.backward()optimizer.step()代码更简洁不需要调init_scale。精度对比Llama2-7B 在昇腾NPU上的训练 loss 曲线配置最终 Loss训练稳定性fp16 Loss Scaling2.51偶尔 NaN需要调 scalebf162.53稳定无需调参fp322.50最稳定但速度慢 2.5×bf16 比 fp16 的 loss 高 0.02差距在统计波动范围内。训练稳定性 bf16 远好于 fp16。性能对比Atlas 800I A2Llama2-7B 训练配置吞吐 (tokens/s/p)训练速度fp161820基准bf161780慢 2%fp32720慢 60%bf16 比 fp16 慢 2%几乎无差异。原因两者的 Cube 单元算力一样2% 的差异来自 bf16 的 cast 开销某些中间步骤需要 bf16→fp32→bf16。选择建议场景推荐大模型预训练bf16省心微调SFT/LoRAbf16数据量小不需要折腾 Loss Scaling对精度极度敏感fp16 精调 Loss Scaling昇腾NPU初学者bf16减少一个调参维度一个重要细节bf16 训练的权重保存后可以用于 bf16 推理也可以转成 fp16 推理。转 fp16 不会损失精度bf16 的范围更大截断到 fp16 的精度范围没问题。反过来 fp16 权重转 bf16 也没问题。和 CANN 算子的兼容性CANN 的所有融合算子都同时支持 fp16 和 bf16 输入# FlashAttentionfp16 和 bf16 都能跑q_fp16torch.randn(1,32,4096,128,devicenpu,dtypetorch.float16)q_bf16torch.randn(1,32,4096,128,devicenpu,dtypetorch.bfloat16)out_fp16torch_npu.npu.flash_attention(q_fp16,k_fp16,v_fp16)# OKout_bf16torch_npu.npu.flash_attention(q_bf16,k_bf16,v_bf16)# OK不需要为不同 dtype 写不同代码。CANN 的融合算子内部自动处理精度转换。在昇腾NPU上训大模型选 bf16 就对了。不需要 Loss Scaling、训练更稳定、性能只慢 2%。把调 Loss Scaling 的时间省下来调模型结构性价比更高。仓库在这里https://atomgit.com/cann/torch_npu