第 7 章 量化:把每个数字压小,代价是什么
前六章反复出现两个数:权重 14 GB,每 token 缓存 128 KiB。这两个数都等于参数量乘以字节数。字节数是唯一可以不动模型结构就改掉的因子,量化改的就是它。
道理很直白:每个参数从两个字节压到一个,权重减半,每步要搬的字节减半,逐字生成的时间也接近减半。但真实的量化不是"把数字改小"这么简单,压缩会引入误差,误差会沿着层数累积,最后体现在输出质量上。这一章讲压缩怎么做、哪里能压、哪里不能压,以及怎么判断压坏了没有。
量化在做什么
一个浮点数由符号、指数、尾数三部分组成。量化做的事情是换一种表示:用更少的位存近似值,同时保存一个缩放因子,读出来时再乘回去。
举个最小的例子。一组权重原来的范围是负 0.8 到正 0.8,用 FP16 存。现在改成 8 位整数,能表示负 128 到正 127。把原来的数除以一个缩放因子,比如 0.0063,映射到整数范围,存下来;计算时再乘回去。缩放因子保证了动态范围,误差来自整数只有 256 个档位。
浮点量化比整数量化更常用,因为浮点自带指数,能表示的动态范围大得多。大模型权重里有一小部分数值特别大,叫离群值;整数格式遇到离群值,要么把范围撑大导致其余数值精度全丢,要么直接截断。浮点格式天生适合处理这种分布。
为什么有效
量化同时改善两个阶段,原因不同。
预填充是算力受限的,而张量核心在低精度下的算力更高。Hopper 上 FP8 的算力是 FP16 的两倍,Blackwell 上 FP4 又是 FP8 的两倍。同样的矩阵乘,精度降一级,时间减半。
逐字生成是带宽受限的,权重减半,每步读的字节减半,时间也减半。
Inference Engineering 给的实践数字是:每降一级精度,LLM 的性能提升大约百分之三十到五十。不到两倍,因为除了权重读取,还有激活、缓存、kernel 启动这些固定开销;也因为有反量化和类型转换的额外工作。
有一个容易忽略的前提:算力翻倍只在支持该精度的硬件上成立。A100 没有 FP8 张量核心,把权重压到 FP8 只能省带宽,预填充还要转换回 FP16 再算,算力一点没多。所以同一份量化方案,在 A100 和 H100 上的收益完全不同。
格式谱系
| 格式 | 位数 | 首次出现的架构 | 用途 |
|---|---|---|---|
| FP16 | 16 | Pascal,2016 | 推理默认 |
| BF16 | 16 | Ampere,2020 | 训练与推理通用 |
| FP8 | 8 | Hopper,2022 | 质量与速度的平衡点 |
| MXFP8 | 8 | Blackwell,2024 | 微缩放,精度更好 |
| FP4 | 4 | Blackwell,2024 | 激进压缩 |
| NVFP4 | 4 | Blackwell,2024 | 块大小 16,FP4 里精度最好 |
FP8 有两种变体,E4M3 尾数长、范围小,E5M2 范围大、精度低,推理通常用前者。MXFP8 和 NVFP4 的共同思路是"分块缩放":不是整个张量共用一个缩放因子,而是每几十个元素一组,各自缩放。这样离群值只影响它所在的那一组,不会拖累全局。
粒度:一组共用几个缩放因子
粒度是量化里最关键的工程取舍。
整个张量一个缩放因子,实现最简单,开销最小,但只要有一个离群值,全张量的精度都会被拉低。按通道缩放,每个输入通道一个因子,精度好很多,开销也可控,这是常见的默认做法。按块缩放,每几十个元素一个因子,精度最好,但缩放因子本身也要存、也要读,开销上来了。
所以"4 比特"不是一个确定的精度。同样是 4 比特,按张量缩放和按块缩放的质量可以差出一个档次。看到某个量化方案宣称的比特数,先问它的粒度和校准方法。
哪里能压,哪里不能
不同部件对误差的敏感度差别很大,从低到高大致是四档。
权重最不敏感。权重是静态的,量化误差不会在推理过程中放大,而且矩阵乘对单个元素的误差有平均效应。所以权重量化最安全,收益也最大。
激活稍敏感。激活是运行时算出来的,分布随输入变化,量化范围要靠校准数据估计;估偏了,整层的输出都会偏。
KV 缓存更敏感。缓存里的误差会沿着生成过程一站一站传下去,第 100 个 token 用的缓存里带着前面 99 步的误差。上下文越长,累积越明显。这也是为什么长上下文场景下,缓存量化要格外小心。
注意力里的 softmax 最敏感。softmax 的输出是一组概率,指数运算会把小的输入差异放大;量化到 8 位以下,注意力分布就变形了。
Inference Engineering 给出的配方是:权重和激活用 FP8,缓存谨慎量化,注意力保持原精度。如果完全不能接受质量损失,除了量化之外的其他手段都是无损的,可以先做那些。
怎么判断压坏了没有
三种方法,从粗到细。
困惑度最简单:拿一段文本,看模型预测下一个词的平均不确定性。量化后困惑度上升超过一点点,就要警惕。它的优点是快,缺点是和实际任务的表现不完全相关。
标准基准更贴近能力:MMLU 测知识,SWE-bench 测代码。看量化前后差多少分。缺点是这些基准本身有噪声,几分之差可能只是采样波动。
自定义评测最可靠:拿你真实业务的一批输入,人工或者用更强的模型打分,比较量化前后的输出。这是唯一能回答"我的场景能不能接受"的方法。
三种方法共同的判断标准是:差异是否落在噪声范围内。如果跑两次同一个模型都能差出两分,那么量化前后差两分就不能说明问题。第 11 章讲评测时会展开这一点。
一次手算
7B 模型,FP16 权重 14 GB,A100,带宽 2039 GB/s。
FP16 时每一步读 14 GB,6.9 毫秒,理论出字约每秒 145 个。
压到 FP8,权重 7 GB,每步 3.4 毫秒,理论出字约每秒 290 个。同时,显存里多出 7 GB 可以给缓存,按每 token 512 KiB 算,能多放一万四千个 token 的上下文,或者多并十几条请求。
再压到 4 比特,权重 3.5 GB,每步 1.7 毫秒,理论出字约每秒 580 个。但 4 比特在 A100 上没有原生算力支持,预填充反而可能变慢,而且质量风险明显上升。
这条曲线的形状是:收益随比特数线性增长,风险却是非线性的。8 比特大多数模型能扛住,4 比特要看模型和任务,再往下基本是专用场景。
校准:量化之前先看数据
量化不是把权重读出来除以一个常数就完事。缩放因子怎么定,取决于这批数值的分布,而分布要靠数据统计出来,这一步叫校准。
校准的做法是拿几百条有代表性的文本,跑一遍模型,记录每一层激活的取值范围。范围定得太宽,精度浪费;定得太窄,超出范围的数值被截断,误差直接爆掉。
校准集的选择很关键。用新闻语料校准,拿去跑代码,激活分布对不上,量化效果就会差一截。所以校准集要覆盖真实负载:多语言、长文本、代码、结构化输出,各放一些。
权重里还有离群值的问题。某些通道的数值特别大,如果整层共用一个缩放因子,这些通道会把其余数值的精度挤掉。常见的处理是保留这些通道用高精度,或者按通道、按块分别缩放——这就是前面说的粒度。
工程上把这类方法叫训练后量化,GPTQ 和 AWQ 是两条代表路线:前者逐层最小化量化误差,后者保护对激活影响大的权重通道。名字不用记,要记的是它们都依赖校准数据,也都需要在下游任务上验证。
什么时候不该量化
三种情况。
质量已经卡在边界上。如果你的任务对输出极其敏感,比如法律条文引用、代码安全审查,省下来的那点成本不值得承担质量风险。
硬件不支持。前面说过,A100 上的 FP8 只省带宽不省算力,如果负载是预填充密集,收益会远低于预期。
瓶颈不在字节上。如果服务卡在排队或者调度,量化只会让单条请求快一点,整体吞吐不变。第 5 章那个判断顺序在这里同样适用:先确认瓶颈,再决定手段。
下一章讲投机解码。它和量化正好互补:量化省的是每一步的成本,投机解码省的是步数。两者都作用在逐字生成这一段,但机制完全不同。