
大模型张量数据类型详解
为什么相同参数量的模型,显存占用差异巨大?
引子:27B 模型的显存之谜
同样是一个 27B(270 亿参数)的大模型,在不同精度下部署时显存占用差异巨大:
| 张量类型 | 每参数字节 | 仅权重显存 | 含推理开销估算 |
|---|---|---|---|
| FP16 / BF16 | 2 字节 | ~54 GB | ~60 GB |
| FP8 | 1 字节 | ~27 GB | ~30 GB |
| FP32 | 4 字节 | ~108 GB | ~120 GB |
通俗类比
这和摄影中 RAW 与 JPG 的区别如出一辙:
- RAW 格式 — 无损,精度拉满,但文件巨大,加载慢
- JPG 格式 — 牺牲了肉眼几乎察觉不到的细节,换来体积骤减,加载飞快
大模型之所以能用低精度,核心前提是它天生的参数冗余性——在成千上亿的参数中,绝大多数参数的微小数值变化对最终输出的影响几乎微乎其微。这让我们能够安全地压缩参数精度。
计算机底层:数字怎么表示
任何数据的本质都是数字,而计算机用二进制来处理数字。一个浮点数在计算机中的表示分为三段:
1 | ┌──────────┬──────────────┬──────────────────┐ |
- 符号位 — 1 bit,标记正负
- 指数位 — 决定数值能表示的大小范围(是 1000 还是 10000)。大模型最怕这里不够用导致数值溢出
- 尾数位 — 决定精细程度,即小数点后能保留多少细节
关键洞察: 大模型对尾数精度的敏感度远低于指数位。这就是为什么 BF16(尾数只有 7 bit)能替代 FP32(尾数 23 bit)而性能几乎不降。
五种主流浮点型张量详解
FP32 — 传统单精度浮点数
1 | 结构:1 bit 符号位 + 8 bit 指数位 + 23 bit 尾数位 = 32 bit (4 字节) |
| 维度 | 评价 |
|---|---|
| 精度 | ⭐⭐⭐⭐⭐ 最高 |
| 显存占用 | ⭐ 最大(7B 模型仅权重需 ~28 GB) |
| 计算速度 | ⭐ 最慢 |
| 动态范围 | ⭐⭐⭐⭐⭐ 几乎拉满 |
典型用途: 梯度更新、优化器状态存储等关键环节,避免因精度损失导致梯度消失或爆炸。
一句话总结: 原画无损格式,什么都好,就是太占空间。
FP16 — 传统半精度浮点数
1 | 结构:1 bit 符号位 + 5 bit 指数位 + 10 bit 尾数位 = 16 bit (2 字节) |
| 维度 | 评价 |
|---|---|
| 精度 | ⭐⭐⭐ 中等 |
| 显存占用 | ⭐⭐⭐⭐ 比 FP32 减半 |
| 计算速度 | ⭐⭐⭐⭐ 快 |
| 动态范围 | ⭐⭐ 最大仅 ±65,504 |
致命缺陷: 指数位只有 5 bit,数值范围太小。大模型的激活值和梯度很容易超出这个范围,直接溢出导致训练崩溃。需要靠损失缩放(Loss Scaling)来补救,麻烦且不稳定。
一句话总结: 简单粗暴砍一半体积,但训练容易溢出,现在很少用了。
BF16 — Brain Float 16(Google 制定)
1 | 结构:1 bit 符号位 + 8 bit 指数位 + 7 bit 尾数位 = 16 bit (2 字节) |
| 维度 | 评价 |
|---|---|
| 精度 | ⭐⭐⭐ 略低于 FP16(尾数少 3 bit) |
| 显存占用 | ⭐⭐⭐⭐ 同 FP16,2 字节 |
| 计算速度 | ⭐⭐⭐⭐ 快 |
| 动态范围 | ⭐⭐⭐⭐⭐ 与 FP32 同级 |
核心优势: 继承了 FP32 的 8 bit 指数位,动态范围与 FP32 一致。虽然尾数从 23 bit 降到 7 bit,但大模型对尾数精度并不敏感,这点损失几乎感觉不到。
一句话总结: 为大模型量身定做的半精度格式,现在绝大多数大模型训练的标准。
TF32 — Tensor Float 32(NVIDIA Ampere 架构引入)
1 | 结构:1 bit 符号位 + 8 bit 指数位 + 10 bit 尾数位 ≈ 19 bit 有效信息 |
| 维度 | 评价 |
|---|---|
| 精度 | ⭐⭐⭐⭐ 介于 BF16 和 FP32 之间 |
| 显存占用 | ⭐ 不省显存(存储格式与 FP32 相同,4 字节) |
| 计算速度 | ⭐⭐⭐⭐⭐ 在 Tensor Core 上加速 |
| 动态范围 | ⭐⭐⭐⭐⭐ 与 FP32 同级 |
定位特殊: TF32 不是为了省显存,而是为了让原本用 FP32 训练的模型几乎不用改代码也能提速。
工作原理: 当 FP32 数据进入 Tensor Core 做矩阵乘法时,硬件会先按 TF32 规则对尾数精度进行截断/舍入,然后参与乘法运算,而累加仍保持 FP32 精度。
1 | FP32 输入 → Tensor Core → 截断为 TF32 尾数 → 矩阵乘法 → FP32 累加输出 |
一句话总结: 表面上走 FP32 流程,底层偷偷提速,不省显存但解决 FP32 跑太慢的问题。
FP8 — 8 位浮点数(最激进压缩)
1 | 结构(两种变体): |
| 维度 | E4M3 | E5M2 |
|---|---|---|
| 精度 | ⭐⭐ 尾数更多,细节更好 | ⭐ 尾数更少,精度更粗 |
| 显存占用 | ⭐⭐⭐⭐⭐ 最小 | ⭐⭐⭐⭐⭐ 最小 |
| 计算速度 | ⭐⭐⭐⭐⭐ 最快 | ⭐⭐⭐⭐⭐ 最快 |
| 动态范围 | ⭐⭐ 数值范围小 | ⭐⭐⭐ 数值范围大 |
工程实践中的经典搭配:
- 前向传播 → E4M3(更擅长保留细节)
- 反向梯度 → E5M2(更擅长避免溢出)
一句话总结: 在显存、精度、速度之间做了最激进的平衡。
五种张量类型对比
| 张量类型 | 总比特数 | 相对 FP32 显存占用 | 核心优势 | 核心劣势 | 核心使用场景 |
|---|---|---|---|---|---|
| FP32 | 32bit 存储、32bit 计算 | 100% | 精度高、范围大、最稳定 | 显存占用高、速度慢 | 训练梯度更新、优化器状态,高精度科学计算 |
| TF32 | 计算 19bit / 存储 32bit | 100% | 基本不用改代码就能加速 FP32 训练 | 不省显存,本质是计算加速模式 | FP32 工作流训练加速 |
| BF16 | 16bit 存储/计算,指数位与 FP32 相同 | 50% | 范围大、训练稳定、显存减半 | 尾数精度低于 FP16/FP32 | 大模型训练主流格式 |
| FP16 | 16bit 存储/计算 | 50% | 显存减半、吞吐高、生态成熟 | 指数范围小,容易溢出 | 旧方案混合精度训练、部分推理 |
| FP8 | 8bit 存储/计算,常见 E4M3 / E5M2 | 25% | 更省显存、吞吐更高 | 精度和范围更紧张,对硬件要求高 | 高端大模型训练、推理加速 |
取舍逻辑总结
五种张量类型真正解决的只有一个问题:你愿意拿多少精度换多少显存和速度?
| 类型 | 取舍逻辑 | 适用场景 |
|---|---|---|
| FP32 | 精度至上,不计代价 | 梯度更新、优化器状态 |
| TF32 | 不改代码的前提下提速 | FP32 工作流加速 |
| BF16 | 牺牲可忽略的精度,换取显存减半 | 大模型训练主流方案 |
| FP16 | 简单粗暴砍体积 | 推理、早期混合精度 |
| FP8 | 激进压缩,精度/范围需取舍 | 部署量化、极致显存优化 |
混合精度训练
在训练大模型时,通常会混用多种精度:
1 | 前向传播:BF16 / FP8 ← 省显存 |
这就是为什么叫”混合精度”——有的环节用高精度算,有的用低精度算。
实用建议
部署阶段
- 显存充足 → FP16 或 BF16 推理即可
- 显存紧张 → 寻找 FP8 量化版本(显存减半)
- 消费级显卡 → 优先 BF16(兼容性好,RTX 30/40 系均支持)或 FP8(需 Hopper 架构)
训练阶段
- 标准方案 → BF16 混合精度训练(当前主流)
- Ampere+ GPU(RTX 30 系 / A100)→ 开启 TF32 加速(无需改代码,框架默认开启)
- Hopper+ GPU(H100)→ 考虑 FP8 训练(Transformer Engine,最新方案)
参考
- IEEE 754 浮点数标准
- NVIDIA Tensor Core 文档
- Google Brain: BFloat16 论文
- NVIDIA FP8 格式规范(OCP Microscaling Format)