1.2 量化基础概念:比特宽度、对称与非对称量化及校准


文档摘要

1.2 量化基础概念:比特宽度、对称与非对称量化及校准 上一节我们把“为什么”讲清了。这一节开始补“是什么”。量化听起来玄乎,本质上就是一个把高精度实数“翻译”成低比特整数的映射过程。把这层窗户纸捅破,后面所有方法(GPTQ、AWQ、GGUF)你都能一眼看穿它们在优化什么。我会从比特宽度讲起,再到对称/非对称两种映射方式,最后落到决定 INT4 成败的“校准”上。 一、比特宽度:模型体积的刻度尺 “比特宽度”(bit-width)就是用来表示一个数值的比特数。比特越少,能表达的状态越少,存储越小,但精度越粗。大模型常用的精度构成一条“阶梯”: 每往下一格,参数体积大致减半。从 FP16(2 字节)到 INT4(0.5 字节)正好 4 倍。

1.2 量化基础概念:比特宽度、对称与非对称量化及校准

上一节我们把“为什么”讲清了。这一节开始补“是什么”。量化听起来玄乎,本质上就是一个把高精度实数“翻译”成低比特整数的映射过程。把这层窗户纸捅破,后面所有方法(GPTQ、AWQ、GGUF)你都能一眼看穿它们在优化什么。我会从比特宽度讲起,再到对称/非对称两种映射方式,最后落到决定 INT4 成败的“校准”上。

一、比特宽度:模型体积的刻度尺

“比特宽度”(bit-width)就是用来表示一个数值的比特数。比特越少,能表达的状态越少,存储越小,但精度越粗。大模型常用的精度构成一条“阶梯”:

```mermaid flowchart TD FP32[FP32 · 32bit · 4字节/参数 · 训练基准] FP16[FP16/BF16 · 16bit · 2字节 · 推理常用] FP8[FP8 · 8bit · 1字节 · 数据中心新宠] INT8[INT8 · 8bit · 1字节 · 成熟稳定] INT4[INT4 · 4bit · 0.5字节 · 本地推理前沿] FP32 --> FP16 --> FP8 --> INT8 --> INT4 ```

每往下一格,参数体积大致减半。从 FP16(2 字节)到 INT4(0.5 字节)正好 4 倍。注意 FP8 和 INT8 都是 8 比特、1 字节,但一个是浮点、一个是整型,数值行为和硬件路径不同:FP8 保留浮点的动态范围特性,INT8/INT4 则是纯整型,依赖“缩放因子”把实数区间映射到整数格。

一个常被问到的问题:INT4 是不是精度的下限? 理论上还能做 INT2(0.25 字节),但 2 比特只有 4 个档位,量化误差大到模型基本“失能”,只在极特殊研究场景出现。工程上 INT4 是当前可用的激进下限,INT8 是稳的底线。

二、量化的核心公式

不管方法多花哨,量化/反量化的数学骨架就两行:

量化:q = round(r / scale) + zero_point
反量化:r_hat = (q - zero_point) * scale

其中 r 是原始实数(权重或激活),q 是量化后的整数,scale(缩放因子)把实数区间“缩”到整数格距,zero_point(零点)负责把实数轴的某个值对齐到整数 0。反量化得到的 r_hat 是近似还原值,误差 r - r_hat 就是量化噪声。所有量化算法的核心任务,就是找一组最优的 scale 和 zero_point,让这个噪声最小

下面这段 Python 用最小代码演示了这个过程(以 INT4 有符号范围 [-8, 7] 为例):

import numpy as np def quantize_int4(r, symmetric=True): # INT4 有符号范围 [-8, 7] qmin, qmax = -8, 7 if symmetric: # 对称:零点为 0,scale 取最大绝对值 scale = max(abs(r.min()), abs(r.max())) / max(abs(qmin), abs(qmax)) zero_point = 0 else: # 非对称:把 [r_min, r_max] 映射到 [qmin, qmax] scale = (r.max() - r.min()) / (qmax - qmin) zero_point = qmin - r.min() / scale q = np.clip(np.round(r / scale + zero_point), qmin, qmax) r_hat = (q - zero_point) * scale # 反量化 return q, r_hat, scale, zero_point w = np.array([-1.7, -0.3, 0.0, 0.9, 2.4, -2.6]) q, r_hat, s, zp = quantize_int4(w, symmetric=True) print("scale:", s, "zero_point:", zp) print("量化值 q:", q) print("反量化 r_hat:", np.round(r_hat, 3)) print("量化误差:", np.round(w - r_hat, 3))

跑这段代码你会直观看到:原本连续的权重,被“钉”到了 -8 到 7 之间的整数上,反量化回来已经和原数有偏差——这就是量化噪声的具象。

三、对称量化 vs 非对称量化

选择 scale/zero_point 的两条路线,对应两种映射哲学:

```mermaid flowchart LR subgraph S[对称量化] direction TB SA["权重分布
围绕 0 对称"] --> SB["zero_point = 0
scale = max|r| 除 (2^(b-1)-1)"] SB --> SC["整数格以 0 为中心"] end subgraph A[非对称量化] direction TB AA["激活分布
常偏正 如 ReLU 输出≥0"] --> AB["zero_point 偏移
对齐 r_min 到 0"] AB --> AC["整数格贴合实际区间"] end ```
  • 对称量化(symmetric):强制 zero_point = 0,即实数 0 映射到整数 0,格点关于原点对称。它最适合权重——因为神经网络权重大多零均值、围绕 0 分布。公式上 scale = max(|r|) / (2^(b-1) - 1)。好处是实现简单、推理时少一次加法(无需减 zero_point),计算快。
  • 非对称量化(asymmetric):允许 zero_point ≠ 0,把实数区间的最小值对齐到整数 0。它适合激活值,尤其像 ReLU 这类输出恒 ≥0、分布明显偏离 0 的张量——若强行对称,一半整数格会被浪费在永远用不到的负数区,精度白白损失。

实践中的经验法则:权重用对称 INT4,激活用非对称 INT8(或对称 INT8,取决于运行时),这是精度和效率的平衡点。很多 INT4 方案(如 GPTQ)对权重采用对称 per-group 量化,正是这个原因。

需要澄清一个常见误解:为什么不能“权重对称、激活也对称”一刀切?因为激活的分布形态和权重不同——权重围绕 0 对称,而激活(尤其经过 ReLU/GELU 之后)常常一边倒地偏正,最小值接近 0、最大值很大。若对激活强行对称,整数格里代表负值的那一半(比如 INT8 的 -128 到 -1)永远用不上,等于白白丢掉一半分辨率。非对称量化通过 zero_point 把“实数最小值”对齐到整数 0,让每一档都用在刀刃上。一句话:对称省一次减法、适合零中心分布;非对称多用一次偏移、适合偏态分布。

四、量化粒度:per-tensor / per-channel / per-group

“用几套 scale”决定了量化的粗细,是影响 INT4 质量的关键旋钮:

```mermaid flowchart TD T[per-tensor
整个张量 1 个 scale
快但粗] C[per-channel
每个输出通道 1 个 scale
权重常用] G[per-group
每 128 个权重 1 个 scale
INT4 标配] T --> C --> G ```
  • per-tensor(逐张量):整个权重矩阵共用一个 scale。最快、存储最省,但太粗——只要有一个巨大离群值,全局 scale 就被撑大,其余权重量化误差爆炸。
  • per-channel(逐通道):对权重矩阵的每个输出通道单独算 scale。对矩阵乘更友好,精度明显优于 per-tensor,是很多 INT8 方案的默认。
  • per-group(逐组):把权重切成小块(典型 group_size = 128),每组一个 scale。这是 INT4 的标配(GPTQ、AWQ、GGUF 的多数方案都用 group_size 128)。为什么?因为大模型权重里存在“离群通道”——极少数通道数值极大,会污染全局 scale;切成小 group 后,离群值被限制在它所在的组里,其余组的 scale 可以“贴合”真实分布,精度大幅提升。

代价是:group 越细,scale 和 zero_point 的存储开销越大,反量化时的查表/计算也略慢。group_size 128 是反复验证过的甜点;调小(如 64)更准但更慢更占空间,调大(如 256)反之。

这里补一个常被忽略的算术直觉:INT4 每组 128 个权重存 1 个 scale(通常 FP16,2 字节),于是每组 128 个 INT4 权重只占 64 字节,加上 2 字节 scale,开销约 5.1%;若 group_size 翻倍到 256,scale 占比降到约 2.6%,但离群值“连累”相邻权重的范围更大、精度更差。这就是为什么 128 是质量与开销的黄金交叉点。再往下走,per-channel 因每组覆盖整条输出通道(数千权重),scale 占比可忽略,但对离群通道完全无能为力——这正是 INT4 必须下沉到 per-group 的根本原因。

五、离群值:INT4 的真正天敌

必须单独强调一个点,因为它是 INT4 比 INT8 难那么多的根本原因:大模型权重和激活中存在少量“离群值”(outlier)——数值比平均值大几十甚至上百倍的通道

如果对这些离群通道和正常通道用同一套 scale,要么离群值被截断失真,要么正常通道被压到几乎没有分辨率。研究者发现,MoE 这类巨型模型离群现象更显著,这也是为什么“随便四舍五入(RTN)做 INT4”往往翻车,而 GPTQ/AWQ 这类方法专门设计来处理离群值。第 2 章会展开这些方法各自的思路。

六、校准(Calibration):scale 不是拍脑袋定的

前面所有公式都依赖 scale 和 zero_point,而它们怎么定?不能只看权重本身的最小值最大值,还要看模型实际跑起来时激活的真实分布——因为最终误差发生在推理的前向传播里。确定这些参数的过程叫“校准”,需要一小批有代表性的数据(校准集,calibration set):

```mermaid flowchart LR D[校准数据
数十到数百条样本] --> O[前向跑一遍
统计激活分布] O --> R[选范围估计法
min-max 或 熵 或 分位裁剪] R --> S[得到 scale + zero_point] S --> Q[用于量化权重与激活] ```

范围估计(range estimation)的几种主流做法:

  • Min-Max(最小最大):scale 直接由观测到的最大最小值得来。最简单,但对离群值极度敏感——一个极端样本就能把 scale 撑爆。
  • 熵 / KL 散度法:在 INT8 时代由 TensorRT 推广,思路是搜索一个最优的“裁剪阈值”,使得量化前后激活的信息分布(用 KL 散度衡量)差异最小。比 min-max 稳健。
  • 分位数裁剪(Percentile / Clipping):不直接取极端值,而是取比如 99.99% 分位数作为上界,把更极端的点裁掉。在 INT4 场景下非常实用,因为它主动牺牲极少数的极端值、保住绝大多数权重的分辨率。

校准集的质量直接决定量化后模型“像不像原来的模型”。经验上,校准数据要覆盖你真实任务的分布:用中文对话数据校准的模型,去做英文代码任务可能掉点。这也是为什么通用量化模型和“领域微调量化模型”质量有差距。

关于校准集,有三个实践要点值得单独拎出来:

第一,数量够用即可,重在“代表性”而非“海量”。多数 INT4 方案的校准集只要几十到几百条样本就能收敛,堆到上万条边际收益极低,反而拖长量化耗时。关键不是多,而是每条样本都能“激活”模型不同的行为模式。

第二,警惕“脏校准”。如果校准数据里混入与正式任务无关的格式(比如全是超短问答,而你要做的是长文档摘要),量化后的模型会在长上下文上明显掉链子。校准分布应当尽量贴齐你上线后的真实输入形态。

第三,校准集一旦选定就不要随意换。对比不同量化方法的效果时,务必用同一份校准集,否则你分不清是“方法更强”还是“校准集更对症”——这是量化实验里最容易被忽视的变量控制。

七、RTN 与学习式量化:两条路线

最后铺垫一个分类,后面章节会深挖:

  • RTN(Round-To-Nearest,最近邻舍入):不加校准、直接按 scale 四舍五入。最快、最省,但 INT4 下质量通常最差——它完全没考虑离群值和层间差异。
  • 学习式量化(GPTQ / AWQ 等):在校准数据上做一层“补偿”——GPTQ 用二阶信息按列补偿量化误差,AWQ 通过激活感知的重要度缩放保护显著权重。它们更慢、更吃校准集,但 INT4 质量显著更好。

给读者的主张:如果你只想快速验证一个模型能不能跑,RTN 类方案(如 GGUF 的 Q4_K_M)够用且极快;如果你要把 INT4 模型用于生产、且对精度敏感,请直接上 GPTQ 或 AWQ,不要省这一步。

最后提醒一点:量化质量的“好坏”不能只看一个总准确率数字。同一份权重,在常识问答上可能几乎不掉点,但在多步算术、长程指代、代码语法正确性上却悄悄退化——这是因为 INT4 的误差会沿推理链累积,越到“需要精确保持中间状态”的任务越容易暴露。所以评估一个 INT4 模型时,务必用你真实任务里最“刁钻”的几类样本做人工复核,而不是只信公开的通用榜单。把这点养成习惯,能帮你避开绝大多数“上线后才发现变傻”的事故。

为了把本节的概念落到“可操作”,给你一张选型速查表,写代码前先对照一遍:

你要量化什么 推荐方式 比特 粒度 校准
权重(绝大多数层) 对称 INT4 per-group 128 需校准集
激活值 非对称 INT8 per-tensor/channel 需校准集
KV Cache 非对称 INT4/INT8 per-token 可选 通常免校准
快速验证原型 RTN INT4 per-group 免校准

记住这张表,第 2 章讲具体方法时你就不会迷失在参数里——它们无非是在这张表的格子里做不同取舍。若你暂时记不住细节也别慌:只要守住“权重对称 INT4 per-group 128 + 校准集覆盖真实分布”这两条铁律,就已经避开了 90% 的 INT4 翻车现场。

八、本节小结

读完这节,你应该能一句话说清量化是什么:量化就是用更少的 bits 把高精度实数“翻译”成整数——通过 scale 和 zero_point 完成映射;权重常用对称 per-group INT4 来压制离群值,而 scale 的好坏由校准集和范围估计法决定,这正是 INT4 质量分水岭所在。

下一章我们将正式拆解 GPTQ、AWQ、GGUF 这几条主流 INT4 量化方法,看它们各自如何聪明地“保住精度”。


发布者: 作者: 前端切图仔转型中的小龙虾 转发
评论区 (0)
U