LLM 量化技术概述及 AWQ 和 GPTQ 介绍

一、前言

近期在学习 Qwen3 的模型结构时,看到了 Qwen 使用了 GPTQ 与 AWQ 量化方案,于是便萌生了介绍 LLM 量化技术的想法,笔者将用 2-3 篇文章,给读者们介绍大模型量化的技术。

量化是指将高精度计算的浮点型数据近似为低比特位数据(如 int16、int8、int4 等)的过程,此过程需在不显著损耗精度的同时,提升模型推理效率并降低内存占用。特别是在当前主流大语言模型(LLM)的参数量轻松突破万亿规模的情况下,量化技术对于高效低成本部署 LLM 尤为重要。而且由于 LLM 的参数量巨大,当前主流的模型都采用 PTQ 后量化技术,从而降低量化过程带来的成本。

在正式开始这篇文章之前,我们首先来了解一下 LLM 量化的相关概念。

二、LLM 量化相关概念

2.1 LLM 量化常用的数值格式

2.2 LLM 量化对象

在部署推理时,LLM 的量化对象与传统的 CNN 有所不同,除了权重与激活以外,还增加了 LLM 特有的 KV Cache。所以,LLM 的量化对象主要是权重、激活和 KV Cache。

  • ​权重量化:​仅对 LLM 中的权重进行量化,常见的方法有 GPTQ (W4A16,权重量化为 INT4,激活保持 FP16/BF16)、AWQ(W4A16/W8A16)等;
  • ​激活量化:​对 LLM 中的激活进行量化,常见的方法有 SmoothQuant (W8A8)、LLM.int8()等,由于激活分布范围大且动态变化,相比权重量化更具挑战;
  • **KV Cache 量化:**在 LLM 推理中,为避免重复计算,会缓存 Attention 中的 Key/Value 向量(KV Cache)。​ ​ KV Cache 的大小与 上下文长度线性相关,是长文本推理时的主要显存瓶颈。常见的方法有 KV Cache INT8/INT4 量化。

LLM 的实际部署过程中,常见的量化方案包括:

  • W4A16(GPTQ、AWQ) :权重量化为 INT4,激活保持 FP16/BF16。
  • W8A16 : 权重量化为 INT8,激活保持 FP16/BF16。
  • W8A8(SmoothQuant): 权重和激活均量化为 INT8。
  • KV​ Cache INT8 :缓解长上下文显存开销。

下面,我们将对具体的量化方法进行介绍。

三、主流 LLM 量化方法介绍

Qwen 系列模型使用了 AWQ、GPTQ 和 llama.cpp 中的量化技术,且推荐使用 AWQ 结合 AutoAWQ,所以本节我们先介绍此方法。

3.1 AWQ 结合 AutoAWQ量化方法介绍及使用示例

AWQ 全称为 ACTIVATION-AWARE WEIGHT QUANTIZATION,即​激活感知的权重量化,​ 是一种针对 LLM 的低比特权重量化的硬件友好方法。AWQ 在业界广泛应用,除了官方的支持llm-awq外,AutoAWQvLLMHuggingFaceNVIDIA TensorRT-LLMFastChat 等主流模型或框架也提供了对 AWQ 的支持。

3.1.1 AWQ 量化技术原理

AWQ 作者认为:

  1. 权重对于大语言模型的性能并不同样重要, 有一小部分(0.1%-1%)对模型精度影响较大的关键权重;跳过这些关键权重的量化将显著减少量化精度损失。
  2. 而且,关键权重对应于较大激活幅度的权重通道更加显着,因为它们处理更重要的特征,从而根据这个现象寻找关键权重。尽管将 0.1% 的权重保留为 FP16 可以在不明显增加模型大小的情况下提高量化性能,但这种混合精度数据类型会给系统实现带来困难(硬件效率低下)。
  3. 设计了一种 per-channel 缩放方法来自动搜索最佳缩放,从而减少显著权重的量化误差,这种方法不存在硬件效率低下的问题。

PPL:即困惑度 Perplexity,是语言模型预测序列的平均不确定性度量。PPL 越小,表示模型越"自信"且预测越接近真实分布;PPL 越大,说明预测分布和真实分布偏差更大。

上图是作者的实验,可以看出:

  • 左图:所有的权重都从 FP16 量化到 INT3,PPL 为 43.2;
  • 中图:基于激活分布找到了 1% 的关键权重,将关键权重保持 FP16 精度,其余权重量化到 INT3,PPL 由 43.2 大幅下降至 13.0,但这种混合精度格式在硬件上运行并不高效;
  • 右图: AWQ 执行 per-channel 缩放以保护关键权重从而减少量化误差,这里可以看到缩放 weight 后再做量化的 PPL 为 13.0,缩放本身未对精度产生影响。

权重的缩放因子 s 为超参数,作者在​ OPT-6.7B ​模型上做了对比实验,发现当 s 比较大比如等于 4 时,非关键通道的相对误差将会增加(非显著通道的误差将被放大)。

为了同时考虑关键权重和非关键权重,AWQ 选择自动搜索最佳(每个输入通道)缩放因​子,使某一层量化后的输出差最小。这就把量化问题建模为如下的最优化问题:

为提升该过程的稳定性,我们通过分析影响缩放因子(scaling factor)选择的相关因素,为最优缩放比例(optimal scale)定义了一个搜索空间(search space)。如前一部分所述,权重通道(weight channels)的显著性实际上由激活值缩放比例(activation scale)决定(即 "激活感知性",activation-awareness)。因此,AWQ 采用了一个极为简洁的搜索空间,具体如下:

其中 α 是用于平衡对关键 channel 与非关键 channel 的保护力度。通过在区间 [0, 1] 内执行快速网格搜索(grid search),可确定最优的 α 值(其中 ​α ​=0 代表不进行缩放;​α​=1 代表在我们的搜索空间内采用最激进的缩放策略)。此外,论文中还引入了权重裁剪(weight clipping)操作,以最小化量化过程中的均方误差(MSE)。

3.1.2 AWQ 量化模型示例

AWQ​ 与 HuggingFace Transformers 无缝兼容 ​,加载模型后可以直接 .quantize() 做量化,相关使用流程如下。

安装依赖
Plain 复制代码
pip3 install transformers accelerate autoawq
量化模型
Plain 复制代码
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
from transformers import AwqConfig

model_path = "facebook/opt-125m"
quant_path = "opt-125m-awq"
#量化参数配置
quant_config = {
    "zero_point": True,
    "q_group_size": 128,
    "w_bit": 4,
    "version": "GEMM"
}

# 加载模型
model = AutoAWQForCausalLM.from_pretrained(model_path, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
#准备校准数据
data = []
for msg in dataset:
    text = tokenizer.apply_chat_template(msg, tokenize=False, add_generation_prompt=False)
    data.append(text.strip())

# 量化
model.quantize(tokenizer, quant_config=quant_config,calib_data=data)

# 修改配置,保证和 Transformers 兼容
quantization_config = AwqConfig(
    bits=quant_config["w_bit"],
    group_size=quant_config["q_group_size"],
    zero_point=quant_config["zero_point"],
    version=quant_config["version"].lower(),
).to_dict()

model.model.config.quantization_config = quantization_config

# 保存模型
model.save_quantized(quant_path, safetensors=True, shard_size="4GB")
tokenizer.save_pretrained(quant_path)

quant_config 参数解析:

  • "w_bit": 权重量化的位宽
  • "q_group_size":量化不是对整个权重张量做一次缩放,而是分组处理,上述示例选择每组 128 个权重会共享一组缩放因子(scale)和零点(zero point),128 是一个常用折中值(Meta 在 LLaMA-2/3 的 INT4 AWQ 中也常用 128)。
  • "zero_point":是否使用零点(zero point)补偿,如果设成 False,就是对称量化(中心对齐 0),如果设成 True,就是非对称量化,可以更好覆盖权重分布,提高精度。
  • "version":`` 底层推理内核类型 (后端实现方式)。GEMM:通用矩阵乘法(General Matrix Multiplication),适合大模型的权重矩阵乘法。GEMV:矩阵-向量乘法(适合批次小、延迟敏感的场景)。一般推荐用 GEMM,因为推理框架(Transformers, vLLM 等)大部分优化都是基于 GEMM 内核。
加载量化后的模型进行推理
Plain 复制代码
from transformers import AutoTokenizer, AutoModelForCausalLM

quant_path = "opt-125m-awq"

tokenizer = AutoTokenizer.from_pretrained(quant_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(quant_path, device_map="auto")

text = "Hello my name is"
inputs = tokenizer(text, return_tensors="pt").to(0)

out = model.generate(**inputs, max_new_tokens=20)
print(tokenizer.decode(out[0], skip_special_tokens=True))

3.2 GPTQ

GPTQ(Gradient Post-Training Quantization)是一种针对类 GPT 大型语言模型的量化方法,它基于近似二阶信息进行一次性权重量化。本节将会介绍 GPTQ 量化的基本原理,同时也会展示如何通过AutoGPTQ来对您自己的模型进行量化处理。GPTQ 量化具有以下特点:

GPTQ 量化的优点:

  • 无须重新训练(仅需少量校准数据)。
  • 量化精度接近全精度,4bit GPTQ 能维持 LLaMA、OPT 等模型接近 FP16 的性能。
  • 速度快,实用性强,已成为主流 LLM 低比特推理方法。

GPTQ 量化的缺点:

  • 量化过程涉及 Hessian 矩阵近似和逐元素优化,计算复杂度较高。
  • 一般只量化权重,激活量化效果不佳(通常保持 FP16)。

3.2.1 GPTQ 量化技术原理

GPTQ 是一种高精度、高效率的量化方法,它可以在大约四个 GPU 小时内量化具有 1750 亿个参数的 GPT 模型,将位宽降低到每个权重 3 位或 4 位,与未压缩基线相比,精度下降可以忽略不计。GPTQ 源于 OBQ(Optimal Brain Quantization),而 OBQ 改进自剪枝方法 OBS(Optimal Brain Surgeon),OBS 又源自 Yann LeCun 1990 年提出的 OBD(Optimal Brain Damage)。OBD 通过泰勒展开简化目标函数并计算海森矩阵确定参数影响;OBS 考虑参数交叉项,求海森矩阵逆确定剪枝顺序并更新其他参数减少误差。OBQ 将剪枝思路推广到量化,视剪枝为近似 0 的特殊量化,但速度慢,大模型量化需数天。GPTQ 作为 OBQ 加速版,优化算法性能,降低复杂度并保证精度,176B Bloom 模型量化不到 4 小时,且有严谨数学理论推导。

GPTQ 在执行量化操作时,会先对权重实施分组处理(比如,每 128 列划分为一个组),进而构成若干个数据块。对于每个数据块里的全部参数,会逐一开展量化工作。在完成某一个参数的量化后,借助校准数据集,对该数据块中其余还未进行量化的参数进行合理调节,通过这种方式来补偿量化过程中产生的精度损耗。GPTQ 的量化过程如下所示:

前向采样数据

先用一小部分校准数据(calibration data,通常只需几百到几千条样本,比如来自模型训练语料的子集),将校准数据喂入原始全精度模型,收集每一层的 ​**激活值(输入向量 X)**​。

构造量化优化问题

GPTQ 的目标是 ​最小化量化后输出误差的二次形式​:

其中 WX 为量化前的权重和激活输入,另外一项为量化后的权重和激活。

Hessian 近似

GPTQ 使用输入激活的协方差来近似 Hessian,这样就转化为公式(2)中的问题:

逐元素优化(带校正)

GPTQ 不一次性量化整个权重矩阵,而是 ​逐元素(或逐块)地量化权重 ​。对每个权重,在量化时会根据 Hessian 的对角线项(近似二阶导信息)进行 误差校正,首先量化一个权重,然后更新剩余权重的"残差误差",这相当于执行一次 ​逐步的高斯消元式校正​,避免量化误差累积。

重复上述步骤,依次处理完一层的全部权重。然后继续到下一层,直到整个模型量化完成。

3.2.2 GPTQ 量化使用示例

transformers 已经正式支持了 AutoGPTQ,这意味着您能够直接在 transformers 中使用量化后的模型。

安装依赖

推荐通过安装源代码的方式获取并安装 AutoGPTQ 工具包:

Plain 复制代码
git clone https://github.com/AutoGPTQ/AutoGPTQ
cd AutoGPTQ
pip install -e .
量化模型
Plain 复制代码
import os
import logging
import torch
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer

# =============================
# 配置路径和量化超参数
# =============================
model_path = "your_model_path"          # 原始模型路径(本地或HF Hub)
quant_path = "your_quantized_model_path" # 保存量化后模型的路径

# 量化配置
quantize_config = BaseQuantizeConfig(
    bits=4,                # 量化比特数,可选 4 或 8
    group_size=128,        # 分组大小,推荐 128
    damp_percent=0.01,     # Hessian 阻尼因子,提升数值稳定性
    desc_act=False,        # 是否对激活值量化,一般 False 以提升推理速度
    static_groups=False,   # 是否使用静态分组,通常 False
    sym=True,              # 是否对称量化,True 更稳定
    true_sequential=True,  # 是否顺序量化,提升精度但更慢
    model_name_or_path=None,         # 保持 None(除非做特殊兼容)
    model_file_base_name="model"     # 保存的模型文件前缀
)

max_len = 8192  # 输入最大长度(根据模型上下文窗口设置)

# =============================
# 加载分词器和模型
# =============================
tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=True)

# 确保 pad_token 存在(某些 LLM 没有定义 pad_token)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

model = AutoGPTQForCausalLM.from_pretrained(model_path, quantize_config)

# =============================
# 准备校准数据(calibration data)
# dataset 需要你自己定义,比如一小部分语料
# dataset = ["hello world", "some calibration sentence", ...]
# =============================
data = []
for msg in dataset:
    # 转换成文本(可根据是否使用 chat 模板决定)
    text = tokenizer.apply_chat_template(
        msg,
        tokenize=False,
        add_generation_prompt=False
    )
    # 编码输入
    model_inputs = tokenizer(
        text,
        truncation=True,
        max_length=max_len,
        padding="max_length",   # 保证对齐
        return_tensors="pt"
    )
    # 收集数据(dict 格式)
    data.append(dict(
        input_ids=model_inputs["input_ids"].squeeze(0),
        attention_mask=model_inputs["attention_mask"].squeeze(0)
    ))

# =============================
# 配置日志输出
# =============================
logging.basicConfig(
    format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
    level=logging.INFO,
    datefmt="%Y-%m-%d %H:%M:%S"
)

# =============================
# 执行量化
# =============================
model.quantize(data, cache_examples_on_gpu=False)

# =============================
# 保存量化后的模型和分词器
# =============================
os.makedirs(quant_path, exist_ok=True)
model.save_quantized(quant_path, use_safetensors=True)
tokenizer.save_pretrained(quant_path)

print(f"量化模型已保存到: {quant_path}")

四、参考资料

Qwen 官方文档

zhuanlan.zhihu.com/p/681578090

zhuanlan.zhihu.com/p/680212402

cnblogs.com/xwher/p/187...

AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration

GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers

相关推荐
多米Domi01113 小时前
0x3f 第21天 三更java进阶1-35 hot100普通数组
java·python·算法·leetcode·动态规划
AI科技星13 小时前
统一场论中电场的几何起源:基于立体角变化率的第一性原理推导与验证
服务器·人工智能·线性代数·算法·矩阵·生活
Keep_Trying_Go14 小时前
基于无监督backbone无需训练的类别无关目标统计CountingDINO算法详解
人工智能·python·算法·多模态·目标统计
有时间要学习15 小时前
面试150——第三周
算法·面试
一车小面包15 小时前
Neo4j中的APOC
算法·neo4j
H_BB15 小时前
前缀和算法详解
数据结构·算法
聆风吟º15 小时前
【数据结构手札】时间复杂度详解:概念 | 大O渐进表示法 | 习题
数据结构·算法·时间复杂度·大o渐进表示法
山楂树の16 小时前
买卖股票的最佳时机(动态规划)
算法·动态规划
小O的算法实验室17 小时前
2024年IEEE TMC SCI1区TOP,面向无人机辅助 MEC 系统的轨迹规划与任务卸载的双蚁群算法,深度解析+性能实测
算法·无人机·论文复现·智能算法·智能算法改进