
先说一句得罪人的大实话:
很多团队做量化,动机是"听说 INT4 能提速 3 倍"。undefined但跑起来发现:要么精度掉得像筛子,要么速度没涨多少,甚至还变慢了。
根因通常不是"量化算法不行",而是你把它当成"压缩 zip"用了——以为把权重从 16bit→4bit 就结束了。
实际上,量化只是第一步;真正的 3x 来自:更小的权重 footprint → 更少显存读 → 更高压的 batch / 更长上下文 → 专用低比特 GEMM kernel 吃满 Tensor Core。
这句话你先记住,后面一切会串联起来:
LLM 推理的瓶颈常常不在"算力(FLOPS)",而在"显存带宽(Memory-Bound)"。量化把 bottleneck 往右挪了一点,于是你开始赚到速度。
LLM 推理里大家最常讨论一种叫 W4A16 的玩法:
这就是 AWQ / GPTQ 的主战场:权重量化,不碰激活的精度(所以"语义"相对稳定),但把模型的体积和带宽消耗狠狠砍一刀。
格式 | 每权重占位 | 70B 显存(近似) | 适合谁 |
|---|---|---|---|
FP16 | 16 bit ≈ 2B/param | ~140 GB | 有钱/有 A100 80GB×多卡 |
INT8 (W8A16) | ~1 byte | ~70 GB | 稳定、几乎无损、但省得不够狠 |
INT4 (W4A16) | ~0.5 byte(有效 ~4-5bit 因为有分组缩放) | ~35-40 GB | 生产性价比之王 |
GGUF Q4_K_M | 有效 ~4.5-4.9bit | 同上量级 | 本地/Mac/消费卡(llama.cpp) |
引用一下实测口径:对 Llama-3 这类模型,Q4_K_M 的 perplexity缺口大约在 +1~3% 相对 FP16,Q5_K_M 更接近 +0.5~1%——它才是"精度掉 1%"的现实含义。
你天然会想:"是不是绝对值最大的那 1% 权重最重要?"
AWQ(MIT Han 组,MLSys 2024 Best Paper)给了一个干净的结论:不是。
真正重要的是——哪些权重通道在乘上 activation 时被"放大"得最狠。
线性层视角(白话版):
y = Wx (x 就是 activation)如果某个输入通道 j 的 activation 幅度 |x_j| 特别大,那么 W 里对应的那一"列"(处理通道 j 的权重)对输出 y 影响就更大。
量化误差在那些列上会被 |x_j| 放大。
所以他们做了两件事很"反直觉"但很硬的工程动作:
m_j = mean_k |X[k,j]|m_j 的 salient 通道做一件代数 trick:undefined先给权重除以 s_j(让它们变小),再给激活乘以 s_j(FP16 侧,精度不受伤),最后把权重 INT4 量化——salient 通道的权重在量化网格上相对更"细",误差更小类比:你要复印一张图纸,但图纸上某条主梁的刻度特别关键。undefinedAWQ 的做法不是"把整张图印得更贵",而是把主梁区域的刻度先放大再缩回来印,让那条线在粗糙网格上依然落得准。
它甚至不依赖反向传播/重建,所以不容易过拟合校准集——这就是它能"泛化到指令微调/多模态"的底气。
论文的消融很经典:OPT-6.7B 做 INT3 + group(很激进):
AWQ 把这件事做成了全 INT4 友好的 per-channel scaling(不是混合精度),硬件侧更干净(Marlin / CUTLASS INT4 GEMM 能吃得动)。
如果说 AWQ 是"聪明缩放",GPTQ 更像"外科补偿":
GPTQ 的代价:
但 GPTQ 在不少纯 CUDA 卡 + 老量化工具链里生态也很深(AutoGPTQ 一路)。
程序员本能想:4-bit 比 16-bit "少算 4 倍"→ 快 4 倍。
现实是:
所以当你把权重从 FP16 → INT4:
实测口径里(例如 vLLM + AWQ 4bit vs FP16 HuggingFace 走 A10G 这类卡):
吞吐可以到 2.7x~3.1x,首 token 延迟也能压下去(因为权重占显存小,加载/调度更顺)
场景 | 推荐 | 原因 |
|---|---|---|
本地跑模型(Mac / 消费卡 / Ollama) | GGUF Q4_K_M | llama.cpp/ Metal / ROCm 的 K-quants 是本地之王;AWQ/GPTQ 在这反而不是首选 |
服务器 GPU 部署(A100/H100/L40S)serving(vLLM/TensorRT-LLM) | AWQ INT4 | 显存压到 ~1/4,有 INT4 GEMM 路径,吞吐收益最稳 |
你想要"最接近 FP16 的安全感"但仍想省 40-50% 显存 | Q5_K_M / Q8_0 / W8A16 | 很多团队生产其实停在 8-bit / 5-bit,求可审计稳定 |
pip install autoawqfrom autoawq import AutoAWQForCausalLM, BaseAWQConfig
model_path = "meta-llama/Llama-3-8B-Instruct"
quant_path = "llama3-8b-awq-4bit"
# 1) 配置:INT4 + group 128 + 校准样本
awq_cfg = BaseAWQConfig(
w_bit=4,
q_group_size=128,
version="GEMM", # 用 INT4 GEMM(vLLM/Marlin 友好)
calib_data="pile", # 内置/或你传 dataset
n_samples=256,
max_calib_seq_len=512,
)
# 2) 加载 + 量化(写磁盘:权重+s/分组元数据)
model = AutoAWQForCausalLM.from_pretrained(model_path)
model.quantize(awq_cfg)
model.save_quantized(quant_path)
# 3) 本地快速验:
from autoawq import AutoAWQForCausalLM
m = AutoAWQForCausalLM.from_quantized(quant_path, device="cuda:0")
tok = AutoAWQForCausalLM.get_tokenizer(quant_path)
out = m.generate(**tok("Why is memory bandwidth king?", return_tensors="pt").to("cuda:0"), max_new_tokens=64)
print(tok.decode(out[0]))然后 serving 侧走 vLLM(它认识 AWQ 格式):
vllm serve llama3-8b-awq-4bit \
--quantization awq \
--gpu-memory-utilization 0.90 \
--max-model-len 8192# 最稳的日常选择:Q4_K_M("掉 ~1-3% 级别"的那档)
ollama run qwen3:14b # 默认就 Q4_K_M 系如果你想自己把 HF 转 GGUF(典型:你做了 LoRA merge,要重新量化):
# 1) HF -> GGUF 转换脚本(llama.cpp 项目里)
python convert-hf-to-gguf.py ./my-merged-14b ./14b-fp16.gguf --outtype f16
# 2) 用量化工具压成 Q4_K_M
./llama-quantize ./14b-fp16.gguf ./14b-Q4_K_M.gguf Q4_K_M别只看 loss 曲线或一句话"感觉差不多",做这 3 层:
# llama.cpp 里的经典方式
./llama-perplexity -m 14b-Q4_K_M.gguf -f wikitext2-test.bin -c 512FP16 当 baseline,Q4 的 PPL 通常 +1~3% 算健康区间。
挑 20 条固定样本覆盖:
模型量化不是"把文件变小",它是把显存带宽这座桥拓宽,再把桥上的货车换成更密装的版本。undefinedAWQ 的聪明不在"压缩",在它认清一个事实:权重值多大不重要,重要的是它被谁乘——activation 才是那把尺。
你省下来的不是 1% 的"正确答案概率",你省下来的是一张卡的命运:同样 A10/4090,FP16 可能卡在 batch=1,INT4 能给你 batch=8 的吞吐——那才是 300% 的来源。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。