DAY 35 · AI INFRA ROADMAP · 60 DAYS

把模型权重压进 4 bit

Day 31-34 讨论了 serving 系统如何管理 KV Cache、调度请求和复用前缀。 Day 35 切到另一条推理优化主线:权重量化。 目标不是让模型“变聪明”,而是把模型权重从 FP16/BF16 压到 INT8/INT4, 让单卡能放下更大的模型,让内存带宽压力下降,并在有合适 kernel 时提升吞吐。 今天重点理解 INT8 / INT4GPTQAWQ, 最后用 AutoGPTQ 或 llama.cpp 完成一次可复现的量化与评估。

DURATION 3 h THEORY 1.3 h HANDS-ON 1.3 h METHODS INT8 · INT4 · GPTQ · AWQ OUTPUT quantized model · eval table

思维导图

OVERVIEW
DAY 35 · Weight QuantizationINT8 · INT4 · GPTQ · AWQ · GGUF01 · WHY显存与带宽02 · BASICSscale / zero point03 · METHODSGPTQ / AWQ04 · PRACTICE量化与评估·权重占用下降·load / swap 更轻·显存留给 KV cache·速度取决于 kernel·symmetric / asymmetric·per-channel·group-wise·校准数据·GPTQ:近似二阶·逐层重构误差·AWQ:激活感知·保护 salient channels·AutoGPTQ·llama.cpp GGUF·perplexity / task eval·latency / memoryDELIVERABLES量化误差笔记INT4 / INT8 对照表量化脚本评估报告
FIG · Day 35 全景:权重量化先压模型体积,再用评估确认质量与性能收益

为什么先做权重量化

25 MIN

推理时,模型权重通常是最大的一块静态显存。 对一个 7B 模型,FP16 权重大约 14GB;如果权重压到 4 bit,理论权重主体约 3.5GB, 再加上 scale、zero point、packing metadata 和运行时 buffer。 这会直接影响能否单卡加载、能否留下更多显存给 KV Cache,以及 decode 阶段读权重时的带宽压力。

FP16 size ≈ params · 2 bytes

7B 权重主体约 14GB,13B 约 26GB,70B 约 140GB。实际还要算 tokenizer、buffer 和框架开销。

INT4 size ≈ params · 0.5 bytes + scales

4 bit 不是“精确四分之一总显存”,因为每组权重还需要 scale / zero point 等元数据。

speedup = memory saving × kernel support

量化文件变小不等于一定更快。若 kernel 需要频繁反量化或没有 Tensor Core / SIMD 优化,收益会被吃掉。

容量收益

更大模型放进同一张卡

权重量化最直接的收益是降低模型常驻显存,让 7B/13B/34B 在更小设备上运行。

吞吐收益

decode 更少搬权重

decode 每步 batch 小、读权重频繁,低 bit 权重可降低内存带宽压力,前提是 kernel 真的高效。

质量风险

低 bit 会引入误差

量化会改变线性层输出。校准集、group size、保留哪些层高精度,都会影响 perplexity 和下游任务。

INT8 / INT4 基础

40 MIN

量化的基本动作是把浮点权重映射到整数格点。 反推理时,整数权重需要和 scale 一起恢复为近似浮点值,或在 mixed-precision kernel 中边乘边反量化。 LLM 权重量化常见的是 weight-only quantization:权重低 bit 存储,激活仍用 FP16/BF16。

WEIGHT-ONLY QUANTIZATION PIPELINEFP16 / BF16 Wfloat weightscalibrate groupscale · zero point · group sizepacked INT4 / INT8weights + scalesruntime: activation(fp16/bf16) × dequantized weight ≈ original linear output质量来自误差控制,速度来自高效 packing 与 mixed precision kernel
FIG · 权重量化不是只改文件后缀,而是 scale、packing 与 kernel 的组合
q = clamp(round(w / s) + z, qmin, qmax)

非对称量化带 zero point。适合范围不以 0 为中心的分布,但硬件实现可能更复杂。

w_hat = s · (q - z)

反量化后的权重近似原权重。量化误差会通过线性层传到 logits。

group-wise: each group owns scale

group size 越小,scale 越细,质量通常更好;但 metadata 更多,packing / kernel 也更复杂。

toy symmetric quantization

import torch

def quantize_symmetric(x, bits=4, group_size=128):
    qmax = 2 ** (bits - 1) - 1
    qmin = -2 ** (bits - 1)
    flat = x.flatten()
    qs, scales = [], []

    for start in range(0, flat.numel(), group_size):
        g = flat[start:start + group_size]
        scale = g.abs().max().clamp_min(1e-8) / qmax
        q = torch.round(g / scale).clamp(qmin, qmax).to(torch.int8)
        qs.append(q)
        scales.append(scale)

    return torch.cat(qs), torch.stack(scales)

def dequantize_symmetric(q, scales, shape, group_size=128):
    chunks = []
    for i, scale in enumerate(scales):
        chunks.append(q[i * group_size:(i + 1) * group_size].float() * scale)
    return torch.cat(chunks).reshape(shape)
方式特点优点风险
INT88 bit 权重,通常质量更稳。误差小,部署风险低。压缩率不如 INT4,速度收益依赖 kernel。
INT44 bit 权重,LLM 本地推理很常见。显存下降明显,适合单卡/边缘设备。质量更依赖校准、group size 和方法。
per-channel每个输出通道有独立 scale。比 per-tensor 更能适配不同通道范围。metadata 增加,实现复杂一点。
group-wise每组若干权重共享 scale,如 group size 128。质量和体积折中较好。group size 选错会影响精度或吞吐。

GPTQ 与 AWQ

45 MIN

直接 round-to-nearest 通常不够好。 GPTQ 和 AWQ 都是 post-training quantization,不需要重新训练完整模型, 但会用少量校准数据判断哪些误差更重要。 粗略说,GPTQ 试图逐层最小化量化后的重构误差;AWQ 则用激活分布识别重要通道, 通过缩放保护 salient weights,尽量让 4 bit 权重保留指令模型的质量。

GPTQ

用近似二阶信息补偿误差

GPTQ 是 one-shot weight quantization。它用校准样本估计层输入分布,逐层量化权重,并用近似 Hessian 信息补偿已量化权重带来的输出误差。

AWQ

用激活识别重要权重通道

AWQ 认为权重本身大小不是唯一信号,应该看真实激活会放大哪些通道。它保护少量 salient channels,常用于 4-bit weight-only 部署。

TWO PTQ IDEASGPTQ1. 收集校准输入 X2. 逐层量化 W3. 用近似二阶信息补偿重构误差minimize layer output errorAWQ1. 收集激活统计2. 找 salient channels3. 缩放通道,降低低 bit 误差activation-aware protection二者都是 PTQ:少量校准数据 + 不做完整训练 + 量化后必须重新评估
FIG · GPTQ 关心重构误差补偿,AWQ 关心激活感知的重要通道保护
方法关键输入典型配置适合场景
RTN只看权重范围。round(w / scale)教学、快速基线,质量通常不是最好。
GPTQ校准样本的层输入分布。bits=4, group_size=128, act_order/desc_actGPU 侧 4-bit GPTQ 格式,常用于 Transformers / ExLlama / Marlin 路线。
AWQ激活统计与重要通道。w_bit=4, q_group_size=128指令模型、多模态模型、对泛化稳定性要求较高的部署。
GGUF k-quantsllama.cpp 的 tensor packing 规则,可选 imatrix。Q4_K_M, Q5_K_M, Q8_0CPU / Metal / Vulkan / 边缘设备上的 llama.cpp 生态。

动手一:AutoGPTQ 路线

35 MIN

AutoGPTQ 是理解 GPTQ 流程的好入口:准备校准样本,配置 bits/group_size, 加载 FP16 模型,执行 model.quantize(examples),保存量化权重。 真实项目里要注意维护状态、模型支持表、CUDA/PyTorch 版本和推理 kernel 是否匹配。

from transformers import AutoTokenizer
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig

pretrained_model_dir = "facebook/opt-125m"
quantized_model_dir = "opt-125m-gptq-4bit"

tokenizer = AutoTokenizer.from_pretrained(pretrained_model_dir, use_fast=True)
examples = [
    tokenizer("Quantization compresses LLM weights for inference."),
    tokenizer("Calibration samples should look like serving prompts."),
]

quantize_config = BaseQuantizeConfig(
    bits=4,
    group_size=128,
    desc_act=False,  # True 可能更准,但推理/兼容性需验证
)

model = AutoGPTQForCausalLM.from_pretrained(
    pretrained_model_dir,
    quantize_config,
    device="cuda:0",
)
model.quantize(examples)
model.save_quantized(quantized_model_dir, use_safetensors=True)
tokenizer.save_pretrained(quantized_model_dir)

选校准集

尽量接近线上 prompt 分布。通用模型可用 C4/WikiText 子集,业务模型用真实脱敏样本。

先量小模型

用 125M/1B 级模型跑通流程,再量 7B/13B,避免一次量化卡在环境和显存问题上。

记录配置

保存 bits、group_size、desc_act、seqlen、校准样本数、CUDA/PyTorch/AutoGPTQ 版本。

重新评估

量化后必须跑 perplexity、任务集、人工样例和 latency,不能只看模型能加载。

动手二:llama.cpp / GGUF 路线

35 MIN

llama.cpp 的常见流程是两步: 先把 Hugging Face 模型转换为高精度 GGUF,再用 llama-quantize 生成 Q4/Q5/Q8 等量化 GGUF。这个路线特别适合本地推理、CPU/Metal/Vulkan 或边缘设备部署。

# 1. 安装 Python 依赖
python3 -m pip install -r requirements.txt

# 2. 从 Hugging Face repo 转成高精度 GGUF
python convert_hf_to_gguf.py \
  --outfile model-bf16.gguf \
  --outtype bf16 \
  --remote meta-llama/Llama-3.2-1B-Instruct

# 3. 量化成常用 Q4_K_M
./build/bin/llama-quantize \
  model-bf16.gguf \
  model-Q4_K_M.gguf \
  Q4_K_M

# 4. 运行量化模型
./build/bin/llama-cli \
  -m model-Q4_K_M.gguf \
  -p "Explain weight quantization in one paragraph." \
  -n 128
GGUF 类型大致定位建议
Q8_0接近高质量低风险量化。作为质量对照或空间还够时使用。
Q5_K_M质量和体积折中偏质量。对回答稳定性要求较高时优先试。
Q4_K_M本地推理常见 4-bit 折中。Day35 推荐先用它跑完整流程。
Q2 / Q3极限压缩。除非设备非常受限,否则要谨慎评估质量下降。

量化后怎么评估

35 MIN

量化评估要同时看三类指标: 质量有没有明显下降,性能有没有真实提升,资源占用是否达成目标。 只看模型文件变小会漏掉最关键的问题:低 bit 权重可能造成困惑度上升、输出格式不稳, 也可能因为 kernel 不匹配而没有速度收益。

维度指标怎么测通过标准
质量perplexity、MMLU/GSM8K、业务 golden set、人工样例。同一 tokenizer、同一 prompt、同一 decoding 参数对比 FP16。重要任务下降在可接受范围内,格式和拒答行为无明显异常。
性能TTFT、TPOT、tokens/s、并发吞吐。固定 batch、context、输出长度,分别测 prefill 和 decode。速度收益来自真实 serving workload,不是单条样例偶然变快。
资源模型文件大小、加载后显存、KV cache 可用空间。记录 nvidia-smi / engine memory report / llama.cpp 日志。权重节省转化为更大 batch、更长 context 或更低硬件成本。

报告模板

model: Llama-3.2-1B-Instruct
baseline: bf16
quantized: Q4_K_M / GPTQ-4bit-g128
calibration:
  samples: 128
  max_seq_len: 2048
  source: mixed chat prompts

quality:
  ppl_fp16: 8.42
  ppl_quant: 8.83
  golden_pass_rate_fp16: 94.0%
  golden_pass_rate_quant: 92.5%

performance:
  peak_vram_fp16: 3.1 GB
  peak_vram_quant: 1.4 GB
  tpot_fp16: 28.4 ms
  tpot_quant: 17.9 ms

decision:
  accept: yes
  reason: quality drop acceptable, memory target met, decode faster

常见误区

20 MIN
误区为什么不准确正确做法
4-bit 一定快 4 倍权重读写减少,但反量化、packing、kernel 和 batch shape 都会影响速度。分别测 prefill、decode、并发吞吐和显存。
文件能加载就算成功加载成功只说明格式和 kernel 兼容,不说明质量可用。跑 perplexity、golden set 和真实 prompt。
校准集随便选GPTQ/AWQ 都依赖校准样本反映激活或层输入分布。校准集要尽量接近线上场景,并记录采样方式。
权重量化解决所有显存问题长上下文 serving 的 KV Cache 仍可能是大头。Day36 继续看 KV Cache 与激活量化,结合 PagedAttention 管理。

常见疑问

FAQ
Q1 权重量化和 KV Cache 量化有什么区别? +
A

权重量化压的是模型参数,通常是离线完成,模型加载后一直使用。KV Cache 量化压的是推理过程中动态产生的 K/V,会影响长上下文和高并发时的显存。Day35 只讲权重,Day36 会讲 KV 与激活。

Q2 GPTQ 和 AWQ 谁更好? +
A

没有固定答案。GPTQ 路线成熟,生态里有很多 kernel 和模型格式;AWQ 对 instruction-tuned、多模态和泛化场景常有不错表现。最终取决于模型、校准集、目标硬件和推理引擎支持。

Q3 为什么 group size 常见 128? +
A

它是质量、元数据开销和 kernel 实现之间的工程折中。更小 group 通常误差更小,但 scale 更多;更大 group 元数据少,但不同权重分布被迫共享同一 scale,误差可能更大。

Q4 什么时候不应该量化到 INT4? +
A

如果任务对细微数值很敏感、模型很小、线上要求严格可复现,或者目标引擎没有高效 INT4 kernel,INT8、FP8 或 BF16 可能更稳。量化是部署选择,不是越低越高级。

复盘问题

REVIEW
  1. 为什么 weight-only quantization 能降低 decode 阶段的内存带宽压力?
  2. scale、zero point、group size 分别控制什么?group size 变小会带来什么代价?
  3. GPTQ 为什么需要校准样本?它试图最小化什么误差?
  4. AWQ 为什么要看 activation 而不只看 weight magnitude?
  5. 量化模型上线前,你会用哪些质量、性能和资源指标做验收?

今日检查清单

CHECKLIST
  • 能解释 INT8 / INT4 权重量化的 scale、zero point 和反量化公式。
  • 能区分 per-tensor、per-channel、group-wise quantization。
  • 能说明 GPTQ 的逐层重构误差思路和 AWQ 的激活感知通道保护思路。
  • 能用 AutoGPTQ 或 llama.cpp 跑通一次小模型量化。
  • 能记录 bits、group_size、校准集、版本、目标硬件和推理 kernel。
  • 能输出一张量化前后质量、延迟、吞吐、显存的对比表。

推荐阅读

LINKS
Paper

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

GPTQ 原论文。重点读 one-shot、近似二阶信息和 3/4-bit 权重量化实验。

Paper

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

AWQ 原论文。重点读 salient weights、activation-aware scaling 和 4-bit weight-only 部署动机。

Tool

AutoGPTQ

GPTQ 工具入口。注意核对当前维护状态、安装方式、支持模型和推理 kernel。

Tool

llama.cpp quantize README

官方量化说明。核心流程是先转换为 GGUF,再用 llama-quantize 生成目标量化格式。

Day 36 预告

NEXT
量化 (2)

KV Cache 与激活量化

下一天会从静态权重走向动态数据: FP8 KV Cache、SmoothQuant、per-channel vs per-token activation quantization, 以及为什么长上下文 serving 里 KV Cache 量化和权重量化同样重要。

量化不是把精度一刀砍掉,而是把误差放到模型最能承受的位置。

DAY 35 · WEIGHT QUANTIZATION · AI INFRA ROADMAP