量化基础:FP16、BF16、INT8、INT4 这些数据类型到底怎么选?
大家好,我是你们的老朋友。今天咱们聊聊量化。
说实话,我刚入行那会儿,觉得量化这事儿挺玄乎的。不就是把模型变小点嘛,能跑就行。后来被线上事故教育了几次,才明白——选错数据类型,轻则精度掉点,重则模型直接崩掉。
这一章,我就把 FP16、BF16、INT8、INT4 这几个常见数据类型的底裤扒干净。咱们不搞理论堆砌,直接说人话。
1. 浮点数的基本逻辑:为什么要有这么多格式?
先问个问题:一个 32 位浮点数(FP32),能表示多大范围?
大概 ±3.4×10³⁸,精度大约 7 位有效数字。但问题是,大模型动辄几十亿参数,全用 FP32 存,显存根本扛不住。
所以大家开始想:能不能砍掉一些位数?
嗯,这就是量化的核心思路——用更少的比特,去近似原来的数值。
核心原则: 量化不是魔法,是精度和效率的 trade-off。你省了多少显存,就得接受多少精度损失。
2. FP16 vs BF16:两个 16 位浮点数的恩怨
FP16(半精度)和 BF16(脑浮点)都是 16 位,但内部结构完全不同。
| 特性 | FP16 | BF16 |
|---|---|---|
| 符号位 | 1 bit | 1 bit |
| 指数位 | 5 bits | 8 bits |
| 尾数位 | 10 bits | 7 bits |
| 表示范围 | ±65504 | ±3.4×10³⁸(同 FP32) |
| 精度 | 约 3.3 位有效数字 | 约 2.3 位有效数字 |
看到区别了吗?BF16 保留了 FP32 的指数范围,但牺牲了精度。FP16 精度更高,但范围窄,容易溢出。
我在项目中遇到过一件事:用 FP16 跑一个 LLaMA 模型,训练到一半 loss 突然变成 NaN。查了半天,发现是某个中间激活值超过了 65504。换成 BF16 后,问题立刻消失。
我的建议: 推理场景优先用 BF16。它几乎不会溢出,精度损失也小。FP16 更适合训练,尤其是梯度值比较小的时候。
3. INT8 和 INT4:整数量化的实战选择
整数量化,说白了就是把浮点数映射到整数范围。比如 INT8 是 [-128, 127],INT4 是 [-8, 7]。
映射过程很简单:
# 伪代码示意
scale = (max_val - min_val) / 255
quantized_val = round((float_val - min_val) / scale) - 128
但这里有个坑:校准数据集的选择。如果你用了一堆不具代表性的数据去算 scale,量化后的模型可能在某些输入上表现极差。
我曾经踩过这个坑。当时给一个对话模型做 INT8 量化,测试集上精度只掉了 0.5%,感觉稳了。结果上线后,用户反馈某些长对话回复质量明显下降。后来发现,校准数据里缺少长文本样本,导致 scale 没覆盖到极端情况。
避坑指南: 校准数据集一定要覆盖真实场景的分布。别偷懒,拿几百条数据就完事。至少准备几千条,而且要包含边界情况。
4. 四种数据类型的选型决策树
你可能会问:那我到底该用哪个?
我一般按这个逻辑来选:
- 显存够用? 直接用 FP16 或 BF16,省心。
- 显存紧张? 上 INT8,精度损失通常可控。
- 显存爆炸? 考虑 INT4,但要做好精度下降的心理准备。
- 对精度极度敏感? 别碰 INT4,老老实实用 BF16。
另外,不同层对量化的敏感度不一样。比如 attention 层的 QKV 投影,量化后精度掉得厉害;而 FFN 层相对鲁棒。所以现在很多框架支持混合精度量化——敏感层用 FP16,其他层用 INT8。
下面这张图是我自己总结的量化选型流程,你参考下:
5. 量化后的精度验证:别信 benchmark,信自己的测试
最后说一句:量化后的模型,一定要用你的业务数据做端到端测试。
我见过太多人只看公开 benchmark 的指标,觉得掉点不多就上线了。结果呢?业务场景里某些 case 直接崩了。
为什么?因为 benchmark 数据集和你的真实数据分布不一样。比如对话模型,量化后可能对长尾词汇的生成质量下降明显,但 benchmark 里这些词出现频率低,看不出来。
我的习惯: 量化后至少跑 1000 条真实业务数据,人工抽检 100 条。如果发现 5 条以上有明显质量下降,就考虑回退到更高精度的格式。
好了,这一章就聊到这儿。量化这事儿,说白了就是「用空间换时间,用精度换效率」。选对了,事半功倍;选错了,debug 到天亮。