大模型量化入门:降低推理显存开销
从内存带宽瓶颈出发,解释低位量化如何压缩模型权重,以及缩放因子、零点和反量化的作用。以纯权重计算,8B 模型使用 FP16 约需 16GB 显存,INT4 理论上可减少 75%,但仍需计入 KV 缓存和运行开销。
AI 深度解读
一个 8B 参数模型,光是 FP16 权重就要约 16 GB 显存,KV-cache 和上下文还没算进去。量化要解决的,就是先把这些权重压小,让模型有机会在现有硬件上跑起来。
作者介绍了量化的计算原理、GGUF、AWQ、GPTQ 的适用方向,以及不同位宽的质量取舍。最有用的结论是:先选推理引擎,再选兼容的量化方案;文件能装下之后,还要用自己的任务验证输出是否可靠。
权重少占 75%,为什么不等于总内存少占 75%
LLM 生成每个 token,都需要把大量参数从内存搬到计算核心。作者把推理瓶颈指向内存带宽:计算单元再快,也得等权重送过来。
FP16 每个权重占 16 bits,包括 1 位符号、5 位指数和 10 位尾数,也就是 2 bytes。INT4 每个权重只用 4 bits,即 0.5 bytes。按权重本身计算,空间节省 75%。
但模型运行还要容纳 KV-cache 和运行时开销。KV-cache 用来保存已处理 token 的注意力相关数据,不能从权重文件大小直接推算出来。
权重缩小 75%,不代表整个推理进程的内存也缩小 75%。
原文也没有给出吞吐量或延迟对照,不能把空间压缩比例直接当成速度提升比例。
量化省掉的空间,是拿什么换来的
量化把浮点权重映射到有限的整数档位。原文给出的关系是 q = round(w / S) + Z,恢复近似权重则用 w ≈ S × (q − Z)。
其中,w 是原始权重,S 是缩放因子,Z 是零点偏移,q 是保存的整数。多个相近的浮点数会落到同一个档位,舍入误差由此产生。推理时可以动态反量化,也可以交给低比特计算内核处理。
下面这段原始示例展示了误差怎么产生,以及理论存储量怎么算:
import numpy as np
rng = np.random.default_rng(0)
w = rng.normal(0, 0.05, 4096).astype(np.float32)
bits = 4
levels = 2**bits - 1
S = (w.max() - w.min()) / levels
Z = round(-w.min() / S)
q = np.clip(np.round(w / S) + Z, 0, levels).astype(np.uint8)
w_hat = S * (q.astype(np.float32) - Z)
print("levels used:", int(q.max()) + 1)
print("mean abs error:", round(float(np.abs(w - w_hat).mean()), 5))
print("size in bytes:", w.size * 2, "->", w.size * bits // 8)
要留意 astype(np.uint8):数组实际仍按每个元素 8 bits 保存,代码没有把两个 4-bit 数值打包进一个字节。最后一行算的是理论大小,比较基线也是 FP16,而生成的 w 实际使用 np.float32。
作者执行 python quantize.py,录制视频时得到:
levels used: 16
mean abs error: 0.00604
size in bytes: 8192 -> 2048
这是随机向量的实测结果,证明了这套映射会产生多大数值误差,不能证明真实模型的回答质量。生产方案还会分组量化,并保存额外的缩放参数,以降低误差。
先选引擎,再看 GGUF、AWQ 和 GPTQ
这几个名字经常一起出现,但层级并不完全相同:GGUF 是文件格式,AWQ 和 GPTQ 是量化方法。部署时要看推理引擎支持哪种组合。
GGUF 用于 llama.cpp 和 Ollama,面向 CPU、Apple Silicon,也支持把模型层分配到 CPU 和 GPU,并以单个文件携带模型。需要本地运行、混合使用硬件时,可以先沿这条路线考察。
AWQ,即 Activation-aware Weight Quantization,适合通过 vLLM、TensorRT-LLM 等引擎进行 GPU 服务部署。作者强调它会保护少量更重要的权重,在 4-bit 下保持较好的质量。
GPTQ 则是面向 GPU 的训练后量化方法,使用样本数据校准 4-bit 权重。
这些是选型方向,不是性能排名。原文没有提供同一模型、同一硬件下的横向测试,无法据此判断 AWQ 一定比其他方案更快。
位宽怎么选,要看任务会在哪里出错
作者给出的梯度是:Q8_0 接近 FP16 的质量,权重内存约减半;Q4_K_M 是本地部署的常见起点,8B 模型约 4.8 GB,可以在 16 GB 工作站运行,通常只有较小质量损失;Q2_K 压缩更激进,可能出现推理能力减弱、JSON 格式错误。
这里的 4.8 GB 是近似值,也不等于理论上所有参数恰好各占 4 bits。前面提到的分组信息和缩放参数也需要空间。
如果用 Ollama,可以通过指定标签获取对应版本:ollama pull llama3.1:8b-instruct-q4_K_M。
对程序员来说,质量下降未必表现为回答明显变差,也可能是结构化输出偶尔少一个括号。原文因此建议,用自己的提示词和几个边界案例测试后再决定。
我的判断
我认同先选引擎、再选量化方案的顺序,也认同把任务测试放在最终决策之前。只比较文件大小,很容易漏掉运行兼容性和实际输出质量。
需要打问号的是「通常只有较小质量损失」。作者没有给出模型评测集、任务通过率或不同位宽的对照数据,这部分应当视作经验之谈。随机向量的平均绝对误差,也不能替代代码正确率或 JSON 解析成功率。
与直接使用 FP16 相比,量化更适合权重内存吃紧的部署;但硬件能装下 FP16 时,原文没有提供足够证据,证明继续降低位宽一定更划算。Q4_K_M 可以作为测试起点,不能直接作为所有团队的默认答案。
能拿来做什么
- 为本地 AI 编程选模型时,先确定使用 Ollama、llama.cpp 还是 GPU 服务引擎,再检查对应量化版本,避免下载后才发现运行方式不兼容。
- 用 Ollama 尝试上述 Q4_K_M 标签,并整理一组日常编程提示词和边界案例;涉及 JSON 输出时,把能否成功解析列为验收条件。
- 估算部署资源时,把权重大小、KV-cache 和运行时开销分别考虑,不要拿约 4.8 GB 的模型文件直接推算整机需求。
- 比较更低位宽前,先保留当前版本的任务输出作为基线,再检查代码和结构化输出是否退化,别只记录省了多少空间。