INT8/INT4 量化与校准:scale、零点与离群值的工程账
更新时间:2026-09-25。前置阅读是 混合精度 FP16/BF16/FP8:浮点格式靠指数段买动态范围,而本篇的 INT8/INT4 没有指数段,全部精度问题都浓缩到一个缩放因子上。整数乘法器为什么省硅,面积账在 MAC 单元的精度设计;模型变小的另一条路是剪枝,见 结构化稀疏与剪枝。
本文要回答的问题
- 一个浮点数怎么变成整数?q = round(x/s) + z 里的 scale 和零点各自负责什么?
- 对称量化与非对称量化差在哪?为什么权重常用对称、激活常用非对称?
- PTQ(训练后量化)不重新训练,scale 从哪来?min-max、percentile、KL 散度三种校准法怎么选?
- per-tensor 和 per-channel 的粒度差异为什么能决定 INT4 能不能用?
- INT4 为什么难?离群值怎样吃掉动态范围?为什么说「权重量化容易、激活量化难」?
- QAT(量化感知训练)凭什么能把 4 bit 的精度补回来?典型精度损失有几个百分点?
一、基本映射:用两个标量把实数轴折进整数格
量化的全部数学只有一对互逆映射。以有符号 INT8(整数范围 −128~127)为例:
- 量化:q = clamp(round(x / s) + z, −128, 127)
- 反量化:x̂ = s · (q − z)
s(scale,缩放因子)是一个正浮点数,决定整数轴上一格代表多大的实数区间,也就是分辨率;z(zero point,零点)是实数零点映射到的整数坐标,让整数格的中心可以对准非对称分布。clamp 处理超出量程的值,它们会被截断到边界,这是量化误差之外的第二种损失来源。
拿一组真实数字走一遍。某激活张量抽样得到 min = −1.5,max = 2.0,先看非对称量化:s = (2.0 − (−1.5)) / 255 = 3.5/255 ≈ 0.01373,z = round(1.5/0.01373) − 128 = 109 − 128 = −19。取 x = 0.1:q = round(0.1/0.01373) − 19 = 7 − 19 = −12,反量化 x̂ = 0.01373 × (−12 + 19) ≈ 0.096,误差约 0.004,不超过半格 s/2 ≈ 0.0069——这就是均匀量化的误差上限。端点也严丝合缝:x = 2.0 落到 q = 127,x = −1.5 落到 q = −128。
再看一个越界的例子。若同一层来了一个 x = 3.0 的激活,q = round(3.0/0.01373) − 19 ≈ 199,超出 127 被 clamp 成 127,反量化只有 2.0——误差不再是半格以内,而是整整 1.0,且无论 x 多大结果都一样。这种截断误差(saturation error)没有上限,是最危险的一类量化损失:舍入误差像在信号上撒细噪声,网络通常耐受;截断误差像把大信号整个削顶,后续层拿到的是失真的极值。量化配置的好坏,往往就是在「舍入误差小(s 小一点)」和「截断样本少(s 大一点)」之间找平衡点。
关键点是:一旦 s 定大了,大量数值挤在少数几个整数格上,分辨率浪费;s 定小了,尾部数值被 clamp 削平。所谓校准(calibration),本质上就是不重新训练的前提下,为每一层、每个张量把 s 和 z 定得让「舍入误差 + 截断误差」之和最小。顺带说明,上面用的是有符号 INT8;ReLU 之后的激活恒非负,也可用无符号整数 0~255,此时 z 取 0 即可,数学完全等价,只是把非对称的偏移挪进了约定里。
二、对称与非对称:零点要不要花钱
非对称量化(asymmetric)允许 min 和 max 绝对值不等,s = (max − min)/(2ⁿ − 1),z = −round(min/s) − 2ⁿ⁻¹,上一节的算例就是它。它在 ReLU 这类单侧分布上几乎不浪费任何格子:激活全部 ≥ 0 时,z 直接落在 −128 附近,256 格都对应真实数据。
对称量化(symmetric)强制 z = 0,量程关于原点对称,s = max(|max|, |min|)/(2ⁿ⁻¹ − 1)。沿用上节数据:s = 2.0/127 ≈ 0.01575,负方向留到 −2.016 却没有数据,约四分之一的格子空转,分辨率比非对称粗 15%。但对称量化在硬件上极其划算:两个对称量化的张量相乘时 q₁ = x₁/s₁、q₂ = x₂/s₂,乘积里没有零点交叉项,整数累加器直接累加 q₁·q₂,最后乘一次 s₁s₂ 即可;非对称的乘积会多出 z₁q₂、z₂q₁ 和常数三项,要么多算、要么要求权重侧对称把这些项消掉。
交叉项具体怎么来的:若两边都非对称,x₁x₂ = s₁s₂(q₁ − z₁)(q₂ − z₂),展开有 q₁q₂、z₁z₂、z₁q₂、z₂q₁ 四项,其中带 z 的三项要么额外占用乘法与累加带宽,要么得由编译器在算子边界补偿。固定权重 z=0 后,公式塌缩回 s₁s₂·q₁q₂ 减一个可预计算的偏置,硬件路径最干净。
工程上的主流组合因此固定下来:权重对称、激活非对称(简称「权对激非」)。权重静态、离线量化,分布天然近似零均值对称,省掉交叉项;激活在线量化,非对称保住 ReLU 后偏置分布的分辨率。INT8 部署框架(TFLite、TensorRT、各家 NCNN 式工具链)基本都是这个配方。还要注意对称量化有个约定分歧:INT8 的负端 −128 用不用。严格对称只用 −127~127(s = |x|max/127),负端多出的一格弃用,电路与数学完全对称;部分工具链允许 −128 落格以换取约 0.8% 的额外量程,两者必须编译链全链路对齐,混用会在反量化时产生一格的系统偏差。
三、PTQ:用一小批校准集统计激活分布
权重的 min/max 离线就能扫出来,激活却必须喂数据才知道分布。PTQ 的做法是:拿 50~1000 张有代表性的真实输入(不需要标签)跑一遍 FP32 模型,在每个量化节点「挂探针」收集激活的统计量,据此算 s、z,然后直接产出整数模型——全程没有梯度更新,几分钟到一小时完成。下图是整条流程。
三种算法对应三种对「尾部」的态度。
min-max 直接用观测极值,公式最简单、分辨率最优,却最怕偶发尖峰:某个 batch 一次异常激活冲到 20,其余 99.9% 的值挤在 ±0.5,s 被这一个尖峰撑大 40 倍,全层退化。实际工程中通常做 moving average 或跨 batch 取 EMA 极值来缓冲。
**percentile(百分位截断)**故意牺牲尾部:取 99.9% 或 99.99% 分位点作为量程,把极少数大值 clamp 掉,换取主体分辨率。阈值怎么定靠校准集上验证——常用做法是扫 99.0%~99.99% 几个档位,选模型输出指标最好的那个。检测、分割这类对激活分布敏感的任务上,percentile 相对 min-max 常常能挽回 1 个百分点以上。
**KL 散度(熵校准)**是 TensorRT 的默认方法,思路最精巧:把激活直方图(如 2048 个细 bin)与量化后的 128 个粗 bin 都归一化成概率分布,搜索一个截断阈值 T,使得量化前后分布的 KL 散度(相对熵)最小——即让量化「抹掉的信息」最少,被截断的尾部以概率质量的形式折算进误差,而不是被无视。具体操作上,对每个候选 T,先把 T 以外的尾部概率质量合并到边界 bin,再把 T 以内的细 bin 折叠成 128 个 bin,逐 T 计算散度、取最小者对应的 s。KL 校准在分类 CNN 上长期是精度上限最稳的选择,代价是要做一遍直方图搜索。
三种方法不是互斥的单选:成熟工具链通常先收集直方图,min-max 作为初值,percentile 作为兜底(极值明显异常时),KL 作为最终选择,并允许逐层覆盖。校准集本身的质量常常比算法选择更重要——它必须代表部署后的真实输入构成:白天拍摄的视觉模型不能只用夜间样本校准,语音模型的校准集要覆盖真实信噪比,否则统计出的 s 是在为另一个分布服务。
四、粒度:per-tensor 与 per-channel
同一层的 s 按什么范围统计,是另一个独立旋钮。per-tensor(张量级)整个权重矩阵或整张特征图共用一个 s,存储零开销、硬件最友好。per-channel(通道级,权重通常按输出通道、激活按 token/分组)每个通道独立统计、独立量化:一个输出通道的权重可能整体偏小,另一个偏大,共用 s 时小通道的有效位数被大通道吃掉;per-channel 让每条通道都用满自己的 256 格。
硬件代价是累加器要支持逐通道 scale:矩阵乘的整数部分不变,只是反量化时每个 K 分组乘不同的 s,可以在累加后融合,现代推理引擎普遍支持。收益在窄位宽下急剧放大——INT8 时 per-tensor 与 per-channel 差距常常只有 0.1~0.3 个百分点;INT4 时 per-tensor 可能直接让模型掉几个百分点甚至崩掉,per-channel 几乎是必需品。
比 per-channel 更细的是分组(groupwise/blockwise)量化:不按通道切,而是把权重每 64 或 128 个元素切成一块,每块独立一个 s。这是 INT4 权重-only 方案的事实标准——粒度比 per-channel 细一到两个数量级,scale 的额外存储(每 64 个 4 bit 权重配一个 16 bit 缩放因子,开销约 6%)仍可接受,配合查找表式内核,矩阵乘的吞吐不被拖累。LLM 侧的激活量化则流行 per-token:每个生成位置沿隐藏维单独定 s,开销是每行一次极值归约,但它正好压住了逐 token 漂移的激活幅值。维度选择有严格的数学约束:scale 只能沿「会被求和折叠掉」的维度独立设置,矩阵乘 A(m,k)·B(k,n) 中权重 B 沿输出通道 n 切、激活 A 沿 token 维 m 切;沿求和维 k 各自量化会让 s₁s₂ 的归并失效,这一条决定了每种张量合法的切法。
五、INT4 为什么难:离群值吃掉动态范围
把格子从 256 个降到 16 个(有符号 INT4 只有 −8~7),所有「将就」都会变成「不行」。最大的敌人是离群值(outlier)。假设某激活 99% 的值落在 ±0.5,但存在 1% 的值到 ±6。对称 INT4 下 s = 6/7 ≈ 0.86,那么 0.5 以内的全部正常值只能落到 q = 0 一个格子——整个分布被量化成一个常数,这一层的信息归零。同一个分布在 INT8 下 s = 6/127 ≈ 0.047,主体还有约 20 个格子可用,毫发无伤。这就是「动态范围被离群值吃掉」:量程由最极端的 1% 决定,分辨率由剩下 99% 均摊。
LLM 研究中这个现象有明确的结构性结论:激活的离群值集中在少数通道、且随模型放大而加剧(经典工作观察到约占隐藏维度 0.1%~1% 的通道贡献了大部分幅值)。由此分出两条路线。权重量化天然容易:权重分布平滑、无极端离群,且离线可见、可 per-channel 甚至 per-group(如每 64/128 个权重一组)精调,GPTQ、AWQ 等方法把权重压到 4 bit,7B~70B 级模型困惑度相对上升普遍控制在 1%~2% 量级。两种代表性方法的直觉不同:GPTQ 在逐列量化时,用基于二阶信息(Hessian)的补偿公式,把当前列的舍入误差按权重分摊到尚未量化的列上,误差不在原地累积;AWQ 观察到一小部分「显著权重」决定了大部分输出质量,量化前先按激活幅值对这些通道做缩放保护,缩放因子在算子前后对消、不改变数学结果,却让显著值不被压扁。激活量化才是硬骨头:激活随输入变化、离群通道动态出现,朴素 INT8 per-tensor 都可能掉点,INT4 激活必须配合特殊处理——SmoothQuant 的思路是把激活的幅值难度沿缩放因子「迁移」一部分给权重(激活好量化、权重有余量),让两边都能 INT8;更激进的方案对离群通道单独保留高精度,其余通道低精度(混合精度量化),用约 1% 的高精度通道换回主体的 4 bit 收益。
另一条路是干脆回到训练里:**QAT(量化感知训练)**在训练前向图中插入「伪量化」节点(fake quant:量化后立刻反量化),让权重和激活在训练阶段就适应量化噪声,并通过 STE(直通估计器,round 的梯度按 1 透传)使梯度可传播。前向时数值经量化、反向时梯度假装 round 不存在直接穿过——这个近似在数学上不严格,却是 QAT 能工作的关键工程妥协。几千步微调后,模型会主动「学小」离群值、把数值分布调整得更适合落格,INT4 QAT 通常能把 PTQ 损失的大头补回来,代价是需要训练资源和标注数据。实践中 QAT 一般从预训练模型出发微调少量 epoch,并沿用 PTQ 的校准流程初始化 s;卷积网络还要先做 BN 折叠(把批归一化参数吸收进相邻卷积权重)再插伪量化节点,否则推理图与训练图的数值行为对不上。蒸馏(用 FP 模型教量化学生)常与 QAT 叠加,对小模型和窄位宽尤其有效。
六、精度损失对照与工程 recipe
下表数字是公开报告与主流框架基准的典型区间(ResNet/BERT 类任务以准确率百分点计,LLM 以困惑度相对上升与任务点计),随模型结构、校准集、分组粒度浮动,只作数量级参考。
| 方案 | 对称性 | 粒度 | 典型精度损失 | 备注 |
|---|---|---|---|---|
| INT8 PTQ 权重 | 对称 | per-channel | 0.0~0.2 pp | 几乎免费 |
| INT8 PTQ 权重+激活 | 权对激非 | 激活 per-tensor | 0.1~0.5 pp(CNN 分类);BERT 类约 0.2~0.7 pp | min-max/KL 校准,首尾层建议回退 |
| INT8 PTQ(朴素 per-tensor,含敏感层) | 全对称 | per-tensor | 0.5~2+ pp | 小模型/检测任务更明显 |
| INT4 权重-only(GPTQ/AWQ) | 对称 | per-group(64/128) | LLM 困惑度 +0.5%~2%,任务点约 0~1.5 pp | 推理省权重带宽,激活仍 FP16 |
| INT4 权重+激活 PTQ | 权对激非 | 必须 per-channel/group | 朴素做法 3~10+ pp;平滑迁移后约 1~3 pp | 不处理离群值会直接失效 |
| INT4 QAT | 权对激非 | per-channel/group | 相对 FP32 约 0.5~2 pp | 需微调,精度可逼近 INT8 PTQ |
落地 recipe 高度一致:先用 INT8 PTQ + 权对激非跑通,校准集覆盖真实业务分布,首尾层、softmax 前 logits 等敏感层白名单回退 FP16;带宽仍不够再上 INT4 权重-only(GPTQ/AWQ),优先享受权重与 KV cache 压缩;需要 INT4 激活则引入 SmoothQuant 类迁移或 QAT。部署侧还有两个工程细节:4 bit 权重在内存中按位打包(两个 INT4 挤进一个字节),读取后再解包,内核要配套实现,否则只省了容量没省带宽;每层的 s、z 数量虽多但总量小,通常预存在算子参数区,运行时不再为它们发起额外访存。另外,量化在小 batch、短序列下的相对开销更重——解包、反量化与类型转换的固定成本占比高,大 batch 矩阵乘才能充分摊薄,这也是边缘端单路小模型有时「量化了却没快多少」的原因。无论哪种方案,验收信号不是校准误差,而是端到端任务指标,以及监控中某层激活长期贴着量程边界——那说明 s 已被离群值绑架,该换 percentile/KL 或让该层提精度了。
这一篇在知识体系里的位置
- 上游格式:混合精度 FP16/BF16/FP8 讲有指数段的低精度路线,本篇是无指数段的整数路线;
- 电路视角:MAC 单元的精度设计 讲 INT8/INT4 MAC 的面积与累加器账;
- 正交压缩:结构化稀疏与剪枝 讲用稀疏把乘法直接归零,可与量化叠加;
- 数学基础:量化误差本质上是定点舍入,与 浮点运算与 IEEE 754 中尾数舍入同源。
总结
INT8/INT4 量化靠一对仿射映射工作:q = round(x/s) + z,scale 定格宽、零点对准分布;权重用对称量化(z=0、省交叉项)、激活用非对称量化(ReLU 偏置分布不空格子)。PTQ 不重新训练,用 50~1000 条校准样本统计激活分布,min-max 简单但怕尖峰、percentile 截断尾部换分辨率、KL 散度搜索让量化前后分布信息损失最小;per-channel 在 INT8 下只是锦上添花,在 INT4 下是必需品。INT4 的真正敌人是离群值——1% 的大值决定量程、99% 的主体挤在同一个格子,所以权重量化容易(GPTQ/AWQ 做 per-group 4 bit,困惑度仅升约 1%~2%)、激活量化难(需 SmoothQuant 迁移或 QAT 带伪量化节点微调)。典型损失:INT8 PTQ 约 0.1~0.5 个百分点,INT4 权重-only 任务点约掉 0~1.5 个百分点,朴素 INT4 全量化可掉 3~10 个点、QAT 能拉回到约 0.5~2 个点;某层激活长期贴量程边界,就是 scale 被离群值绑架的硬信号。