如果你也和我一样,每天第一件事是打开 nvidia-smi 看显存和功耗,第二件事是翻训练日志里每秒蹦出来的 token 数,那么这篇文章你应该能直接用上。做 LLM 训练和推理的人这几年都有同一个感受:token 就是钱,算力就是命。模型每吞一个 token,背后都是实打实的前向计算、反向传播、显存读写和排队等待;而混合精度训练——FP16 配合 TF32——恰恰是在这些环节里压缩成本最有效的组合拳之一。
这不是什么新概念,但我在实际给团队搭训练管线、帮别人调模型的时候发现,绝大多数人只停留在"有个 FP16 能省显存"的层面,对 TF32 的原理、两者的适用边界、以及为什么开了之后训练会突然变 NaN,基本是一头雾水。所以这篇不只是列步骤,我会把底层机制、完整配置、踩坑排查和实测数据全部摊开,尽量让看的人少走弯路。适合正在做大模型训练、微调 LoRA、或者被高昂 token 成本压得喘不过气的工程师参考。
1. 先把账算明白:token消耗、算力占用和精度之间的三角关系
很多人在还没搞清楚自己瓶颈在哪的时候,就急着开 FP16,结果要么收益不明显,要么训练直接崩掉。我建议动手之前先把这个三角关系捋清楚:精度影响的是什么,token 消耗和算力占用又是从哪来的。
1.1 一个 token 从进入模型到离开,到底烧掉了什么
拿一个 decoder-only 的大模型举例。训练时每个 token 要经历前向传播和反向传播:前向的计算量大约是 2 倍参数量(单位 FLOPs),反向传播大约是前向的 2 倍,也就是 4 倍参数量,所以训练一个 token 的总计算量约等于 6 倍参数量 FLOPs。
以 7B 模型来算,每个 token 训练大概要烧 42 GFLOPs。听起来不多,但一个 batch 处理 4 万 token,就是 1680 TFLOPs。这时候 GPU 的算力就变成了硬约束:
- A100 的 FP32 算力大概 19.5 TFLOPS,处理这批数据要 86 秒;
- 同样的卡,FP16 配合 Tensor Core 能跑到 312 TFLOPS,只要 5.4 秒。
这就是为什么说"精度模式直接决定每秒钟能消化多少 token"。算力利用率上去了,单位时间内能处理的 token 数量才能上去,单 token 的成本才会降下来。
1.2 算力占用的本质是"每 token 的单位成本"和显存配额
除了计算时间,显存同样重要。模型权重、梯度、优化器状态、中间激活值,每一块都在占显存。显存一旦爆掉,要么降低 batch size,要么用更短的序列长度,这两种操作都会直接拉低 token 吞吐量。
看一组最朴素的对比:7B 模型全参数训练,模型权重本身 FP32 要占 28GB,FP16 只要 14GB。当你只有一张 80GB 的卡,省下来这 14GB 意味着可以塞进更大的 batch、更长的序列,或者为激活值留出更多空间,这些都是实打实的 token 吞吐量。更关键的是,显存减半之后,梯度同步和参数更新的通信量也会跟着下降,这在多卡训练里收益会被进一步放大。
1.3 一张卡上的精度模式差异到底有多大
我给了一张 A100 80GB 上不同精度模式的对照表,这是理解后面所有操作的基础:
| 精度模式 | 7B 模型权重显存 | 主要算力规格 | 特点 |
|---|---|---|---|
| FP32 | 28GB | 19.5 TFLOPS | 动态范围和精度都最稳,但最慢 |
| TF32 | 28GB(权重仍存 32 位) | 156 TFLOPS(Tensor Core) | 只加速计算,不省显存,精度接近 FP32 |
| FP16 | 14GB | 312 TFLOPS(Tensor Core) | 计算和显存双降,需要额外维护数值稳定 |
很多人会把 TF32 误解成"半精度",其实完全不同。TF32 是 Tensor Core 上的一个计算模式,权重在内存里还是 32 位存储,只是做矩阵乘的时候把尾数截断,用硬件加速;FP16 则是从存储到计算全都减半。搞清楚这个区别,你才能知道自己到底该开哪个开关。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. FP16和TF32的底层原理:不是简单"砍一半精度"那么粗暴
我见过太多人把 FP16 理解成"数字变小了所以省内存",这个说法只对了一半。要想用好混合精度,至少得知道这两种格式到底动了哪几位。
2.1 FP16的动态范围和精度:为什么训练时梯度容易"消失"
浮点数由三部分组成:1 位符号位、指数位和尾数位。FP32 是 1 位符号 + 8 位指数 + 23 位尾数;FP16 是 1 位符号 + 5 位指数 + 10 位尾数。指数位从 8 位变成 5 位,意味着能表示的最大值从大约 3.4e38 掉到了 65504,最小正常值从 1.18e-38 涨到了 6.1e-5。
问题就出在这。深度
