AWQ 与 GPTQ 量化方法对比
AWQ 和 GPTQ 是当前最主流的 LLM 训练后量化方法,两者都支持 INT4 量化但在算法原理和性能特征上有显著差异。本文深入对比两种方法的原理、效果和适用场景。
GPTQ 原理
GPTQ 基于 Optimal Brain Surgeon(OBS)框架,逐层量化权重并最小化量化误差:
- 按行依次量化每个权重
- 利用 Hessian 矩阵计算量化误差
- 通过剩余权重的调整来补偿量化误差
python
# GPTQ 量化核心逻辑
def gptq_quantize_layer(weight, hessian, bits=4, group_size=128):
"""GPTQ 逐行量化"""
quantized = torch.zeros_like(weight)
errors = torch.zeros_like(weight)
for i in range(weight.shape[0]):
# 量化当前行
w = weight[i]
q = quantize_to_int(w, bits, group_size)
quantized[i] = q
# 计算量化误差
err = (w - dequantize(q)) / torch.diag(hessian)[i]
errors[i] = err
# 补偿到后续权重
weight[i+1:] -= err.unsqueeze(1) * hessian[i, i+1:].unsqueeze(0)
return quantizedAWQ 原理
AWQ(Activation-Aware Weight Quantization)的核心观察是:并非所有权重同等重要,与激活值交互大的权重(salient weights)对精度影响更大:
- 分析激活值识别 salient 权重
- 对 salient 权重应用缩放因子降低量化误差
- 搜索最优缩放因子
python
# AWQ 核心逻辑
def awq_quantize(weight, activations, bits=4, group_size=128):
"""AWQ 激活感知量化"""
# 1. 识别 salient 权重
importance = activations.abs().max(dim=0).values # 逐通道重要性
salient_mask = importance > importance.median()
# 2. 搜索最优缩放因子
best_scale = search_optimal_scale(
weight, activations, salient_mask, bits, group_size
)
# 3. 应用缩放后量化
scaled_weight = weight * best_scale.unsqueeze(0)
quantized = quantize_to_int(scaled_weight, bits, group_size)
return quantized, best_scale关键差异对比
| 维度 | GPTQ | AWQ |
|---|---|---|
| 核心思想 | Hessian 补偿 | 激活感知缩放 |
| 校准数据需求 | 128+ 样本 | 128+ 样本 |
| 量化速度 | 较慢(Hessian 计算) | 较快(仅前向传播) |
| 推理速度 | 与 AWQ 相当 | 与 GPTQ 相当 |
| 精度(INT4) | 好 | 略好 |
| FP16 残差 | 不需要 | 不需要 |
| 实现复杂度 | 高 | 中 |
选择建议
- 追求最高精度:AWQ(激活感知通常带来更好的量化质量)
- 已有 GPTQ 工作流:继续使用 GPTQ
- 需要快速量化:AWQ 速度更快
- 多模态模型:AWQ 对视觉编码器更友好
实际性能测试
在 Llama-2-7B 上的 MMLU 评测:
| 量化方案 | MMLU | HumanEval | 显存占用 |
|---|---|---|---|
| FP16 | 45.3 | 12.8 | 13.5GB |
| GPTQ-INT4 (g128) | 43.8 | 11.5 | 3.9GB |
| AWQ-INT4 (g128) | 44.1 | 12.0 | 3.9GB |
| GPTQ-INT4 (g32) | 44.5 | 12.2 | 4.2GB |
| AWQ-INT4 (g32) | 44.8 | 12.5 | 4.2GB |
注意
具体精度差异取决于模型和校准数据。AWQ 在某些任务上优于 GPTQ,但在其他任务上可能持平。建议针对目标应用做 A/B 测试。