首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《模型量化实战:从 FP16 到 INT4,如何在精度损失 ~1% 的情况下提速 300%?》

《模型量化实战:从 FP16 到 INT4,如何在精度损失 ~1% 的情况下提速 300%?》

原创
作者头像
七条猫
发布2026-07-24 08:17:43
发布2026-07-24 08:17:43
550
举报

别被"精度掉 1%"骗了——你省的不是"精度",你省的是显存带宽

先说一句得罪人的大实话:

很多团队做量化,动机是"听说 INT4 能提速 3 倍"。undefined但跑起来发现:要么精度掉得像筛子,要么速度没涨多少,甚至还变慢了。

根因通常不是"量化算法不行",而是你把它当成"压缩 zip"用了——以为把权重从 16bit→4bit 就结束了。

实际上,量化只是第一步;真正的 3x 来自:更小的权重 footprint → 更少显存读 → 更高压的 batch / 更长上下文 → 专用低比特 GEMM kernel 吃满 Tensor Core。

这句话你先记住,后面一切会串联起来:

LLM 推理的瓶颈常常不在"算力(FLOPS)",而在"显存带宽(Memory-Bound)"。量化把 bottleneck 往右挪了一点,于是你开始赚到速度。


一、先把世界切开:FP16 / INT8 / INT4 到底在说啥?

LLM 推理里大家最常讨论一种叫 W4A16 的玩法:

  • W4:权重(Weight)压到 4-bit 整数(INT4 / 等价表示)
  • A16:激活(Activation)仍用 FP16/BF16 走矩阵乘undefined推理时权重会被 反量化(dequant)→ FP16,或走 INT4 GEMM kernel(更快)

这就是 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%"的现实含义。


二、AWQ 的核心思想:为什么"1% 关键权重"这个说法不是玄学?

1)先纠正直觉:权重的重要性 ≠ 权重大小(绝对值)

你天然会想:"是不是绝对值最大的那 1% 权重最重要?"

AWQ(MIT Han 组,MLSys 2024 Best Paper)给了一个干净的结论:不是。

真正重要的是——哪些权重通道在乘上 activation 时被"放大"得最狠

线性层视角(白话版):

代码语言:TXT
复制
y = Wx   (x 就是 activation)

如果某个输入通道 j 的 activation 幅度 |x_j| 特别大,那么 W 里对应的那一"列"(处理通道 j 的权重)对输出 y 影响就更大。

量化误差在那些列上会被 |x_j| 放大。

所以他们做了两件事很"反直觉"但很硬的工程动作:

  1. 用少量校准数据(forward 跑几十条样本)拿到 per-channel mean activation magnitude m_j = mean_k |X[k,j]|
  2. m_j 的 salient 通道做一件代数 trick:undefined先给权重除以 s_j(让它们变小),再给激活乘以 s_j(FP16 侧,精度不受伤),最后把权重 INT4 量化——salient 通道的权重在量化网格上相对更"细",误差更小

类比:你要复印一张图纸,但图纸上某条主梁的刻度特别关键。undefinedAWQ 的做法不是"把整张图印得更贵",而是把主梁区域的刻度先放大再缩回来印,让那条线在粗糙网格上依然落得准。

它甚至不依赖反向传播/重建,所以不容易过拟合校准集——这就是它能"泛化到指令微调/多模态"的底气。

2)那句"保护 1% 通道 ≈ 几乎无损"怎么理解?

论文的消融很经典:OPT-6.7B 做 INT3 + group(很激进):

  • RTN(最朴素四舍五入):PPL 飙得很难看
  • "把 1% 按激活选出的通道保留 FP16":明显救回来
  • 但"按权重大小选 1%":几乎没救(≈ 随机)

AWQ 把这件事做成了全 INT4 友好的 per-channel scaling(不是混合精度),硬件侧更干净(Marlin / CUTLASS INT4 GEMM 能吃得动)。


三、GPTQ:另一条路——用二阶信息"缝"误差

如果说 AWQ 是"聪明缩放",GPTQ 更像"外科补偿":

  • 它用 activation 的协方差 → Hessian 近似 来衡量"哪个权重变化对输出影响多大"
  • 逐层/逐列量化,并把当前列的量化残差,按 Hessian 信息补偿到尚未量化的列(lazy batch 按 block=128 列一组做)
  • 所以 GPTQ 的关键词是:校准 数据 / group_size(=128) / damping / act_order

GPTQ 的代价:

  • 离线量化更"重"(要跑校准 forward,算逆 Hessian 相关结构)
  • 在某些新模型/新 tokenizer/新 MoE 路由下,AWQ 往往更稳(泛化更好)、也更简单(无重建过拟合风险)

但 GPTQ 在不少纯 CUDA 卡 + 老量化工具链里生态也很深(AutoGPTQ 一路)。


四、最关键的"反直觉观点":INT4 的 3x 提速,来自带宽,不来自 ALU

程序员本能想:4-bit 比 16-bit "少算 4 倍"→ 快 4 倍。

现实是:

  • LLM 的 Prefill(处理输入 prompt) 更 compute-bound(能吃到 Tensor Core)
  • LLM 的 Decode(吐 token,batch 小) 是典型的 Memory-Bound:瓶颈在 读权重矩阵进缓存/寄存器,而不是乘多少次

所以当你把权重从 FP16 → INT4:

  1. 权重体积 ↓ 4x → 读权重花的时间 ↓
  2. KV Cache / 其他 buffer 相对占比改善
  3. 同张卡能开更大 batch / 更长 context,吞吐就被榨出来了

实测口径里(例如 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,求可审计稳定


六、动手:从 FP16 权重到"能跑的 INT4"——两条最常用管线的命令级流程

A)AWQ:用 AutoAWQ 做离线量化(生产最主流)

代码语言:bash
复制
pip install autoawq
代码语言:python
复制
from 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 格式):

代码语言:bash
复制
vllm serve llama3-8b-awq-4bit \
  --quantization awq \
  --gpu-memory-utilization 0.90 \
  --max-model-len 8192

B)本地党(Ollama / llama.cpp):你其实不用"做 AWQ",直接拉 GGUF 就完事

代码语言:bash
复制
# 最稳的日常选择:Q4_K_M("掉 ~1-3% 级别"的那档)
ollama run qwen3:14b   # 默认就 Q4_K_M 系

如果你想自己把 HF 转 GGUF(典型:你做了 LoRA merge,要重新量化):

代码语言:bash
复制
# 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

七、怎么证明"精度掉 1%,我没把自己炸了"?(最小验收清单)

别只看 loss 曲线或一句话"感觉差不多",做这 3 层:

L1:Perplexity(必要但不充分)

代码语言:bash
复制
# llama.cpp 里的经典方式
./llama-perplexity -m 14b-Q4_K_M.gguf -f wikitext2-test.bin -c 512

FP16 当 baseline,Q4 的 PPL 通常 +1~3% 算健康区间。

L2:任务探针(真正拦住事故的地方)

挑 20 条固定样本覆盖:

  • 事实问答 / 指令跟随 / JSON 输出 / 代码补全
  • 对比 FP16 输出是否"掉能力"还是仅仅"换表述"

L3:分布健康检查(防"平均好看、尾部崩盘")

  • 长上下文尾部(≥ 4k/8k token)跑一遍:看重复、脱轨
  • 工具/function-call 输出:看 JSON schema 违例率(量化有时会让"格式刚性"松动)

八、踩坑实录:我替你撞过的 5 个钉子

  1. 以为 RTN(round-to-nearest)够用了undefined论文/实测都反复证明:RTN 在 3/4bit 会明显更差;AWQ 的激活-aware scaling 很关键。
  2. AWQ 选错 group_size / 跑在老卡undefinedINT4 GEMM 要 Tensor Core 支持(SM7.5+,A100/H100/40系+);老卡会 fallback 到慢路径,你会怀疑人生。
  3. 校准集太窄 / 太干净undefinedAWQ 不重建,但它仍看 activation 分布——建议至少覆盖你真实 domain 的 128~512 条样本(别只用 wikitext 一条路)。
  4. batch 一大就 OOM,不是量化没用undefined别搞反因果:INT4 省权重显存,但 KV cache 才是 batch 的天花板。提速 3x 的前提是:你把省下的权重显存真拿去扩 batch/ctx,而不是什么都不调。
  5. "1% 掉精度"是均值;边际场景可能掉得多undefined推理/数学/长链输出往往比 PPL 更早显出退化——所以生产别拍脑袋定档,至少做一次 L2 任务探针。

结语

模型量化不是"把文件变小",它是把显存带宽这座桥拓宽,再把桥上的货车换成更密装的版本。undefinedAWQ 的聪明不在"压缩",在它认清一个事实:权重值多大不重要,重要的是它被谁乘——activation 才是那把尺。

你省下来的不是 1% 的"正确答案概率",你省下来的是一张卡的命运:同样 A10/4090,FP16 可能卡在 batch=1,INT4 能给你 batch=8 的吞吐——那才是 300% 的来源。


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 别被"精度掉 1%"骗了——你省的不是"精度",你省的是显存带宽
  • 一、先把世界切开:FP16 / INT8 / INT4 到底在说啥?
  • 二、AWQ 的核心思想:为什么"1% 关键权重"这个说法不是玄学?
    • 1)先纠正直觉:权重的重要性 ≠ 权重大小(绝对值)
    • 2)那句"保护 1% 通道 ≈ 几乎无损"怎么理解?
  • 三、GPTQ:另一条路——用二阶信息"缝"误差
  • 四、最关键的"反直觉观点":INT4 的 3x 提速,来自带宽,不来自 ALU
  • 五、实操:三种最常见"落地场景"该选哪条路?
  • 六、动手:从 FP16 权重到"能跑的 INT4"——两条最常用管线的命令级流程
    • A)AWQ:用 AutoAWQ 做离线量化(生产最主流)
    • B)本地党(Ollama / llama.cpp):你其实不用"做 AWQ",直接拉 GGUF 就完事
  • 七、怎么证明"精度掉 1%,我没把自己炸了"?(最小验收清单)
    • L1:Perplexity(必要但不充分)
    • L2:任务探针(真正拦住事故的地方)
    • L3:分布健康检查(防"平均好看、尾部崩盘")
  • 八、踩坑实录:我替你撞过的 5 个钉子
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档