在 模型 量化的时候 ,改变了模型的数值 ,怎么保证 模型的 推理结果 一致呢
不能绝对保证完全一致。模型量化的本质就是:
把连续/高精度数值,压到低 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
那么一点量化误差就可能改变输出。
所以一句话:
量化不是保证数值完全一样,而是保证误差没有大到改变模型的主要决策边界。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)