跳转至

LLM 模型量化实战:GGUF / GPTQ / AWQ 三格式对比 + 量化位宽取舍矩阵

1. 引言:为什么量化是本地 LLM 的第一课

我是在一个周六的晚上彻底明白这件事的。

那天公司让我把一套 13B 的对话模型部署到一台双卡 3090 的机器上,给内部运维知识库的问答系统用。我从 Hugging Face 拉下 FP16 权重,vllm serve 一启动,两张 24GB 卡瞬间吃满,然后 CUDA out of memory 直接把我打回桌面。13B 的 FP16 权重光模型就 26GB,两张卡加起来 48GB 看着很多,可 KV cache 一分配、激活一撑,分分钟爆炸。我盯着报错看了五分钟,最后老老实实去查量化方案。

那是我第一次意识到:在本地跑 LLM,显存不是「够不够」的问题,而是「怎么把模型塞进去」的问题。一台 24GB 的 4090,FP16 下最多跑个 13B 还要扣掉 KV cache 的空间,而同样的卡用 Q4_K_M 能轻松跑 30B 甚至更大。量化不是锦上添花,它是本地部署的生存技能。

这篇文章不聊理论,就聊我在生产里踩过的坑、试过的配置、测出来的数。从 GGUF 到 GPTQ 到 AWQ,从 Q4_K_M 到 FP16,从 8GB 卡到 48GB 卡,每一段都是我实际部署验证过的。

2. 量化原理 60 秒:为什么 4-bit 是性价比拐点

先花一分钟把量化是什么讲清楚,因为后面所有决策都建立在这上面。

2.1 FP16 到 INT4:权重压缩的本质

大模型的权重默认是 FP16(16-bit 浮点),每个权重占 2 个字节。以 7B 模型为例,光权重就是 70 亿 × 2 字节 ≈ 14GB。量化就是把每个权重的精度从 16-bit 压到更低位宽,比如 INT8(1 字节)、INT4(0.5 字节),这样权重体积直接砍半甚至砍到四分之一。

我常用的一个公式,部署前先心算一遍:

模型权重显存 ≈ 参数量 × 位宽 / 8
  • 7B 模型 FP16:7e9 × 16 / 8 ≈ 14GB
  • 7B 模型 INT8:7e9 × 8 / 8 ≈ 7GB
  • 7B 模型 INT4:7e9 × 4 / 8 ≈ 3.5GB

13B 模型 INT4 就是 13e9 × 4 / 8 ≈ 6.5GB。一张 3090 就能跑,这就是量化的魔力。

2.2 为什么 4-bit 是性价比拐点

我把不同位宽在 7B 模型上的实测数据列一下(Qwen2.5-7B-Instruct,RTX 4090,下文所有数据都是我自己实测的近似值):

位宽 权重显存 相对 FP16 质量损失 推理速度(相对 FP16)
FP16 ~14GB 基准 1.0×
INT8 ~7GB 可忽略 1.05-1.15×
INT4 ~3.5GB 轻微(可接受) 1.3-1.6×
INT3 ~2.6GB 明显(易崩) 1.2-1.4×(不稳定)
INT2 ~1.75GB 严重(基本不可用) 波动大

我的结论很直接:4-bit 是质量与体积的平衡点。往上看 INT8 质量几乎无损但体积只省一半;往下看 INT3 体积再省一点,但质量断崖式下跌,模型开始胡言乱语。4-bit 省了 75% 的显存、质量损失在多数业务场景可接受、速度还快,所以业界 4-bit 是默认档位。

2.3 量化对质量与速度的双刃剑

这里有个容易误会的点,我用一句话点破:量化把「权重」压小了,但「计算精度」也降了。

  • 好的方面:权重变小 → 内存带宽压力小 → 推理更快;显存占用小 → 能跑更大模型。
  • 坏的方面:权重精度降低 → 输出质量下降;某些 kernel 在低 bit 下需要反量化到 FP16 再计算,反而有开销。

所以量化不是「越狠越好」,而是「够用就好」。我见过有人为了省显存把模型压到 INT3,结果业务评测掉得没法看,最后又老老实实回到 Q4。量化位宽的选择,本质是显存预算和质量预算的一场拔河。

3. 三种量化格式对比:GGUF / GPTQ / AWQ

这是本文的核心章节。我直接上对比表,然后逐个说清楚适用场景和坑。

3.1 一张表看懂三种格式

维度 GGUF GPTQ AWQ
全称 无(llama.cpp 自家格式) Generalized Post-Training Quantization Activation-aware Weight Quantization
所属生态 llama.cpp / Ollama AutoGPTQ / vLLM MIT-Han Lab / vLLM
量化方式 块级量化(K-quant),权重分块每块独立量化 逐层贪心量化,最小化输出误差 激活感知量化,保护显著权重通道
典型位宽 Q4_0 / Q4_K_M / Q5_K_M / Q8_0 4-bit(gptq-4bit) 4-bit(awq)
运行设备 CPU + GPU 混合 GPU-only GPU-only
主流加载器 Ollama / llama.cpp vLLM / ExLlama / AutoGPTQ vLLM / AutoAWQ
Kernel 加速 无特殊要求 Marlin / ExLlama kernel Marlin(vLLM 支持)
适用场景 个人部署、CPU 推理、Ollama 一键跑 生产 GPU 服务、高吞吐推理 生产 GPU 服务、质量敏感任务
优缺点 生态最全、CPU/GPU 通吃、量化档位细 速度快、vLLM 集成好、但 4-bit 质量略逊 AWQ 质量最稳、保护显著权重、速度与 GPTQ 相当

3.2 GGUF:llama.cpp 生态的「万能钥匙」

GGUF 是 llama.cpp 为了替代老 GGML 格式推出的模型格式,它的特点是把所有元数据和权重打包进一个单文件,并且支持 CPU 和 GPU 混合推理。Ollama 底层用的就是 llama.cpp,所以 GGUF 是 Ollama 的原生格式。

GGUF 最值得说的是它的 K-quant 体系——llama.cpp 搞了一套分块量化方案,把权重按重要性分块,每块用不同精度的量化,常见的档位有:

  • Q4_0:基础 4-bit 量化,最快但质量一般。
  • Q4_K_M:K-quant 混合 4-bit,质量接近 Q5,体积接近 Q4,我最推荐的通用档。
  • Q5_K_M:混合 5-bit,质量更好,体积略大。
  • Q6_K:6-bit,接近无损,体积约 FP16 的 40%。
  • Q8_0:8-bit 量化,质量几乎无损,体积约 FP16 的 55%。

我的经验是:Q4_K_M 是性价比之王,Q5_K_M 是质量焦虑者的安慰剂,Q8_0 是「我显存多但想省一点」的选项。

GGUF 最大的优势是单文件、可移植、CPU 也能跑。我经常把它当「模型集装箱」,下载一个 .gguf 文件,拷到哪都能用。缺点是 GPU 上性能不如专门优化的 GPTQ/AWQ kernel。

3.3 GPTQ:GPU 生产部署的老牌选手

GPTQ 是 2022 年出的后训练量化方法,逐层做量化并最小化输出误差,主打 GPU 推理。它的看家本领是 Marlin kernel——vLLM 专门为 GPTQ 写的高性能反量化 kernel,能把 4-bit 推理速度拉到接近 FP16 的水平。

GPTQ 在 vLLM 里是这样加载的:

vllm serve /data/models/llama-7b-gptq \
  --quantization gptq \
  --max-model-len 8192

GPTQ 的坑我踩过两个:

  1. 默认 kernel 慢。vLLM 里如果没启用 Marlin,4-bit 量化反而可能比 FP16 慢,因为逐权重反量化有开销。启用 Marlin 后速度立刻上来。检查方法是看启动日志里有没有 Marlin 字样。
  2. group size 不匹配会报错。GPTQ 量化时的 group_size(通常 128 或 64)必须和推理配置一致,否则 --quantization-param-path 会报错。

3.4 AWQ:保护「显著权重」的质量担当

AWQ(Activation-aware Weight Quantization)是 MIT 出的方法,它的核心洞察是:并非所有权重都同等重要。有些权重通道对应的激活值很大,这些「显著权重」对模型输出影响更大,AWQ 会专门保护它们,量化误差最小化。

这就带来了一个实际差异:在同样的 4-bit 位宽下,AWQ 的质量通常比 GPTQ 更稳。我有一次在业务评测集上对比,7B 模型的 AWQ 比 GPTQ 高 0.5-1 分(百分制),虽然差距不大,但在质量敏感的客服场景里,这个差距足以影响选型。

AWQ 在 vLLM 里的加载:

vllm serve /data/models/qwen-7b-awq \
  --quantization awq \
  --max-model-len 8192

AWQ 和 GPTQ 的加载命令几乎一样,只有 --quantization 参数不同。这也意味着选哪个格式,更多是看模型作者有没有提供对应的量化权重,而不是格式本身谁更好。

3.5 我实际的选择逻辑

说了这么多,我的选型逻辑其实很朴素:

  • 要在 CPU 上跑,或者用 Ollama → 选 GGUF(Q4_K_M)。
  • 要在 GPU 上做生产服务,用 vLLM → 选 GPTQ 或 AWQ,优先 AWQ(质量稳),模型作者只提供 GPTQ 就 GPTQ。
  • 不确定时 → 先跑 GGUF Q4_K_M 试水,确认质量可用再上 GPTQ/AWQ 做生产。

记住一点:格式没有绝对的好坏,只有「适不适合你的推理引擎」。GPU 上硬用 GGUF 不是不行,但拿不到 Marlin kernel 的性能加成,属于浪费硬件。

4. 量化位宽取舍矩阵:Q4_K_M 到 FP16 实测对比

这一章是我在 RTX 4090(24GB)和 RTX A100(40GB/48GB)上实测的数据,模型用 Qwen2.5-7B-Instruct 和 Llama-3.1-8B 各测了一轮,取近似值。数据是「参考量级」,不同硬件和模型会有浮动,但趋势是准的。

4.1 质量:困惑度与 MMLU 近似分

我用困惑度(Perplexity)和 MMLU 近似分做质量指标(困惑度越低越好,MMLU 越高越好):

量化档位 权重显存(7B) 困惑度(相对 FP16) MMLU 近似分(相对 FP16) 我的评价
FP16 ~14GB 基准(最低) 基准(最高) 质量天花板
Q8_0 ~7.5GB +0.02~0.05 -0.3~0.5 几乎无损
Q6_K ~6GB +0.05~0.1 -0.5~1 高质量
Q5_K_M ~5GB +0.1~0.2 -1~1.5 质量敏感推荐
Q4_K_M ~4.2GB +0.1~0.3 -1~2 性价比首选
Q4_0 ~3.8GB +0.2~0.5 -2~3 快但质量一般
INT3 ~3GB +0.5~1.5 -5~10 明显掉质量

核心结论:Q4_K_M 到 Q5_K_M 是一个平滑的过渡,再往上 Q6/Q8 就接近无损了。而 Q4 以下(INT3)是悬崖,不建议碰。

4.2 显存:不同位宽的「跑得动」边界

我按「模型权重 + KV cache(context 8192)+ 激活 + 框架开销」估算整机显存需求:

模型规模 FP16 Q8_0 Q5_K_M Q4_K_M INT3
7B 16GB 9GB 7GB 6GB 5GB
13B 28GB 16GB 12GB 10GB 8GB
30B 64GB 34GB 26GB 21GB 17GB
70B 148GB 78GB 59GB 48GB 38GB

这张表是「能跑起来」的最低估算,不是「舒服地跑」。实际部署一定要留 2-3GB 余量给 KV cache 和 CUDA context。

4.3 速度:tok/s 实测(7B,RTX 4090)

以 FP16 为 1.0 基准的倍数:

量化档位 推理速度倍数 实测 tok/s(近似) 说明
FP16 1.0× ~55-65 基准
Q8_0 1.1× ~60-72 略快
Q5_K_M 1.25× ~70-80 更快
Q4_K_M 1.4× ~75-90 速度质量均衡
Q4_0 1.5× ~80-95 最快但质量一般

注意:这里的速度提升主要来自「权重变小 → 内存带宽需求变小」。如果你的瓶颈是计算(compute-bound)而不是显存带宽(memory-bound),量化带来的速度提升会大打折扣。小模型、短输出、大 batch 时,量化提速不明显。

4.4 按显存给推荐位宽

这是我最常用的决策表,直接对着你的显存选:

你的显存 能跑多大的模型 推荐位宽 理由
8GB(3060/4060 Ti) 7B 勉强 Q4_K_M(甚至 Q4_0) 8GB 只能塞 4-bit,还得压缩 KV cache
16GB(4080/5070 Ti) 13B 舒服 / 7B 高质 Q5_K_M 或 Q4_K_M 13B 用 Q4_K_M,7B 可以用 Q5/Q6
24GB(3090/4090/5090) 30B 勉强 / 13B 舒服 Q4_K_M(30B)/ Q6_K(13B) 24GB 是甜点卡,4-bit 能上 30B
48GB(A6000/2×3090) 70B 勉强 / 30B 舒服 Q4_K_M(70B)/ Q5_K_M(30B) 双卡或大显存卡上大模型
80GB+(A100/H100) 70B 舒服 Q5_K_M 或 Q6_K 显存够,优先质量

一句话总结:显存小就 Q4,显存大就 Q5/Q6,显存无敌就 FP16。

5. GGUF 实操:Ollama 导入与部署

Ollama 是最容易上手的方式,我用它跑 GGUF 模型最多。这一章全是能直接复制的命令。

5.1 从 Hugging Face 下载 GGUF 模型

推荐用 huggingface-cli 或 hf 下载,断点续传稳:

# 用 hf 下载 Q4_K_M 版本(推荐,支持断点续传)
hf download TheBloke/Qwen2.5-7B-Instruct-GGUF \
  qwen2.5-7b-instruct-q4_k_m.gguf \
  --local-dir /data/models/qwen7b-gguf

# 或者用 huggingface-cli(老版本)
huggingface-cli download TheBloke/Qwen2.5-7B-Instruct-GGUF \
  qwen2.5-7b-instruct-q4_k_m.gguf \
  --local-dir /data/models/qwen7b-gguf

期望输出:

/download/qwen2.5-7b-instruct-q4_k_m.gguf: 4.68GB [100%] Done

下载后确认文件大小,Q4_K_M 的 7B 模型大概在 4.5-4.7GB:

ls -lh /data/models/qwen7b-gguf/qwen2.5-7b-instruct-q4_k_m.gguf
# -rw-r--r-- 1 root root 4.7G Oct 27 10:00 qwen2.5-7b-instruct-q4_k_m.gguf

5.2 用 Modelfile 导入 Ollama

Ollama 不直接吃 .gguf,需要写一个 Modelfile 声明模型:

mkdir -p /data/ollama-modelfiles
cat > /data/ollama-modelfiles/Qwen7B-Q4.modelfile <<'EOF'
FROM /data/models/qwen7b-gguf/qwen2.5-7b-instruct-q4_k_m.gguf
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
PARAMETER num_gpu 999
EOF

# 创建模型(名字里带冒号是版本号)
ollama create qwen7b-q4 -f /data/ollama-modelfiles/Qwen7B-Q4.modelfile

期望输出:

success

num_gpu 999 表示尽可能多的层放到 GPU,num_ctx 8192 设置上下文长度。

5.3 启动并测试

# 启动服务
ollama serve &
sleep 3

# 测试推理
ollama run qwen7b-q4 "用一句话解释什么是量化"

期望输出:

量化是把模型权重的精度从高精度压缩到低位宽(如 4-bit),
以大幅减少显存占用并加速推理,代价是轻微的质量损失。

5.4 关键环境变量:量化 KV cache

这是我最容易被忽略的优化项。Ollama 的 KV cache 默认是 FP16,会占大量显存。开启 KV cache 量化能省一大块:

# 让 KV cache 也用 8-bit 或 4-bit 量化(q8_0 / q4_0)
export OLLAMA_KV_CACHE_TYPE=q8_0

# 如果显存极度紧张,用 q4_0
export OLLAMA_KV_CACHE_TYPE=q4_0

# 重启 Ollama 生效
pkill ollama
ollama serve &

期望效果:7B 模型 context 8192 时,KV cache 从 ~1GB 降到 ~0.5GB(q8_0)或 ~0.25GB(q4_0)。

5.5 看显存占用

Ollama 有专门的命令看模型占了多少显存:

ollama ps

期望输出:

NAME          ID              SIZE    PROCESSOR    UNTIL
qwen7b-q4     a1b2c3d4e5f6    5.1 GB  100% GPU     4 minutes ago

这里 SIZE 5.1 GB 是权重 + KV cache 的总占用。如果显示 100% GPU,说明模型完全在显卡上跑;如果显示 GPU/CPU 混合,说明部分层被卸载到内存了,速度会明显变慢——这时候要检查显存是否真的不够。

6. GPTQ / AWQ 实操:vLLM 部署

vLLM 是生产环境的首选,吞吐量高、PagedAttention 省显存。这一章讲 GPTQ 和 AWQ 的完整部署流程。

6.1 下载 GPTQ 和 AWQ 权重

# GPTQ 4-bit
hf download Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4 \
  --local-dir /data/models/qwen7b-gptq

# AWQ 4-bit
hf download Qwen/Qwen2.5-7B-Instruct-AWQ \
  --local-dir /data/models/qwen7b-awq

期望输出(目录结构):

/data/models/qwen7b-gptq/
  config.json
  generation_config.json
  model.safetensors       # ~4.5GB
  quantization_config.json

/data/models/qwen7b-awq/
  config.json
  model.safetensors       # ~4.6GB
  quantization_config.json

6.2 vLLM 加载 GPTQ

vllm serve /data/models/qwen7b-gptq \
  --quantization gptq \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000

期望输出(启动日志关键行):

INFO 05-01 10:00:00 model_runner.py:... Model: Qwen2.5-7B-Instruct-GPTQ-Int4
INFO 05-01 10:00:01 model_runner.py:... Quantization method: GPTQ
INFO 05-01 10:00:01 marlin.py:... Using Marlin kernel for GPTQ  # 关键:Marlin 已启用
INFO 05-01 10:00:02 engine.py:... Starting vLLM engine on GPU 0

注意看 Using Marlin kernel 这一行。如果没出现,说明 Marlin 没启用,速度会慢,需要检查 vLLM 版本(Marlin 需要 vLLM >= 0.4 且 GPU 支持)。

6.3 vLLM 加载 AWQ

vllm serve /data/models/qwen7b-awq \
  --quantization awq \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000

期望输出(启动日志关键行):

INFO 05-01 10:00:00 model_runner.py:... Quantization method: AWQ
INFO 05-01 10:00:01 marlin.py:... Using Marlin kernel for AWQ

6.4 量化对 KV cache 的影响

这里有个很多新手不知道的点:vLLM 的 KV cache 默认是 FP16,跟量化无关。你量化了权重,但 KV cache 还是 FP16,照样吃显存。vLLM 提供了 KV cache 量化选项(需要 vLLM >= 0.6):

# 开启 KV cache 8-bit 量化(需编译时启用)
vllm serve /data/models/qwen7b-awq \
  --quantization awq \
  --kv-cache-dtype fp8_e5m2 \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000

期望效果:KV cache 显存减半,能支持更长的 context 或更大的 batch。但要注意 fp8_e5m2 需要 GPU 支持 FP8(H100/Ada 架构),老卡会报错,这时用 fp8_e4m3 或不量化 KV cache。

我的建议:如果显存够,KV cache 先别量化,因为 KV cache 对输出质量影响不小(尤其长上下文)。只有当显存实在紧张,或者 context 需求很大时,才量化 KV cache。

6.5 为什么量化模型要配 Marlin kernel

这是我在部署 GPTQ 时最深刻的一个教训。我最早用 --quantization gptq 跑 7B,速度竟然比 FP16 还慢,查了半天发现没启用 Marlin。

原因很简单:GPTQ 4-bit 权重存的是量化后的整数,每次计算都要先反量化回 FP16。如果 kernel 实现得不好,反量化开销比省下的内存带宽还大,量化就「白量化」了。Marlin kernel 用高度优化的矩阵乘法把反量化融入计算过程,速度能翻倍。

Marlin 启用条件:

  1. vLLM 版本 >= 0.4.0(越新越好)。
  2. GPU 架构支持(Ampere 及以上,即 30 系、40 系、A100、H100)。
  3. group_size 匹配(Marlin 通常要求 group_size 为 128 或 64)。

检查是否启用:

vllm serve /data/models/qwen7b-gptq \
  --quantization gptq \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000 2>&1 | grep -i marlin

期望输出:

INFO 05-01 10:00:01 marlin.py:... Using Marlin kernel for GPTQ

如果没有这一行,把 vLLM 升级到最新版再试:

pip install -U vllm

7. 量化质量验证:怎么判断「能不能用」

这是最容易翻车的地方。我见过太多人量化完只看一眼输出「感觉还行」就上生产,结果业务数据一跑全是问题。量化质量的唯一真相,是你的业务评测集。

7.1 困惑度对比:快速粗筛

困惑度(Perplexity, PPL)是衡量语言模型「对真实文本的困惑程度」的指标,越低越好。量化后 PPL 上升越小,质量越好。

# 用 llama.cpp 的 perplexity 工具(GGUF)
./llama-perplexity -m /data/models/qwen7b-gguf/qwen2.5-7b-instruct-q4_k_m.gguf \
  -f /data/test/sample_text.txt

期望输出:

perplexity: 8.4321

对比 FP16 的基线:

./llama-perplexity -m /data/models/qwen7b-gguf/qwen2.5-7b-instruct-fp16.gguf \
  -f /data/test/sample_text.txt
# perplexity: 8.2310

判断标准:PPL 上升幅度 < 5% 算优秀,< 10% 可接受,> 15% 警惕。

7.2 MMLU 跑分:通用能力粗测

MMLU 是通用知识基准,可以用来粗测量化损失。用 lm-evaluation-harness 跑:

pip install lm-eval
lm_eval --model vllm \
  --model_args pretrained=/data/models/qwen7b-awq,quantization=awq \
  --tasks mmlu \
  --batch_size auto

期望输出:

|      Groups      |Version|Filter|n_iters|     Metric     |   Value   |
|------------------|-------|------|-------|----------------|-----------|
| mmlu             |       |      |       |acc             | 0.7120    |

对比 FP16 跑一次,看差值。MMLU 掉 1-2 分正常,掉 5 分以上要警惕。

7.3 关键:用你自己的业务评测集

这是我最想强调的一点。公开榜单的分数代表「通用能力」,不代表你的业务。你的客服场景、运维问答、代码生成,模型在量化后可能在某些特定任务上掉得特别多。

我的做法是维护一个业务评测集,每个候选模型都跑一遍:

# 我的业务评测脚本示例(伪代码)
# 从 /data/eval/business_prompts.jsonl 读取 200 条业务 prompt
# 分别用 FP16 和量化模型生成答案
# 用 LLM-as-judge 或人工打分对比
python evaluate_business.py \
  --model vllm \
  --model_path /data/models/qwen7b-awq \
  --quantization awq \
  --prompts /data/eval/business_prompts.jsonl \
  --output /data/eval/result_awq.json

判断标准:

  • 量化模型业务分 >= FP16 的 97% → 放心上生产。
  • 92%-97% → 边缘,看具体任务,质量敏感场景升级位宽。
  • < 92% → 不合格,换更高位宽或回退 FP16。

为什么必须用自己评测集:我有一次量化一个代码生成模型,MMLU 只掉了 1 分,看着很好,但一跑业务代码评测,F1 掉了 8 分。原因很直接——量化对长尾任务(如复杂代码生成)的伤害远大于通用知识任务。公开榜单的 MMLU 恰好是「最不容易掉」的那类任务,用它做唯一标准会给你假信心。

8. 五个真实生产案例

这一章是我踩坑的结晶。每个案例都是我实际遇到过的,写出来供你避雷。

8.1 案例 A:Q4_K_M 跑 7B 在 8GB 卡上 OOM

现象:一张 8GB 的 RTX 3060,跑 Qwen2.5-7B Q4_K_M,ollama serve 起来后模型加载失败,报 CUDA out of memory。

排查:Q4_K_M 权重约 4.2GB,但 KV cache 默认 FP16,context 开到 8192 时 KV cache 要 1GB+,加上 CUDA context 和激活,8GB 被吃穿。ollama ps 显示模型只加载了一半就 OOM。

解决(两层优化):

# 1. KV cache 量化(q4_0 最省)
export OLLAMA_KV_CACHE_TYPE=q4_0

# 2. 减小 context
cat > /data/ollama-modelfiles/Qwen7B-Q4.modelfile <<'EOF'
FROM /data/models/qwen7b-gguf/qwen2.5-7b-instruct-q4_k_m.gguf
PARAMETER num_ctx 4096
PARAMETER num_gpu 999
EOF

ollama create qwen7b-q4-8g -f /data/ollama-modelfiles/Qwen7B-Q4.modelfile
ollama run qwen7b-q4-8g "测试"

结果:权重 4.2GB + KV cache(q4_0)约 0.25GB,8GB 卡能稳稳跑起来,虽然 context 只有 4096,但业务够用。

教训:8GB 卡跑 7B,位宽和 KV cache 必须双管齐下,光量化权重不够。

8.2 案例 B:量化后反而变慢

现象:vLLM 加载 GPTQ 4-bit 7B 模型,速度比之前 FP16 版本还慢,tok/s 掉了 30%。

排查:看启动日志,没有 Using Marlin kernel 行——Marlin 没启用。另外检查发现 KV cache 是 FP16,且 batch size 设置得很小(并发只有 1-2 个请求),量化计算优势被调度开销覆盖。

解决:

# 1. 升级 vLLM 确保 Marlin 启用
pip install -U vllm

# 2. 检查 Marlin
vllm serve /data/models/qwen7b-gptq \
  --quantization gptq \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000 2>&1 | grep -i marlin
# 期望看到 "Using Marlin kernel for GPTQ"

# 3. 提高并发(用更大的 batch 摊薄开销)
# 在客户端侧提高并发请求数,或在 vllm 里调大 max_num_seqs
vllm serve /data/models/qwen7b-gptq \
  --quantization gptq \
  --max-model-len 8192 \
  --max-num-seqs 64 \
  --port 8000

结果:Marlin 启用 + 并发提升后,tok/s 反而比 FP16 快 30-40%。

教训:量化提速的前提是「内存带宽是瓶颈」。并发太小或 kernel 没优化时,量化可能白忙活。

8.3 案例 C:AWQ 与 GPTQ 的质量差异在业务评测中体现

现象:同一份权重,AWQ 和 GPTQ 两个 4-bit 版本,在 MMLU 上只差 0.3 分(AWQ 略高),看着差不多。

排查:用业务评测集(200 条客服问答 prompt)分别跑,AWQ 得分 91.2,GPTQ 得分 89.8,差距 1.4 分。进一步分析发现,差距集中在需要「精确抽取实体」的任务上(如提取订单号、日期),AWQ 保护显著权重的优势在这些细节任务上放大了。

解决:业务场景对精确抽取敏感,选 AWQ。

教训:AWQ 和 GPTQ 的差距在通用任务上很小,但在细节/抽取类任务上会放大。用你的评测集选型,别看 MMLU 就下结论。

8.4 案例 D:量化模型输出质量肉眼可见下降

现象:一个 30B 模型用 Q4_K_M 部署后,生成的中文有少量错别字,长回答逻辑连贯性变差,用户投诉「变笨了」。

排查:业务评测集对比,量化版本得分 88,FP16 得分 96,掉了 8 分,远超可接受范围。进一步确认是量化位宽太低(Q4_K_M 对 30B 模型质量损失比 7B 更明显)。

解决:回退到 Q5_K_M,得分升到 93.5;再升级到 Q6_K,得分 95.2,接近 FP16。最终用 Q6_K 部署,显存 48GB(双卡)刚好够。

教训:模型越大,低位宽量化的相对质量损失越大。大模型(30B+)建议至少 Q5_K_M,质量敏感用 Q6_K。

8.5 案例 E:多卡并行量化模型部署

现象:双卡 3090 部署 30B GPTQ 模型,--tensor-parallel-size 2 启动报错。

排查:报错信息指向 GPTQ 的 group_size 与 tensor parallel 不兼容。GPTQ 量化时如果 group_size 不是「能被 TP 度整除」的配置,分片时会出错。

解决:

# 用 tensor-parallel-size 2 + 检查 group_size 兼容
vllm serve /data/models/qwen30b-gptq \
  --quantization gptq \
  --tensor-parallel-size 2 \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000

如果还是报错,检查权重目录的 quantization_config.json:

cat /data/models/qwen30b-gptq/quantization_config.json
# 期望看到 "group_size": 128

结果:确认 group_size=128(能被 2 整除)后,双卡并行成功,吞吐接近翻倍。

教训:多卡并行量化模型,一定先确认 group_size 与 tensor-parallel-size 的整除关系,否则 vLLM 会报分片错误。

9. 决策树:一句话选型

我把整个选型逻辑浓缩成一棵决策树,照着走就行:

你的显存够跑全精度吗?
├── 够 → 用 FP16(质量天花板,别折腾)
└── 不够
    ├── 在 CPU 上跑,或用 Ollama?
    │   └── 是 → GGUF,选 Q4_K_M(显存紧)或 Q5_K_M(质量优先)
    ├── 在 GPU 上跑 vLLM?
    │   ├── 质量敏感(客服、抽取、代码)?
    │   │   └── 是 → AWQ 4-bit(保护显著权重)
    │   │   └── 否 → GPTQ 4-bit(速度优先,配 Marlin)
    └── 显存特别紧张?
        └── 是 → 降低位宽到 Q4_0,并量化 KV cache(q8_0/q4_0)
             └── 还不够 → 减 context 或换更小模型

补充三条经验规则:

  1. 先跑通,再优化。先用 Q4_K_M 把模型跑起来,确认业务可用,再考虑升级位宽或换格式。
  2. 质量敏感任务,位宽宁高勿低。客服、金融、代码生成这类任务,Q5_K_M 起步,别省那 1GB 显存。
  3. 延迟敏感,优先 kernel。吞吐/延迟敏感场景,AWQ/GPTQ + Marlin 比 GGUF 更值得,因为 GPU kernel 优化带来的速度收益大于位宽收益。

10. 15 条命令速查 Cheat Sheet

我平时部署就靠这张表,复制即用。

# ── 1. 下载 GGUF(Q4_K_M) ──────────────────────────
hf download TheBloke/Qwen2.5-7B-Instruct-GGUF \
  qwen2.5-7b-instruct-q4_k_m.gguf \
  --local-dir /data/models/qwen7b-gguf

# ── 2. 下载 GPTQ 4-bit ──────────────────────────────
hf download Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4 \
  --local-dir /data/models/qwen7b-gptq

# ── 3. 下载 AWQ 4-bit ───────────────────────────────
hf download Qwen/Qwen2.5-7B-Instruct-AWQ \
  --local-dir /data/models/qwen7b-awq

# ── 4. Ollama 导入 GGUF ─────────────────────────────
cat > /data/ollama-modelfiles/Qwen7B-Q4.modelfile <<'EOF'
FROM /data/models/qwen7b-gguf/qwen2.5-7b-instruct-q4_k_m.gguf
PARAMETER num_ctx 8192
PARAMETER num_gpu 999
EOF
ollama create qwen7b-q4 -f /data/ollama-modelfiles/Qwen7B-Q4.modelfile

# ── 5. Ollama 启动 + KV cache 量化 ───────────────────
export OLLAMA_KV_CACHE_TYPE=q8_0
ollama serve &

# ── 6. Ollama 查看显存占用 ──────────────────────────
ollama ps

# ── 7. vLLM 加载 GPTQ(Marlin) ─────────────────────
vllm serve /data/models/qwen7b-gptq \
  --quantization gptq \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000

# ── 8. vLLM 加载 AWQ(Marlin) ──────────────────────
vllm serve /data/models/qwen7b-awq \
  --quantization awq \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --port 8000

# ── 9. vLLM 检查 Marlin 是否启用 ────────────────────
vllm serve /data/models/qwen7b-gptq \
  --quantization gptq --port 8000 2>&1 | grep -i marlin

# ── 10. vLLM KV cache 量化(需 GPU 支持 FP8) ───────
vllm serve /data/models/qwen7b-awq \
  --quantization awq \
  --kv-cache-dtype fp8_e5m2 \
  --port 8000

# ── 11. vLLM 多卡并行(TP=2) ───────────────────────
vllm serve /data/models/qwen30b-gptq \
  --quantization gptq \
  --tensor-parallel-size 2 \
  --port 8000

# ── 12. 困惑度验证(GGUF) ──────────────────────────
./llama-perplexity -m /data/models/qwen7b-gguf/qwen2.5-7b-instruct-q4_k_m.gguf \
  -f /data/test/sample_text.txt

# ── 13. MMLU 跑分(vLLM 后端) ──────────────────────
lm_eval --model vllm \
  --model_args pretrained=/data/models/qwen7b-awq,quantization=awq \
  --tasks mmlu --batch_size auto

# ── 14. 业务评测集验证 ──────────────────────────────
python evaluate_business.py \
  --model vllm --model_path /data/models/qwen7b-awq \
  --quantization awq \
  --prompts /data/eval/business_prompts.jsonl

# ── 15. 模型权重显存快速估算 ────────────────────────
# 公式:显存(GB) ≈ 参数量(B) × 位宽 / 8
# 例:7B Q4 → 7 × 4 / 8 = 3.5GB;13B Q4 → 13 × 4 / 8 = 6.5GB

11. FAQ

Q1:GGUF、GPTQ、AWQ 三种量化格式怎么选?

A:按你的推理引擎选,不是越新越好。

  • CPU 或 Ollama → 选 GGUF,Q4_K_M 是最稳的通用档,CPU/GPU 混合推理都能跑。
  • GPU 生产服务(vLLM) → 选 GPTQ 或 AWQ。AWQ 保护显著权重,质量更稳;GPTQ 生态更老、模型更多。两者都需要 Marlin kernel 才能发挥速度。
  • 不确定 → 先用 GGUF Q4_K_M 试水,质量确认可用再上 GPTQ/AWQ 做生产。

核心原则:格式跟推理引擎走,别跨生态硬用。

Q2:量化到 4-bit 质量会掉多少?

A:以 7B 模型为例,Q4_K_M 相对 FP16:

  • 困惑度(PPL)上升 0.1-0.3(约 1-5%)。
  • MMLU 分数掉 1-2 分(百分制)。
  • 业务评测多数场景可接受,但抽取/代码/长尾任务可能掉更多。

判断标准:业务评测分 >= FP16 的 97% 可接受,92%-97% 边缘需权衡,< 92% 必须升级位宽。我的建议是:关键任务永远用你自己的评测集确认,别只看公开榜单。

Q3:量化后反而变慢怎么回事?

A:三个最常见原因,按概率排序:

  1. Marlin kernel 没启用(GPTQ/AWQ)——vLLM 启动日志没有 Using Marlin kernel,量化反量化开销大于内存带宽收益。升级 vLLM 到最新版解决。
  2. KV cache 没量化——KV cache 默认 FP16,没省到显存,也没提速。显存紧张时用 --kv-cache-dtype fp8_e5m2。
  3. batch 太小——量化提速的前提是内存带宽是瓶颈,并发只有 1-2 个请求时,量化计算优势被调度开销覆盖。调大 --max-num-seqs 或提高并发。

Q4:8GB 显存能跑多大的量化模型?

A:

  • 7B Q4_K_M:权重 ~4.2GB + KV cache(q4_0)~0.25GB + 激活/CUDA context,8GB 勉强能跑,但必须量化 KV cache 并把 context 降到 4096 左右。
  • 7B Q5_K_M:权重 ~5GB,8GB 卡很吃力,建议 Q4_K_M。
  • 13B Q4_K_M:权重 ~6.5GB,加 KV cache 和开销 8GB 装不下,8GB 卡跑不动。
  • 3B Q4_K_M:权重 ~1.8GB,8GB 卡很轻松,还能开大 context。

一句话:8GB 卡的甜点是 3B-7B 的 Q4_K_M,14B 及以上别想。

Q5:GGUF 能用在 vLLM 上吗?

A:可以,但不推荐。vLLM 从 0.4 开始支持 GGUF,但 GGUF 是给 llama.cpp/Ollama 设计的,vLLM 对 GGUF 的 kernel 优化不如 GPTQ/AWQ,速度拿不到 Marlin 级别。GPU 生产服务用 GPTQ/AWQ,CPU 或 Ollama 用 GGUF,各用所长。

Q6:量化模型能不能微调(LoRA/全参微调)?

A:量化权重一般不适合直接微调。原因:量化后的权重是低精度整数,梯度反传精度不够,微调容易崩溃或质量退化。正确做法是:

  1. 用 QLoRA(4-bit 量化 + LoRA)在量化基座上微调,这是标准做法。
  2. 或者先反量化回 FP16 微调,再重新量化。

我的建议:如果要做 LoRA 微调,直接用 QLoRA 方案,别对已经量化的权重再微调。

Q7:为什么模型越大,量化损失越明显?

A:大模型(30B+)的权重分布更分散,低位宽量化时误差累积更严重,而且大模型的「冗余度」相对更低(小模型靠参数冗余抗量化误差,大模型参数更「金贵」)。实测经验:7B 用 Q4_K_M 可接受,30B 用 Q4_K_M 质量损失更明显,70B 建议至少 Q5_K_M。这也是为什么大模型量化要更谨慎。

结语

量化这件事,说到底是在显存预算和质量预算之间找平衡。我见过太多人要么「无脑 Q4 到底」结果业务崩了,要么「死守 FP16」结果 OOM。真正靠谱的做法是:

  1. 先用 Q4_K_M 跑通,确认基本可用。
  2. 用你的业务评测集量化打分,而不是看公开榜单。
  3. 根据分数决定:够用就保持,边缘就升级位宽,不够就回退或换格式。

显存不够才量化,4-bit 是性价比拐点,CPU 用 GGUF、GPU 用 GPTQ/AWQ 配 Marlin,量化后务必用自己业务评测集验证。这几句话记住,你的量化部署路能少走一半弯路。