模型量化全解析:GPTQ、AWQ、GGUF 原理与实践

1. 引言

一个 70B 参数的 LLM 在 FP16 下需要 140GB 显存,这远超消费级硬件的能力。模型量化通过降低数值精度(FP16 → INT8/INT4)来减少内存占用和提升推理速度,同时尽量保持模型质量。

三大主流量化方案:

方案 精度 速度 质量 适用场景
GPTQ INT4/INT8 GPU 推理
AWQ INT4 更快 更好 GPU 推理(推荐)
GGUF Q2-Q8 中等 灵活 CPU/混合推理

2. 量化原理

2.1 基本概念

原始权重 (FP16): 0.0234  →  占用 16 bit
量化后 (INT8):   59      →  占用 8 bit + scale/zero_point

量化公式: q = round(w / scale) + zero_point
反量化:   w ≈ (q - zero_point) * scale

2.2 对称 vs 非对称量化

对称量化 (Symmetric):
  scale = max(|w|) / 127
  q = round(w / scale)
  范围: [-127, 127]

非对称量化 (Asymmetric):
  scale = (max(w) - min(w)) / 255
  zero_point = round(-min(w) / scale) - 128
  q = round(w / scale) + zero_point
  范围: [-128, 127]

3. GPTQ 量化

3.1 原理

GPTQ 基于 OBQ (Optimal Brain Quantization) 框架,逐列量化权重矩阵,同时用 Hessian 信息补偿量化误差:

核心思想:
1. 逐列处理权重矩阵
2. 量化一列后,用 Hessian 逆矩阵调整剩余列来补偿误差
3. 使用 Cholesky 分解加速计算

3.2 实践代码

from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig

# 加载模型
model_id = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_id)

# GPTQ 量化配置
quantization_config = GPTQConfig(
    bits=4,                    # 量化位数
    dataset="c4",              # 校准数据集
    group_size=128,            # 分组大小
    desc_act=True,             # 按激活值降序处理
    damp_percent=0.01,         # Hessian 正则化
)

# 量化
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    quantization_config=quantization_config,
    device_map="auto",
)

# 保存
model.save_pretrained("llama2-7b-gptq-4bit")
tokenizer.save_pretrained("llama2-7b-gptq-4bit")

3.3 自定义校准数据

from datasets import load_dataset

def get_calibration_dataset(tokenizer, num_samples=128):
    """准备校准数据集"""
    dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="train")
    
    samples = []
    for item in dataset:
        if len(item["text"].strip()) > 100:
            tokens = tokenizer(item["text"], return_tensors="pt")
            samples.append(tokens)
            if len(samples) >= num_samples:
                break
    return samples

quantization_config = GPTQConfig(
    bits=4,
    dataset=get_calibration_dataset(tokenizer),  # 自定义校准数据
    group_size=128,
)

4. AWQ 量化

4.1 原理

AWQ (Activation-aware Weight Quantization) 的核心洞察:不是所有权重同等重要。少数"显著通道"对模型输出影响巨大,应该被保护。

AWQ 方法:
1. 用校准数据计算每个权重通道的激活值大小
2. 对"显著通道"乘以缩放因子 s > 1,放大后再量化
3. 缩放因子通过搜索优化,最小化量化误差

关键优势:不依赖反向传播,不需要大量校准数据

4.2 实践代码

# 使用 AutoAWQ 工具
pip install autoawq
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

model_path = "meta-llama/Llama-2-7b-hf"
quant_path = "llama2-7b-awq-4bit"

# 加载模型
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)

# 量化配置
quant_config = {
    "zero_point": True,       # 非对称量化
    "q_group_size": 128,      # 分组大小
    "w_bit": 4,               # 量化位数
    "version": "GEMM",        # GEMM 内核(更快)
}

# 执行量化
model.quantize(
    tokenizer,
    quant_config=quant_config,
    calib_data="pileval",     # 校准数据集
)

# 保存
model.save_pretrained(quant_path)
tokenizer.save_pretrained(quant_path)

4.3 AWQ 推理

from transformers import AutoModelForCausalLM, AutoTokenizer, AwqConfig

model = AutoModelForCausalLM.from_pretrained(
    "llama2-7b-awq-4bit",
    device_map="auto",
    attn_implementation="flash_attention_2",  # 配合 FlashAttention
)

tokenizer = AutoTokenizer.from_pretrained("llama2-7b-awq-4bit")
inputs = tokenizer("Hello, how are you?", return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=100)
print(tokenizer.decode(outputs[0]))

5. GGUF 量化

5.1 原理

GGUF (GPT-Generated Unified Format) 是 llama.cpp 使用的格式,支持多种量化类型:

Q2_K:  2-bit 量化,最小体积,质量损失大
Q3_K_M: 3-bit 中等
Q4_0:  4-bit 基础量化
Q4_K_M: 4-bit 中等(推荐平衡点)
Q5_K_M: 5-bit 高质量
Q6_K:  6-bit 接近原始质量
Q8_0:  8-bit 几乎无损

5.2 转换流程

# 1. 克隆 llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make -j

# 2. 转换 HF 模型为 GGUF
python convert_hf_to_gguf.py \
    /path/to/llama2-7b-hf \
    --outfile llama2-7b-f16.gguf \
    --outtype f16

# 3. 量化
./llama-quantize \
    llama2-7b-f16.gguf \
    llama2-7b-q4_k_m.gguf \
    Q4_K_M

# 4. 测试推理
./llama-cli \
    -m llama2-7b-q4_k_m.gguf \
    -p "Hello, how are you?" \
    -n 100 \
    --temp 0.7

5.3 Python 推理

from llama_cpp import Llama

llm = Llama(
    model_path="llama2-7b-q4_k_m.gguf",
    n_ctx=4096,        # 上下文长度
    n_gpu_layers=35,   # GPU 层数(剩余用 CPU)
    n_threads=8,       # CPU 线程数
)

output = llm.create_chat_completion(
    messages=[{"role": "user", "content": "解释量子计算的基本原理"}],
    temperature=0.7,
    max_tokens=500,
)
print(output["choices"][0]["message"]["content"])

6. 量化方案对比

6.1 显存占用对比(Llama-2-7B)

格式 位数 模型大小 推理显存 质量损失
FP16 16 14GB ~16GB 基准
GPTQ 4 3.8GB ~5GB <1%
AWQ 4 3.8GB ~5GB <0.5%
GGUF Q4_K_M 4 4.1GB ~5.5GB ~1%
GGUF Q8_0 8 7.2GB ~8.5GB <0.1%

6.2 推理速度对比(tokens/s, RTX 3090)

格式 7B 13B 70B
FP16 45 25 OOM
GPTQ-4bit 68 40 12
AWQ-4bit 75 45 15
GGUF Q4_K_M (GPU) 55 30 8

6.3 选择建议

纯 GPU 推理 → AWQ (速度最快,质量最好)
需要 CPU 混合推理 → GGUF Q4_K_M
需要最大兼容性 → GPTQ (生态最成熟)
质量优先 → GGUF Q8_0 或 Q6_K

7. 总结

模型量化是 LLM 部署的关键技术。核心要点:

  1. AWQ 是目前 GPU 推理的最佳选择:速度快、质量好
  2. GGUF 适合 CPU 或混合推理:灵活的量化级别选择
  3. GPTQ 生态最成熟:HuggingFace 支持最好
  4. 量化不是免费的:总有质量损失,关键是找到可接受的平衡点

更多推荐