Files
2026-05-16 17:16:51 +08:00

224 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GPTQ与AWQ
## GPTQGeneral Purpose Post-Training Quantization
GPTQ是一种针对LLM的后训练量化方法由Frantar等人于2023年提出主要用于将模型权重从FP16量化到INT4/INT3同时保持较好的模型质量。
### 核心思想:逐层量化与权重反解
GPTQ的核心是**权重反解**Weight Dequantization技术逐层处理而非一次性量化
### 量化流程
```
输入: 预训练好的FP16模型权重 W ∈ R^(n×m)
输出: 量化权重 W_q ∈ INT^n×m兼反量化辅助信息
对每一层Column-wise
1. 量化当前层权重为INT
2. 计算量化误差
3. 将误差补偿回后续未量化列
4. 迭代处理所有列
```
### 权重反解原理
GPTQ利用了神经网络的权重冗余性通过精确计算每个权重列的量化误差并将误差补偿"反解")到后续列,可以最小化整体量化误差。
```python
# GPTQ 伪代码
def gptq_quantize(W, bits=4):
n, m = W.shape
W_quant = torch.zeros_like(W, dtype=torch.int8)
H = W.T @ W # Hessian矩阵近似
for col in range(m):
# 量化当前列
w_col = W[:, col]
scale = w_col.abs().max() / (2**(bits-1) - 1)
w_q = (w_col / scale).round()
# 计算量化误差
error = w_col - w_q * scale
# 将误差补偿到后续列
# 使用Hessian信息指导误差分配
if col < m - 1:
W[:, col+1:] -= error @ H[col, col+1:] / H[col, col]
W_quant[:, col] = w_q
return W_quant, scales
```
### Cholesky分解加速
原始GPTQ对每一列都需要矩阵求逆计算量极大。优化版本使用Cholesky分解预处理
```python
# Cholesky分解预处理
L = torch.cholesky(H, upper=False)
L_inv = torch.inverse(L)
# 后续处理时可以直接用 L_inv
```
将预处理复杂度从 O(m² n) 降低到 O(m³),对于大模型效果显著。
### GPTQ性能指标
| 量化精度 | 权重大小压缩 | 精度损失Benchmark |
|---------|------------|----------------------|
| FP16 | 1x | 基线 |
| INT4 | ~4x | <2% (部分任务) |
| INT3 | ~5.3x | 3-5% |
| INT2 | ~8x | >10% (显著) |
## AWQActivation-aware Weight Quantization
AWQ是由Lin等人在2024年提出的量化方法核心洞察是**并非所有权重对模型精度同等重要,量化误差对模型影响不同,需要根据激活值分布来决定量化策略**。
### 核心思想:激活感知
AWQ发现在LLM中只有约1%的权重(对应较大的激活值)贡献了大部分模型性能。保护这些重要权重可以显著减少量化误差。
### 量化方法
```python
def awq_quantize(W, X, bits=4):
"""
W: 权重矩阵 (out_features, in_features)
X: 校准激活值 (batch, in_features)
"""
# 计算每个权重通道的重要性
importance = (X.abs().mean(dim=0)) # per-channel 重要性
# 找出最重要权重top-k
k = int(W.shape[1] * 0.01) # 1% 保护比例
idx = torch.topk(importance, k).indices
# 对重要权重使用更高精度
W_protected = W[:, idx]
W_quantized = quantize(W[:, idx], bits=bits) # 保持高精度
# 其他权重正常量化
W_normal = quantize(W[:, idx], bits=bits)
return W_quantized, W_normal, scales
```
### 与GPTQ的对比
| 特性 | GPTQ | AWQ |
|------|------|-----|
| 量化依据 | 基于Hessian梯度敏感性 | 基于激活值分布 |
| 关注点 | 权重本身的重要性 | 权重对激活值的影响 |
| 保护策略 | 误差补偿 | 跳过/高精度保留 |
| 精度INT4 | 较好 | 更优(部分场景) |
| 速度 | 较慢(逐列处理) | 较快 |
### AWQ实现要点
```python
# AWQ校准流程
1. 收集激活值统计X = 模型中某层的输入
2. 计算重要性I = |X|.mean(dim=0)
3. 保护最重要权重W[:, top_k_idx] 保持FP16
4. 量化剩余权重W[:, others] INT4
5. 反量化计算恢复FP16用于推理
```
## LLM.int8()方法
LLM.int8()是Bitwise BLAS提出的INT8推理方法解决大模型>6B参数INT8量化精度下降严重的问题。
### 离群值问题
研究发现LLM的激活值中存在系统性离群值Outliers约0.1%的维度有远大于平均值的激活值,这些离群值如果被量化会引入巨大误差。
```
激活值分布: 大部分值在 [-1, 1],但某些维度在 [10, 100]
量化后: INT8范围 [-128, 127],离群值被截断,误差巨大
```
### 混合精度分解
LLM.int8()将计算分解为两部分:
```python
def llm_int8_linear(x, w, bias=None):
# x: (batch, seq, hidden)
# w: (out_features, in_features) INT8
# Step 1: 分离离群值维度
x_normal = x[:, :, normal_dims] # INT8处理
x_outlier = x[:, :, outlier_dims] # FP16处理
# Step 2: INT8矩阵乘法正常维度
y1 = F.linear(x_normal, w_normal) # INT8 → INT32 → FP16
# Step 3: FP16矩阵乘法离群值
y2 = F.linear(x_outlier, w_outlier) # FP16
# Step 4: 结果融合
y = y1 + y2 + bias if bias else y1 + y2
return y
```
### 离群值检测
```python
def find_outlier_dims(X, threshold=2.5):
"""检测离群值维度"""
# 计算每个维度的重要性(绝对值统计)
importance = X.abs().mean(dim=(0, 1)) # (hidden,)
# 超过阈值的维度标记为离群值
outlier_mask = importance > threshold * importance.median()
return outlier_mask
```
### LLM.int8()效果
| 模型 | INT8精度损失 | 内存节省 | 速度提升 |
|------|------------|---------|---------|
| LLaMA-7B | <0.5% | ~50% | ~1.4x |
| LLaMA-13B | <0.5% | ~50% | ~1.4x |
| LLaMA-65B | <1% | ~50% | ~1.3x |
### 与GPTQ/AWQ的关系
1. **GPTQ** → 解决权重压缩问题,但未处理激活离群值
2. **AWQ** → 通过激活感知优化权重分配,间接缓解离群值问题
3. **LLM.int8()** → 直接处理离群值问题提供完整INT8推理方案
## 实践建议
### 量化方法选择
| 场景 | 推荐方法 | 理由 |
|------|---------|------|
| 4-bit推理 | AWQ | 精度更好 |
| 8-bit推理 | LLM.int8() | 离群值处理成熟 |
| 快速部署 | GPTQ | 工具链完善 |
| 内存极度受限 | GPTQ 3-bit | 压缩比高 |
### 量化工具
```bash
# GPTQ (AutoGPTQ库)
pip install auto-gptq
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(...).quantize(quantization_config)
# AWQ (vLLM集成)
pip install vllm
from vllm import LLM
llm = LLM(model, quantization="awq")
# LLM.int8() (Transformers集成)
model = AutoModelForCausalLM.from_pretrained(
..., load_in_8bit=True, llm_int8_threshold=6.0
)
```