大语言模型中的低精度数据格式

如果只用一句话概括过去三年大模型训练与推理的硬件趋势 — 训练在从 BF16 走向 FP8,推理在从 FP8 走向 FP4。每一次精度下移一档,同样的硅片面积就能多塞一倍的 MAC 单元,LLM 训练吞吐与推理 token/s 直接翻倍。这条主线背后是一整张数据格式的谱系:FP32 / TF32 / FP16 / BF16 / FP8(两个变体) / FP6(两个变体) / FP4(MX 与 NV 两个版本) / INT8 / INT4。

但「精度下移」从来不是「整个模型换一种格式」这么简单。同一个 Transformer block 里,矩阵乘法可能跑 FP8、Softmax 在 FP32 下做、KV cache 又压到 INT8 — 精度是按运算类型混用的,不是按层混用。要看清这件事,得把每种格式的位结构、动态范围、硬件支持、和在 Transformer 里的实际位置全部铺开。

这篇文章的目的就是把这张谱系图一次性铺开,只覆盖有硬件原生 Tensor Core / 矩阵引擎支持的格式 — 纯软件模拟、研究阶段、或者只在论文里出现的格式(INT2、二值化、posit 等)略过不谈。

引子 — 低精度不只是省显存

很多人对低精度的第一反应是「省显存」。这只对了一半 — 真正决定为什么 NVIDIA / AMD / Google 都在卷低精度的,是另一个事实:每一代 Tensor Core 在更低精度下的吞吐量大致翻倍[3][4]

架构旗舰卡同 SM 算力(主精度)同 SM 算力(最低精度)
Ampere (2020)A100312 TF (FP16)624 TOPS (INT8)
Hopper (2022)H100990 TF (FP16)1979 TF (FP8)
Blackwell (2024)B2002250 TF (FP16)9000 TF (FP4)
Rubin (2026)R100~8000 TF (FP16)50,000 TF (FP4)

把这张表放在一起看 — 位宽每减半,同代硅片上 MAC 单元数量翻倍,算力直接翻倍。所以低精度不只是「同样模型显存占用更小」,更是「同样硅片可以跑更大的模型」。这是 NVIDIA / AMD / Intel 这三年集体扑向 FP8 / FP4 的根本动力。

省显存当然也重要 — BF16 的模型权重比 FP32 小一半,INT4 量化的权重比 FP16 小四倍。但如果只有显存红利,没有算力红利,这条路根本走不远。

浮点数的通用表示 — 一个公式套所有格式

任何一个 IEEE 754 风格的浮点数都由三部分组成:

value=(1)S×2Ebias×(1.M)\text{value} = (-1)^S \times 2^{E - \text{bias}} \times (1.M)

所以一个浮点格式的关键参数就是「总位数 + ee + mm。决定动态范围的是 ee,决定精度的是 mm。下文所有格式都按这个模板套。

整数格式则是另一种 — 完全没有指数位,只有有符号 / 无符号的整数表示。它的范围线性、精度均匀,但表达范围受位宽硬性限制,实际使用时永远要搭一个外部的 scale factor 把整数「拉伸」或「收缩」到目标数值区间。这一点放到后面整数那一节细说。

浮点格式逐一拆解 — FP32 → FP4 同尺度对比

先一张图把九种浮点格式按同尺度铺开 — 每一格就是一个比特位。指数位宽决定动态范围、尾数位宽决定精度,这两个数轴的取舍是所有格式设计的核心:

浮点数位结构同尺度对比 · 每格 = 1 bitS = 符号 · E = 指数(决定动态范围) · M = 尾数(决定精度)FP321 + 8 + 23TF321 + 8 + 10FP161 + 5 + 10BF161 + 8 + 7FP8 E4M31 + 4 + 3FP8 E5M21 + 5 + 2FP6 E3M21 + 3 + 2FP6 E2M31 + 2 + 3FP4 E2M11 + 2 + 1符号 S指数 E · 决定动态范围尾数 M · 决定精度
九种浮点格式按同尺度对齐(每格 = 1 bit)。FP32 / FP16 来自 IEEE 754,TF32 来自 NVIDIA,BF16 来自 Google Brain,FP8 由 NVIDIA / Arm / Intel 联合定义,FP6 / FP4 是 OCP 微缩格式标准的一部分。BF16 跟 FP32 共享 8 位指数,所以动态范围相同但精度只剩一半;FP8 的两个变体是同位宽下的两种取舍(E4M3 重精度走前向、E5M2 重范围走反向);FP4(E2M1) 一共只有 4 位 — 单看完全不可用,必须以 MX 微缩格式形式使用。

FP32 / TF32 — 基准与一个工程巧思 — 同 8 位指数 · 不同尾数

FP32(1 + 8 + 23 = 32 位)是所有现代 CPU / GPU 的基准格式。指数 8 位、bias = 127、动态范围约 1.2×10381.2 \times 10^{-38}3.4×10383.4 \times 10^{38},精度约 7 位十进制有效数字。

TF32(1 + 8 + 10 = 19 位,存在 32 位寄存器里)是 NVIDIA 在 A100 上引入的一个工程巧思 — 指数位和 FP32 完全一样(8 位),尾数砍到和 FP16 一样(10 位)。它名字里带「32」,但有效宽度只有 19 位。这么做的好处是:动态范围跟 FP32 完全相同 → 训练时不需要 loss scaling;精度只剩 ~3 位十进制 → 算得快很多。A100 / H100 / Blackwell 上 TF32 是 FP32 GEMM 的默认替代,不改一行代码就能加速[4]

FP16 / BF16 — 训练时代的两个 16 位 — 一个重精度 · 一个重范围

FP16(1 + 5 + 10 = 16 位)是 IEEE 754 半精度,指数 5 位、bias = 15、动态范围约 6×1056 \times 10^{-5}6.5×1046.5 \times 10^{4},精度约 3-4 位十进制。这个动态范围对训练来说非常窄 — 梯度一旦小到 10710^{-7} 量级就会下溢成零。所以 FP16 训练必须配合 loss scaling(把 loss 放大到不会下溢的量级再反向传播)。

BF16(1 + 8 + 7 = 16 位)是 Google Brain 在 TPU v2 时代提出的格式,设计思想很直接 — 把 FP32 的尾数砍掉 16 位,保留全部 8 位指数。这样:

这就是为什么 2020 年之后,大模型训练几乎清一色地选 BF16 而不是 FP16 — 大模型训练对动态范围远比对精度敏感。这条经验是后面 FP8 设计的直接原型。[6]

FP8 — 两个变体的分工 — E4M3 走前向 · E5M2 走反向

FP8 由 NVIDIA / Arm / Intel 在 2022 年联合提出[2],H100 / MI300 / Gaudi 2/3 都原生支持。它同时定义了两个变体:

变体位结构动态范围精度用途
E4M31+4+3±448\pm 448(最大值)· 最小 normal 数 262^{-6}较高前向:权重 / 激活
E5M21+5+2±57344\pm 57344 · 最小 normal 数 2142^{-14}较低反向:梯度

为什么要两个变体?因为激活和梯度的动态范围差很多。激活值经过 LayerNorm 之后通常集中在小范围内,适合 E4M3 的「重精度、轻范围」;梯度的数值可能跨好几个数量级,必须用 E5M2 的「重范围、轻精度」兜住。

注意 E4M3 不严格遵循 IEEE 754 — 它没有 inf,把本来表示 inf 的编码挪用来扩展数值范围(最大值是 448 而不是 240)。E5M2 则严格遵循 IEEE 754,有 inf 和 NaN。

FP8 的 scaling:单看 8 位完全不够覆盖训练时的数值范围。所以 FP8 几乎总是搭配一个 FP32 的 per-tensor scaling factor(H100 的 Transformer Engine 自动维护)[7],实际存储的数值是 FP8_value×scale\text{FP8\_value} \times \text{scale}。后面提到的 DeepSeek-V3 在这一层做了更细粒度的改进。

FP6 与 FP4 — 进入微缩格式时代 — 单看无意义 · 必须按块共享 scale

FP6 和 FP4 是 2023 年 OCP(Open Compute Project)发布的 微缩格式(Microscaling, MX) 规范的一部分[1],Blackwell 是第一代把它写进 Tensor Core 的硬件。[3]

FP6 有两个变体:E3M2(范围大、精度低)和 E2M3(范围小、精度高),按场景选用。

FP4 在 Tensor Core 上有两个版本,都用 E2M1 的位结构,差别在于 scale 的粒度:

版本位结构块大小块 scale外层 scale
MXFP4(OCP)E2M1 (4 bit)32 元素E8M0(8 bit,纯指数)
NVFP4(NVIDIA)E2M1 (4 bit)16 元素FP8 E4M3(8 bit)FP32 per-tensor

为什么需要微缩格式?因为 FP4 一共只有 4 位,能表示的不同数值只有 16 个(包括正负零)。如果不分组共享一个 scale,这 16 个值根本覆盖不了实际权重的分布。MX 把「per-tensor scaling」做成「per-block scaling」 — 每 32 个数共享一个 8 位的指数 scale,等于在硬件层面就内置了 fine-grained 量化。

NVFP4 把块大小从 32 缩到 16,把块 scale 从纯指数 E8M0 换成更精细的 FP8(E4M3),再外面套一层 FP32 per-tensor scale — 三层 scale 结构让它在 Blackwell 推理上的精度比 MXFP4 明显更好,代价是 metadata 占用稍微多一点。[3]

整数格式 INT8 / INT4 — 没有指数 · 完全靠 scale 兜底

整数格式可以看成是「全部尾数、零位指数」的特殊浮点 — 精度均匀、但动态范围完全靠外部 scale 决定。INT8 与 INT4 在 LLM 推理上几乎占据了消费级 / 部署侧的全部场景。[8][14]

INT8 vs INT4 · 单独存在时能表示的数值线性轴(不再是浮点的对数轴) · 整数格式离不开外部 scaleINT8 · 有符号 · 256 个值−1280+127256 个均匀分布的值 · 必须配 scale 与 zero point 才能映射到实际数值INT4 · 有符号 · 16 个值-8-7-6-5-4-3-2-101234567只有 16 个不同的值 · 必须以 group-wise 共享 scale(每 32/64/128 个数一个 FP16 scale)
INT8 与 INT4 的取值范围 — 整数格式没有指数位,数值均匀分布在固定的整数区间内。INT4 一共只有 16 个不同的值(从 −8 到 +7),离开外部 scale 完全不能用。GPTQ、AWQ 这些权重量化算法的核心做的就是「找到一组合适的 group-wise scale,让权重量化后的精度损失最小」。

实际使用整数格式时,定点 → 浮点的映射公式是:

xfps(xintz)x_{\text{fp}} \approx s \cdot (x_{\text{int}} - z)

其中 ss 是 FP16 / FP32 的 scale,zz 是 zero point(对称量化 z=0z=0,非对称量化 z0z \neq 0)。INT8 通常 per-tensor 或 per-channel 一个 scale 就够;INT4 几乎必须 group-wise — 每 32 / 64 / 128 个数共享一个 scale,否则精度损失太大。GPTQ[9]、AWQ[10] 这些权重量化算法,核心都是在「找一组合适的 group-wise scale」。[15]

它们能表示的范围 — 对数轴上的一字排开

把所有浮点格式的动态范围放在同一条对数轴上,差异一眼就能看出来:

浮点格式动态范围 · 对数轴对齐每条 bar 的横向长度 = log₁₀(max) − log₁₀(min) · 颜色按位宽分组10⁻⁴⁰10⁻³⁰10⁻²⁰10⁻¹⁰110¹⁰10²⁰10³⁰10⁴⁰log scaleFP321.2e−38 → 3.4e38TF32同 FP32BF16同 FP32FP166.1e−5 → 6.5e4FP8 E5M26.1e−5 → 5.7e4FP8 E4M32e−3 → 448FP6 E3M20.06 → 28FP6 E2M30.13 → 7.5FP4 E2M10.5 → 6BF16 / TF32 与 FP32 共享 8 位指数 · 三条 bar 完全重合 · 这就是 BF16 训练可以无痛替换 FP32 的原因
九种浮点格式的动态范围(对数轴)。三条蓝色 bar(FP32 / TF32 / BF16) 长度一致,因为它们共享 8 位指数;FP16 与 FP8-E5M2 范围接近,都是「重指数」的设计;FP4 的 bar 几乎只是一个点 — 这正是它必须依赖 block-scale 才能用起来的直观证据。

几个值得点出来的观察:

Transformer block 内部的精度分配 — 训练 vs 推理

整个模型从来不是「全部用一种精度」 — Transformer 的每个组件按运算特性各自选精度。下面这张图把训练和推理两种模式的精度分配并排画出来:

Transformer block 内部精度分配 · 训练 vs 推理每个组件按运算类型混用不同精度 — 这是低精度真正难的地方训练 (Training)前向 + 反向 + 优化器推理 (Inference)前向 only · 权重已固定Master Weights + Optimizer StateFP32 · master + Adam m / v · ≈ 4× 参数EmbeddingBF16RMSNormFP32Q / K / V ProjectionFP8 in · FP32 accQ × KᵀFP8 / BF16SoftmaxFP32Attention × VFP8 / BF16Output ProjectionFP8-E4M3+ ResidualFP32 accFFN Linear 1FP8-E4M3SwiGLUBF16 / FP32FFN Linear 2FP8-E4M3+ ResidualFP32 acc… × N 层 …Backward · 梯度 GEMMFP8-E5M2 · 梯度兜底Loss + Loss ScalingFP32Static Weights · 离线量化 · 不再更新INT4 / FP4 + group-wise FP16 scaleEmbeddingBF16RMSNormBF16Q / K / V ProjectionINT4 W · BF16 actQ × KᵀBF16SoftmaxBF16KV cacheFP8 / INT8Attention × VBF16Output ProjectionINT4 / FP4+ ResidualBF16FFN Linear 1INT4 / FP4SwiGLUBF16FFN Linear 2INT4 / FP4+ ResidualBF16… × N 层 …LM Head · 输出 logitsBF16FP32BF16FP8INT8INT4 / FP4
Transformer block 内部的精度分配 — 左列训练、右列推理。训练侧几乎是「两层结构」 — 所有 GEMM 用 BF16 / FP8 输入但在 FP32 下累加,其余对数值敏感的 norm / softmax / residual 全部 FP32;再加上必须保留的 FP32 master weights 和 Adam 优化器状态(BF16 + FP32 master + Adam m + Adam v ≈ 14 字节/参数)。推理侧走向另一个极端 — 权重压到 INT4 / FP4,激活仍是 BF16,KV cache 单独压到 FP8 / INT8。

几个非显然的细节:

微缩格式 MX — 让 FP4 / FP6 真正能用的关键

前面铺过的对数轴上,FP4 / FP6 的范围短到几乎是个点。它们能在硬件上跑起来,根本不在格式本身,而在它们外挂的 block scaling

OCP 的 MX 规范定义了一个非常简单的结构 — 每 32 个数共享一个 8 位的指数 scale:[1]

块大小: 32 元素
每元素:  FP4 (4 bit) 或 FP6 (6 bit) 或 FP8 (8 bit)
块 scale: E8M0 (8 bit, 纯指数, 无符号无尾数)

E8M0 是个特殊的「纯指数」格式 — 8 位全部用来存指数,没有符号也没有尾数,表示的就是 2E2^E 这个缩放因子。块内 32 个 FP4 数的实际数值 = FP4_value×2E\text{FP4\_value} \times 2^E

为什么是 32 元素一组、为什么 scale 用 E8M0?这是工程权衡 — 块越小精度越高但 metadata 占用越大;scale 用纯指数可以让乘法在硬件上变成「移位」,完全免费。Blackwell 的 FP4 Tensor Core 之所以能直接跑这个格式,就是因为硬件层面内置了这套块级 scale 解码电路。

NVIDIA 又在 MXFP4 基础上做了一个更激进的版本 NVFP4 — 块大小缩到 16、块 scale 换成 FP8(E4M3)、外面再套一层 FP32 per-tensor scale。三层 scale 结构让它的有效精度比 MXFP4 明显更好,代价是 metadata 多占一点空间。Blackwell 上 NVFP4 已经被实测能让推理精度接近 FP8,这是 FP4 真正变成「可用格式」的关键证据。[3][12]

硬件支持矩阵 — V100 → Rubin · TPU · MI300

低精度真正能「飞起来」的根本原因是硬件加速 — 没有 Tensor Core / 矩阵引擎的原生支持,低精度只是节省内存,但算不快。[4] 下面这张矩阵把主流加速器按代际列出来,看每种格式从哪一代开始原生支持:

硬件支持矩阵 · 形式 × 代际实心绿 = 原生 Tensor Core / 矩阵引擎支持 · 半透明 = 仍支持但非主推 · 空圈 = 不支持NVIDIAAMDGOOGLE TPUV1002017T42018A1002020H1002022B2002024R1002026MI3002023MI3502025TPUv42021TPUv52024FP32IEEE 754TF32NVIDIAFP16IEEE 754BF16Google BrainFP8E4M3 / E5M2FP6MX 微缩FP4MXFP4 / NVFP4INT8定点INT4定点原生 Tensor Core支持但非主推不支持
主流加速器对各种低精度格式的硬件支持矩阵。绿色对角线从 FP16(2017 全员支持)一路下降到 FP4(2024 才出现于 Blackwell),大约每两年下移一格 — 对应「位宽每两年减半」的硬件节奏。FP32 / FP16 / INT8 是三个全员支持的「栈底」,越往下走越锁定到特定厂商和代际。Google TPU 一行有趣 — 跳过了 FP8 / FP4 这条线,坚持 BF16 + INT8 这套保守组合。

几个值得点出的模式:

Rubin — 没有新格式 · 把 FP4 拉到 50 PFLOPS

英伟达 2024 年 GTC 上公布的下一代架构 Rubin(2026 年量产)有几个值得记一下的判断:

主流大模型的精度选择 — Llama 4 · DeepSeek-V3 · Gemini · Claude

把目前主流大模型的训练 / 推理精度放在一张表里:

主流大模型的训练与推理精度选择 · 2024-2026封闭模型的训练精度按硬件代际推测 · 开源模型有明确技术报告训练 (Training)服务端推理本地 / 量化推理Llama 2Meta · 2023BF16BF16 / FP16INT4 GPTQLlama 3 / 3.1Meta · 2024BF16 · 16K H100BF16 / FP8INT4 GPTQ / AWQLlama 4Meta · 2025FP8 · 32K H100FP8FP4 / INT4DeepSeek-V2DeepSeek · 2024BF16BF16 / FP8INT4DeepSeek-V3DeepSeek · 2024FP8 fine-grainedFP8INT4 / FP4Qwen 2.5Alibaba · 2024BF16BF16 / FP8INT4 AWQMixtral 8x7BMistral · 2024BF16BF16 / FP8INT4Gemini 2Google · 2024BF16 (TPU)BF16GPT-4OpenAI · 2023BF16 (推测)FP8 (推测)Claude 3 / 4Anthropic · 2024BF16 (推测)未公开BF16FP8INT4 / FP4未公开 / N/A
2024-2026 主流大模型的训练与推理精度选择。绿色 = BF16,橙色 = FP8,红色 = INT4 / FP4。可以看到一条清晰的迁移路径 — Llama 系列从 Llama 3 的 BF16 训练切到 Llama 4 的 FP8 训练;DeepSeek-V2 还在 BF16,V3 已经做到了 FP8 fine-grained 训练。Gemini 因为绑死 TPU 一直在 BF16。OpenAI / Anthropic 不公开训练精度,推测仍以 BF16 为主。

几个值得展开的观察:

总结 — 训练 BF16→FP8 · 推理 FP8→FP4

如果用一句话概括目前 LLM 数据格式的趋势:训练在 BF16 → FP8 的路上,推理在 FP8 → FP4 的路上,而每一步落地都依赖一代新硬件的 Tensor Core 支持

把全文拎成几条:

下一篇可以继续深挖的几个方向 — DeepSeek-V3 的 FP8 fine-grained scaling 工程细节、Blackwell 上 NVFP4 vs MXFP4 在不同 workload 上的精度对比、TPU 坚持 BF16 路线背后的「软硬协同」哲学。

参考资料 — 标准 · 论文 · 工程博客

标准与白皮书

论文 — 训练侧

论文 — 推理量化

工程博客与文档