不能绝对保证完全一致。模型量化的本质就是:

把连续/高精度数值,压到低 bit 离散格点上。

所以权重、激活、KV cache 的数值一定会变。能保证的是:

误差足够小 → logits 排序基本不变 → 输出结果大体一致

但不是数学上 100% 完全相同。


1. 量化到底改变了什么?

以 INT8 / INT4 为例,原始浮点数 x 会被映射成整数:

q = round(x / scale)

再推理时近似还原:

x_hat = q × scale

所以真正参与计算的是:

x_hat ≈ x

不是原来的 x

误差是:

error = x_hat - x

如果是均匀量化,单个数的最大量化误差大约是:

|error| <= scale / 2

也就是说,scale 越小,量化越精细,误差越小。


2. 为什么误差小,输出还能基本一致?

大模型最终输出 token,是看 logits:

logits = model(x)

然后选择概率最高的 token。

如果量化前:

token_A logit = 12.0
token_B logit = 9.0

量化后有一点误差:

token_A logit = 11.8
token_B logit = 9.2

排序没变,输出还是 token_A。

但如果量化前两个 token 很接近:

token_A logit = 10.01
token_B logit = 10.00

量化后可能变成:

token_A logit = 9.98
token_B logit = 10.02

那输出就变了。

所以核心判断是:

如果 top1 和 top2 的 logit 差距 > 量化误差影响,输出大概率一致;
如果 top1 和 top2 很接近,量化很容易导致 token 翻转。

这也是为什么大模型量化后,经常不是整体崩,而是在某些边界问题上输出变差。


3. 怎么尽量保证推理结果一致?

主要靠这些方法。

第一,选好 scale

量化不是随便除一个数,而是要选 scale。

常见方式:

scale = max(|x|) / qmax

例如 INT8 对称量化:

qmax = 127
scale = max(|x|) / 127

这样最大值不会溢出。

但如果某一层有极端 outlier,会导致 scale 很大,普通小值的分辨率变差。

比如:

大多数值在 [-1, 1]
但有一个 outlier = 100

如果用 100 / 127 做 scale,那么小值会被量化得很粗,精度损失很大。

所以大模型量化的核心问题之一就是:

怎么处理 outlier。

第二,per-channel / per-group 量化

不要整个矩阵共用一个 scale,而是分组。

例如权重矩阵:

W: [out_dim, in_dim]

可以:

per-tensor:整个 W 共用一个 scale
per-channel:每个输出通道一个 scale
per-group:每 32/64/128 个元素一个 scale

越细粒度,误差越小:

per-tensor 误差最大
per-channel 更好
per-group 通常更适合 LLM

大模型 INT4 常用 group-wise quantization,例如 group size = 32/64/128。


第三,关键层保留高精度

不是所有层都适合量化。

通常比较敏感的部分包括:

embedding
lm_head
RMSNorm / LayerNorm
attention softmax
部分 activation
少数 outlier 明显的层

所以工程上常见做法是:

大部分权重量化到 INT4 / INT8
Norm 保持 FP16/BF16
激活保持 FP16/BF16 或 INT8
lm_head 有时保持 FP16/BF16

这叫 mixed precision。

也就是说:

不是整个模型一刀切量化。

第四,校准 calibration

PTQ,也就是 Post-Training Quantization,通常需要校准数据。

流程是:

拿一批代表性样本
跑一遍模型
统计每层 activation 分布
决定 scale / zero point / clipping 范围

如果 calibration 数据和真实推理场景接近,量化效果就更稳。

例如你做机器人视觉语言模型,校准数据最好包含:

真实相机图像
真实任务指令
真实机械臂场景
真实深度/视觉分布

而不是随便拿通用文本。


第五,处理 outlier

大模型里很多误差来自 outlier channel。

例如某些 activation 通道数值特别大:

普通通道:[-2, 2]
异常通道:[-80, 80]

这些 outlier 会破坏量化 scale。

所以很多方法会做:

SmoothQuant:把 activation 的 outlier 转移到 weight
AWQ:保护对输出影响最大的权重通道
GPTQ:用二阶近似减少权重量化后的输出误差

这些方法的本质不是“数值不变”,而是:

让量化后的矩阵乘输出尽量接近原模型输出。

4. 数学上为什么不能严格保证一致?

因为神经网络是多层复合函数:

y = f_L(...f_2(f_1(x, W_1), W_2)..., W_L)

量化后变成:

y_hat = f_L(...f_2(f_1(x, W_1 + ΔW_1), W_2 + ΔW_2)...)

每一层都有误差:

W_hat = W + ΔW

单层误差可能很小,但经过很多层后会累积。

尤其 Transformer 里有:

MatMul
Attention
Softmax
Residual
MLP
MoE routing
LayerNorm / RMSNorm

这些结构会让误差传播路径很复杂。

所以严格来说:

量化不能保证所有输入下输出 token 完全一致。

它只能保证:

在测试集/校准集/真实业务分布上,精度下降足够小。

5. LLM 推理里“一致”到底怎么衡量?

不是只看一句话是否完全一样。

更常用的是这些指标:

1. logits 误差
2. top-k token 是否一致
3. perplexity 是否上升
4. benchmark 分数是否下降
5. 真实任务成功率是否下降
6. 长文本稳定性是否下降
7. 多轮对话是否更容易胡说

如果是确定性解码:

temperature = 0
top_p = 1

可以比较 token 是否完全一致。

如果是采样解码:

temperature > 0
top_p < 1

即使不量化,每次输出也可能不一样,这时就不能要求逐 token 完全一致,只能比较统计质量。


6. 对你写推理引擎来说,最关键的是这几点

如果你自己做 INT8 / INT4 推理引擎,要优先保证:

1. scale 计算正确
2. zero point 正确
3. group size 正确
4. weight layout 正确
5. dequant 公式正确
6. accumulation 精度正确
7. RoPE / RMSNorm / softmax 保持高精度
8. logits 和原模型误差可测

尤其注意 accumulation。

例如 INT8 GEMM 不是直接 INT8 累加到 INT8,而是:

INT8 × INT8 → INT32 accumulate → FP16/BF16/FP32 output

INT4 也类似:

INT4 weight × FP16 activation
    ↓
dequant
    ↓
FP16/BF16 accumulate 或 Tensor Core 路径

如果 accumulation 精度不对,模型很容易崩。


7. 最简洁结论

量化不会让模型数值完全不变。

它保证推理结果接近,靠的是:

1. 量化误差足够小
2. logits 排序基本不变
3. 敏感层保留高精度
4. per-channel / per-group scale 减少误差
5. calibration 数据匹配真实分布
6. GPTQ / AWQ / SmoothQuant 等方法修正 outlier 和输出误差
7. 用 benchmark / perplexity / logits 误差验证

一句话:

量化不是保证“数值完全一致”,而是保证“误差不改变模型的主要决策边界”。当 top1 和 top2 很接近时,量化后的输出 token 发生变化是正常的。

可以。用几个最小数学例子说明:量化改变数值,但为什么推理结果还能保持一致。


1. 一个最简单的线性层例子

假设模型某一层是:

y = w1*x1 + w2*x2

原始 FP16/FP32 权重:

w = [0.52, -1.37]
x = [2.0, 1.0]

原始输出:

y = 0.52*2.0 + (-1.37)*1.0
  = 1.04 - 1.37
  = -0.33

现在把权重量化成 INT4。

假设 scale = 0.2,INT4 范围用 [-7, 7]

量化公式:

q = round(w / scale)

所以:

q1 = round(0.52 / 0.2) = round(2.6) = 3
q2 = round(-1.37 / 0.2) = round(-6.85) = -7

反量化:

w_hat = q * scale

得到:

w_hat = [3*0.2, -7*0.2]
      = [0.6, -1.4]

量化后输出:

y_hat = 0.6*2.0 + (-1.4)*1.0
      = 1.2 - 1.4
      = -0.2

对比:

原始输出:-0.33
量化输出:-0.20
误差:0.13

数值变了,但如果下一步只是判断正负:

y < 0 → 类别 A
y > 0 → 类别 B

那么:

原始:-0.33 < 0 → A
量化:-0.20 < 0 → A

结果一致。


2. 为什么有时结果会变?

还是同一个判断边界:

y < 0 → A
y > 0 → B

如果原始输出非常接近 0:

原始 y = 0.03

量化误差是:

error = -0.08

那么量化后:

y_hat = 0.03 - 0.08 = -0.05

结果就变成:

原始:0.03 > 0 → B
量化:-0.05 < 0 → A

所以关键不是“数值是否改变”,而是:

量化误差有没有跨过决策边界

一句话:

离边界远,量化后大概率不变;离边界近,量化后容易变。


3. 对应到大模型 logits 的例子

大模型最后会输出一组 logits,比如三个候选 token:

token A: 12.0
token B: 9.5
token C: 3.0

模型会选最大值:

A 最大 → 输出 A

量化后 logits 可能变成:

token A: 11.7
token B: 9.8
token C: 3.1

虽然数值变了,但排序还是:

A > B > C

所以输出还是 A。

这就是量化后仍然一致的原因:

logits 排序没有变

4. logits 很接近时,就容易翻转

假设原始 logits 是:

token A: 10.01
token B: 10.00
token C: 2.00

原始输出:

A 最大 → 输出 A

量化后可能变成:

token A: 9.98
token B: 10.02
token C: 2.01

此时排序变了:

B > A > C

输出就变成 B。

所以量化不会保证每个 token 完全一致,它保证的是:

大多数情况下,logits 的主要排序结构不被破坏

5. 用矩阵乘看量化误差

Transformer 里最核心的是矩阵乘:

y = Wx

量化后权重变成:

W_hat = W + ΔW

所以输出变成:

y_hat = W_hat x
      = (W + ΔW)x
      = Wx + ΔW x

也就是:

y_hat = y + ΔW x

其中:

ΔW x = 量化带来的输出误差

所以只要:

|ΔW x| 足够小

模型输出就接近原模型。

这就是 GPTQ、AWQ、SmoothQuant 等方法的核心目标:

不是让 W_hat = W
而是让 W_hat x ≈ W x

因为模型真正关心的是每一层的输出,而不是每个权重是否完全相同。


6. 一个二维分类边界例子

假设模型最后做二分类:

score = 2*x1 - x2

判断规则:

score > 0 → 类别 1
score < 0 → 类别 0

输入:

x1 = 3
x2 = 4

原始:

score = 2*3 - 4 = 2

结果:

score = 2 > 0 → 类别 1

现在量化后权重有误差:

2 → 1.9
-1 → -1.1

量化后:

score_hat = 1.9*3 - 1.1*4
          = 5.7 - 4.4
          = 1.3

结果仍然:

score_hat = 1.3 > 0 → 类别 1

虽然 score 从 2 变成了 1.3,但没有跨过 0,所以分类不变。

但是如果原始输入是:

x1 = 2.01
x2 = 4

原始:

score = 2*2.01 - 4
      = 4.02 - 4
      = 0.02

离边界非常近。

量化后:

score_hat = 1.9*2.01 - 1.1*4
          = 3.819 - 4.4
          = -0.581

结果变成:

原始:类别 1
量化:类别 0

这说明:

靠近决策边界的样本,量化后更容易改变结果。

7. 为什么 per-group 量化误差更小?

假设一组权重:

W = [0.1, 0.2, 0.3, 10.0]

如果整个 tensor 共用一个 INT4 scale:

scale = max(|W|) / 7 = 10 / 7 ≈ 1.43

那么前三个小值量化:

0.1 / 1.43 ≈ 0.07 → round → 0
0.2 / 1.43 ≈ 0.14 → round → 0
0.3 / 1.43 ≈ 0.21 → round → 0

反量化后:

[0, 0, 0, 10.01]

小权重全没了。

如果分组,把前三个小值单独一组:

group1 = [0.1, 0.2, 0.3]
scale1 = 0.3 / 7 ≈ 0.043

量化后:

0.1 / 0.043 ≈ 2
0.2 / 0.043 ≈ 5
0.3 / 0.043 ≈ 7

反量化:

[0.086, 0.215, 0.301]

就接近原始值。

所以:

per-tensor 量化容易被 outlier 破坏
per-group / per-channel 量化可以减少误差

这就是大模型 INT4 通常用 group-wise quantization 的原因。


8. 最核心数学结论

量化后:

W_hat = W + ΔW

模型输出变成:

y_hat = W_hat x = Wx + ΔW x

如果:

|ΔW x| 很小

那么:

y_hat ≈ y

如果最终 logits 满足:

top1_logit - top2_logit > 量化误差影响

则输出 token 不变。

如果:

top1_logit - top2_logit ≈ 0

那么一点量化误差就可能改变输出。

所以一句话:

量化不是保证数值完全一样,而是保证误差没有大到改变模型的主要决策边界。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐