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
用一个模型的输出训练另一个通常更小的模型