BFloat16 (BF16)
BFloat16(BF16)是一种 16 位浮点格式,采用与 FP32 相同的 8 位指数和 7 位尾数,以一半的内存占用保留 FP32 的数值范围。本文介绍它与 FP16 的区别。
BFloat16(脑浮点格式,BF16)是一种 16 位浮点格式,包含 1 个符号位、8 个指数位和 7 个尾数位。它的指数位宽与标准 32 位浮点格式(FP32)相同,因此数值范围也相同,约为 1.2 × 10⁻³⁸ 至 3.4 × 10³⁸,同时每个数只占用一半的内存。代价是精度较低:7 个尾数位只能提供两到三位有效十进制数字。深度学习能够很好地容忍这种舍入;这种格式最初由 Google Brain 为 Google 的 TPU 设计,如今已成为在现代 GPU和加速器上训练大型模型的默认 16 位格式。
BF16 如何表示数字#
BF16 的值恰好是 FP32 值的高半部分:符号位和指数位相同,尾数则从 23 位截短到 7 位。因此,将 FP32 转换为 BF16 时只需舍弃较低的 16 位并进行舍入;转换回来时补上零即可,这让该格式在硬件上实现起来成本低廉。另一种常见的 16 位格式是 IEEE FP16,也称为半精度,其位数分配不同,包含 5 个指数位和 10 个尾数位,以牺牲数值范围换取精度。比较这三种格式在相同数值上的表现,可以看出它们的取舍:
import torch
# Largest value, smallest normal value, and spacing just above 1.0 for each format
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
info = torch.finfo(dtype)
print(f"{dtype!s:15} max={info.max:.3g} tiny={info.tiny:.3g} eps={info.eps:.3g}")
# A large activation, a tiny gradient, and a value that needs fine precision
x = torch.tensor([70000.0, 1e-8, 1.001953125])
print("FP16:", x.half().tolist())
print("BF16:", x.bfloat16().tolist())BF16 的最大值为 3.39e38,而 FP16 的最大值为 65,504;BF16 的最小正规数为 1.18e-38,与 FP32 相同。FP16 会将 70,000 转为无穷大、将 1e-8 转为零,而 BF16 则能分别保留为 70,144 和约 1.0e-8。FP16 的间距更细(eps 为 0.000977,BF16 为 0.00781),因此能精确保留 1.001953125;BF16 则会将其舍入为 1.0。
训练时的 BF16 与 FP16#
训练时,数值范围比精度更重要。梯度常常会低于 FP16 的最小可表示值而消失,因此 FP16 混合精度训练会在反向传播前将损失乘以一个缩放因子,并在梯度溢出时降低该因子;这项技术称为损失缩放。BF16 的数值范围与 FP32 相同,因此不需要损失缩放;在相同迭代次数下,BF16 训练在视觉、语音、语言和推荐模型上都能达到与 FP32 相当的结果,且无需更改超参数。混合精度训练会将权重的主副本保留为 FP32,并使用 BF16 执行矩阵乘法、用 FP32 累加,因为在 BF16 中累加许多只有 7 位尾数的乘积会造成过大精度损失。两种 16 位格式的选择主要取决于硬件:
PyTorch 通过 torch.autocast(device_type="cuda", dtype=torch.bfloat16) 使用 BF16 自动混合精度。Ultralytics YOLO 也提供相同的开关,用于训练,参数为 amp="bf16";在原生支持 BF16 的 CUDA GPU 上,该选项会在 BF16 autocast 下执行前向传播,并关闭默认 FP16 模式使用的梯度缩放器。
硬件支持#
Google 第二代和第三代 Cloud TPU 会在矩阵单元中使用 BF16 执行乘法,并用 FP32 累加,后续 TPU 也保留了这种格式。NVIDIA GPU 从 Ampere 一代开始便可在 Tensor Core 上运行 BF16,包括 A100、H100 和 Blackwell GPU;支持高级矩阵扩展(AMX)的 Intel Xeon 处理器则可在 CPU上加速 BF16。在 NVIDIA A100 上,BF16 和 FP16 Tensor Core 运算的峰值性能均为 312 teraFLOPS,是 GPU 标准 FP32 运算 19.5 teraFLOPS 的 16 倍。
BF16 与相关格式#
- 半精度(FP16):IEEE 16 位格式,精度更高,但数值范围小得多。它仍常用于推理,因为激活值通常不会超出其范围,而且额外的精度很有帮助。
- TF32:NVIDIA Tensor Core 的一种数学运算模式,指数位为 8 位(与 BF16 相同),尾数位为 10 位(与 FP16 相同)。在 Ampere 及更新的 GPU 上,库可以用它执行 FP32 矩阵乘法,同时张量仍以 32 位格式存储。
- FP8:E4M3 和 E5M2 等 8 位浮点格式,Hopper 和 Blackwell GPU 支持这些格式,可在训练和推理最大规模模型时进一步将内存占用减半。
- 模型量化:将训练后的权重转换为 INT8 或 INT4 等整数格式,以供部署。
大型语言模型中的 BF16#
大多数开放权重大型语言模型(LLM)都以 BF16 混合精度训练,并以 BF16 权重发布;这些权重是衡量量化 FP8 和 INT4 版本的基准。每个参数占 2 字节,因此一个 700 亿参数模型的权重以 BF16 格式存储时需要 140 GB,超过单块 NVIDIA H100 的 80 GB;因此,以 BF16 运行这类模型需要将模型拆分到至少两块 GPU 上。










