Dutta 等(2024-07,NeurIPS 2024)把 Llama2-70B-chat 用 GPTQ 压成 W4A16,MMLU 准确率只少 0.45 个点,可是有 4.26% 的题对错翻转了。Marchisio 等(2024-07)把 103B 的 Command R+ 压成分组的 4 bit 权重,日语的自动评测平均只掉 1.7%,人工评审在真实提示上看到的却是 16.0%。只看一个平均分,会漏掉量化最常见的伤害方式。验收量化要同时做三件事:在固定文本上比较两个模型下一个 token 的分布(KL 散度和 top-1 一致率);在同一批题上逐题配对比较,看翻转了多少、净变化的区间有多宽;专门去看最先坏的切片(slice),也就是长上下文、非英语、数学推理和长输出。
待审核草稿。 这一篇还没有经过站主审阅,事实、取舍和措辞都可能修改;引用前请回到文中链接的一手资料核对。
配对比较的统计推导(McNemar 检验、配对 bootstrap)在分数涨了,究竟能说明什么里已经算过;评测配置要记下哪些字段,见选 benchmark 之前。这里只讲用在量化上要注意什么。
几把尺子,各量什么
| 尺子 | 怎么算 | 好在哪里 | 看不到什么 |
|---|---|---|---|
| 困惑度(perplexity) | 每个 token 负对数似然的平均,再取指数 | 便宜,对量化敏感 | 少数位置的大错被平均掉;校准(calibration)数据和评测文本重叠时偏乐观 |
| KL 散度、top-1 一致率 | 同一段文本上逐位置比较两个模型的分布 | 直接量“离原模型多远”,不需要标签 | 不知道错在哪类任务,也不分好坏方向 |
| 任务准确率 | 两个模型各报一个分数 | 贴近实际用途 | 净变化小时,看不到对错互换 |
| 逐题翻转(flips) | 原来对现在错,加上原来错现在对,除以总题数 | 直接看行为变了多少 | 要保存两边的逐题结果 |
| 切片分数 | 长上下文、各语言、数学、长输出分开报 | 最先坏的地方 | 题少时区间很宽 |
| 自由生成的成对比较 | 评审模型或人工比较两份回答 | 最接近用户感受 | 贵;评审自身有偏差,见 LLM-as-a-Judge |
困惑度与 KL:平均值藏住了什么
困惑度是
$$ \mathrm{PPL}=\exp\Big(-\frac1N\sum_{t=1}^{N}\log p(x_t\mid x_{<t})\Big). $$
AWQ 报告 Llama-2-7B 从 FP16 的 5.47 变成 INT4-g128 的 5.60,换算成平均每个 token 的负对数似然只多了 $\ln(5.60/5.47)\approx0.0235$ nat。这个数说不出损失集中在哪些位置。
KL 散度量的是同一个位置上两个分布差多远,$p$ 是原模型,$q$ 是量化后的模型:
$$ D_{\mathrm{KL}}(p\,\|\,q)=\sum_{v}p(v)\log\frac{p(v)}{q(v)}. $$
手算两个位置(教学数字,词表(vocabulary)只取 4 个 token):
- 位置 1:$p=[0.60,\ 0.25,\ 0.10,\ 0.05]$,$q=[0.52,\ 0.30,\ 0.12,\ 0.06]$。KL 约 0.0129;最可能的 token 没变。
- 位置 2:$p=[0.40,\ 0.38,\ 0.12,\ 0.10]$,$q=[0.37,\ 0.41,\ 0.12,\ 0.10]$。KL 约 0.0023,不到位置 1 的五分之一,可是前两名换了位置。
贪心解码时,位置 2 会输出另一个 token,后面的每一步都建立在不同的前缀上,整段文本从这里分叉。所以要同时报平均 KL 和 top-1 一致率,并且去看 KL 最大、或者前两名很接近的那些位置。Dutta 等报告,在 MMLU 上 KL 和翻转率的 Spearman 相关系数是 0.981;Llama2-13B 在 WikiText-2 上的 KL,GPTQ W8A16 是 $7.9\times10^{-5}$,GPTQ W4A16 是 0.02。Kurtic 等(2024-11)用贪心解码在 Arena-Hard 提示上比较量化前后的回答,8B 在 FP8、INT8、INT4 三种方案上平均,ROUGE-L 只有 0.46,BERTScore 却有 0.92:措辞变了不少,意思大多还在,所以逐字比较文本没有意义,要比分布和任务分数。
还有一个容易漏的偏差:困惑度要在校准数据之外的文本上测。GPTQ 自己注明,校准数据取自 C4 训练集,所以它报告的 C4 困惑度不完全是零样本结果。用你自己领域的留出文本,而且不要和校准集重叠。
配对比较:同一批题,逐题看
教学例子:1,000 道选择题,原模型和量化模型都跑一遍,逐题对照。
| 量化后答对 | 量化后答错 | |
|---|---|---|
| 原模型答对 | 690 | 30 |
| 原模型答错 | 26 | 254 |
准确率从 72.0% 到 71.6%,净变化 −0.4 个点;翻转率是 $(30+26)/1000=5.6\%$。记丢掉的题数为 $\ell=30$、新答对的为 $g=26$,配对的差值和它的标准误是:
$$ \hat d=\frac{g-\ell}{n},\qquad \widehat{\mathrm{SE}}(\hat d)=\sqrt{\frac{(g+\ell)/n-\hat d^{\,2}}{n}}. $$
代进去,$\hat d=-0.004$,标准误约 0.0075,95% 区间约为 $[-1.87,\ +1.07]$ 个点;精确 McNemar 检验的 p 值约 0.69。1,000 道题分不清“没变”和“掉了 1.5 个点”,而翻转的 56 道题才是要逐个去看的。
from math import comb, sqrt
def paired_report(base_correct, quant_correct):
pairs = list(zip(base_correct, quant_correct))
n = len(pairs)
lost = sum(b and not q for b, q in pairs)
gained = sum(q and not b for b, q in pairs)
d = (gained - lost) / n
se = sqrt(((gained + lost) / n - d * d) / n)
k = gained + lost
tail = sum(comb(k, j) for j in range(min(gained, lost) + 1))
p = min(1.0, 2 * tail / 2 ** k) if k else 1.0
return {"flips": k / n, "diff": d, "ci95": (d - 1.96 * se, d + 1.96 * se), "p": p}
base = [True] * 720 + [False] * 280
quant = [True] * 690 + [False] * 30 + [True] * 26 + [False] * 254
r = paired_report(base, quant)
assert r["flips"] == 0.056 and abs(r["diff"] + 0.004) < 1e-12
assert abs(r["p"] - 0.6889) < 1e-4
公开的测量(Dutta 等,MMLU 5-shot,表 5;翻转只数对→错和错→对,不数错→另一个错):
| 模型 | 量化方案 | 准确率变化(点) | 翻转率 |
|---|---|---|---|
| Llama2-70B-chat | GPTQ W4A16 | −0.45 | 4.26% |
| Llama2-7B-chat | GPTQ W4A16 | −0.58 | 8.26% |
| Llama2-7B-chat | AWQ W4A16 | −0.62 | 7.50% |
| Llama2-7B-chat | SmoothQuant W8A8 | −2.36 | 13.62% |
| Llama2 与 Yi 各尺寸 | GPTQ W8A16 | — | 0.26%–1.40% |
最后一行是 8 位只压权重,翻转少得多。生成类任务如果用采样解码,两边要用同样的解码参数,并各跑多个种子;否则采样本身的随机性也会被算成翻转。
切片:先去看最先坏的地方
接着上面的 1,000 道题,假设其中 100 道是长上下文题(教学数字):
| 切片 | 题数 | 原模型 | 量化后 | 变化 |
|---|---|---|---|---|
| 短输入 | 900 | 675(75.0%) | 677(75.2%) | +0.2 个点 |
| 长上下文 | 100 | 45(45%) | 39(39%) | −6 个点 |
| 合计 | 1,000 | 720(72.0%) | 716(71.6%) | −0.4 个点 |
合计只掉 0.4,长上下文切片掉了 6 个点。100 道题的区间也很宽,但这正是该加题、深挖的信号。切片要事先定好,事后从几十个切片里挑最差的那个,会高估问题,见对照实验和切片。公开研究里最先坏的几类:
- 长上下文。 Mekala 等(2025-05,EMNLP 2025)在 5 个数据集、共 9.7K 个测试样例上(长输入指超过 64K token,另有长输出任务),测了 FP8、GPTQ-int8、AWQ-int4、GPTQ-int4、BNB-nf4 在 Llama-3.1 8B、70B 和 Qwen-2.5 7B、32B、72B 上的表现:8 bit 平均只掉 0.2(FP8)和 0.8(GPTQ-int8)个点;4 bit 平均掉 1.8(AWQ)到 6.9(BNB-nf4)个点;非英语输入更差;而且很依赖模型:同是 BNB-nf4、同在 OneRuler 上,Llama-3.1 70B 掉 59 个点,Qwen-2.5 72B 几乎不变(+0.6)。Kurtic 等在 RULER 上的结果温和一些:8B 的 W4A16 是 81.1,BF16 是 82.8。
- 多语言。 Marchisio 等(Findings of EMNLP 2024)发现非拉丁文字的语言受影响最大,数学这类难任务退化最快;自动指标会低估人感受到的退化(上面的 1.7% 对 16.0%)。
- 推理模型。 Liu 等(2025-04,COLM 2025)在 DeepSeek-R1 蒸馏(distillation)模型、QwQ-32B 和 Qwen3-8B 上测 AIME、MATH-500、GSM8K、GPQA、LiveCodeBench:W8A8 或 W4A16 可以做到无损(掉 1% 以内),更低位数风险明显;温和的设置下输出没有变长,W3G128、W4A4KV4 这类激进设置下输出反而变长;模型大小、来源和题目难度是关键因素。
- 总体。 Kurtic 等在 Llama-3.1 上做了 50 多万次评测:FP8 近似无损,调好的 INT8 掉 1–3%,W4A16 和 8 bit 相当。这是平均意义上的结论,不代替你在自己的切片上测。
每次量化要记下什么
| 记什么 | 为什么 |
|---|---|
| 原模型和量化模型的版本或哈希 | 能复现、能回滚 |
| 量化方法、工具及版本;位数、组长、对称与否;哪些层没压(输出层、embedding、sliding-window 层) | 同叫“INT4”,做法可以差很多 |
| 校准数据的来源、条数、长度,是否和评测文本重叠 | GPTQ 的 C4 困惑度就是一例 |
| 权重、激活、KV 各自的格式和 scale 定法(静态、动态、每个 head) | vLLM 未校准时 KV 的 scale 是 1.0 |
| 引擎版本、attention backend、kernel、GPU 型号 | 硬件不支持时会悄悄退回 W8A16 |
| 评测协议:模板、解码参数、种子、上下文长度 | 量化前后必须一致 |
| 两边的逐题结果;固定文本上的平均 KL 和 top-1 一致率 | 才能算翻转和配对区间 |
| 切片分数、输出长度分布、格式错误率、拒答率 | 先坏的地方,常常不在平均分里 |
| 目标 batch 下的延迟和吞吐 | 质量和速度要一起决定 |
常见故障:症状和先查什么
| 症状 | 可能的原因 | 先查什么 |
|---|---|---|
| 困惑度几乎不变,用户说回答变差了 | 平均值掩盖了少数位置;短英文测试集不敏感 | 逐题翻转;长上下文、非英语切片;KL 最大的位置 |
| 某个领域明显变差,其他领域正常 | 校准数据和这个领域分布不同 | 换成目标领域的校准数据重做;AWQ 报告过 GPTQ 跨领域时困惑度升高 2.3–4.9 |
| 长对话后半段开始出错 | KV 量化误差随长度积累,或某些层(如 sliding window)更敏感 | 关掉 KV 量化做对照;用跳过敏感层的选项 |
| 偶发 NaN 或个别输出崩坏 | FP8 超出范围(溢出模式下 E4M3 会变 NaN);静态 scale 偏小 | 统计激活和 KV 的最大值;改用饱和模式或动态 scale |
| 显存省了,速度没变 | 硬件没有对应的 Tensor Core,退回反量化;或 batch 已经进入算力受限区 | 引擎日志里实际用的 kernel;GPU 的 compute capability;第 2 篇的 batch 表 |
| 量化前后的输出逐字不同 | 接近平局的 token 翻转以后,后文整体分叉 | 比较分布和任务分数,不比逐字文本 |
这张表是排查的起点,不是诊断结论;每一行都要用对照实验确认。
这一篇替你回答不了什么
- 掉多少能接受,是产品决定:哪些切片必须守住、哪些可以让步,要事先写下来。
- 统计功效(power):上面 1,000 道题时,95% 区间宽约 3 个点,想分辨更小的变化,要更多题或者更细的配对设计。
- 一个模型上的结论不能直接搬到另一个模型:Mekala 等测到,同是 BNB-nf4、同在 OneRuler 上,Qwen-2.5 72B 几乎不变,Llama-3.1 70B 掉 59 个点。
最后:复习题。
本页术语 5
- 切片 slice
- 按内容或用户属性划分的子群体,用来检查平均值掩盖了什么
- 困惑度 perplexity
- exp(交叉熵),语言模型的常用指标
- 校准 calibration
- 模型给出的置信度是否等于真实正确率
- 词表 vocabulary
- 模型能输出的所有 token 的集合
- 蒸馏 distillation
- 用一个模型的输出训练另一个通常更小的模型