🔬 为什么bf16更稳定?—— 指数位的差异
核心区别在于数据表示的范围。两者都是用16位来存储一个浮点数,但分配方式不同:
fp16(半精度):1位符号 + 5位指数 + 10位尾数。它的指数范围较小,能表示的最大数值约是65504。bf16(脑浮点数):1位符号 +8位指数+ 7位尾数。它的指数范围与fp32(单精度) 完全相同,因此能表示的最大数值约为3.39e38,和fp32是一个量级。
这个差异直接带来了bf16的优势:
无需损失缩放 (Loss Scaling):在
fp16训练中,梯度值很容易因为过小(小于指数能表示的范围)而“下溢”为0,或者因为过大(如 loss 突然 spike)而“上溢”为inf。因此需要动态损失缩放,将 loss 放大后再反向传播,这增加了一个调参环节和计算开销。bf16因为拥有和fp32一样大的指数范围,极少发生溢出问题,所以训练过程更稳定,你可以直接使用,无需担心数值问题。精度换范围:
bf16的尾数位更少,意味着它在表示小数的精确度上比fp16要粗糙一些。但在深度学习场景中,梯度的大致方向和量级比绝对精度更重要,模型对噪声也有一定的容忍度,所以这种“精度换范围”的权衡在绝大多数大模型训练中是划算的。
⚙️ 实际使用注意
- 硬件支持是前提:
bf16需要支持 AVX-512 指令集或新一代 Tensor Core 的硬件(如 A100、RTX 3090/4090 及更新的架构)。在较旧的 GPU(如 V100、T4)上强行使用可能会导致性能回退甚至报错。 - 权衡:在某些对数值精度非常敏感的少数任务(如某些数学推理)中,
bf16的较低尾数精度可能导致结果轻微波动。但总体而言,混合精度训练时,硬件支持则优先选bf16已成为业界共识。
💎 总结
简单来说,bf16就像一个为深度学习“特化”的精度格式:它牺牲了不重要的精度,换来了与fp32同等的稳定范围,从而带来了更简化的训练流程(无需 loss scaling)和更少的数值问题。这使得它成为当前大模型训练中首选的混合精度格式。
补充一点,bf16和fp16除了范围不同,它们的尾数精度和硬件支持也存在差异。如果你对浮点数表示和深度学习的精确数值优化感兴趣,我们可以继续展开说说这些细节。