模型量化深入实战:GPTQ 与 AWQ 怎么选怎么跑

当大模型从实验室走向生产,显存与带宽成为最硬的约束。一个 70B 参数的模型在 FP16 下要占用超过 140GB 显存,意味着即便用两张 80GB 的 A100 也难以放下。权重量化把每个参数从 16 位压到 4 位甚至 3 位,几乎可以线性地缩减显存占用,同时借助专用 kernel 还能提升推理吞吐。本文聚焦两种最主流的训练后权重量化(Post-Training Quantization,简称 PTQ)方法:GPTQ 与 AWQ。我们会讲清它们各自的原理差异、校准集到底在干什么,并给出可直接照抄运行的量化与推理示例,帮你把「选哪个、怎么跑」这件事一次弄明白。

一、权重量化回顾:从 INT8 到 INT4

量化本质是用量化的低比特整数去近似原始的浮点权重。最常见的做法是仿射映射:

W_int = round(W_float / scale) + zero_point
W_float ≈ (W_int - zero_point) * scale

这里 scale 是缩放因子,zero_point 把浮点的零点对齐到整数网格。围绕这两个量,有几组常被混淆的概念。

比特宽度:INT8 把权重压到 8 位,精度损失通常很小,是早期部署的稳妥选择;INT4 把权重压到 4 位,显存直接砍到原来的四分之一,但精度风险显著上升,也是 GPTQ 与 AWQ 主攻的战场;再往下 INT3、INT2 已经接近可用性的边界,需要非常谨慎的校准。

对称与非对称:对称量化令 zero_point = 0,即整数网格以 0 为中心,实现简单、在 GPU 上乘加友好;非对称量化允许非零的 zero_point,能更好贴合权重分布不关于 0 对称的情况(例如激活值多为非负)。权重通常用对称量化即可,而激活更常用非对称量化。

groupwise(分组量化):如果对整个权重矩阵只用一对 scale/zero_point,大数值会撑爆动态范围、小数值被舍入误差吞掉。分组量化把权重按行(或按固定大小的块)切成若干 group,每组独立计算量化参数。GPTQ 与 AWQ 在实践中普遍采用 group size 为 128 的配置,即在每 128 个连续权重上共享一份量化参数,这是精度与开销之间被广泛验证的甜点。

需要强调的是,本文讨论的是「权重量化」(weight-only quantization):只把权重压成低比特,激活仍保留较高精度(如 FP16)。它和省算力、压激活的「权激活联合量化」不同,目标是在几乎不掉点的前提下最大化显存收益。

二、GPTQ 原理:二阶信息驱动的逐列量化

GPTQ 来自论文「GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers」(arXiv:2210.17323,ICLR 2023,作者 Elias Frantar 等,已核验)。它要解决的核心难题是:当比特数降到 3 到 4 位时,逐权重独立四舍五入会累积出不可接受的错误,怎么办。

GPTQ 的理论基础是 OBQ(Optimal Brain Quantization)这一类利用二阶信息的量化方法。它的关键观察是:量化某一列权重造成的误差,可以通过「补偿」尚未量化的其他列来部分抵消。具体流程如下:

  1. 逐列(column-wise)量化:GPTQ 按列处理权重矩阵。处理某一列时,这一列先被量化;由于量化引入了误差,算法会计算一个补偿量,把它加回到「尚未量化」的剩余列上,让整体重构误差最小。
  2. Hessian 补偿:补偿量来自该层激活的二阶统计量,也就是 Hessian 矩阵(近似为激活外积的平均)。直观地说,Hessian 告诉我们「改哪个权重代价更小」,于是 GPTQ 把量化误差按这个敏感度分配到其他列,做到误差最小化。
  3. 校准集的作用:Hessian 需要数据来估计,这就是校准集(calibration set)的用途。校准集不需要很大,通常从几百到几千条文本里取一小批(例如 128 个样本、每个 2048 token)就足够。它只用来统计激活分布,不参与任何反向传播或梯度更新,因此 GPTQ 仍是 PTQ 而非 QAT(量化感知训练)。

GPTQ 的工程价值在于效率和规模:论文报告它可以在约 4 个 GPU 小时内把 175B 模型量化到 3 到 4 位,且仅带来可忽略的精度下降;配合专用 kernel,相对 FP16 推理可获约 3.25 倍(A100)到 4.5 倍(A6000)的加速。

值得注意,GPTQ 依赖校准集来估计 Hessian,如果校准域和部署域偏差太大,补偿方向可能不够准。这是后续选型时要权衡的点。

三、AWQ 原理:激活感知的重要权重保护

AWQ 来自论文「AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration」(arXiv:2306.00978,MLSys 2024 Best Paper,作者 Ji Lin 等,已核验)。它的出发点与上述「逐列补偿」路线完全不同。

AWQ 的核心发现是:大模型里并非所有权重都同等重要,只要保护约 1% 的「显著权重」(salient weights),就能大幅降低量化误差。而判断哪些权重显著,不能看权重本身的大小,要看「激活分布」——那些对应大激活的权重通道更为关键。

由此 AWQ 的做法是:

  1. 激活感知地识别显著通道:通过离线采集一批激活,统计每个权重通道对应的激活大小,激活大的通道就是要保护的对象。
  2. 用 scale 缩放来保护,而非混合精度:如果直接把显著权重保留高精度、其余压成低比特,就成了硬件不友好的混合精度,kernel 很难写得快。AWQ 巧妙地改为:给显著通道乘上一个放大系数,把它们的数值「撑大」,再做统一低比特量化;推理时再用等效的逆向缩放抵消回来。由于这只是对权重的等价变换,硬件上仍是纯粹的低比特乘法,kernel 友好。
  3. scale 搜索:放大系数不是拍脑袋定的,而是在一个小的搜索空间里找到让量化误差最小的那个值,通常用网格搜索配合校准集上的困惑度(perplexity)评估。

AWQ 的一大卖点是「不依赖重建、不易过拟合校准集」。它不做基于反向传播的重建,因此泛化性更好,跨领域、多模态(如视觉语言模型)也能直接套用,论文还配套了 TinyChat 推理框架做端侧加速。

四、GPTQ 与 AWQ 全面对比

把两种方法放在一张表里对照,选型会更清晰:

维度GPTQAWQ
核心思想二阶信息逐列量化加误差补偿激活感知保护显著权重通道
是否依赖校准集依赖,用于估计 Hessian依赖,用于统计激活与搜 scale
是否做重建是,基于近似二阶补偿否,仅靠等价缩放变换
典型比特4-bit / 3-bit 成熟4-bit 成熟,更低比特在演进
精度表现校准充分时极好,规模大也稳泛化好,跨域与多模态友好
硬件友好度需配套 GPTQ kernel纯低比特乘法,kernel 友好
推理生态AutoGPTQ、ExLlama、vLLM 等autoawq、llama.cpp、vLLM、TinyChat
显存收益4-bit 约为 FP16 的四分之一同左,相近
速度特征高带宽机型加速明显端侧与融合 kernel 加速明显

简单来说:两者都能把模型压到 4-bit 并获得接近 FP16 的效果,显存收益相当。差异主要在「方法论与泛化倾向」——GPTQ 走精确补偿路线,AWQ 走保护显著通道、更重硬件与泛化的路线。

五、实战:用 AutoGPTQ 与 autoawq 量化一个模型

下面给出两个最小可运行示例。先安装依赖:

pip install auto-gptq transformers accelerate
pip install awq

5.1 用 AutoGPTQ 量化

AutoGPTQ 的 BaseQuantizer 需要一份校准数据加载器。下面示例加载一个因果语言模型,并用一段文本构造 128 条校准样本。

from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizer
from auto_gptq.utils import get_dataset

MODEL_ID = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"

# 1. 准备分词器与基座模型
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, use_fast=True)
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, device_map="auto")

# 2. 构造校准数据(取 128 个样本,每个最多 2048 token)
calib_data = get_dataset(
    "pileval",
    tokenizer,
    nsamples=128,
    max_seqlen=2048,
)

# 3. 定义量化配置并启动量化
quantizer = BaseQuantizer(
    model,
    tokenizer,
    calibration_dataset=calib_data,
    wandb=False,
)
quantizer.quantize()

# 4. 保存为 GPTQ 权重与配套 config
quantizer.save_quantized("tinyllama-1.1b-gptq-4bit", use_safetensors=True)
print("GPTQ 量化完成,已保存到 tinyllama-1.1b-gptq-4bit")

要点:calibration_dataset 是把校准集喂给模型的唯一入口;group size 等更细的参数在 BaseQuantizer 的实际构造里通过 qparams 传入,常用 bits=4group_size=128。校准集不必与下游任务同域,通用语料(如 pileval、wiki)通常就够。

5.2 用 autoawq 量化

autoawq 的 API 更简洁,同样需要校准数据。下面示例用本地文本列表作为校准源。

from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

MODEL_ID = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
quant_config = {"zero_point": False, "q_group_size": 128, "w_bit": 4}

# 1. 加载模型与分词器
model = AutoAWQForCausalLM.from_pretrained(MODEL_ID)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)

# 2. 准备校准文本(这里用若干条示例句子演示)
calib_texts = [
    "模型量化是压缩大模型的重要手段。",
    "Activation-aware 方法关注激活分布。",
    "GPTQ 使用二阶信息做逐列补偿。",
] * 40  # 复制以凑足样本量

# 3. 量化并保存
model.quantize(tokenizer, quant_config=quant_config, calib_data=calib_texts)
model.save_quantized("tinyllama-1.1b-awq-4bit")
tokenizer.save_pretrained("tinyllama-1.1b-awq-4bit")
print("AWQ 量化完成,已保存到 tinyllama-1.1b-awq-4bit")

要点:q_group_size=128w_bit=4 是官方推荐起点;calib_data 直接传文本列表即可,autoawq 内部会做分词与统计。它不做重建,所以校准集即使偏小也不易过拟合。

六、推理侧:llama.cpp 与 vLLM 加载量化权重

量化产出的权重要落到推理引擎上才有价值。下面演示两条最常用路径。

6.1 vLLM 加载 GPTQ / AWQ 权重

vLLM 原生支持两种格式,只要模型目录带对应的 quantization_config 即可自动识别:

# 以 AWQ 为例启动兼容 OpenAI 的推理服务
python -m vllm.entrypoints.openai.api_server \
    --model tinyllama-1.1b-awq-4bit \
    --quantization awq \
    --dtype half \
    --gpu-memory-utilization 0.9

# 若加载 GPTQ 权重,则把 --quantization 改为 gptq

在代码里调用同样简单:

from vllm import LLM, SamplingParams

llm = LLM(model="tinyllama-1.1b-awq-4bit", quantization="awq")
params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=256)
outputs = llm.generate(["请用量化术语解释什么是 groupwise。"], params)
for o in outputs:
    print(o.outputs[0].text)

6.2 llama.cpp 走 GGUF 路线

llama.cpp 使用自有的 GGUF 格式,需要先把 HF 权重转成 GGUF,再做 imatrix 校准以获得更好效果:

# 1. 把量化后的 HF 模型转成 GGUF(以 AWQ 权重为例)
python convert_hf_to_gguf.py tinyllama-1.1b-awq-4bit \
    --outfile tinyllama-1.1b-q4_0.gguf

# 2. (可选)用校准集生成重要性矩阵 imatrix
./llama-imatrix -m tinyllama-1.1b-q4_0.gguf \
    -f calib.txt -o tinyllama-imatrix.dat

# 3. 用 imatrix 做更精准的 Q4_K_M 量化
./llama-quantize --imatrix tinyllama-imatrix.dat \
    tinyllama-1.1b-q4_0.gguf tinyllama-1.1b-q4_k_m.gguf Q4_K_M

# 4. 本地运行推理
./llama-cli -m tinyllama-1.1b-q4_k_m.gguf -p "模型量化的好处有哪些?" -n 256

说明:llama.cpp 的 Q4_K_M 等是它自带的 k-quant 方案,与 GPTQ/AWQ 的思路同源(都是 4-bit 权重量化),但格式独立。若你已有 GPTQ/AWQ 权重,更省事的做法是让 vLLM 直接加载;若目标是 CPU 或端侧、跨平台部署,GGUF 是更顺的选择。

七、选型建议:不同部署场景怎么选

结合上面的原理与生态,给出一份可操作的选型清单:

  • 云端高吞吐、用 vLLM 批量服务:GPTQ 与 AWQ 都可。若你的校准集与线上域一致、追求极限精度,GPTQ 的二阶补偿往往更稳;若模型要跨多领域、含多模态,或你不愿花心思调校准,AWQ 的泛化更省心。
  • 端侧与边缘 GPU(如手机、笔记本独显):优先 AWQ 及其 TinyChat,或者 llama.cpp 的 GGUF 路线。AWQ 的纯低比特乘法对端侧 kernel 友好,GGUF 则胜在零依赖、跨平台。
  • 显存极度紧张、要塞进单张消费级显卡:两者 4-bit 显存收益相近,关键看哪个格式在你的推理框架里支持更成熟。vLLM + GPTQ/AWQ 在数据中心常见,llama.cpp 在本地更轻。
  • 要压到 3-bit 甚至更低:GPTQ 在低位宽下的补偿理论更占优,但务必用有代表性的校准集并做困惑度验证;AWQ 在极低比特下收益会下降,建议先小模型实测再决定。
  • 不想准备校准集、想要开箱即用:llama.cpp 提供了大量社区预量化好的 GGUF 文件,直接下载即可;若坚持自己量化,AWQ 对校准集规模与质量的敏感度相对更低。

一句话总结:校准充分且追求极致精度选 GPTQ,重视泛化与硬件友好、跨域部署选 AWQ,端侧与多平台则把 llama.cpp 的 GGUF 纳入备选。

小结

本文系统梳理了 GPTQ 与 AWQ 两种主流权重量化方法。GPTQ 用近似二阶信息做逐列量化,并通过 Hessian 补偿把误差分摊到未量化列,校准集用于估计 Hessian;AWQ 则激活感知地识别约 1% 的显著权重,通过等效缩放而非混合精度来保护它们,且不依赖重建、泛化更好。两者在 4-bit 下都能把显存压到 FP16 的四分之一并维持接近原模型的精度。实战部分给出了用 AutoGPTQ 与 autoawq 量化的可运行脚本,以及用 vLLM 加载量化权重、用 llama.cpp 走 GGUF 路线的完整命令。选型上:校准充分求精度选 GPTQ,重泛化与硬件友好选 AWQ,端侧多平台再考虑 GGUF。量化不是「越狠越好」,务必用困惑度与下游任务指标验证后再上线。

参考与延伸阅读

  1. Elias Frantar、Saleh Ashkboos、Torsten Hoefler、Dan Alistarh。GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers。arXiv:2210.17323,ICLR 2023。(已核验:标题与编号经 arXiv 页面确认)
  2. Ji Lin、Jiaming Tang、Haotian Tang 等。AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration。arXiv:2306.00978,MLSys 2024 Best Paper。(已核验:标题与编号经 arXiv 页面确认)
  3. AutoGPTQ 项目文档与示例。https://github.com/PanQiWei/AutoGPTQ (链接有效性待核实,访问前请确认仓库最新地址)
  4. autoawq(llm-awq)仓库与 TinyChat 说明。https://github.com/mit-han-lab/llm-awq (链接有效性待核实,访问前请确认仓库最新地址)
  5. llama.cpp 仓库与 GGUF 量化说明。https://github.com/ggerganov/llama.cpp (链接有效性待核实,访问前请确认仓库最新地址)
  6. vLLM 官方文档中关于 GPTQ 与 AWQ 后端的说明。https://docs.vllm.ai (链接有效性待核实,请按官网导航查找量化相关章节)
本文累计阅读