cover

为什么相同参数量的模型,显存占用差异巨大?

引子:27B 模型的显存之谜

同样是一个 27B(270 亿参数)的大模型,在不同精度下部署时显存占用差异巨大:

张量类型每参数字节仅权重显存含推理开销估算
FP16 / BF162 字节~54 GB~60 GB
FP81 字节~27 GB~30 GB
FP324 字节~108 GB~120 GB

通俗类比

这和摄影中 RAW 与 JPG 的区别如出一辙:

  • RAW 格式 — 无损,精度拉满,但文件巨大,加载慢
  • JPG 格式 — 牺牲了肉眼几乎察觉不到的细节,换来体积骤减,加载飞快

大模型之所以能用低精度,核心前提是它天生的参数冗余性——在成千上亿的参数中,绝大多数参数的微小数值变化对最终输出的影响几乎微乎其微。这让我们能够安全地压缩参数精度。


计算机底层:数字怎么表示

任何数据的本质都是数字,而计算机用二进制来处理数字。一个浮点数在计算机中的表示分为三段:

┌──────────┬──────────────┬──────────────────┐
│ 符号位    │   指数位      │     尾数位        │
│ (Sign)   │ (Exponent)   │  (Mantissa)      │
│ 1 bit    │  决定范围     │   决定精度        │
└──────────┴──────────────┴──────────────────┘
  • 符号位 — 1 bit,标记正负
  • 指数位 — 决定数值能表示的大小范围(是 1000 还是 10000)。大模型最怕这里不够用导致数值溢出
  • 尾数位 — 决定精细程度,即小数点后能保留多少细节

关键洞察: 大模型对尾数精度的敏感度远低于指数位。这就是为什么 BF16(尾数只有 7 bit)能替代 FP32(尾数 23 bit)而性能几乎不降。


五种主流浮点型张量详解

FP32 — 传统单精度浮点数

结构:1 bit 符号位 + 8 bit 指数位 + 23 bit 尾数位 = 32 bit (4 字节)
维度评价
精度⭐⭐⭐⭐⭐ 最高
显存占用⭐ 最大(7B 模型仅权重需 ~28 GB)
计算速度⭐ 最慢
动态范围⭐⭐⭐⭐⭐ 几乎拉满

典型用途: 梯度更新、优化器状态存储等关键环节,避免因精度损失导致梯度消失或爆炸。

一句话总结: 原画无损格式,什么都好,就是太占空间。


FP16 — 传统半精度浮点数

结构:1 bit 符号位 + 5 bit 指数位 + 10 bit 尾数位 = 16 bit (2 字节)
维度评价
精度⭐⭐⭐ 中等
显存占用⭐⭐⭐⭐ 比 FP32 减半
计算速度⭐⭐⭐⭐ 快
动态范围⭐⭐ 最大仅 ±65,504

致命缺陷: 指数位只有 5 bit,数值范围太小。大模型的激活值和梯度很容易超出这个范围,直接溢出导致训练崩溃。需要靠损失缩放(Loss Scaling)来补救,麻烦且不稳定。

一句话总结: 简单粗暴砍一半体积,但训练容易溢出,现在很少用了。


BF16 — Brain Float 16(Google 制定)

结构: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 bit 符号位 + 8 bit 指数位 + 10 bit 尾数位 ≈ 19 bit 有效信息
维度评价
精度⭐⭐⭐⭐ 介于 BF16 和 FP32 之间
显存占用⭐ 不省显存(存储格式与 FP32 相同,4 字节)
计算速度⭐⭐⭐⭐⭐ 在 Tensor Core 上加速
动态范围⭐⭐⭐⭐⭐ 与 FP32 同级

定位特殊: TF32 不是为了省显存,而是为了让原本用 FP32 训练的模型几乎不用改代码也能提速。

工作原理: 当 FP32 数据进入 Tensor Core 做矩阵乘法时,硬件会先按 TF32 规则对尾数精度进行截断/舍入,然后参与乘法运算,而累加仍保持 FP32 精度。

FP32 输入 → Tensor Core → 截断为 TF32 尾数 → 矩阵乘法 → FP32 累加输出

一句话总结: 表面上走 FP32 流程,底层偷偷提速,不省显存但解决 FP32 跑太慢的问题。


FP8 — 8 位浮点数(最激进压缩)

结构(两种变体):

E4M3:1 bit 符号 + 4 bit 指数 + 3 bit 尾数 = 8 bit (1 字节)
E5M2:1 bit 符号 + 5 bit 指数 + 2 bit 尾数 = 8 bit (1 字节)
维度E4M3E5M2
精度⭐⭐ 尾数更多,细节更好⭐ 尾数更少,精度更粗
显存占用⭐⭐⭐⭐⭐ 最小⭐⭐⭐⭐⭐ 最小
计算速度⭐⭐⭐⭐⭐ 最快⭐⭐⭐⭐⭐ 最快
动态范围⭐⭐ 数值范围小⭐⭐⭐ 数值范围大

工程实践中的经典搭配:

  • 前向传播 → E4M3(更擅长保留细节)
  • 反向梯度 → E5M2(更擅长避免溢出)

一句话总结: 在显存、精度、速度之间做了最激进的平衡。


五种张量类型对比

张量类型总比特数相对 FP32 显存占用核心优势核心劣势核心使用场景
FP3232bit 存储、32bit 计算100%精度高、范围大、最稳定显存占用高、速度慢训练梯度更新、优化器状态,高精度科学计算
TF32计算 19bit / 存储 32bit100%基本不用改代码就能加速 FP32 训练不省显存,本质是计算加速模式FP32 工作流训练加速
BF1616bit 存储/计算,指数位与 FP32 相同50%范围大、训练稳定、显存减半尾数精度低于 FP16/FP32大模型训练主流格式
FP1616bit 存储/计算50%显存减半、吞吐高、生态成熟指数范围小,容易溢出旧方案混合精度训练、部分推理
FP88bit 存储/计算,常见 E4M3 / E5M225%更省显存、吞吐更高精度和范围更紧张,对硬件要求高高端大模型训练、推理加速

取舍逻辑总结

五种张量类型真正解决的只有一个问题:你愿意拿多少精度换多少显存和速度?

类型取舍逻辑适用场景
FP32精度至上,不计代价梯度更新、优化器状态
TF32不改代码的前提下提速FP32 工作流加速
BF16牺牲可忽略的精度,换取显存减半大模型训练主流方案
FP16简单粗暴砍体积推理、早期混合精度
FP8激进压缩,精度/范围需取舍部署量化、极致显存优化

混合精度训练

在训练大模型时,通常会混用多种精度

前向传播:BF16 / FP8        ← 省显存
反向传播:FP32 / FP16       ← 保证梯度精度
权重存储:BF16              ← 节省存储

这就是为什么叫"混合精度"——有的环节用高精度算,有的用低精度算。


实用建议

部署阶段

  • 显存充足 → 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)

大模型张量数据类型详解
转载前请阅读本站 版权协议,文章著作权归 饼铛 所有,转载请注明出处。

目录