AI AI infra · 推理侧
第 1 章
AI infra · 推理侧

第 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 上的收益完全不同。

格式谱系

格式位数首次出现的架构用途
FP1616Pascal,2016推理默认
BF1616Ampere,2020训练与推理通用
FP88Hopper,2022质量与速度的平衡点
MXFP88Blackwell,2024微缩放,精度更好
FP44Blackwell,2024激进压缩
NVFP44Blackwell,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 章那个判断顺序在这里同样适用:先确认瓶颈,再决定手段。

下一章讲投机解码。它和量化正好互补:量化省的是每一步的成本,投机解码省的是步数。两者都作用在逐字生成这一段,但机制完全不同。