> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/llm_internals/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/llm_internals/di-er-bu-fen-xun-lian-pian/07_distributed_training/7.6_mixed_precision.md).

# 7.6 混合精度训练：精度与速度的权衡

**混合精度训练**（Mixed Precision Training）使用半精度浮点数（FP16 或 BF16）代替全精度 FP32 进行大部分计算，是现代大语言模型训练的标配技术。7.5 把激活值的安置摆成了三选一——留在显存、重计算、卸载到主机内存——三条路争的都是「哪些数该留下」，每个数自身的位宽一位没动。本节换的正是位宽这条轴：数还是那些数。

## 7.6.1 为什么使用半精度

FP32 使用 32 位存储一个浮点数，FP16 只需 16 位。半精度带来两个直接好处：

**显存减半**：同样大小的模型参数和激活值只需一半的显存，允许训练更大的模型或使用更大的批次。

**计算加倍**：现代 GPU（如 NVIDIA A100、H100）的 FP16/BF16 吞吐量是 FP32 的 2 倍以上。Tensor Core 针对半精度矩阵运算进行了专门优化。

## 7.6.2 精度问题与解决方案

直接使用 FP16 训练会遇到精度不足的问题：

**梯度下溢**：FP16 能表示的最小正数约为 $$6 \times 10^{-8}$$，小于此值的梯度会变为零（下溢），导致无法学习。**损失缩放**（Loss Scaling）是标准的解决方案：在计算反向传播之前将损失值乘以一个大常数（如 1024 或自动调整），放大梯度以避免下溢，然后在参数更新前将梯度除以同样的常数恢复原始尺度。

**参数更新精度**：微小的权重更新可能在 FP16 下被舍入为零。因此，混合精度训练维护一份**FP32 的主权重副本**，参数更新在 FP32 下进行，然后将更新后的参数截断为 FP16 用于下一轮前向传播。

## 7.6.3 BF16 的优势

**BF16**（Brain Floating Point 16）与 FP16 使用相同的 16 位，但分配了更多的位给指数部分（8 位指数 vs FP16 的 5 位），从而拥有与 FP32 相同的**数值范围**。代价是精度略低（7 位尾数 vs FP16 的 10 位）。

BF16 的关键优势是：**不容易出现梯度下溢问题**，因此通常不需要损失缩放。这简化了训练流程，减少了调试难度。如今，支持 BF16 的硬件（A100 及以后）上的大模型训练几乎都使用 BF16。

## 7.6.4 FP8 的前沿探索

NVIDIA H100 和 H200 GPU 引入了 FP8 精度支持（8 位浮点数），可进一步提升吞吐量。FP8 常见 E4M3 和 E5M2 两种编码：前者保留 3 位尾数、精度更高，后者保留 2 位尾数、动态范围更大，因此需要更精细的量化策略：

* **Per-tensor 动态缩放**：与 FP16 的单一损失缩放不同，FP8 通常需要为每个张量（激活值、梯度、权重）单独维护缩放因子，根据其数值范围动态调整。
* **数值范围处理**：由于精度极限，过小的梯度可能下溢为零，过大的值可能溢出为 inf/NaN 或饱和，需要更激进的缩放策略、amax 跟踪和异常检测。
* **动态缩放与 amax 跟踪**：FP8 训练通常维护每个张量或块的缩放元数据，根据当前或历史 amax 动态调整；分布式张量还需要在 rank 间同步 amax。某些推理或离线量化流程会做校准，但这不是 Transformer Engine 式 FP8 训练的唯一机制。

DeepSeek-V3 等前沿模型已开始探索 FP8 训练，通过**分组 FP8**（将权重矩阵按行或块分组，每组使用独立的缩放因子）在保持数值稳定性的同时实现了显著的效率提升。

还有一个比缩放策略更隐蔽的坑：**低精度 GEMM 的准确性主要取决于累加精度，而 Tensor Core 的累加精度未必够**。[DeepSeek-V3 技术报告实测发现](https://arxiv.org/abs/2412.19437)，H800 上 FP8 GEMM 的累加精度只保留约 **14 位**，远低于 FP32 累加；内积维度 $$K$$ 越大，误差累积越明显——用两个随机矩阵在 $$K = 4096$$ 下测试，Tensor Core 有限的累加精度带来了接近 **2%** 的最大相对误差。这个量级足以毁掉训练，而它与你怎么设缩放因子无关。

他们的对策是**周期性提升累加精度**：每累加 $$N\_C = 128$$ 个元素的 MMA 就把中间结果提升到 CUDA Core 上做一次高精度累加，再继续。这是一个典型的「在硬件行为与数值需求之间打补丁」的做法——也提示了排查低精度训练问题时的一个检查项：**在怀疑缩放策略之前，先确认累加是在哪里、以什么精度做的**。

## 7.6.5 4 比特预训练：NVFP4

精度压缩的下一站是 4 比特。这里首先要分清两件常被混为一谈的事：**推理侧的 4 比特量化**（把训练好的权重压到 4 位再部署，见 [10.4 节](/llm_internals/di-san-bu-fen-tui-li-yu-bu-shu-pian/10_inference_optimization/10.4_quantization.md)）和**训练侧的 4 比特预训练**（前向、反向、梯度本身就在 4 位算）。后者难得多，直到 2025 年下半年才出现可信的大规模证据。

需要区分的还有格式本身：**NVFP4** 是 NVIDIA 定义、Blackwell 原生支持的 4 位浮点格式，与 MXFP4 或泛称的“FP4”并不等同；当前有据可查的大规模 4 比特预训练几乎都是 NVFP4。

NVIDIA 在 [Pretraining Large Language Models with NVFP4](https://arxiv.org/abs/2509.25149) 中给出了第一个关键证据：用 NVFP4 训练一个 120 亿参数模型、消耗 10 万亿词元，论文称这是当时**公开记录中最长的 4 比特精度训练运行**，其训练损失与下游任务准确率与 FP8 基线相当（例如 MMLU-Pro 为 62.58% 对 62.62%）。它靠四件事把 4 位训稳：

* **随机 Hadamard 变换**（RHT）：抑制块内离群值，让数值分布更适合极低位宽
* **二维量化方案**：保证前向与反向传播使用一致的表示
* **随机舍入**：获得无偏的梯度估计，避免确定性舍入累积系统性偏差
* **选择性高精度层**：一部分敏感层不做 4 位量化

之后 NVIDIA 的 Nemotron 3 系列把它推到了前沿规模：Super（120B 总参 / 12B 激活）用 NVFP4 完成了 25T 词元量级的预训练，Ultra（550B / 55B 激活）沿用同一 NVFP4 配方，预训练规模为 20T 词元。Ultra 的做法清楚地展示了“选择性高精度”在实践中意味着什么——网络最后 15%（16 层）、Mamba 输出投影、latent 投影、QKV 与注意力投影、MTP 层和嵌入层都保留在更高精度，其余大部分线性层才走 NVFP4。

这里有两个必须诚实交代的口径。第一，**基线是 FP8 而非 BF16**，官方措辞是“与 FP8 基线相当”（comparable），不是“无损”——实测确实存在可测量的差距。第二，**不要把 DeepSeek-V4 当作 FP4 预训练的例子**：它的 FP4 用在后训练的量化感知训练（MoE 专家权重与 indexer 的 QK 路径）和推理阶段，其大规模预训练框架仍是 FP8。这正是本节开头那条区分的现实意义。
