Inference Quantization Formats
先读懂量化记法
一句话先给结论:**WnAm 只是一段算子精度标签,不是一份完整的量化规格。**
W是目标算子的 weight,A是输入 activation。n、m是计划使用的位宽,不说明数值是 INT、FP 还是 MX。A不是 accumulator。累加器可能是 INT32、FP16、FP32 或硬件定义的更宽内部格式。- 标签不说明尺度、零点、分组粒度、静态或动态量化、输出 dtype、排除模块、KV cache 和实际 kernel。
因此,看到一个短标签时,至少补全下面六列:
| 维度 | 必须追问 | 例子 |
|---|---|---|
| 数值家族 | INT、FP、MX,还是厂商格式 | INT8、E4M3、MXFP4、NVFP4 |
| 量化对象 | 权重、激活、KV、通信张量 | W4A16 只压目标权重 |
| 粒度 | tensor、channel、group、token、block | GPTQ group 128、MX block 32 |
| 参数 | scale、zero point、是否对称 | INT8 对称量化可令 z=0 |
| 时机 | 离线、静态校准、每次动态生成 | per-token dynamic activation |
| 执行 | 原生低比特、融合解量化、fallback | MXFP4 → W4A8 或 W4A16 |
常见记法可以先按“主要压缩对象”归类:
| 记法 | 权重 | 激活 | 首要收益 | 首要风险 |
|---|---|---|---|---|
| W4A16 | 4 bit | 16 bit | 权重常驻量和读取带宽 | 解量化成本、权重误差 |
| W8A16 | 8 bit | 16 bit | 较稳健的权重压缩 | 加速幅度依赖 kernel |
| W8A8 | 8 bit | 8 bit | 两侧带宽与低精度 GEMM | 激活异常值、在线量化 |
| W16A8 | 16 bit | 8 bit | 激活/通信带宽 | 不降低权重常驻量 |
| W8A4 | 8 bit | 4 bit | 更激进的激活与计算压缩 | A4 精度与硬件约束 |
| MXFP8 W8A8 | E4M3 | E4M3 | 小块动态范围与原生 MX kernel | 块尺度布局、硬件限制 |
| MXFP4 W4A4/A8 | E2M1 | FP4/FP8 | 极低位宽 MoE/GEMM | fallback 后可能变成 A16 |
量化算子的共同骨架
无论是 INT 还是 FP/MX,量化线性层都要处理三类对象:低精度元素、恢复数值范围的尺度,以及比输入更宽的累加/输出。区别主要发生在映射公式、尺度粒度和生成时机。
整数量化
对 b bit 整数,最常见的仿射量化写成:
$$
q = \operatorname{clip}\left(
\operatorname{round}\left(\frac{x}{s}\right) + z,,
q_{\min},q_{\max}
\right), \qquad
\hat{x}=s(q-z).
$$
其中 s 是 scale,z 是 zero point。对称量化通常令 z=0;若使用有符号 b bit 且按最大绝对值定标,可近似取:
$$
s=\frac{\max |x|}{2^{b-1}-1}.
$$
这两个式子只定义了数值映射。实现还要决定 max 在整张量、每通道、每组还是每 token 上求;粒度越细,局部范围通常越容易拟合,但尺度数量、布局和 kernel 复杂度也越高。TensorRT 的量化文档给出了 INT8 的舍入、裁剪和反量化定义,可作为理解框架。TensorRT Quantized Types
静态、动态与 QAT
- PTQ(Post-Training Quantization):训练完成后,用权重统计或校准样本生成量化参数。GPTQ、AWQ 和许多 FP8 转换属于这一大类。
- 静态激活量化:提前校准并保存激活尺度;运行时少做一次范围统计,但尺度对输入分布漂移更敏感。
- 动态激活量化:每次执行按 token、row 或 group 求范围并生成尺度;适应性更好,但增加归约与转换。
- QAT(Quantization-Aware Training):训练或 SFT 阶段模拟量化误差,使模型主动适应低精度。Kimi K3 的模型卡明确把 MXFP4/MXFP8 与 QAT 绑定。Kimi K3 Model Card
一个完整的动态量化线性层可以规范化为:
1 | def dynamic_quantized_linear(x16, packed_weight, weight_scale, spec): |
TorchAO 也把 weight-only、dynamic activation plus weight、static quantization 分为不同工作流;“都是 8 bit”并不意味着运行阶段相同。 torchao Quantization Overview
W4/W8A16:只压权重
Weight-only 是最直接的推理量化:先缩小模型生命周期内的权重对象,不改变进入算子的高精度激活。
原理
W4A16 或 W8A16 的目标是让持久化权重从 BF16/FP16 变成低比特元素,加上较少的 scale/zero-point side data。激活仍保持 BF16/FP16。推理时有两种主要执行方式:
- 融合解量化 GEMM:kernel 读 packed weight,在寄存器或片上块内恢复数值,然后直接参与乘加。
- 原生混合低比特 GEMM:硬件和 kernel 直接支持低比特权重与 A16 的组合。
如果框架先把整层权重完整反量化到显存,再调用普通 GEMM,就会额外产生大张量和带宽,削弱 weight-only 的优势。
7aea73d8 的 compressed_tensors_wNa16.py:2–8 bit 权重、pack factor、packed INT32 权重与 scale 注册,以及 A16 kernel 配置。图示不表示所有模块都被量化。适用性与效果
- 适合:模型主要受权重常驻量或 HBM 读取限制;目标硬件没有稳定的低比特激活路径;希望降低部署风险。
- 直接效果:目标线性层的 packed weight 变小,权重带宽下降。
- 不能推出:模型整体严格缩小为
4/16;embedding、norm、LM head、视觉编码器和尺度仍可能是高精度。 - 性能边界:大 batch/prefill 可能更 compute-bound;解码时 weight-only 往往更容易体现权重带宽收益,但仍要实测。
vLLM 当前的通用 WnA16 scheme 明确注册了 packed 权重、尺度与目标激活 dtype,apply 最终委托给选中的 kernel,而不是由 W4A16 标签自行保证速度。vLLM WnA16 source
W8A8:权重与激活同时压缩
W8A8 进一步把当前算子生命周期内的 activation 也变窄,因而把动态范围统计和尺度流带进在线关键路径。
原理
INT8 W8A8 常把权重离线量化,把激活在线动态量化。一个 token/row 到达线性层后,前处理先求 amax、生成 input scale、舍入到 INT8;GEMM 再读取 INT8 weight 与 weight scale,使用较宽累加器,最后反缩放并输出 BF16/FP16。
7aea73d8 的 compressed_tensors_w8a8_int8.py:INT8 weight/scale、可选静态 input scale/zero point 与 kernel apply。适用性与效果
- 适合:目标硬件具有成熟 INT8/FP8 Tensor Core,权重和激活搬运都显著,校准或动态范围策略已验证。
- 潜在收益:两侧操作数更窄,低精度矩阵吞吐更高。
- 新增成本:动态
amax归约、激活转换、scale 传递和可能的离群值处理。 - 精度风险:激活分布比权重更依赖输入;少数大异常值会把 scale 拉大,使普通值的有效分辨率下降。
W16A8 与 W8A4:非对称混合位宽
这两个标签都让 W/A 位宽不同,但前者保留高精度权重,后者对激活施加更激进的四位约束,不能作为同一强度的优化理解。
W16A8:只压激活
W16A8 保留 16 bit 权重,只把激活变成 8 bit。它的价值通常在:
- 减少激活读写或跨设备通信;
- 使用硬件提供的 W16×A8 混合 kernel;
- 让精度敏感权重保持高精度。
它不会降低权重常驻量。若部署的主要瓶颈是数百 GiB 模型权重,W16A8 不能解决“模型放不下”的问题;若后端没有原生混合 kernel,在线量化甚至可能只增加开销。
W8A4:激进压缩激活
W8A4 把权重压到 8 bit、激活压到 4 bit。四位激活的可表达值很少,通常需要更细的 per-token/per-group 动态尺度,并依赖专用 kernel。
vLLM 当前一个 WnA4 compressed-tensors 路径明确限制为对称、动态、per-token/per-group INT4 activation,并拒绝静态、per-tensor 或非对称组合。这说明 W8A4 更像一个接口族,而不是跨框架完全一致的格式。vLLM WnA4 source
规范化控制流是:
1 | def mixed_width_linear(x16, weight, mode): |
工程上必须先调用后端能力矩阵确认 kernel_w16_a8 或 kernel_w8_a4 确实存在;否则标签可能被拒绝、重新量化,或者 fallback 到 A16。
FP8、MXFP8 与 MXFP4:浮点格式不是整数位宽
浮点低精度不仅要看总 bit,还要看指数/尾数怎样分配,以及尺度是全局、张量级还是每个小块共享。
FP8
OCP OFP8 规范定义两种八位浮点交换编码:
- E4M3:1 sign、4 exponent、3 mantissa,最大有限幅值为
448,精度相对更高。 - E5M2:1 sign、5 exponent、2 mantissa,最大有限幅值为
57,344,动态范围更大。
OCP 规范定义的是编码和从更宽格式转换时的舍入、饱和行为,并不规定模型必须怎样选 scale,也不保证速度或精度。OCP OFP8 Specification
普通 FP8 推理仍常使用 per-tensor 或较大 block 的 scale。一个“FP8 checkpoint”也可能只量化部分 Linear,并排除 attention、embedding、LM head 或视觉模块。
MXFP8
MXFP8 在元素编码之外增加 microscaling:
$$
x_i \approx s_b q_i,\qquad q_i \in \text{E4M3},\qquad
s_b \in \text{E8M0},\qquad i\in \text{block}_b.
$$
NVIDIA Transformer Engine 的 MXFP8 路径让 32 个连续 E4M3 元素共享一个 E8M0 scale。E8M0 只有指数,表示 2 的幂。每块大致执行:
- 求 32 个值的
amax; - 以 E4M3 最大幅值
448为基准生成 E8M0 指数; - 用块尺度缩放;
- 舍入到 E4M3。
理想原始存储为:
$$
\frac{32\times 8 + 8}{32}=8.25\ \text{bit/value}.
$$
这还未计算 scale 布局、对齐、元数据和可能同时保存的 rowwise/columnwise 表示。MXFP8 的行向与列向分块方向不同,不能简单把一个量化张量转置后当成另一个方向的同值表示。NVIDIA MXFP8
MXFP4 与 fallback
MXFP4 使用 E2M1 四位元素,并为每 32 个元素提供一个 E8M0 scale。真正决定运行位宽的是后端:
- vLLM 当前
compressed_tensors_w4a4_mxfp4.py在支持的 SM100+ FlashInfer 路径使用低比特 activation; - 不满足该路径时,可以改走 Marlin W4A16。
因此,同一个 MXFP4 权重仓库可以在一台机器上表现为 W4A8/W4A4,在另一台机器上表现为 W4A16。 vLLM MXFP4 source
开放权重盘点
下面只统计模型组织的官方量化权重,以及与 GLM-5.2 明确绑定的 NVIDIA/AMD 厂商版本;不把持续变化的社区 GGUF、AWQ 或二次量化仓库混入同一口径。
统计口径
下表采用以下固定口径:
- 调用 Hugging Face model API 记录当前 repository SHA。
- 递归列出仓库文件。
- 对所有以
.safetensors结尾的文件求字节和。 - 同时报告十进制 GB 和二进制 GiB:
1 GB=10^9 bytes,1 GiB=2^30 bytes。
这比 参数量 × 标称 bit 更可靠,因为真实仓库还包含未量化模块、scale、zero point、padding、多模态组件和 MTP 权重。它仍只是检查点权重下界,不是进程峰值。
Qwen3.5
Qwen 的官方 Qwen3.5 集合当前为 397B-A17B、122B-A10B、35B-A3B 和 27B 列出 FP8 与 GPTQ-Int4;9B、4B、2B、0.8B 没有列出官方量化仓库。社区 GGUF/AWQ 版本更新频繁,不混入这张官方口径表。
| 官方仓库 | 当前 SHA | safetensors | 维护者给出的实际部署下界 |
|---|---|---|---|
| 397B-A17B-FP8 | ea5b4f81 |
406.15 GB / 378.26 GiB | SGLang:8×H100、4×H200、4×B200 或 2×B300;vLLM 另验证 8×H200 |
| 397B-A17B-GPTQ-Int4 | df333de3 |
235.71 GB / 219.52 GiB | vLLM recipe 标注约 239 GB aggregate;实际并行拓扑需按后端核验 |
| 122B-A10B-FP8 | a099dee7 |
127.16 GB / 118.43 GiB | 2×H200 或 4×H100 |
| 122B-A10B-GPTQ-Int4 | 30cd92cb |
78.86 GB / 73.45 GiB | 1×80 GB GPU |
| 35B-A3B-FP8 | 9d1823d2 |
37.46 GB / 34.89 GiB | 单张 H100/H200;recipe 运行预算约 42 GB |
| 35B-A3B-GPTQ-Int4 | 3af5ca29 |
24.42 GB / 22.74 GiB | 1×24 GB GPU |
| 27B-FP8 | 97f5941b |
30.87 GB / 28.75 GiB | 1×40 GB GPU |
| 27B-GPTQ-Int4 | 8f0c09f2 |
30.24 GB / 28.16 GiB | recipe 写 1×24 GB,但当前完整仓库字节已超过 24 GB,见下方警告 |
这些设备结论来自 vLLM Qwen3.5 recipes 和 SGLang Qwen3.5 cookbook,并受 engine 版本、上下文、并发和 text-only/multimodal 模式约束。
Kimi K3
Kimi K3 是 2.8T total / 104B active 的 MoE,模型卡声明 MXFP4 weight、MXFP8 activation,并从 SFT 开始做 QAT。当前 SHA 9f62e4e9 的 96 个 safetensors 合计:
$$
\mathbf{1{,}560.94\ GB = 1{,}453.74\ GiB}.
$$
104B active 主要降低每 token 参与计算的专家数,不意味着只需常驻 104B 参数。当前 config 的 MXFP4 group size 为 32,但 attention、shared experts、普通 MLP pattern、LM head、vision tower 和 projector 等存在高精度排除项。
部署方面:
- vLLM 表示最容易的路径是 8×B300 或 8×MI355X;
- 在 B200/GB200 这一代上至少需要 16 张;
- SGLang 给出的其他拓扑包括 16×H200 和 32×H100。
更重要的是,SGLang 说明 Blackwell 可走 FlashInfer MXFP4/W4A8,而 H100/H200 固定走 Marlin W4A16。模型卡的“MXFP8 activation”是训练与目标数值方案,checkpoint config 只序列化 weight compression;最终 activation 路径仍由运行 kernel 决定。vLLM Kimi K3 SGLang Kimi K3
GLM-5.2
Z.ai 的官方 GLM-5.2 集合当前只有 BF16 与 FP8。NVIDIA NVFP4 和 AMD MXFP4 是厂商量化仓库,应与 Z.ai 官方权重分开标注。
| 仓库 | 身份与量化范围 | safetensors | 维护者实际部署下界 |
|---|---|---|---|
| zai-org/GLM-5.2-FP8 | 官方 FP8,部分模块排除 | 755.63 GB / 703.74 GiB | vLLM:8×H200/H20;完整 1M context 使用 8×B200 + FP8 KV |
| nvidia/GLM-5.2-NVFP4 | 厂商版;主要量化 MoE expert linear,其他模块保留 FP8/BF16 | 464.82 GB / 432.90 GiB | Blackwell-only,vLLM recipe 使用 TP8 |
| amd/GLM-5.2-MXFP4 | 厂商版;主要量化 MoE weight,非 MoE 模块高精度 | 438.00 GB / 407.92 GiB | 8×MI355X TP8 推荐;TP4 可做有余量的验证 |
GLM-5.2 FP8 的纯权重算术看似约五张 141 GB 设备就够,但维护者使用八张,是因为模型还要满足分片、非均匀模块、KV、workspace、图和服务余量。这正是“文件放得下”与“系统跑得起来”的差别。 vLLM GLM-5.2 Recipe SGLang GLM-5.2
显存下界与可运行拓扑
检查点回答“持久化权重至少占多少”,运行拓扑还要回答这些权重怎样分片,以及每个请求把哪些短期和长期状态带入设备。
完整峰值
推理峰值应拆为:
$$
\begin{aligned}
M_{\text{peak}}={}&M_{\text{weights}}
+M_{\text{KV/recurrent}}
+M_{\text{live activations}}\
&+M_{\text{operator workspace}}
+M_{\text{graph/communication}}
+M_{\text{allocator reserve}}
+M_{\text{margin}}.
\end{aligned}
$$
- Weights:packed elements、scale、zero point、未量化模块和运行时重排。
- KV/recurrent:KV cache、MLA latent、GDN/KDA state,随上下文、batch 和并发变化。
- Activations:prefill 通常比单 token decode 保留更多中间量。
- Workspace:动态量化、GEMM、Attention、MoE routing、repack/swizzle 的临时对象。
- Graph/communication:CUDA Graph、TP/EP collective、对称内存和通信 staging。
- Reserve/margin:分配器保留、碎片和服务波动余量。
Mweights 的下界;其余项必须用具体 engine、硬件、上下文、并发和多模态模式测量。三个必须分开的数字
- 仓库字节:固定 SHA 后求 safetensors 之和,是可复核事实。
- 权重算术下界:
ceil(checkpoint_GiB / device_GiB),只能回答聚合容量,不能证明能分片或启动。 - 维护者拓扑:给出硬件、TP/EP/PP、KV dtype、上下文和验证状态,才接近工程最低配置。
本地测量方法
真正要确认“我的机器至少多少显存”,应固定下列条件再启动:
1 | model repo + revision |
然后分别记录:
- load 完成后的 resident memory;
- 首次 warmup/JIT/repack 峰值;
- prefill 峰值;
- steady decode 峰值;
- 最大上下文和目标并发下的峰值;
- 每卡最大值,而不是只看集群总和。
选型与验证清单
如果目标是“先跑起来,再逐步提速”,可以按以下顺序选择:
- 先核对 kernel:目标 GPU/加速卡是否原生支持该 W/A 组合;没有原生路径时,先把它当成兼容性或存储格式,而不是性能承诺。
- 再看压缩对象:
- 权重放不下:优先 W4/W8A16、GPTQ/AWQ、FP8/MXFP4 weight。
- 激活或通信瓶颈:再考虑 W16A8、W8A8。
- 追求极限低比特:W8A4、W4A4/A8,但先做精度回归。
- 读取 config:确认 target、group size、dynamic/static、symmetric、ignore 和 KV scheme。
- 核对完整仓库字节:不要从模型名推断大小。
- 按维护者拓扑预留:权重算术下界只能用来发现“不可能”,不能用来证明“可运行”。
- 分阶段验收精度:至少覆盖 perplexity/任务集、长上下文、工具调用、多模态和极端 activation。
- 分阶段验收性能:区分 load、prefill、decode、单请求延迟、并发吞吐和每卡峰值。
最终判断可以压缩为:
位宽决定候选表示,尺度与粒度决定数值误差,kernel 决定实际执行,完整对象生命周期决定显存。
参考资料
- OCP 8-bit Floating Point Specification
- OCP Microscaling Formats Specification
- NVIDIA Transformer Engine MXFP8
- TensorRT Quantized Types and Schemes
- PyTorch torchao Quantization Overview
- vLLM Quantization Schemes at
7aea73d8 - Qwen3.5 Official Collection
- Kimi K3 Model Card
- GLM-5.2 Official Collection
- vLLM Recipes
- SGLang Model Cookbooks
Inference Quantization Formats