BF16和FP16的区别
2026/7/23大约 3 分钟
在 AI Infra 的真实机房里,FP16 和 BF16 简直就是“地狱与天堂”的分界线。
虽然它们俩占用的显存一模一样(都是 2 字节/16 个比特),但英伟达和 Google 对这 16 个P比特的“切分方式”不同,导致了它们在物理命运上的天壤之别。
我们可以用最直观的表格和比喻把它们彻底拆解:
核心物理差异:这 16 个比特是怎么分配的?
计算机存储小数分为三个部分:符号位(管正负)、指数位(管数字能表示得多大)、尾数位(管小数点后面有多精确)。
| 精度类型 | 总大小 | 指数位 (决定范围) | 尾数位 (决定精确度) | 能表示的最大数字范围 | SRE 真实体感 |
|---|---|---|---|---|---|
| FP16 | 16 Bit | 5 位 (太窄) | 10 位 (很精细) | ±65,504 | 动不动就溢出崩溃 |
| BF16 | 16 Bit | 8 位 (极宽) | 7 位 (较粗糙) | ±3.4 × 10³⁸ (和 FP32 一样大) | 稳如老狗,绝不溢出 |
通俗比喻:两把不同的尺子
- FP16 就像是一把“极度精密的短游标卡尺”:
它能精确到零点零几毫米(尾数位多),但它整把尺子最多只能量到 65.5 米。如果你用它去量一座 100 米的大楼,尺子直接“啪”地一声断了。这就是所谓的 溢出 (Overflow)。 - BF16 (Brain Floating Point) 就像是一把“粗犷的登月卷尺”:
它能一直拉到月球那么远(指数位多,范围极大),但代价是它的刻度有点粗糙,只能精确到厘米,量不出毫米。
SRE 实战:为什么大模型必须抛弃 FP16,全面拥抱 BF16?
在古典深度学习时代(比如训练 ResNet 图像识别),模型很小,大家用的都是 FP16,因为它足够精确。
但到了大语言模型(LLM)时代,几十亿、几百亿参数的矩阵在疯狂相乘,特别是在我们上一节讲的 Backward Pass(反向传播算梯度) 的时候,梯度累加起来的数字极其庞大,轻轻松松就会突破 65,504 这个微弱的上限。
1. 当你使用 FP16 训练大模型时(噩梦模式)
一旦反向传播时某个梯度算出来是 70,000,超出了 FP16 的上限。
- 物理表现: 显卡装不下这个数,直接把它变成
Inf(无穷大)。 - 连锁反应: 任何数乘以无穷大都会变成
NaN(非数)。 - 最终结局: 你的 Loss 曲线突然变成
NaN,几百张卡直接白跑,昨天晚上的训练记录全部作废。为了防止这个问题,算法工程师每天都要痛苦地去调Loss Scaling(动态缩放参数),心惊胆战。
2. 当你使用 BF16 训练大模型时(无脑飞升模式)
Google 的大脑团队(Google Brain,也是 BF16 名字的由来)发现了一个大模型的底层潜规则:大模型其实是一个“近视眼”。它根本不在乎小数点后面第五位是几(不需要尾数位精细),但它绝对不能忍受数字装不下(必须保证指数位够大)!
- 物理表现: BF16 直接从 FP32 那里“抄”来了完整的 8 位指数,让它的范围大到了 $10^{38}$。
- 最终结局: 梯度永远不可能溢出。不需要任何复杂的动态缩放,Loss 曲线稳稳当当一路向南。
SRE 终极避坑口诀:
“不管算法团队给的模型叫什么名字,只要你看到启动脚本里写着 fp16=True,立刻把它改成 bf16=True。这一个参数,能帮你挡掉线上 90% 莫名其妙的训练崩溃故障。”
