为什么要做量化?
为什么量化对神经网络精度影响不大?
1.权重和输入经过归一化,数值范围较小
➤ 通常神经网络训练后会对输入和权重做标准化(Normalization),避免极端值,使得量化误差变小。
2.激活函数平滑误差影响
➤ 比如 ReLU、GELU 等非线性激活函数会压制或滤除部分噪声,量化带来的误差可能被自然平滑掉。
3.分类任务对绝对值不敏感
➤ 多数神经网络用于分类任务,只要“正确类别”的输出概率比其他类别高即可,不需要特别精确的数值。
特点:
流程:
特点:
流程:
ZeroQuant 是一种针对大语言模型(LLM)部署和推理阶段的训练后量化(PTQ)技术框架主要目的是在不重新训练的前提下大幅降低模型精度(如INT8甚至INT4)而仍保持高精度。
ZeroQuant提出了多个版本:
名称 | 主要特点 | 支持精度 |
---|---|---|
ZeroQuant | 基础版,首次提出 PTQ 精准方案 | INT8 |
ZeroQuant-V2 | 引入 block-wise 重构与非对称量化 | INT4 |
ZeroQuant-V3 | 更进一步,结合 GPT 结构优化 | INT4、NF4 |
以 ZeroQuant-V2 为例,主要包含以下关键点:
✅ 1. Block-wise Reconstruction(块级重构)
✅ 2. 混合精度量化(Mixed-Precision)
不强制所有模块都用 INT4:比如 LayerNorm 和某些 sensitive 层仍保留 FP16/INT8。
比如:
✅ 3. 逐通道非对称量化
✅ 4. 逐层误差控制
引入 量化敏感性分析(sensitivity-aware),找到最容易失真层,给它更高精度。
基于统计数据对每一层的激活和权重做误差追踪。
观察:激活值的变化范围远大于权重,因此需要更细粒度。
方法:
优化手段:层级蒸馏(Layer-wise Distillation),对每层输出做MSE匹配,减少精度损失。
优势:
发现问题:激活值中存在“离群值”(Outliers),极大影响量化。
解决方案:混合精度量化:
离群值特点(6.7B以上模型尤为严重):
LLM.int8()
的核心思想
它 不是简单地把所有权重都压成 INT8,而是:
对每个线性层的每一列进行 重要性评估
对于 不重要的列(95%以上):
对于 重要的列(~5%):
通过小批量输入校准数据,计算量化误差对输出影响,识别:
哪些列量化后对模型输出影响大?哪些不影响?
这就是 LLM.int8()
的创新点 —— 量化选择机制!
实现
以一个 Linear 层为例:
权重矩阵 W ∈ R n × m W \in \mathbb{R}^{n \times m} W∈Rn×m
将每一列 w j w_j wj 转换为:
INT8 表示 w ^ j \hat{w}_j w^j (若不重要)
保留 FP16 表示(若很重要)
整体表示为:
W = W int8 + W fp16 W = W_{\text{int8}} + W_{\text{fp16}} W=Wint8+Wfp16
推理时:
Y = X ⋅ ( W int8 + W fp16 ) Y = X \cdot (W_{\text{int8}} + W_{\text{fp16}}) Y=X⋅(Wint8+Wfp16)
观察:激活值比权重要难量化。
方法:
引入缩放因子 s s s,调整激活值和权重的数值范围:
通常 s j = ∣ X j ∣ ⋅ ∣ W j ∣ s_j = \sqrt{|X_j| \cdot |W_j|} sj=∣Xj∣⋅∣Wj∣,并设置超参 α = 0.5 \alpha = 0.5 α=0.5
优点:
Optimal Brain Quantization(OBQ)的流程,是一种以最小化模型输出误差为目标的后训练量化(PTQ)方法,其核心是通过最小化权重量化误差在整个模型输出(如 logits)上的影响,来实现高精度量化。OBQ逐层(Layer-wise)进行权重量化与输出拟合优化,依次处理每一层,直到整网完成。
下面是对 OBQ 流程的详细讲解(分步骤):
第 1 步:收集校准数据
目的:构造真实输入空间,捕捉量化对输出的实际影响。
第 2 步:获取原始输出(浮点精度)
Y = X W Y = XW Y=XW
这作为参考输出。
第 3 步:初始化量化权重
W q = Quant ( W , scale , zero-point ) W_q = \text{Quant}(W, \text{scale}, \text{zero-point}) Wq=Quant(W,scale,zero-point)
第 4 步:误差重构优化(核心)
目标:找到最优的量化权重解,使得量化后的输出最接近原始输出 Y Y Y。
数学表达为一个最小二乘优化问题:
min W ^ q ∥ X W ^ q − Y ∥ 2 s.t. W ^ q ∈ quantized space \min_{\hat{W}_q} \| X \hat{W}_q - Y \|^2 \quad \text{s.t. } \hat{W}_q \in \text{quantized space} W^qmin∥XW^q−Y∥2s.t. W^q∈quantized space
W ^ q \hat{W}_q W^q:在量化空间内逼近原始输出的最优解
这是一个 投影 + 修正问题,可用以下方式求解:
线性最小二乘 + clip 到量化区间
求解近似的连续最优 W ∗ W^* W∗:
W ∗ = ( X T X ) − 1 X T Y W^* = (X^TX)^{-1} X^TY W∗=(XTX)−1XTY
第 5 步:逐层更新模型
第 6 步:全模型前向验证
GPTQ 是一种无需训练的低比特后训练量化(Post-Training Quantization,PTQ)方法,广泛用于 LLaMA、OPT、BERT 等大型模型的 INT4 量化,尤其适合 Transformer 的 Linear 层权重量化。
✅核心思想:
GPTQ 的目标是对每一层的权重 W W W 进行逐列量化,使得:
量化后的输出 X W q XW_q XWq 尽可能接近原始输出 X W XW XW,且考虑量化误差对整体输出的影响最小。
对于一层线性变换(如 Linear 层):
Y = X W Y = XW Y=XW
GPTQ 逐列处理权重矩阵 W W W 的每一列 w j w_j wj,以最小化:
min w ^ j ∈ quant space ∥ X w ^ j − X w j ∥ 2 \min_{\hat{w}_j \in \text{quant space}} \| X \hat{w}_j - Xw_j \|^2 w^j∈quant spacemin∥Xw^j−Xwj∥2
但关键在于,它不仅考虑本列误差,还考虑误差对输出的影响,使用二阶信息(Hessian 近似)。
✅具体步骤
① 收集校准数据(X)
② 构造输入协方差矩阵 H H H
GPTQ 关键:使用输入协方差 H = X T X H = X^T X H=XTX 来估计量化误差的影响
③ 列优先贪心量化
对于权重矩阵 W = [ w 1 , w 2 , … , w d ] W = [w_1, w_2, \dots, w_d] W=[w1,w2,…,wd],每次处理一个列 w j w_j wj:
将 w j w_j wj 量化为 w ^ j \hat{w}_j w^j,在量化空间中选择最接近的向量:
w ^ j = Quant ( w j ) \hat{w}_j = \text{Quant}(w_j) w^j=Quant(wj)
可以是对称或非对称 INT4、INT8,支持 per-channel/group 量化。
将量化引入的误差反馈到后续列中:
W j + 1 : d ← W j + 1 : d − ( w j − w ^ j ) T H j + 1 : d , j H j j W_{j+1:\,d} \leftarrow W_{j+1:\,d} - \frac{(w_j - \hat{w}_j)^T H_{j+1:\,d, j}}{H_{jj}} Wj+1:d←Wj+1:d−Hjj(wj−w^j)THj+1:d,j
这类似于在正交方向上“修正”后续列,使误差不会积累。
这个步骤就是 GPTQ 的亮点:
基于 Hessian 的权重误差传播修正,防止前面列的量化误差污染后面列
④ 重复直到所有列处理完毕
完成整层的量化,得到 W q W_q Wq
⑤ 替换权重,验证模型输出
PyTorch 框架中的 GPTQ 示例流程(简略)
X = collect_calibration_activations(layer, data) # shape [N, in_dim]
H = (X.T @ X) / N # Hessian approximation
for j in range(W.shape[1]):
w_j = W[:, j]
w_hat_j = quantize_column(w_j)
error = w_j - w_hat_j
W[:, j+1:] -= (error.T @ H[:, j+1:]) / H[j, j] # error feedback
W[:, j] = w_hat_j
其核心思想是:通过分析激活值的分布动态调整权重量化策略,显著降低量化误差,尤其适合GPT、LLaMA等百亿参数级模型。
核心原理
1. 传统量化的问题
权重平等量化:普通INT8量化对所有权重统一缩放,但实际中不同权重对模型输出的影响差异极大。
激活值分布不均:某些通道(Channel)或注意力头的激活值范围更大,需要更高精度。
2. AWQ的解决方案
保护关键权重:
按通道缩放Per-channel Scaling:
对每个权重通道单独计算缩放因子,而非全局统一缩放。
如何做?
识别激活的“重要区域”(高幅值、高敏感性)
对重要的列或 group 的权重提前放大(scaling)
用一个缩放因子 s j s_j sj 缩放列 W j W_j Wj:
W j ′ = s j ⋅ W j W'_j = s_j \cdot W_j Wj′=sj⋅Wj
然后对 W j ′ W'_j Wj′ 做量化:
W ^ j ′ = Quantize ( W j ′ ) \hat{W}'_j = \text{Quantize}(W'_j) W^j′=Quantize(Wj′)
最后在推理时再除以 s j s_j sj,即:
X W ≈ X ⋅ ( 1 s j ⋅ W ^ j ′ ) X W \approx X \cdot \left( \frac{1}{s_j} \cdot \hat{W}'_j \right) XW≈X⋅(sj1⋅W^j′)
方法 | 是否量化激活 | 是否量化权重 | 离群值处理 | 是否需要蒸馏 | 优点 | 缺点 |
---|---|---|---|---|---|---|
ZeroQuant | ✅(token-wise) | ✅(group-wise) | ❌ | ✅(层级蒸馏) | 适用于多种场景 | 推理略慢 |
LLM.int8() | ✅(混合精度) | ✅(INT8) | ✅ | ❌ | 精度好 | 推理速度下降20% |
SmoothQuant | ✅(缩放值处理) | ✅ | ❌ | ❌ | 缩放处理简单有效 | 需提前统计缩放值 |
GPTQ | ❌ | ✅(INT4) | ❌ | ✅(优化最小误差) | 高压缩率 | 训练复杂 |