模型量化

1955 字
10 分钟
模型量化

模型量化概述#

模型量化就是利用低精度的数据类型表示模型中的权重、激活以及 KV Cache,从而降低显存占用、减少内存带宽需求,并利用低精度 Tensor Core 提高推理吞吐。

比如 FP16 权重通常可以量化到 INT8 或 INT4。最简单的做法是根据一组 scale,把浮点数映射到整数:

q=round(x/s)q = \operatorname{round}(x/s)

推理时再通过

x^=sq\hat{x} = sq

近似恢复原来的浮点值。

量化的核心问题是:如何在降低 bit-width 的同时尽可能减少精度损失

常见数据类型#

类型总位数符号位指数位尾数位大致动态范围典型用途
FP32321823≈ ±3.4×10³⁸训练、累加
FP16161510≈ ±6.55×10⁴训练/推理
BF1616187≈ ±3.39×10³⁸LLM 训练/推理
FP8 E4M38143≈ ±448推理/训练
FP8 E5M28152≈ ±5.73×10⁴梯度/训练
INT88-128 ~ 127量化推理
INT44-8 ~ 7Weight-only

BF16 与 FP16 都是 16 bit,但 BF16 的动态范围接近 FP32,而 FP16 的精度更高。 因为 FP16 给更多的 bit 给 mantissa,所以精度更高;BF16 用更多 bit 给 exponent,所以动态范围更大。 现在的 LLM 相比 FP16 更喜欢 BF16,因为 LLM 的问题往往不是“需要特别高的有效数字精度”,而是需要足够大的动态范围来避免 overflow / underflow

想要更低精度的量化就需要更加精密的量化方法。 对于 INT8,它能表示 -128~127 的整数,使用普通的 scale 量化往往就能工作。 而对于 INT4,它只能表示 -8~7 的整数,范围非常有限,这意味着 INT4 的量化误差远比 INT8 敏感。 所以对于目标 INT4 的量化,一般采用 AWQ/GPTQ 方法。

Note

NVIDIA Blackwell 架构 GPU 引入了 NVFP4,详见 Introducing NVFP4 for Efficient and Accurate Low-Precision Inference

NVFP4
NVFP4

为什么量化可以加速?#

量化主要带来三个收益:

  1. 显存占用降低:例如 FP16 -> INT4,权重理论上变成原来的 1/4。
  2. 内存带宽压力降低:LLM decode 阶段经常是 memory-bound,权重读取量降低后,可以提高 token generation 的吞吐。
  3. 低精度计算单元吞吐更高:现代 GPU 的 Tensor Core 对 INT8、FP8、INT4 等低精度计算有专门支持,因此在计算受限的场景下也可能获得加速。

但实际性能取决于硬件、kernel 是否支持该精度、dequantization 开销以及模型处于 prefill 还是 decode 阶段,所以不能简单认为 INT4 就一定比 FP16 快。

量化分类#

如果按量化的对象进行分类可以这么分

类型量化对象典型用途
Weight-only只量化权重LLM 推理,尤其 decode
Activation quantization量化激活INT8/FP8 inference
Weight + Activation权重和激活都量化更激进的低精度推理
KV Cache quantization量化 KV Cache长上下文、提高 batch
Note

对 LLM inference 来说,weight-only quantization 非常重要,因为 decode 阶段每生成一个 token,都需要从显存读取大量模型权重,而计算量相对有限,所以降低权重的数据量可以显著缓解 memory bandwidth bottleneck。

还可以分成 PTQQAT

PTQ (Post-Training Quantization) 就是模型训练完以后再量化,不需要重新训练或者只需要少量校准数据

QAT (Quantization-Aware Training) 就是在训练过程中模拟量化误差,让模型主动适应低精度。 相比 PTQ,它的优点是精度损失更小,而缺点是需要重新训练或者 fine-tuning,成本更高。

Note

工程部署中 PTQ 更常见,因为不需要重新训练整个模型;当低 bit 量化导致精度损失比较严重时,可以考虑 QAT。

模型量化方法#

AWQ#

AWQ,全称 Activation-aware Weight Quantization,是一种针对 LLM 的 weight-only PTQ 方法。

它的核心思想是:并不是所有权重对量化误差同样敏感,而一些对应重要激活通道的权重更加敏感

因此,AWQ 利用少量校准数据观察 activation 的分布,识别重要的 channels,然后通过对权重进行 per-channel scaling,使这些重要通道在量化时受到更好的保护,最后通常将权重量化到 INT4。

它的一个重要特点是:它不需要对模型进行反向传播或者重新训练,而是通过 activation statistics 来决定如何保护重要权重,因此非常高效,校准成本极低。

Important

论文原话:

Weights of LLMs are not equally important: there is a small fraction of salient weights that are much more important for LLM’s performance compared to others.

AWQ
AWQ

为什么用 Activation 来判断权重的重要性?#

一个自然的疑问是:既然要保护权重,为什么不直接看权重本身的大小,而要看 activation?

直觉上,线性层的输出为 y=Wxy = Wx。某个权重 wijw_{ij} 对输出的贡献不仅取决于它自身的数值,还取决于对应输入 xjx_j 的大小。 如果某个激活通道 xjx_j 的幅值始终很大,那么该通道对应的所有权重 w:jw_{:j} 的量化误差就会被”放大”地传递到输出。 因此,activation 的统计信息比 weight 本身更能反映量化误差的实际影响

AWQ 正是利用这一点:用校准数据跑一遍前向传播,统计每个输入通道的激活幅值(通常取 mean magnitude),以此作为该通道”重要性”的度量。

AWQ 到底“怎么保护重要权重”?#

最朴素的想法是:把重要权重保留为 FP16,其余量化为 INT4。 但这会导致权重矩阵中混合 INT4 和 FP16 两种精度,不仅增加存储格式的复杂性,还对硬件 kernel 的实现极不友好(难以利用 INT4 的并行计算优势)。

AWQ 的作者提出了一个更优雅的变通方法——Per-channel Scaling

回顾:量化是怎么做的

对于 NN bit 的对称均匀量化,一个简单的量化过程可以表示为:

Q(w)=ΔRound(wΔ),Δ=max(w)2N1Q(w) = \Delta \cdot \text{Round}(\frac{w}{\Delta}), \quad \Delta = \frac{\max(|w|)}{2^{N-1}}

其中,Δ\Delta 是量化步长(scale),它决定了连续的浮点数如何映射到有限的离散整数表示。 量化精度越低,可表示的离散值越少,因此量化误差也越明显。

AWQ 会在量化前,将权重先 scale 一下,也就是乘以一个缩放系数 ss

Q(ws)xs=ΔRound(wsΔ)x1s,Q(w\cdot s)\cdot \frac{x}{s} = \Delta^{'} \cdot \text{Round}(\frac{ws}{\Delta^{'}}) \cdot x \cdot \frac{1}{s},

量化引入的误差如下:

Err(Q(w)x)=Q(w)xwx=(Q(w)w)x=(ΔRound(wΔ)w)x=Δ(Round(wΔ)wΔ)x=ΔRoundErr(wΔ)x\begin{aligned} \texttt{Err}(Q(w)\cdot x) &= Q(w) \cdot x - w \cdot x \\ &= (Q(w) - w) \cdot x \\ &= (\Delta \cdot \text{Round}(\frac{w}{\Delta}) - w) \cdot x \\ &= \Delta (\text{Round}(\frac{w}{\Delta}) - \frac{w}{\Delta}) \cdot x \\ &= \Delta \cdot \texttt{RoundErr}(\frac{w}{\Delta})\cdot x \\ \end{aligned}Err(Q(ws)(xs))=ΔRoundErr(wsΔ)x1s\begin{aligned} \texttt{Err}(Q(w \cdot s)(\frac{x}{s})) &= \Delta^{'}\cdot \texttt{RoundErr}(\frac{ws}{\Delta^{'}})\cdot x\cdot \frac{1}{s} \end{aligned}

AWQ 作者发现 RoundErr()RoundErr(\cdot) 来源于将一个浮点数四舍五入到一个整数,它的范围在 [0,0.5][0,0.5] 间,也就是说平均误差是 0.25。 我们可以将 RoundErr()RoundErr(\cdot) 用均值 0.25 代替,然后让两个误差做比,可以得到引入 ss 后的误差变化:

Err(Q(ws)(xs))/Err(Q(w)x)=ΔΔ1s\texttt{Err}(Q(w \cdot s)(\frac{x}{s})) / \texttt{Err}(Q(w)\cdot x) = \frac{\Delta^{'}}{\Delta} \cdot \frac{1}{s}
Note

实际上论文的推导还是很模糊的, Δ/Δ\Delta^{'} / \Delta 理论上就是 ss,两者一比实际上是 1。

AWQ 认为 ss 在一定范围内有 ΔΔ\Delta^{'} \approx \Delta,因此在这个合理的范围内 ss 越大越好。

理论上最合适的 ss 应该求解下面的问题:

s=argminsL(s)L(s)=Q(Wdiag(s))(diag(s)1X)WX\begin{aligned} \mathbf{s}^* &= \mathop{\arg\min}_{\mathbf{s}} \mathcal{L}(\mathbf{s}) \\ \mathcal{L}(\mathbf{s}) = \lVert Q(\mathbf{W}\cdot \text{di}&\text{ag}(\mathbf{s})) (\mathbf{\text{diag}(s)^{-1}} \cdot \mathbf{X}) - \mathbf{W}\mathbf{X} \rVert \end{aligned}

但是作者给出一个更小的搜索空间

s=sXα,α=argminαL(sXα)\mathbf{s}=\mathbf{s_X}^{\alpha}, \quad \alpha^*=\mathop{\arg\min}_{\alpha}\mathcal{L}(\mathbf{s_X}^{\alpha})

sX\mathbf{s_X} 是各通道的平均激活值,α\alpha 是引入的一个常数。

参考#

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
模型量化
https://llm-tech.com.cn/posts/quantization/
作者
Ming
发布于
2026-08-12
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Ming
你是来找 Ming 学习的吗
🎉 欢迎来到 Ming 的博客
这里是我的个人博客,分享 AI Infra、LLM 等技术内容。欢迎关注交流!
分类
标签
站点统计
文章
19
分类
8
标签
16
总字数
55,114
运行时长
0
最后活动
0 天前

目录