跳到主要内容
推理优化 · 更新于 2026年11月1日

4.3 Weight-only INT4

理解 W4A16 的存储与计算、GPTQ 的二阶误差补偿、AWQ 的激活感知缩放,以及 Marlin 如何把 INT4 存储变成推理收益

GPTQ AWQ INT4 Weight-only Marlin

4.2 节的 W8A8 要同时对付权重和激活。这一节换个思路:激活保留 FP16/BF16,只把权重压到 4-bit,即 W4A16。 Decode 阶段每步都要把整份权重从显存读一遍,权重变成四分之一,读的时间也跟着少。难点在于 4-bit 只有 16 个编码,格距是 8-bit 的约 17 倍(4.1 节),必须想办法让误差少伤输出。

GPTQ 和 AWQ 是两种主流的解法:前者像拼图时边放边调整剩下的块,后者先找出最要紧的几块重点保护。Marlin 则负责让 GPU 高效地读和算这些 4-bit 权重。

⏰ 信息时效:算法部分依据各自论文;vLLM 的 Kernel 选择与工具链建议基于 vLLM v0.32.0(2026-10-18 发布)。

📑 目录


1. W4A16 的存储和计算

INT4 权重在显存里是打包存放的:一个 32-bit 字装 8 个 4-bit 编码,另外存每组的 Scale,非对称方案还有零点。推理时,Kernel 读出编码,在片上反量化成 FP16/BF16,再和 16-bit 激活做矩阵乘法。

所以看到”INT4 模型”,要分清两件事:权重的存储类型是 INT4,乘法指令的输入类型仍是 16-bit。这条路径省的是读权重的带宽,不是乘法的算力。

下图左边是显存里的一组权重(蓝色为 INT4 编码,黄色为 FP16 Scale,白格为非对称方案才有的零点),右边是 Kernel 在片上把编码还原成 FP16 权重(绿色)后,再和 16-bit 激活(红色)相乘:

W4A16 的存储与计算:显存里 8 个 4-bit 编码打包成一个 32-bit 字,每 128 个权重共享一个 FP16 Scale,Kernel 在片上解包、反量化成 FP16 后再与 16-bit 激活做矩阵乘

分组参数也占空间。每 128 个 INT4 权重共用一个 FP16 Scale 时,平均每个权重占:

48+2128=0.515625 byte\frac{4}{8}+\frac{2}{128}=0.515625\ \text{byte}

再加零点和对齐还会略多。能省多少时间,要看解包与反量化是否和矩阵乘法融合在同一个 Kernel 里,以及 KV 读取、Attention 等其他部分还占多大比例。


2. GPTQ:边量化边补偿

2.1 目标要带上输入

如果只最小化 ∥W−W^∥F2\|W-\widehat W\|_F^2,每个权重的误差都按同样的分量计分。可经过线性层后,有的误差被输入放大,有的则互相抵消。GPTQ 用校准输入,把目标换成层输出的重建误差:

min⁡W^ ∥XW−XW^∥F2\min_{\widehat W}\ \|XW-X\widehat W\|_F^2

取一个输出通道的权重列 ww,记 δ=w−w^\delta=w-\widehat w:

E=∥Xδ∥22=δTXTXδ,H=2XTXE=\|X\delta\|_2^2=\delta^\mathsf TX^\mathsf TX\delta, \qquad H=2X^\mathsf TX

HH 刻画了在真实输入下,每个方向上的误差要付多大代价,以及输入通道之间的相关性。它只来自单个线性层的重建目标,不需要整个语言模型损失的 Hessian。

2.2 补偿的直觉

拼图时一块放歪了,可以挪一挪旁边还没固定的块来弥补。GPTQ 也是这样:每固定一个权重,就把它的误差分摊给还没量化的权重。

看两维的例子。若校准输入的两列几乎相同,输出主要取决于 w1+w2w_1+w_2。w1=0.26w_1=0.26 量化成 0.30,多了 0.04;趁 w2w_2 还没量化,让它减 0.04,输出就基本不变。两列不完全相同时,补多少由 HH 决定,不是简单的一加一减。

设 FF 是尚未固定的权重下标集合,HFH_F 是对应的子矩阵。把 wiw_i 固定为量化值 w^i\widehat w_i 后,最优补偿是:

ΔwF=−wi−w^i[HF−1]ii[HF−1]:,i\Delta w_F=-\frac{w_i-\widehat w_i}{[H_F^{-1}]_{ii}}[H_F^{-1}]_{:,i}

第 ii 项正好把 wiw_i 变成 w^i\widehat w_i,其余项给出剩余权重的调整量。w^i\widehat w_i 是反量化后的值,和原权重同一尺度,不是整数编码。官方实现还加了阻尼项保证数值稳定,并用 Cholesky 分解和分块更新降低成本。

两个”128”别搞混:更新块大小决定一次处理多少列、多久批量传播一次误差;Group Size 决定多少权重共享一个 Scale。

2.3 代价与效果

论文报告,GPTQ 量化 175B 参数的 GPT 模型约需 4 个 GPU 小时,降到 3–4 bit 时精度损失可忽略,第一次让 175B 模型能在单张 GPU 上运行。量化是离线一次性的成本,推理时不再需要 HH。


3. AWQ:按激活找出要保护的通道

3.1 权重大,不一定最要紧

对 y=∑jxjwjy=\sum_jx_jw_j,权重误差 Δwj\Delta w_j 对输出的影响是 xjΔwjx_j\Delta w_j。一个权重要不要紧,取决于和它相乘的激活有多大,而不是它自己有多大。

AWQ 的论文指出两点:只保护 1% 的显著权重就能大幅减小量化误差;而找显著通道应该看激活分布,不是看权重。

3.2 用缩放代替混合精度

最直接的保护是让这 1% 保持 FP16,但混合精度的存储布局对 Kernel 很不友好。AWQ 改用缩放:量化前把显著通道的权重放大 sjs_j 倍,输入相应除以 sjs_j。沿用 4.2 节的符号:

Y=(XS−1)(SW),Y^=(XS−1) D(Q(SW))Y=(XS^{-1})(SW),\qquad \widehat Y=(XS^{-1})\,D(Q(SW))

QQ 生成低比特编码,DD 用 Scale 和零点反量化。放大后,只要这个通道所在组的量化格距没有同比例变大,除回 sjs_j 后它的有效误差就变小了。但放得太大会撑宽整组量程,误伤同组其他权重,所以 AWQ 要在校准数据上搜索缩放因子,再配合截断控制总误差。

部署出来的仍是规则的 INT4 布局,没有单独存成 FP16 的权重,所以高效 Kernel 容易支持它。论文配套的 TinyChat 在桌面和移动 GPU 上比 Hugging Face FP16 实现快 3 倍以上。

3.3 与 SmoothQuant 对照

两者都用配对缩放,但目的不同:

📊 方法处理什么缩放的方向典型配置
SmoothQuant激活离群值让 INT8 激活难量化压平激活,难度转给权重INT8 W8A8
AWQ显著通道的权重误差被大激活放大放大显著权重,降低其相对误差INT4 W4A16

只做 AWQ 的缩放搜索,得到的还是浮点权重,后面仍要量化和打包。比较方法时要分清三种中间产物:缩放后的浮点权重、模拟量化(QDQ)后的浮点权重、真正编码打包的部署权重。


4. Marlin:让解包不拖慢计算

编码好了,还有一道工程题。最笨的做法是先把整层 INT4 展开成 FP16 写回显存,再调用普通 GEMM,结果多了一次大规模读写,省下的带宽全吐了回去。

Marlin 把读权重、解包、反量化和矩阵乘法放进同一个 Kernel 的流水线里。下图左边是朴素做法,展开后的 FP16 权重(绿色)要写回显存再读一遍;右边是 Marlin,蓝色的搬运和黄色的片上计算错开一个 Tile 同时进行:

朴素做法先把 INT4 展开成 FP16 写回显存再做 GEMM,每个权重约读写 4.5 B;Marlin 在一个 Kernel 里分块读入 INT4,在片上解包、反量化并做矩阵乘,搬运与计算重叠

权重事先重排成适合运行时读取的布局,再用模块二讲过的 Tiling 与多级缓冲,把数据搬运和计算重叠起来。论文报告,Batch 在 16–32 以内时能接近 4 倍的理论加速上限,到 64–128 时加速逐渐减小但仍然明显;接入 vLLM 后端到端最高约 2.8 倍。

Batch 一大,计算重新成为瓶颈,少读权重的优势就被摊薄,这和第 1 章的 Roofline 分析一致。

4.1 vLLM 中的 Kernel 选择

在 vLLM v0.32.0 中,量化方法名和 Kernel 已经解耦:

  • --quantization 写 awq、awq_marlin 或 auto_awq,都解析为同一个 auto_awq 配置;GPTQ 的 gptq、gptq_marlin、auto_gptq 同理。不写时按检查点里的 quant_method 自动识别。
  • 具体用哪个 Kernel,由 vLLM 按优先级逐个检查能否实现当前层。CUDA 上 INT4 W4A16 权重的主要候选依次是 Machete(仅 Hopper,计算能力 9.0)、Marlin(Turing 及以上)、Conch、Exllama、Triton 等,第一个通过检查的胜出。
  • 启动日志会打印 Using MarlinLinearKernel for AutoAWQMarlinLinearMethod 这类信息;AWQ 层不满足条件时则会提示 “Falling back to unoptimized AWQ kernels”。
  • 想排除某个 Kernel 做对比,可以用环境变量 VLLM_DISABLED_KERNELS。

GPTQ/AWQ 决定编码的误差,Kernel 决定编码跑得多快。 判断实际走了哪条路径,看启动日志,不要只凭模型名字。


5. 怎样比较 GPTQ 与 AWQ

5.1 先拆开四个维度

“GPTQ、INT4 W4A16、Group Size 128、Marlin”这一串描述,分别回答了四个不同的问题:

📊 维度例子回答什么
量化算法GPTQ 或 AWQ怎样控制量化误差
格式与位宽INT4 W4A16权重怎么编码,激活保留什么精度
量化粒度Group Size 128多少权重共享一组 Scale
执行实现Marlin、Machete编码怎样被读取和计算

W4A16 不限定算法,同一种算法也可以配不同的分组和 Kernel。

下图把两种算法放在一起:左边 GPTQ 逐列量化(蓝色已量化、蓝色斜线为当前列),把误差补偿给右侧尚未量化的浮点权重(绿色);右边 AWQ 先按激活幅值找出显著通道(红色实心),放大对应权重后再整组量化。两条路最后都导出同样规整的 INT4 检查点:

GPTQ 逐列量化并把误差补偿给剩余权重,AWQ 按激活找出显著通道并缩放保护,两者最终都打包成 INT4 并导出 Scale 与零点

四项都固定了,再比较算法本身:

📊 维度GPTQAWQ
校准数据用来做什么构造 HH,衡量层输出误差统计激活,搜索通道缩放
主要手段逐列量化,补偿剩余权重放大显著通道,降低其相对误差
离线计算逐层求解重建问题,更新每个权重搜索每通道的缩放因子,不逐个改写权重
推理速度取决于导出布局与 Kernel同样取决于导出布局与 Kernel

“AWQ 一定更快""GPTQ 一定更准”都缺少前提。公平对比至少要固定基础模型、位宽、Group Size、校准集、输入长度和负载,并记录实际加载的 Kernel。

5.2 用什么工具量化

  • LLM Compressor:vLLM 项目维护,支持 GPTQ、AWQ 和 SmoothQuant,导出的 compressed-tensors 格式可直接被 vLLM 加载。vLLM 文档的 INT4 W4A16 示例用 GPTQModifier(scheme="W4A16")。
  • GPTQModel:专注 GPTQ 系列,文档注明兼容的模型可以用 vLLM 的 Marlin 和 Machete Kernel。
  • AutoAWQ:已停止维护,vLLM 文档建议改用 LLM Compressor 中的 AWQ。已有的 AutoAWQ 检查点仍能正常加载。

5.3 排查顺序

  1. 确认检查点:量化配置是否完整,基础模型和版本是否一致。
  2. 看加载日志:最终用了哪个 Kernel,有没有回退。
  3. 先验质量:用独立的任务集评测,别只挑几条好看的输出。
  4. 再测性能:分别看小 Batch Decode、大 Batch 和长 Prompt;量化省出的显存也可能被用来扩大并发。

📝 总结

  • W4A16 只压缩权重,存储是 INT4,乘法输入仍是 16-bit,省的是读权重的带宽。
  • GPTQ 用校准输入衡量层输出误差,借助 H=2XTXH=2X^\mathsf TX 把每个权重的误差补偿给尚未量化的权重。
  • AWQ 按激活大小找显著通道,用缩放代替混合精度来保护它们,部署布局仍然规整。
  • Marlin 把解包、反量化和矩阵乘法融合进一条流水线,小 Batch 下接近 4 倍理论加速,Batch 变大后收益递减。
  • vLLM v0.32.0 中 AWQ/GPTQ 的方法别名已统一,Kernel 按优先级自动选择,以启动日志为准。
  • 比较 GPTQ 与 AWQ 前,先固定格式、粒度和 Kernel。

🎯 自我检验清单

  • 能算出 Group Size 128、FP16 Scale 下每个 INT4 权重的平均存储量
  • 能解释为什么 ∥XW−XW^∥\|XW-X\widehat W\| 比单看权重误差更贴近层输出
  • 能用两个高度相关的输入通道说明 GPTQ 的补偿思路
  • 能区分 GPTQ 的更新块大小和量化 Group Size
  • 能说明 AWQ 为什么看激活而不是权重来找显著通道,以及它为何用缩放代替混合精度
  • 能对比 AWQ 与 SmoothQuant 中配对缩放的方向和目的
  • 能解释 Marlin 的加速为什么会随 Batch 变大而减小
  • 能从 vLLM 启动日志判断 INT4 模型实际使用的 Kernel

📚 参考资料