量化深度解析:GPTQ / AWQ / FP8 / GGUF 全谱
让 70B 模型塞进 24GB 显存——量化是消费级硬件跑大模型的关键。这一篇详解各家方案。
L7-03 推理优化里提过量化的基础—— 这一篇深度展开主流量化方法。
读完你能:在 RTX 4090 上跑 Llama 3 70B、理解所有 HuggingFace bitsandbytes 参数、选对量化方案。
量化的本质
原始 LLM 权重:通常 FP16(16-bit float)。 70B 模型 = 70B × 2 bytes = 140 GB。
一张 RTX 4090 只有 24GB——根本装不下。
量化的目标:用更少的位数存权重,几乎不损失精度。
数据类型对比
| 类型 | 位数 | 范围 | 精度 | 用途 |
|---|---|---|---|---|
| FP32 | 32 | ±10³⁸ | 极高 | 训练(部分) |
| FP16 | 16 | ±10⁴ | 中-高 | 训练 + 推理标准 |
| BF16 | 16 | ±10³⁸ | 中(动态范围大) | 训练首选 |
| FP8 | 8 | 中 | 低-中 | 新硬件(H100+)训练 |
| INT8 | 8 | -128~127 | 低(需 scale) | 推理 |
| INT4 | 4 | -8~7 | 很低(需 scale) | 推理(最常用) |
| INT2 | 2 | -2~1 | 极低 | 实验性 |
基础量化的数学
把 FP16 权重 → INT8:
# 假设权重在 [-2.0, 2.0] 之间
def quantize(weights, n_bits=8):
max_int = 2 ** (n_bits - 1) - 1 # 127 for INT8
scale = max(abs(weights.min()), abs(weights.max())) / max_int
quantized = round(weights / scale) # 现在是整数
quantized = clip(quantized, -max_int, max_int)
return quantized, scale
def dequantize(quantized, scale):
return quantized * scale
存的是:量化后的整数 + 一个 scale 因子。 用的时候:dequantize 回浮点。
显存:每个权重 1 字节 + 偶尔的 scale ≈ 2 倍节省(vs FP16)。
各种量化方法
1. PTQ(Post-Training Quantization)—— 训完再量化
最简单——拿训好的模型直接转换:
LLM.int8()
bitsandbytes 库的方法。核心 insight:
- LLM 权重大部分能 INT8 量化
- 但有少数”outlier”通道(值非常大)—— 它们保留 FP16
from transformers import BitsAndBytesConfig
config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained("Llama-3-70B", quantization_config=config)
# 70B 模型从 140GB → 70GB
性能损失 < 1%。但 PTQ 简单粗暴——还有更好的方法。
2. GPTQ(最经典的 INT4 方法)
2022 年发表的 GPTQ(Generative Pretrained Transformer Quantization)—— 用一小批校准数据,逐层最小化量化误差。
核心思路
不简单四舍五入—— 调整每个权重,让”量化后的输出”和”量化前的输出”在校准集上最接近:
对每个权重 w_i:
寻找量化值 q_i ∈ {-8, -7, ..., 7}
使得 ||W·x - W_quantized·x|| 最小
逐列优化,用 Hessian 信息加速
用法
pip install auto-gptq
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
quantize_config = BaseQuantizeConfig(
bits=4, # INT4 量化
group_size=128, # 每 128 个权重共享一个 scale
desc_act=False,
)
# 用一些校准数据量化
model = AutoGPTQForCausalLM.from_pretrained("Llama-3-70B")
model.quantize(calibration_data)
model.save_quantized("./Llama-3-70B-GPTQ")
结果:
- 70B 模型从 140GB → 35GB ← INT4,4 倍压缩
- 精度损失:2-5%
- 推理速度:和 FP16 相当或略快
3. AWQ(Activation-aware Weight Quantization)
2023 年提出的改进版—— 观察到少数”重要”权重比其它重要得多。
核心思路
不是平等量化每个权重—— 给”重要权重”分配更多位数,“不重要”权重大幅压缩。
判断”重要”的标准:这个权重对应的激活有多大。
直觉:如果激活恒为 0,再大的权重也没用。反之亦然。
优点(vs GPTQ)
- 不需要长时间校准——更快
- 某些任务上精度更好
- 部分硬件友好(推理加速更稳定)
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_name = "Llama-3-70B"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoAWQForCausalLM.from_pretrained(model_name)
model.quantize(tokenizer, quant_config={"bits": 4})
4. GGUF(llama.cpp 用的)
GGUF(GPT-Generated Unified Format)—— llama.cpp 项目的格式。
特点:
- 极度灵活:支持 INT2、INT3、INT4、INT5、INT6、INT8 等
- 混合精度:每个张量独立选位数
- CPU 友好:能在没有 GPU 的机器上跑
- 量化命名约定:
Q4_K_M(INT4, K-quant, Medium)
用法(社区最爱)
# 用 Ollama(最易用的 wrapper)
ollama pull llama3.3:70b-q4_K_M
ollama run llama3.3:70b-q4_K_M
或用 llama.cpp:
./llama-cli -m llama-3-70b.Q4_K_M.gguf -p "Tell me about AI"
GGUF 是消费级用户跑 LLM 的事实标准——Apple Silicon、AMD CPU 上跑 LLM 的首选。
5. FP8(新硬件友好)
H100/B200 等新 GPU 原生支持 FP8—— 用 8 位浮点数做训练 + 推理。
优势:
- 比 INT8 动态范围大
- 训练时仍然能用(INT8 训练困难)
- 硬件加速直接支持
# 用 NVIDIA Transformer Engine
import transformer_engine.pytorch as te
linear = te.Linear(in_features, out_features)
with te.fp8_autocast(enabled=True, fp8_recipe=fp8_recipe):
output = linear(input)
预言:FP8 会逐渐取代 INT8 成为推理标准——前提是硬件支持普及。
6. QLoRA(量化 + LoRA)
L4-05 详讲过—— 把基础模型量化到 4-bit,再加 LoRA 微调:
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # 4-bit NormalFloat
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True, # 量化 scale 本身
)
model = AutoModelForCausalLM.from_pretrained(
"Llama-3-70B",
quantization_config=bnb_config,
)
# 加 LoRA → 单卡 H100 微调 70B
这是 2023-2024 年最重要的工程突破之一。
量化方法横向对比
| 方法 | 位数 | 精度损失 | 速度 | 易用性 | 推荐场景 |
|---|---|---|---|---|---|
| LLM.int8() | INT8 | < 1% | 中 | 极易 | 内存紧但要精度 |
| GPTQ | INT4 | 2-5% | 快 | 中等 | GPU 推理 |
| AWQ | INT4 | 2-4% | 快 | 中等 | GPU 推理(首选) |
| GGUF | INT2-INT8 | 5-15%(INT4) | 中 | 极易 | CPU/Mac/消费级 |
| FP8 | FP8 | <1% | 极快 | 中 | H100+ |
| QLoRA | INT4+LoRA | 训练时 | 训练用 | 中 | 单卡微调 |
哪些不能量化
不是所有部分都适合量化:
1. Embedding 层
输入层 / 输出层的 embedding 矩阵—— 对量化敏感——量化后效果显著下降。 通常保留 FP16。
2. LayerNorm 参数
参数少(每层只有 1-2 个向量),没必要量化。
3. 训练时的优化器状态
Adam 的 m、v 状态—— 量化后训练不稳定。 仍然用 FP16/FP32。
一个真实数据:Llama 3 70B 的量化对比
实测在 MMLU benchmark 上:
| 量化 | 大小 | MMLU 分数 | 损失 |
|---|---|---|---|
| FP16 (原版) | 140 GB | 79.5 | - |
| INT8 (LLM.int8) | 70 GB | 79.2 | -0.3 |
| INT4 (GPTQ) | 40 GB | 77.8 | -1.7 |
| INT4 (AWQ) | 40 GB | 78.4 | -1.1 |
| INT4 (GGUF Q4_K_M) | 42 GB | 78.0 | -1.5 |
| INT2 (GGUF Q2_K) | 24 GB | 71.2 | -8.3 |
INT4 是 sweet spot——精度损失小,但显存压力大幅缓解。 INT2 损失太大——除非有特殊需求否则不用。
量化的工程坑
1. 校准数据的选择
GPTQ/AWQ 需要校准数据—— 选错数据 = 量化模型在某些任务上特别差。
经验:用多样化的校准集(C4 等通用数据,加你的目标域数据)。
2. Group Size
group_size 越小(如 32),精度越高,但 metadata 开销大。
经验值:128。
3. 推理框架支持
不是所有框架支持所有量化:
- vLLM:支持 GPTQ、AWQ
- TensorRT-LLM:支持自家量化 + GPTQ
- llama.cpp:GGUF
- HF Transformers:bitsandbytes(INT8/INT4)
选量化方法前先确认你的推理框架支持。
4. 跨硬件性能
INT4 在 A100 上跑可能比 H100 还慢—— 因为 H100 有原生 INT4/FP8 加速。 新硬件用新量化。
一个推荐选择流程
你的场景是?
├── 笔记本/Mac 上跑 → GGUF(Ollama / llama.cpp)
├── GPU 推理服务 → AWQ 或 GPTQ
├── 单卡微调 → QLoRA(INT4 base + LoRA)
├── 训练(H100+) → FP8 + BF16
└── 实验/快速原型 → bitsandbytes 8bit
量化的”未来”
1. 极端低位
研究在探索 INT2、INT1 量化—— 1-bit LLM(BitNet, 2024)甚至能保持大部分性能。 如果成熟——能让模型再小 16 倍。
2. 训练时量化
QAT(Quantization-Aware Training)—— 训练时就考虑量化。 比 PTQ 精度高,但成本贵。
3. 混合精度量化
不同层用不同位数—— 重要的留 INT8,不重要的压到 INT2。 理论上能精确分配”精度预算”。
4. 硬件 + 软件协同
新硬件(H100/B200)原生支持各种低位运算—— 软件优化 + 硬件支持 让量化成为标准。
别一开始就上 INT4——按这个顺序:
- FP16 直接跑,看是否够用
- 显存不够 → INT8(损失极小)
- 还是不够 → INT4 GPTQ/AWQ
- 还是不够 → 上 GGUF + CPU offloading
精度损失永远是 trade-off—— 对生产关键场景,INT8 一般是上限。 INT4 适合”次重要”场景或个人玩。
下一篇推荐:L7-05 模型部署 + 服务化 或 L7-06 训练优化进阶。
读到这里说明你认真在学 🎯
订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。
讨论区
· 用 GitHub 账号登录评论src/components/Comments.astro 顶部填入
仓库 ID 和分类 ID(见组件注释里的配置步骤)。