37

浮点数——FP32、FP16、BF16 各是什么

阅读约 5 分钟第 37/60 课

计算机存储小数的方式叫浮点数(floating point)。同一个小数可以用不同的精度来存——用更多的比特(bit)可以更精确,但也更占空间、算得更慢。

浮点数是怎么存的

一个浮点数由三部分组成:符号位(正还是负)、指数(数字的量级)、尾数(有效数字)。

可以类比科学计数法:

−3.14 × 10⁸

符号:负号(−)
有效数字:3.14
指数:8

计算机用二进制版本的科学计数法来存浮点数。指数位越多,能表示的范围越大(从极小到极大的数字)。尾数位越多,有效数字越精确。

FP32:32 比特浮点数

FP32 用 32 个比特来存一个数字——1 个符号位、8 个指数位、23 个尾数位。

[1 位符号][8 位指数][23 位尾数]

它能精确到约 7 位有效数字,能表示从约 10⁻³⁸ 到 10³⁸ 的范围。

7 位精度意味着:1.0000001 和 1.0000002 是可以区分的两个不同的数字。这对绝大多数计算来说绰绰有余。

FP32 的缺点是:一个数字占 4 字节。Sophia 的十亿个参数用 FP32 存储需要 4 GB。如果前向和反向传播的所有中间结果也用 FP32,显存开销会翻倍。

FP16:16 比特浮点数

FP16 用 16 个比特——1 个符号位、5 个指数位、10 个尾数位。

[1 位符号][5 位指数][10 位尾数]

精度只有约 3-4 位有效数字。一个数字只占 2 字节——存储空间和计算量都是 FP32 的一半。GPU 上 FP16 矩阵乘法的速度是 FP32 的两倍以上。

但 FP16 有一个致命的限制:指数只有 5 位,最大值只有 65,504。训练过程中,梯度或中间结果很容易超过这个范围——数值变成 inf(无穷大)或 NaN(非数字),训练直接崩溃。

FP32 最大值:约 3.4 × 10³⁸     ← 绰绰有余
FP16 最大值:65,504              ← 训练中常常不够

梯度在某些层可能达到几万甚至几十万的量级,轻松超过 65,504。这就是为什么纯 FP16 训练大模型是不可行的。

BF16:Brain Float 16

BF16 是 Google Brain 团队设计的一种折中方案:16 个比特——1 个符号位、8 个指数位、7 个尾数位。

FP32: [1 位符号][8 位指数][23 位尾数]    ← 精确,但占 4 字节
FP16: [1 位符号][5 位指数][10 位尾数]    ← 小,但容易溢出
BF16: [1 位符号][8 位指数][ 7 位尾数]    ← 小,且不溢出

注意 BF16 跟 FP16 的区别:指数位跟 FP32 一样多(8 位),牺牲的是尾数位。 这意味着 BF16 的数值范围跟 FP32 完全一样(约 10⁻³⁸ 到 10³⁸),但精度只有约 2-3 位有效数字。

格式 大小 指数位 尾数位 范围 精度
FP32 4 字节 8 23 ±10³⁸ ~7 位
FP16 2 字节 5 10 ±65504 ~3-4 位
BF16 2 字节 8 7 ±10³⁸ ~2-3 位

BF16 的精度比 FP16 低一点,但不会溢出——因为它的范围跟 FP32 一样大。对深度学习来说,不溢出比多一位精度重要得多——梯度可以很大很小都没问题,只是精度稍差,训练结果几乎不受影响。

BF16 还有一个实际的好处:FP32 和 BF16 的转换极其简单——直接截断 FP32 的尾数部分后 16 位就得到 BF16。不需要复杂的重新编码。

BF16 已经成为大模型训练的事实标准精度。Sophia 的训练全程使用 BF16。

下一章节来看混合精度训练——为什么有些计算用 BF16、有些必须用 FP32。