量化技术的完整体系与可运行实现
目录
- 第一部分:量化基础理论
- 第一章:绪论------为什么需要模型量化
- 第二章:量化的数学基础------标量量化
- 第三章:信息论视角------率失真理论
- 第二部分:权重量化方法
- 第四章:训练后量化(PTQ)基础
- 第五章:GPTQ------基于 Hessian 的量化
- 第六章:AWQ------激活感知权重量化
- 第七章:SmoothQuant 与 QuIP------进阶 PTQ 方法
- 第三部分:激活量化与混合精度
- 第八章:激活量化的挑战与异常值问题
- 第九章:量化感知训练(QAT)
- 第十章:W4A8 与 W8A8------权重-激活联合量化
- 第四部分:量化格式与硬件
- 第十一章:量化数据类型------从 INT4 到 FP8
- 第十二章:量化推理引擎与硬件加速
- 第五部分:完整可运行代码实现
- 第十三章:从零实现标量量化系统
- 第十四章:从零实现 GPTQ
- 第十五章:从零实现 AWQ 与 SmoothQuant
- 第十六章:完整 PTQ Pipeline 与精度对比
- 第六部分:理论分析与前沿
- 第十七章:量化的统一理论框架与 Scaling Law
- 附录
第一部分:量化基础理论
第一章:绪论------为什么需要模型量化
1.1 大语言模型的存储与计算瓶颈
1.1.1 模型规模的增长趋势
现代大语言模型(LLM)的参数量呈指数增长:
| 模型 | 发布年份 | 参数量 | 权重格式 | 存储需求 |
|---|---|---|---|---|
| GPT-2 | 2019 | 1.5B | FP32 | 6 GB |
| GPT-3 | 2020 | 175B | FP16 | 350 GB |
| LLaMA-65B | 2023 | 65B | FP16 | 130 GB |
| LLaMA-2-70B | 2023 | 70B | FP16 | 140 GB |
| Mixtral 8×7B | 2024 | 47B(活跃 13B) | FP16 | 94 GB |
| LLaMA-3-405B | 2024 | 405B | BF16 | 810 GB |
这里涉及几个关键术语:
- FP32(32 位浮点数,Single Precision) :1 位符号 + 8 位指数 + 23 位尾数,动态范围约 ±3.4×1038\pm 3.4 \times 10^{38}±3.4×1038,精度约 7 位有效十进制数字
- FP16(16 位浮点数,Half Precision) :1 位符号 + 5 位指数 + 10 位尾数,动态范围约 ±6.5×104\pm 6.5 \times 10^4±6.5×104,精度约 3.3 位有效十进制数字
- BF16(Brain Float 16):1 位符号 + 8 位指数 + 7 位尾数,与 FP32 相同的动态范围但精度更低(约 2.4 位有效数字)
1.1.2 推理时的内存瓶颈
模型推理时的内存占用由以下几部分组成:
1. 模型权重(Model Weights) :这是最大的内存消耗。对于参数量为 Φ\PhiΦ 的模型,使用 bbb 比特存储每个参数:
Mweights=Φ⋅b8 字节M_{\text{weights}} = \frac{\Phi \cdot b}{8} \text{ 字节}Mweights=8Φ⋅b 字节
2. KV 缓存(Key-Value Cache) :在自回归生成中,为了避免重复计算,需要缓存每一层、每个注意力头的历史 Key 和 Value 向量。对于序列长度 sss、层数 LLL、头数 hhh、头维度 dhd_hdh:
MKV=2⋅L⋅h⋅dh⋅s⋅bact/8 字节M_{\text{KV}} = 2 \cdot L \cdot h \cdot d_h \cdot s \cdot b_{\text{act}} / 8 \text{ 字节}MKV=2⋅L⋅h⋅dh⋅s⋅bact/8 字节
其中 bactb_{\text{act}}bact 是激活值的比特数,因子 2 来自同时存储 K 和 V。
3. 激活值(Activations):前向传播中各层的中间结果,大小取决于 batch size 和序列长度。
4. 运行时开销:包括计算图、临时缓冲区等。
1.1.3 计算瓶颈
Transformer 的核心计算是矩阵乘法。对于线性层 Y=XWY = XWY=XW,其中 X∈RB×dinX \in \mathbb{R}^{B \times d_{\text{in}}}X∈RB×din,W∈Rdin×doutW \in \mathbb{R}^{d_{\text{in}} \times d_{\text{out}}}W∈Rdin×dout:
FLOPs=2⋅B⋅din⋅dout\text{FLOPs} = 2 \cdot B \cdot d_{\text{in}} \cdot d_{\text{out}}FLOPs=2⋅B⋅din⋅dout
(每个输出元素需要 dind_{\text{in}}din 次乘法和 din−1d_{\text{in}} - 1din−1 次加法,近似为 2din2 d_{\text{in}}2din 次浮点运算。)
计算强度(Arithmetic Intensity) 定义为浮点运算次数与内存访问字节数的比值:
I=FLOPsBytes AccessedI = \frac{\text{FLOPs}}{\text{Bytes Accessed}}I=Bytes AccessedFLOPs
当 I<IpeakI < I_{\text{peak}}I<Ipeak(硬件的峰值计算强度)时,推理是内存带宽受限(Memory-Bandwidth Bound) 的------处理器在等待数据从内存传输,而非在计算。
定理 1.1(Transformer 推理的带宽受限性) :在自回归生成阶段(batch size B=1B = 1B=1),每个 token 的生成需要访问所有模型权重一次,但只进行 O(d2)O(d^2)O(d2) 次浮点运算。因此计算强度为:
I=2d22d2⋅(b/8)=8b FLOPs/ByteI = \frac{2 d^2}{2 d^2 \cdot (b/8)} = \frac{8}{b} \text{ FLOPs/Byte}I=2d2⋅(b/8)2d2=b8 FLOPs/Byte
对于 FP16(b=16b = 16b=16),I=0.5I = 0.5I=0.5 FLOPs/Byte。而现代 GPU(如 A100)的 Ipeak≈156I_{\text{peak}} \approx 156Ipeak≈156 FLOPs/Byte,内存带宽峰值为 2 TB/s。因此自回归推理严重受限于内存带宽。
量化如何帮助? 将权重量化为 INT4(b=4b = 4b=4)后,I=2I = 2I=2 FLOPs/Byte,内存访问量减少 4 倍,直接提升推理吞吐量。
1.2 量化的核心动机
量化(Quantization) 是将连续值(或高精度离散值)映射到低精度离散值的过程。在深度学习中,量化特将模型的权重和/或激活值从高精度格式(FP32/FP16/BF16)转换为低精度格式(INT8/INT4 等)。
量化带来的好处:
| 效益 | 说明 | 数学刻画 |
|---|---|---|
| 减少存储 | 每个参数的比特数从 16 降至 4-8 | 存储减少 2×2\times2×-4×4\times4× |
| 减少内存带宽 | 推理时需要读取的数据量更少 | 带宽需求同比降低 |
| 加速计算 | 整数运算比浮点运算更快,且可利用专用指令 | 理论加速 2×2\times2×-4×4\times4× |
| 降低能耗 | 整数运算的能耗远低于浮点运算 | INT8 乘法能耗约为 FP16 的 1/301/301/30 |
1.3 量化方法的分类
量化方法按时机 和是否需要训练可以分为两大类:
1.3.1 训练后量化(Post-Training Quantization, PTQ)
在模型训练完成后,直接对权重(和/或激活值)进行量化,不需要重新训练。
优点:简单快速,不需要训练数据的大量计算资源。
缺点:量化误差无法通过训练补偿,精度下降可能较大。
代表方法:RTN(Round-to-Nearest)、GPTQ、AWQ、SmoothQuant。
1.3.2 量化感知训练(Quantization-Aware Training, QAT)
在训练过程中模拟量化的效果,使模型学习适应量化误差。
优点:精度通常优于 PTQ。
缺点:需要完整的训练流程,计算成本高。
代表方法:LSQ(Learned Step Size Quantization)、LLM-QAT。
1.3.3 量化方案的另一个维度
按量化粒度分类:
| 粒度 | 说明 | 精度 | 开销 |
|---|---|---|---|
| 逐张量(Per-Tensor) | 整个张量共用一组量化参数 | 最低 | 最小 |
| 逐通道(Per-Channel) | 每个输出通道一组量化参数 | 中等 | 中等 |
| 逐组(Per-Group) | 每 ggg 个元素一组量化参数 | 较高 | 较大 |
| 逐元素(Per-Element) | 每个元素独立量化 | 最高 | 最大 |
按对称性分类:
| 类型 | 说明 | 零点 |
|---|---|---|
| 对称量化 | 量化范围关于 0 对称 | z=0z = 0z=0 |
| 非对称量化 | 量化范围可以不对称 | z≠0z \neq 0z=0 |
第二章:量化的数学基础------标量量化
2.1 量化问题的形式化定义
2.1.1 标量量化
定义 2.1(标量量化) :给定连续随机变量 X∈xmin,xmaxX \in x_{\\min}, x_{\\max}X∈xmin,xmax,bbb 比特标量量化是一个映射 Q:R→CQ: \mathbb{R} \to \mathcal{C}Q:R→C,其中码本(codebook)C={c0,c1,...,c2b−1}\mathcal{C} = \{c_0, c_1, \dots, c_{2^b - 1}\}C={c0,c1,...,c2b−1} 包含 2b2^b2b 个量化级别(quantization levels)。
量化过程分为两步:
-
编码(Encoding) :将连续值映射到离散索引
i=Qenc(x)=argminj∈{0,...,2b−1}∣x−cj∣i = Q_{\text{enc}}(x) = \arg\min_{j \in \{0, \dots, 2^b - 1\}} |x - c_j|i=Qenc(x)=argj∈{0,...,2b−1}min∣x−cj∣
-
解码(Decoding) :将离散索引映射回(近似的)连续值
x^=Qdec(i)=ci\hat{x} = Q_{\text{dec}}(i) = c_ix^=Qdec(i)=ci
量化误差(Quantization Error) 定义为:
e=x−x^=x−Q(x)e = x - \hat{x} = x - Q(x)e=x−x^=x−Q(x)
2.1.2 仿射量化(Affine Quantization)
最常见的量化方案是仿射量化(也称为零点量化,zero-point quantization),它用一个线性映射将实数映射到整数:
i=round(xs)+zi = \text{round}\left(\frac{x}{s}\right) + zi=round(sx)+z
x^=s⋅(i−z)\hat{x} = s \cdot (i - z)x^=s⋅(i−z)
其中:
- s>0s > 0s>0 是缩放因子(scale),也称为步长(step size)
- z∈Zz \in \mathbb{Z}z∈Z 是零点(zero-point),确保实数零被精确表示
- iii 是量化后的整数索引,取值范围为 0,2b−10, 2\^b - 10,2b−1(无符号)或 −2b−1,2b−1−1-2\^{b-1}, 2\^{b-1} - 1−2b−1,2b−1−1(有符号)
缩放因子和零点的计算:
s=xmax−xmin2b−1s = \frac{x_{\max} - x_{\min}}{2^b - 1}s=2b−1xmax−xmin
z=round(−xmins)z = \text{round}\left(-\frac{x_{\min}}{s}\right)z=round(−sxmin)
这里 xmaxx_{\max}xmax 和 xminx_{\min}xmin 是待量化张量中的最大值和最小值(或它们的某种估计)。
2.1.3 对称量化(Symmetric Quantization)
对称量化是非对称量化的特例,强制 xmin=−xmaxx_{\min} = -x_{\max}xmin=−xmax(即量化范围关于零对称),从而 z=0z = 0z=0:
i=round(xs)i = \text{round}\left(\frac{x}{s}\right)i=round(sx)
x^=s⋅i\hat{x} = s \cdot ix^=s⋅i
s=max(∣xmax∣,∣xmin∣)2b−1−1s = \frac{\max(|x_{\max}|, |x_{\min}|)}{2^{b-1} - 1}s=2b−1−1max(∣xmax∣,∣xmin∣)
(对于有符号 bbb 比特整数。)
对称 vs 非对称量化的理论比较:
定理 2.1 :对于均值为 μ≠0\mu \neq 0μ=0 的分布,非对称量化的均方误差(MSE)严格小于对称量化。
证明:设分布为 p(x)p(x)p(x),支撑集为 a,ba, ba,b,a<0<ba < 0 < ba<0<b,μ=EX≠0\mu = \mathbb{E}X \neq 0μ=EX=0。
对称量化使用区间 −Δ,Δ-\\Delta, \\Delta−Δ,Δ,其中 Δ=max(∣a∣,∣b∣)\Delta = \max(|a|, |b|)Δ=max(∣a∣,∣b∣)。
非对称量化使用区间 a,ba, ba,b。
非对称量化的量化区间 a,b⊂−Δ,Δa, b \subset -\\Delta, \\Deltaa,b⊂−Δ,Δ(除非 ∣a∣=∣b∣|a| = |b|∣a∣=∣b∣),因此在相同比特数下,非对称量化的步长更小:
sasym=b−a2b−1<2Δ2b−1=ssyms_{\text{asym}} = \frac{b - a}{2^b - 1} < \frac{2\Delta}{2^b - 1} = s_{\text{sym}}sasym=2b−1b−a<2b−12Δ=ssym
更小的步长意味着更小的量化误差。□\square□
实际意义:权重分布通常近似对称(均值接近零),适合对称量化;激活值分布通常不对称(经过 ReLU/GELU 后全为非负),适合非对称量化。
2.2 均匀量化(Uniform Quantization)
2.2.1 定义
定义 2.2(均匀量化) :如果所有量化级别 cic_ici 之间的间距相等,即 ci+1−ci=sc_{i+1} - c_i = sci+1−ci=s(常数),则称该量化为均匀量化。
均匀量化的码本为:
ci=s⋅i+z′,i=0,1,...,2b−1c_i = s \cdot i + z', \quad i = 0, 1, \dots, 2^b - 1ci=s⋅i+z′,i=0,1,...,2b−1
其中 z′z'z′ 是偏移量。
2.2.2 量化误差分析
定理 2.2(均匀量化的误差分布) :对于步长为 sss 的均匀量化,当输入信号在量化范围内充分"活跃"(即信号变化远大于步长)时,量化误差 e=x−Q(x)e = x - Q(x)e=x−Q(x) 近似服从均匀分布 U(−s/2,s/2)U(-s/2, s/2)U(−s/2,s/2)。
证明:设 xxx 落在某个量化区间 [ci−s/2,ci+s/2)[c_i - s/2, c_i + s/2)[ci−s/2,ci+s/2) 内,则:
e=x−ci∈[−s/2,s/2)e = x - c_i \in [-s/2, s/2)e=x−ci∈[−s/2,s/2)
当 xxx 在整个量化范围内均匀分布时(或至少在一个步长范围内近似均匀),误差 eee 在 [−s/2,s/2)[-s/2, s/2)[−s/2,s/2) 上近似均匀。□\square□
推论 2.1:均匀量化的均方误差(MSE)为:
MSE=Ee2=∫−s/2s/2e2⋅1sde=1s⋅e33∣−s/2s/2=s212\text{MSE} = \mathbb{E}e\^2 = \int_{-s/2}^{s/2} e^2 \cdot \frac{1}{s} de = \frac{1}{s} \cdot \frac{e^3}{3}\Big|_{-s/2}^{s/2} = \frac{s^2}{12}MSE=Ee2=∫−s/2s/2e2⋅s1de=s1⋅3e3 −s/2s/2=12s2
这个 s2/12s^2/12s2/12 公式是量化理论中最基本的结果之一,它告诉我们:量化噪声的方差与步长的平方成正比。
2.2.3 信噪比(SNR)
定义 2.3(信号量化噪声比,SQNR):
SQNR=Ex2Ee2=σx2+μx2s2/12\text{SQNR} = \frac{\mathbb{E}x\^2}{\mathbb{E}e\^2} = \frac{\sigma_x^2 + \mu_x^2}{s^2 / 12}SQNR=Ee2Ex2=s2/12σx2+μx2
对于零均值信号(μx=0\mu_x = 0μx=0)和满量程对称量化(xmax=s⋅2b−1x_{\max} = s \cdot 2^{b-1}xmax=s⋅2b−1),若信号的标准差 σx=xmax/3\sigma_x = x_{\max} / 3σx=xmax/3(典型的 3-sigma 规则):
SQNR=(xmax/3)2s2/12=xmax2/9xmax2/(12⋅4b−1)=4b⋅129⋅4=4b3\text{SQNR} = \frac{(x_{\max}/3)^2}{s^2/12} = \frac{x_{\max}^2 / 9}{x_{\max}^2 / (12 \cdot 4^{b-1})} = \frac{4^b \cdot 12}{9 \cdot 4} = \frac{4^b}{3}SQNR=s2/12(xmax/3)2=xmax2/(12⋅4b−1)xmax2/9=9⋅44b⋅12=34b
以 dB 为单位:
SQNR (dB)=10log10(4b3)=6.02b−4.77 dB\text{SQNR (dB)} = 10 \log_{10}\left(\frac{4^b}{3}\right) = 6.02b - 4.77 \text{ dB}SQNR (dB)=10log10(34b)=6.02b−4.77 dB
这就是著名的 6 dB/bit 规则:每增加 1 比特量化精度,SQNR 提高约 6 dB。
| 比特数 bbb | 理论 SQNR | 步长数 |
|---|---|---|
| 8 | 43.3 dB | 256 |
| 4 | 19.2 dB | 16 |
| 3 | 13.2 dB | 8 |
| 2 | 7.2 dB | 4 |
| 1 | 1.2 dB | 2 |
关键洞察:4 比特量化(16 个级别)的理论 SQNR 仅约 19 dB------这在信号处理中很低。但神经网络具有内在的容错能力(冗余性、正则化效应),使得 4 比特量化在实践中仍然可用。
2.3 非均匀量化(Non-Uniform Quantization)
2.3.1 为什么需要非均匀量化?
均匀量化假设信号分布近似均匀。但实际的神经网络权重和激活值通常服从非均匀分布------最常见的是近似正态分布或拉普拉斯分布。
对于正态分布 N(0,σ2)\mathcal{N}(0, \sigma^2)N(0,σ2),大部分概率质量集中在 −2σ,2σ-2\\sigma, 2\\sigma−2σ,2σ,但尾部延伸到很远。均匀量化必须覆盖整个范围,导致步长很大,在高概率区域的量化精度不足。
非均匀量化通过在高概率区域分配更多量化级别、在低概率区域分配更少级别,可以更高效地利用有限的比特数。
2.3.2 μ-law 压扩
μ-law 压扩(ITU-T G.711)是一种经典的非均匀量化方法,广泛用于语音编码。
压缩函数:
F(x)=sgn(x)⋅ln(1+μ∣x∣/xmax)ln(1+μ)F(x) = \text{sgn}(x) \cdot \frac{\ln(1 + \mu |x| / x_{\max})}{\ln(1 + \mu)}F(x)=sgn(x)⋅ln(1+μ)ln(1+μ∣x∣/xmax)
其中 μ\muμ 是压缩参数(通常 μ=255\mu = 255μ=255),xmaxx_{\max}xmax 是信号的最大绝对值。
扩展函数(逆变换):
F−1(y)=sgn(y)⋅xmaxμ(1+μ)∣y∣−1F^{-1}(y) = \text{sgn}(y) \cdot \frac{x_{\max}}{\mu} \left(1 + \\mu)\^{\|y\|} - 1\\rightF−1(y)=sgn(y)⋅μxmax(1+μ)∣y∣−1
μ-law 压缩将信号先进行非线性变换(压缩动态范围),然后进行均匀量化,最后在解码时进行逆变换(扩展)。
2.3.3 对数量化
对数量化使用对数函数作为压缩函数:
Qlog(x)=sgn(x)⋅s⋅round(log2(∣x∣/xmin)s)Q_{\log}(x) = \text{sgn}(x) \cdot s \cdot \text{round}\left(\frac{\log_2(|x| / x_{\min})}{s}\right)Qlog(x)=sgn(x)⋅s⋅round(slog2(∣x∣/xmin))
对数量化的一个重要性质是相对误差恒定:
∣x−x^∣∣x∣≤constant\frac{|x - \hat{x}|}{|x|} \leq \text{constant}∣x∣∣x−x^∣≤constant
这与均匀量化的绝对误差恒定 (∣x−x^∣≤s/2|x - \hat{x}| \leq s/2∣x−x^∣≤s/2)形成对比。
2.4 最优量化器:Lloyd-Max 算法
2.4.1 问题形式化
问题 :给定信号的概率密度函数 p(x)p(x)p(x) 和比特数 bbb,找到最优的码本 C={c0,...,cN−1}\mathcal{C} = \{c_0, \dots, c_{N-1}\}C={c0,...,cN−1}(N=2bN = 2^bN=2b)和决策边界 {t0,...,tN}\{t_0, \dots, t_{N}\}{t0,...,tN},使得均方量化误差最小:
minC,{ti}MSE=∫−∞∞(x−Q(x))2p(x)dx=∑i=0N−1∫titi+1(x−ci)2p(x)dx\min_{\mathcal{C}, \{t_i\}} \text{MSE} = \int_{-\infty}^{\infty} (x - Q(x))^2 p(x) dx = \sum_{i=0}^{N-1} \int_{t_i}^{t_{i+1}} (x - c_i)^2 p(x) dxC,{ti}minMSE=∫−∞∞(x−Q(x))2p(x)dx=i=0∑N−1∫titi+1(x−ci)2p(x)dx
其中 t0=−∞t_0 = -\inftyt0=−∞,tN=+∞t_N = +\inftytN=+∞,t1,...,tN−1t_1, \dots, t_{N-1}t1,...,tN−1 是决策边界。
2.4.2 最优条件
定理 2.3(Lloyd-Max 最优条件):最优量化器满足以下两个必要条件:
条件 1(最近邻条件):每个样本被分配到最近的量化级别:
ti=ci−1+ci2,i=1,...,N−1t_i = \frac{c_{i-1} + c_i}{2}, \quad i = 1, \dots, N-1ti=2ci−1+ci,i=1,...,N−1
条件 2(质心条件):每个量化级别是其对应区间的条件期望:
ci=EX∣ti≤X\
证明:对 MSE 分别关于 cic_ici 和 tit_iti 求导并令其为零。
关于 cic_ici:
∂MSE∂ci=−2∫titi+1(x−ci)p(x)dx=0\frac{\partial \text{MSE}}{\partial c_i} = -2 \int_{t_i}^{t_{i+1}} (x - c_i) p(x) dx = 0∂ci∂MSE=−2∫titi+1(x−ci)p(x)dx=0
解得:ci=∫titi+1xp(x)dx∫titi+1p(x)dxc_i = \frac{\int_{t_i}^{t_{i+1}} x p(x) dx}{\int_{t_i}^{t_{i+1}} p(x) dx}ci=∫titi+1p(x)dx∫titi+1xp(x)dx。
关于 tit_iti(1≤i≤N−11 \leq i \leq N-11≤i≤N−1):
∂MSE∂ti=(ti−ci−1)2p(ti)−(ti−ci)2p(ti)=0\frac{\partial \text{MSE}}{\partial t_i} = (t_i - c_{i-1})^2 p(t_i) - (t_i - c_i)^2 p(t_i) = 0∂ti∂MSE=(ti−ci−1)2p(ti)−(ti−ci)2p(ti)=0
当 p(ti)>0p(t_i) > 0p(ti)>0 时,(ti−ci−1)2=(ti−ci)2(t_i - c_{i-1})^2 = (t_i - c_i)^2(ti−ci−1)2=(ti−ci)2,解得 ti=ci−1+ci2t_i = \frac{c_{i-1} + c_i}{2}ti=2ci−1+ci。□\square□
2.4.3 Lloyd-Max 算法
Lloyd-Max 算法(也称为迭代量化算法)交替执行上述两个条件,直至收敛:
算法 2.1(Lloyd-Max 算法):
输入:概率密度 p(x),比特数 b,初始码本 {c_i^0}
输出:最优码本 {c_i*} 和决策边界 {t_i*}
重复:
1. 计算决策边界:t_i = (c_{i-1} + c_i) / 2
2. 更新码本:c_i = E[X | t_i ≤ X < t_{i+1}]
直到 收敛(码本变化 < 阈值)
定理 2.4(Lloyd-Max 算法的收敛性):Lloyd-Max 算法单调递减地收敛到 MSE 的一个局部最优解。即:
MSE(k+1)≤MSE(k)\text{MSE}^{(k+1)} \leq \text{MSE}^{(k)}MSE(k+1)≤MSE(k)
证明:每一步分别对 cic_ici 或 tit_iti 进行最优更新,MSE 不会增加。由于 MSE 有下界(≥0\geq 0≥0),序列收敛。□\square□
2.5 量化噪声的统计分析
2.5.1 加性噪声模型
将量化视为在原始信号上叠加一个加性噪声:
x^=Q(x)=x+e\hat{x} = Q(x) = x + ex^=Q(x)=x+e
其中 e=Q(x)−xe = Q(x) - xe=Q(x)−x 是量化噪声。
定理 2.5(量化噪声的经典假设):在以下条件下:
- 量化步长 sss 足够小
- 信号在量化区间内变化充分
- 信号与量化噪声不相关
量化噪声满足:
- e∼U(−s/2,s/2)e \sim U(-s/2, s/2)e∼U(−s/2,s/2)(均匀分布)
- Ee=0\mathbb{E}e = 0Ee=0(零均值)
- Vare=s2/12\text{Var}e = s^2 / 12Vare=s2/12
- eee 与 xxx 不相关:Exe=0\mathbb{E}xe = 0Exe=0
证明:条件 1 和 2 由定理 2.2 得到。条件 3 由推论 2.1 得到。
对于条件 4,设 xxx 落在第 iii 个量化区间 [ci−s/2,ci+s/2)[c_i - s/2, c_i + s/2)[ci−s/2,ci+s/2),e=x−cie = x - c_ie=x−ci:
Exe∣x∈区间i=E(ci+e)e∣x∈区间i=ciEe⏟=0+Ee2=s2/12\mathbb{E}xe \| x \\in \\text{区间} i = \mathbb{E}(c_i + e)e \| x \\in \\text{区间} i = c_i \underbrace{\mathbb{E}e}_{=0} + \mathbb{E}e\^2 = s^2/12Exe∣x∈区间i=E(ci+e)e∣x∈区间i=ci=0 Ee+Ee2=s2/12
当信号在各区间均匀分布时,cic_ici 的均值为零(对于对称分布),因此 Exe≈0\mathbb{E}xe \approx 0Exe≈0。□\square□
2.5.2 加性噪声模型的局限
注意 :上述加性噪声模型是近似的。当以下条件不满足时,模型失效:
- 信号幅度很小(接近零):此时多个连续值可能被量化到同一个级别,噪声不再是均匀分布
- 过载(Clipping) :当信号超出量化范围时,误差远大于 s/2s/2s/2
- 信号变化缓慢:相邻样本的量化误差高度相关,不再是白噪声
2.6 数值实现
python
import numpy as np
import matplotlib.pyplot as plt
def uniform_quantize(x: np.ndarray, num_bits: int, symmetric: bool = True) -> tuple:
"""均匀量化。
Args:
x: 输入数组
num_bits: 量化比特数
symmetric: 是否使用对称量化
Returns:
x_q: 量化后的值
scale: 缩放因子
zero_point: 零点
"""
if symmetric:
abs_max = np.max(np.abs(x))
abs_max = max(abs_max, 1e-8) # 避免除零
qmax = 2 ** (num_bits - 1) - 1
qmin = -2 ** (num_bits - 1)
scale = abs_max / qmax
zero_point = 0
else:
x_min, x_max = np.min(x), np.max(x)
qmax = 2 ** num_bits - 1
qmin = 0
scale = (x_max - x_min) / (qmax - qmin)
scale = max(scale, 1e-10)
zero_point = np.round(qmin - x_min / scale).astype(int)
# 编码:实数 -> 整数索引
x_int = np.clip(np.round(x / scale) + zero_point, qmin, qmax).astype(int)
# 解码:整数索引 -> 实数
x_q = (x_int - zero_point).astype(float) * scale
return x_q, scale, zero_point
def demonstrate_quantization_basics():
"""演示量化基础:误差分析、比特数影响。"""
np.random.seed(42)
# 生成正态分布信号
n = 10000
x = np.random.randn(n) * 3.0 # N(0, 9)
print("=" * 70)
print("均匀量化基础演示")
print("=" * 70)
print(f"信号: {n} 个样本, 均值={x.mean():.4f}, 标准差={x.std():.4f}")
print()
for bits in [8, 4, 3, 2, 1]:
x_q, scale, zp = uniform_quantize(x, bits, symmetric=True)
mse = np.mean((x - x_q) ** 2)
theoretical_mse = scale ** 2 / 12
# SQNR
sqnr = 10 * np.log10(np.mean(x ** 2) / mse)
theoretical_sqnr = 6.02 * bits - 4.77
print(f" {bits}-bit 量化:")
print(f" 步长 s = {scale:.6f}")
print(f" 实际 MSE = {mse:.6f}, 理论 MSE (s²/12) = {theoretical_mse:.6f}")
print(f" 实际 SQNR = {sqnr:.2f} dB, 理论 SQNR = {theoretical_sqnr:.2f} dB")
print(f" 量化级别数: {2**bits}")
print()
def demonstrate_symmetric_vs_asymmetric():
"""演示对称 vs 非对称量化。"""
np.random.seed(42)
# 非对称分布(模拟 ReLU 后的激活值)
x = np.abs(np.random.randn(5000)) * 2.0 # 半正态分布
print("=" * 70)
print("对称 vs 非对称量化对比")
print("=" * 70)
print(f"信号: 半正态分布, 均值={x.mean():.4f}, 范围=[{x.min():.4f}, {x.max():.4f}]")
print()
for bits in [8, 4, 3]:
# 对称量化
x_q_sym, _, _ = uniform_quantize(x, bits, symmetric=True)
mse_sym = np.mean((x - x_q_sym) ** 2)
# 非对称量化
x_q_asym, _, _ = uniform_quantize(x, bits, symmetric=False)
mse_asym = np.mean((x - x_q_asym) ** 2)
improvement = (mse_sym - mse_asym) / mse_sym * 100
print(f" {bits}-bit:")
print(f" 对称量化 MSE: {mse_sym:.6f}")
print(f" 非对称量化 MSE: {mse_asym:.6f}")
print(f" 非对称改善: {improvement:.1f}%")
print()
def lloyd_max_quantizer(x: np.ndarray, num_bits: int, max_iter: int = 100, tol: float = 1e-8) -> tuple:
"""Lloyd-Max 最优量化器。
通过迭代优化找到最优的量化级别和决策边界。
Args:
x: 输入信号
num_bits: 量化比特数
max_iter: 最大迭代次数
tol: 收敛阈值
Returns:
codebook: 最优码本
boundaries: 决策边界
mse: 均方误差
"""
N = 2 ** num_bits
# 用经验分位数初始化码本
percentiles = np.linspace(0, 100, N + 1)
boundaries = np.percentile(x, percentiles)
codebook = np.array([
np.mean(x[(x >= boundaries[i]) & (x < boundaries[i + 1])])
if np.sum((x >= boundaries[i]) & (x < boundaries[i + 1])) > 0
else (boundaries[i] + boundaries[i + 1]) / 2
for i in range(N)
])
prev_mse = np.inf
for iteration in range(max_iter):
# 步骤 1: 更新决策边界(最近邻条件)
boundaries = np.concatenate([
[-np.inf],
(codebook[:-1] + codebook[1:]) / 2,
[np.inf]
])
# 步骤 2: 更新码本(质心条件)
for i in range(N):
mask = (x >= boundaries[i]) & (x < boundaries[i + 1])
if np.sum(mask) > 0:
codebook[i] = np.mean(x[mask])
# 计算 MSE
# 量化每个样本
x_q = np.zeros_like(x)
for i in range(N):
mask = (x >= boundaries[i]) & (x < boundaries[i + 1])
x_q[mask] = codebook[i]
mse = np.mean((x - x_q) ** 2)
if abs(prev_mse - mse) < tol:
break
prev_mse = mse
return codebook, boundaries, mse
def compare_uniform_vs_lloyd_max():
"""对比均匀量化和 Lloyd-Max 量化。"""
np.random.seed(42)
# 标准正态分布
x = np.random.randn(50000)
print("=" * 70)
print("均匀量化 vs Lloyd-Max 最优量化")
print("=" * 70)
print(f"信号: 标准正态分布 N(0,1), {len(x)} 个样本")
print()
print(f" {'比特数':>6} {'均匀 MSE':>15} {'Lloyd-Max MSE':>15} {'改善':>10}")
print(f" {'-'*6} {'-'*15} {'-'*15} {'-'*10}")
for bits in [4, 3, 2]:
# 均匀量化
x_q_uniform, _, _ = uniform_quantize(x, bits, symmetric=True)
mse_uniform = np.mean((x - x_q_uniform) ** 2)
# Lloyd-Max 量化
codebook, _, mse_lloyd = lloyd_max_quantizer(x, bits)
improvement = (mse_uniform - mse_lloyd) / mse_uniform * 100
print(f" {bits:>6} {mse_uniform:>15.6f} {mse_lloyd:>15.6f} {improvement:>9.1f}%")
if __name__ == "__main__":
demonstrate_quantization_basics()
print()
demonstrate_symmetric_vs_asymmetric()
print()
compare_uniform_vs_lloyd_max()
第三章:信息论视角------率失真理论
3.1 熵与信息量
3.1.1 信息熵
定义 3.1(香农信息熵) :离散随机变量 XXX 取值于 {x1,...,xn}\{x_1, \dots, x_n\}{x1,...,xn},概率为 pi=P(X=xi)p_i = P(X = x_i)pi=P(X=xi),其信息熵定义为:
H(X)=−∑i=1npilog2piH(X) = -\sum_{i=1}^{n} p_i \log_2 p_iH(X)=−i=1∑npilog2pi
信息熵衡量了随机变量的不确定性------或者说,编码该随机变量的最优平均比特数。
关键性质:
- H(X)≥0H(X) \geq 0H(X)≥0,等号成立当且仅当 XXX 是确定性的
- H(X)≤log2nH(X) \leq \log_2 nH(X)≤log2n,等号成立当且仅当 XXX 是均匀分布的
- 对于连续随机变量,使用微分熵:h(X)=−∫p(x)log2p(x)dxh(X) = -\int p(x) \log_2 p(x) dxh(X)=−∫p(x)log2p(x)dx
3.1.2 量化与熵编码
量化后的索引 i=Qenc(x)i = Q_{\text{enc}}(x)i=Qenc(x) 是一个离散随机变量,其概率为:
pi=P(Qenc(X)=i)=∫titi+1p(x)dxp_i = P(Q_{\text{enc}}(X) = i) = \int_{t_i}^{t_{i+1}} p(x) dxpi=P(Qenc(X)=i)=∫titi+1p(x)dx
根据香农信源编码定理,无损编码这些索引至少需要 H(Q(X))H(Q(X))H(Q(X)) 比特/样本。
定理 3.1(量化后的熵):均匀量化器的输出熵为:
H(Q(X))=−∑i=02b−1pilog2piH(Q(X)) = -\sum_{i=0}^{2^b - 1} p_i \log_2 p_iH(Q(X))=−i=0∑2b−1pilog2pi
当信号均匀分布时,pi=1/2bp_i = 1/2^bpi=1/2b,H=bH = bH=b 比特------每个量化级别等概率,需要完整 bbb 比特来编码。
当信号高度非均匀时(如正态分布),H<bH < bH<b------某些量化级别出现频率更高,可以用更短的编码。
实际意义 :这启发了熵编码量化(Entropy-Coded Quantization) ------先量化,再用算术编码或霍夫曼编码压缩量化索引,实际比特率可以低于 bbb。
3.2 率失真理论
3.2.1 率失真函数
定义 3.2(率失真函数) :给定信源 XXX 和失真度量 d(x,x^)d(x, \hat{x})d(x,x^),率失真函数 R(D)R(D)R(D) 定义为在失真不超过 DDD 的约束下,编码所需的最小比特率:
R(D)=minp(x^∣x):Ed(X,X\^)≤DI(X;X^)R(D) = \min_{p(\hat{x}|x): \mathbb{E}d(X, \\hat{X}) \leq D} I(X; \hat{X})R(D)=p(x^∣x):Ed(X,X\^)≤DminI(X;X^)
其中 I(X;X^)I(X; \hat{X})I(X;X^) 是 XXX 和 X^\hat{X}X^ 之间的互信息(mutual information):
I(X;X^)=∫∫p(x,x^)log2p(x,x^)p(x)p(x^)dx dx^I(X; \hat{X}) = \int \int p(x, \hat{x}) \log_2 \frac{p(x, \hat{x})}{p(x) p(\hat{x})} dx \, d\hat{x}I(X;X^)=∫∫p(x,x^)log2p(x)p(x^)p(x,x^)dxdx^
互信息衡量了 X^\hat{X}X^ 关于 XXX 的信息量------即量化后保留了多少原始信息。
3.2.2 高斯信源的率失真函数
定理 3.2(高斯信源的率失真函数) :对于 X∼N(0,σ2)X \sim \mathcal{N}(0, \sigma^2)X∼N(0,σ2) 和均方误差失真 d(x,x^)=(x−x^)2d(x, \hat{x}) = (x - \hat{x})^2d(x,x^)=(x−x^)2:
R(D)={12log2σ2Dif 0≤D≤σ20if D>σ2R(D) = \begin{cases} \frac{1}{2} \log_2 \frac{\sigma^2}{D} & \text{if } 0 \leq D \leq \sigma^2 \\ 0 & \text{if } D > \sigma^2 \end{cases}R(D)={21log2Dσ20if 0≤D≤σ2if D>σ2
证明(逆定理方向):由数据处理不等式和高斯分布的性质。
设 X^\hat{X}X^ 是 XXX 的任意估计,E(X−X\^)2≤D\mathbb{E}(X - \\hat{X})\^2 \leq DE(X−X\^)2≤D。则:
I(X;X^)=h(X)−h(X∣X^)≥h(X)−h(X−X^)I(X; \hat{X}) = h(X) - h(X | \hat{X}) \geq h(X) - h(X - \hat{X})I(X;X^)=h(X)−h(X∣X^)≥h(X)−h(X−X^)
由高斯分布的最大熵性质:h(X−X^)≤12log2(2πeD)h(X - \hat{X}) \leq \frac{1}{2} \log_2(2\pi e D)h(X−X^)≤21log2(2πeD)
因此:I(X;X^)≥12log2(2πeσ2)−12log2(2πeD)=12log2σ2DI(X; \hat{X}) \geq \frac{1}{2} \log_2(2\pi e \sigma^2) - \frac{1}{2} \log_2(2\pi e D) = \frac{1}{2} \log_2 \frac{\sigma^2}{D}I(X;X^)≥21log2(2πeσ2)−21log2(2πeD)=21log2Dσ2。□\square□
推论 3.1 :要将高斯信源的均方误差失真限制在 DDD 以下,至少需要:
b≥12log2σ2D 比特/样本b \geq \frac{1}{2} \log_2 \frac{\sigma^2}{D} \text{ 比特/样本}b≥21log2Dσ2 比特/样本
等价地,使用 bbb 比特能达到的最小失真为:
Dmin=σ2⋅2−2b=σ24bD_{\min} = \sigma^2 \cdot 2^{-2b} = \frac{\sigma^2}{4^b}Dmin=σ2⋅2−2b=4bσ2
这与均匀量化的 s2/12s^2/12s2/12 公式对比:
| 方法 | MSE 公式 | 说明 |
|---|---|---|
| 均匀量化 | s2/12s^2 / 12s2/12 | 简单但非最优 |
| Lloyd-Max | Dmax2⋅ϵ(b)D_{\max}^2 \cdot \epsilon(b)Dmax2⋅ϵ(b) | 最优标量量化 |
| 率失真下界 | σ2/4b\sigma^2 / 4^bσ2/4b | 理论下界,需要矢量量化逼近 |
3.2.3 率失真函数的物理含义
R(D)R(D)R(D) 函数告诉我们一个深刻的道理:
比特率=R(D)≈12log2σ2D ⟹ D≈σ2⋅4−R\text{比特率} = R(D) \approx \frac{1}{2} \log_2 \frac{\sigma^2}{D} \implies D \approx \sigma^2 \cdot 4^{-R}比特率=R(D)≈21log2Dσ2⟹D≈σ2⋅4−R
- 每增加 1 比特 ,失真减少为原来的 1/41/41/4(即 -6 dB,与前面的 6 dB/bit 规则一致)
- 失真与方差成正比:信号方差越大,需要更多比特来达到相同精度
- 存在理论下界 :无论用什么方法,bbb 比特量化不可能达到低于 σ2/4b\sigma^2/4^bσ2/4b 的 MSE
3.3 矢量量化与标量量化的差距
3.3.1 矢量量化
矢量量化(Vector Quantization, VQ) 将 kkk 个标量样本组成一个矢量 x=(x1,...,xk)∈Rk\mathbf{x} = (x_1, \dots, x_k) \in \mathbb{R}^kx=(x1,...,xk)∈Rk,然后在 Rk\mathbb{R}^kRk 空间中整体量化。
矢量量化的码本包含 NNN 个 kkk 维码字:C={c1,...,cN}\mathcal{C} = \{\mathbf{c}_1, \dots, \mathbf{c}_N\}C={c1,...,cN}。
编码:i=argminj∥x−cj∥2i = \arg\min_j \|\mathbf{x} - \mathbf{c}_j\|^2i=argminj∥x−cj∥2
3.3.2 矢量量化的优势
定理 3.3(矢量量化的渐近最优性) :随着矢量维度 k→∞k \to \inftyk→∞,矢量量化的性能可以逼近率失真函数 R(D)R(D)R(D)。
定理 3.4(标量量化的次优性) :标量量化的性能一般无法 达到率失真函数。两者之间的差距称为率失真差距(rate-distortion gap)。
对于高斯信源,kkk 维矢量量化的渐近 MSE 为:
Dk≈σ24b⋅kk+1⋅Vk2/kD_k \approx \frac{\sigma^2}{4^b} \cdot \frac{k}{k+1} \cdot V_k^{2/k}Dk≈4bσ2⋅k+1k⋅Vk2/k
其中 VkV_kVk 是 kkk 维单位球的体积。当 k→∞k \to \inftyk→∞ 时,Dk→σ2/4bD_k \to \sigma^2 / 4^bDk→σ2/4b(率失真下界)。
实际意义:理论上,将多个权重一起量化(矢量量化)可以比逐个量化(标量量化)更高效。但矢量量化的编码复杂度随维度指数增长,实际中通常使用结构化的矢量量化(如格量化、乘积量化)。
3.4 量化比特数的理论下界
3.4.1 给定精度要求的最低比特数
问题 :对于一个参数矩阵 W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n,如果要求量化后的模型精度(如任务准确率)下降不超过 ϵ\epsilonϵ,最少需要多少比特?
定理 3.5(量化精度-比特数关系) :设模型输出为 f(x;W)f(x; W)f(x;W),量化后的输出为 f(x;W^)f(x; \hat{W})f(x;W^)。在权重扰动的一阶近似下:
∣f(x;W)−f(x;W^)∣≤∥∇Wf∥∗⋅∥W−W^∥F|f(x; W) - f(x; \hat{W})| \leq \|\nabla_W f\|_* \cdot \|W - \hat{W}\|_F∣f(x;W)−f(x;W^)∣≤∥∇Wf∥∗⋅∥W−W^∥F
其中 ∥⋅∥∗\|\cdot\|_*∥⋅∥∗ 是核范数。
如果使用 bbb 比特均匀量化,∥W−W^∥F2≈mn⋅s212\|W - \hat{W}\|_F^2 \approx \frac{mn \cdot s^2}{12}∥W−W^∥F2≈12mn⋅s2。因此:
b≥12log2mn⋅maxiσi(W)212ϵ2/∥∇Wf∥∗2b \geq \frac{1}{2} \log_2 \frac{mn \cdot \max_i \sigma_i(W)^2}{12 \epsilon^2 / \|\nabla_W f\|_*^2}b≥21log212ϵ2/∥∇Wf∥∗2mn⋅maxiσi(W)2
3.4.2 不同层的量化敏感度
观察:模型中不同层对量化的敏感度不同。量化误差通过网络逐层传播,越靠近输出的层,其误差对最终输出的影响越大。
定理 3.6(误差传播) :对于 LLL 层的顺序网络 f=fL∘fL−1∘⋯∘f1f = f_L \circ f_{L-1} \circ \dots \circ f_1f=fL∘fL−1∘⋯∘f1,第 lll 层的量化误差 δl\delta_lδl 传播到输出的上界为:
∥δoutput∥≤∥δl∥⋅∏j=l+1L∥Jj∥\|\delta_{\text{output}}\| \leq \|\delta_l\| \cdot \prod_{j=l+1}^{L} \|J_j\|∥δoutput∥≤∥δl∥⋅j=l+1∏L∥Jj∥
其中 Jj=∂fj/∂xJ_j = \partial f_j / \partial xJj=∂fj/∂x 是第 jjj 层的 Jacobian 矩阵。
实际含义 :对梯度较大 或Jacobian 谱范数较大的层,量化应该更谨慎(使用更多比特)。
python
import numpy as np
from numpy.linalg import norm
def rate_distortion_gaussian(sigma_sq: float, bits: float) -> float:
"""计算高斯信源的率失真函数值。
Args:
sigma_sq: 信号方差
bits: 比特率
Returns:
D: 最小可达到的 MSE
"""
return sigma_sq * (2 ** (-2 * bits))
def entropy_of_quantized_gaussian(sigma: float, num_bits: int, num_samples: int = 100000) -> float:
"""计算高斯分布均匀量化后的熵。
Args:
sigma: 标准差
num_bits: 量化比特数
num_samples: 样本数
Returns:
H: 量化后的熵 (bits)
"""
x = np.random.randn(num_samples) * sigma
x_q, scale, _ = uniform_quantize(x, num_bits, symmetric=True)
# 统计各量化级别的概率
unique_vals, counts = np.unique(x_q, return_counts=True)
probs = counts / counts.sum()
# 熵
H = -np.sum(probs * np.log2(probs))
return H
def demonstrate_rate_distortion():
"""演示率失真理论。"""
np.random.seed(42)
print("=" * 70)
print("率失真理论演示")
print("=" * 70)
sigma_sq = 1.0
print(f"信号: 高斯分布 N(0, {sigma_sq})")
print()
print(f" {'比特数 b':>10} {'理论下界 D':>15} {'均匀量化 MSE':>15} {'Lloyd-Max MSE':>15} {'差距(dB)':>12}")
print(f" {'-'*10} {'-'*15} {'-'*15} {'-'*15} {'-'*12}")
x = np.random.randn(100000) * np.sqrt(sigma_sq)
for bits in [8, 4, 3, 2]:
# 率失真下界
D_rd = rate_distortion_gaussian(sigma_sq, bits)
# 均匀量化
x_q_uniform, scale, _ = uniform_quantize(x, bits, symmetric=True)
mse_uniform = np.mean((x - x_q_uniform) ** 2)
# Lloyd-Max
codebook, _, mse_lloyd = lloyd_max_quantizer(x, bits)
# 差距
gap_db = 10 * np.log10(mse_lloyd / D_rd)
print(f" {bits:>10} {D_rd:>15.6f} {mse_uniform:>15.6f} {mse_lloyd:>15.6f} {gap_db:>12.2f}")
def demonstrate_entropy_coding():
"""演示熵编码量化的优势。"""
np.random.seed(42)
print("\n" + "=" * 70)
print("量化后熵 vs 固定比特数")
print("=" * 70)
sigma = 1.0
print(f"信号: 高斯分布 N(0, {sigma})")
print()
print(f" {'量化比特':>10} {'固定比特率':>12} {'实际熵':>12} {'节省':>10}")
print(f" {'-'*10} {'-'*12} {'-'*12} {'-'*10}")
for bits in [8, 4, 3, 2]:
H = entropy_of_quantized_gaussian(sigma, bits)
savings = (bits - H) / bits * 100
print(f" {bits:>10} {bits:>12.1f} {H:>12.3f} {savings:>9.1f}%")
if __name__ == "__main__":
demonstrate_rate_distortion()
demonstrate_entropy_coding()
第二部分:权重量化方法
第四章:训练后量化(PTQ)基础
4.1 Round-to-Nearest(RTN)量化
4.1.1 基本定义
Round-to-Nearest(RTN) 是最简单的量化方法------直接将每个权重值四舍五入到最近的量化级别:
w^=s⋅round(ws)\hat{w} = s \cdot \text{round}\left(\frac{w}{s}\right)w^=s⋅round(sw)
其中步长 sss 的选择至关重要。
4.1.2 最优步长的选择
问题 :对于给定的权重张量 WWW 和比特数 bbb,如何选择最优的步长 s∗s^*s∗ 使量化误差最小?
s∗=argmins>0∥W−Qs(W)∥F2s^* = \arg\min_{s > 0} \|W - Q_s(W)\|_F^2s∗=args>0min∥W−Qs(W)∥F2
定理 4.1(最优步长的一阶条件):在连续近似下(忽略 round 操作的离散性),最优步长满足:
s∗=12⋅MSEtarget1=12mn∑i,jwij2⋅12bs^* = \sqrt{\frac{12 \cdot \text{MSE}{\text{target}}}{1}} = \sqrt{\frac{12}{mn} \sum{i,j} w_{ij}^2} \cdot \frac{1}{2^b}s∗=112⋅MSEtarget =mn12i,j∑wij2 ⋅2b1
但在实践中,更常用的是基于绝对最大值(absmax) 的方法:
sabsmax=maxi,j∣wij∣2b−1−1s_{\text{absmax}} = \frac{\max_{i,j} |w_{ij}|}{2^{b-1} - 1}sabsmax=2b−1−1maxi,j∣wij∣
或基于分位数(percentile) 的方法:
spercentile=Percentile99.99(∣W∣)2b−1−1s_{\text{percentile}} = \frac{\text{Percentile}_{99.99}(|W|)}{2^{b-1} - 1}spercentile=2b−1−1Percentile99.99(∣W∣)
定理 4.2(absmax 量化的最优性) :对于对称分布(如正态分布),absmax 量化在所有对称均匀量化方案中最小化 MSE,当且仅当分布的支撑集恰好等于 −s⋅(2b−1−1),s⋅(2b−1−1)-s \\cdot (2\^{b-1} - 1), s \\cdot (2\^{b-1} - 1)−s⋅(2b−1−1),s⋅(2b−1−1)。
但实际上,由于正态分布有无限支撑集,总会有一些值超出量化范围(过载),导致额外误差。
4.1.3 过载误差与颗粒误差
量化的总误差可以分解为两部分:
MSEtotal=MSEgranular+MSEoverload\text{MSE}{\text{total}} = \text{MSE}{\text{granular}} + \text{MSE}_{\text{overload}}MSEtotal=MSEgranular+MSEoverload
颗粒误差(Granular Error) :在量化范围内的误差,来自有限精度的离散化。对于步长 sss:
MSEgranular≈s212\text{MSE}_{\text{granular}} \approx \frac{s^2}{12}MSEgranular≈12s2
过载误差(Overload Error) :当信号值超出量化范围 −xmax,xmax-x_{\\max}, x_{\\max}−xmax,xmax 时产生的截断误差:
MSEoverload=2∫xmax∞(x−xmax)2p(x)dx\text{MSE}{\text{overload}} = 2 \int{x_{\max}}^{\infty} (x - x_{\max})^2 p(x) dxMSEoverload=2∫xmax∞(x−xmax)2p(x)dx
权衡 :增大步长 sss(扩大量化范围)减少过载误差但增加颗粒误差;减小步长 sss(缩小量化范围)反之。最优步长使两者的和最小。
4.2 逐通道量化与逐组量化
4.2.1 逐张量量化的局限
逐张量量化(Per-Tensor Quantization) 对整个权重张量使用同一组量化参数(s,zs, zs,z)。当张量中不同通道(或不同行/列)的数值范围差异很大时,这会导致严重的精度损失。
例子 :考虑一个权重矩阵 W∈R3×4W \in \mathbb{R}^{3 \times 4}W∈R3×4:
W=(0.10.20.150.055.06.05.54.50.010.020.0150.005)W = \begin{pmatrix} 0.1 & 0.2 & 0.15 & 0.05 \\ 5.0 & 6.0 & 5.5 & 4.5 \\ 0.01 & 0.02 & 0.015 & 0.005 \end{pmatrix}W= 0.15.00.010.26.00.020.155.50.0150.054.50.005
逐张量量化时,xmax=6.0x_{\max} = 6.0xmax=6.0,步长 s=6.0/7≈0.857s = 6.0 / 7 \approx 0.857s=6.0/7≈0.857(4 比特)。
第一行的值(0.05-0.2)占步长的很小一部分,量化后精度极差。第三行更是几乎全部量化为零。
4.2.2 逐通道量化
逐通道量化(Per-Channel Quantization) 为每个输出通道(行或列)使用独立的量化参数:
sc=maxj∣wcj∣2b−1−1s_c = \frac{\max_j |w_{cj}|}{2^{b-1} - 1}sc=2b−1−1maxj∣wcj∣
w^cj=sc⋅round(wcjsc)\hat{w}{cj} = s_c \cdot \text{round}\left(\frac{w{cj}}{s_c}\right)w^cj=sc⋅round(scwcj)
定理 4.3(逐通道量化的误差上界) :设权重矩阵 WWW 的第 ccc 行的最大绝对值为 Mc=maxj∣wcj∣M_c = \max_j |w_{cj}|Mc=maxj∣wcj∣,则:
MSEper-channel=1mn∑c=1mMc212(2b−1−1)2⋅n=112m(2b−1−1)2∑c=1mMc2\text{MSE}{\text{per-channel}} = \frac{1}{mn} \sum{c=1}^{m} \frac{M_c^2}{12(2^{b-1} - 1)^2} \cdot n = \frac{1}{12m(2^{b-1} - 1)^2} \sum_{c=1}^{m} M_c^2MSEper-channel=mn1c=1∑m12(2b−1−1)2Mc2⋅n=12m(2b−1−1)21c=1∑mMc2
而逐张量量化的 MSE 为:
MSEper-tensor=(maxcMc)212(2b−1−1)2\text{MSE}{\text{per-tensor}} = \frac{(\max{c} M_c)^2}{12(2^{b-1} - 1)^2}MSEper-tensor=12(2b−1−1)2(maxcMc)2
由于 ∑cMc2/m≤(maxcMc)2\sum_c M_c^2 / m \leq (\max_c M_c)^2∑cMc2/m≤(maxcMc)2(当各通道范围不等时严格小于),逐通道量化的 MSE 严格更小。
4.2.3 逐组量化
逐组量化(Per-Group Quantization) 将每个通道分成大小为 ggg 的组,每组使用独立的量化参数。这是逐通道量化的泛化------当 g=1g = 1g=1 时退化为逐元素量化,当 g=ng = ng=n 时退化为逐通道量化。
sc,k=maxj∈groupk∣wcj∣2b−1−1s_{c,k} = \frac{\max_{j \in \text{group}k} |w{cj}|}{2^{b-1} - 1}sc,k=2b−1−1maxj∈groupk∣wcj∣
参数开销 :逐组量化需要额外存储每组的缩放因子。对于 m×nm \times nm×n 权重矩阵,组大小为 ggg:
额外存储=mng×(32 bits for scale)=4mng 字节\text{额外存储} = \frac{mn}{g} \times (32 \text{ bits for scale}) = \frac{4mn}{g} \text{ 字节}额外存储=gmn×(32 bits for scale)=g4mn 字节
当 g=128g = 128g=128(常用设置)时,额外开销约为 4/128=3.1%4/128 = 3.1\%4/128=3.1%。
4.3 对称 vs 非对称量化
4.3.1 对称量化的数学性质
对称量化(也称为零点量化,z=0z = 0z=0)假设量化范围关于零对称:
Qsym(x)=s⋅round(x/s),s=max∣x∣2b−1−1Q_{\text{sym}}(x) = s \cdot \text{round}(x / s), \quad s = \frac{\max |x|}{2^{b-1} - 1}Qsym(x)=s⋅round(x/s),s=2b−1−1max∣x∣
优点:
- 实现简单,不需要存储零点
- 对于近似对称分布(如权重),效率高
- 硬件实现更高效(不需要零点偏移运算)
4.3.2 非对称量化的数学性质
非对称量化(也称为仿射量化)允许量化范围不对称:
Qasym(x)=s⋅(round(x/s)+z),s=xmax−xmin2b−1,z=round(−xmin/s)Q_{\text{asym}}(x) = s \cdot (\text{round}(x / s) + z), \quad s = \frac{x_{\max} - x_{\min}}{2^b - 1}, \quad z = \text{round}(-x_{\min} / s)Qasym(x)=s⋅(round(x/s)+z),s=2b−1xmax−xmin,z=round(−xmin/s)
定理 4.4(非对称量化的最优性条件) :当待量化分布的均值 μ≠0\mu \neq 0μ=0 时,非对称量化严格优于对称量化。
证明:设分布支撑集为 a,ba, ba,b,a<0<ba < 0 < ba<0<b,μ≠0\mu \neq 0μ=0。设 Δ=max(∣a∣,∣b∣)\Delta = \max(|a|, |b|)Δ=max(∣a∣,∣b∣)。
对称量化范围为 −Δ,Δ-\\Delta, \\Delta−Δ,Δ,步长 ssym=2Δ/(2b−1)s_{\text{sym}} = 2\Delta / (2^b - 1)ssym=2Δ/(2b−1)。
非对称量化范围为 a,ba, ba,b,步长 sasym=(b−a)/(2b−1)s_{\text{asym}} = (b - a) / (2^b - 1)sasym=(b−a)/(2b−1)。
由于 b−a≤2Δb - a \leq 2\Deltab−a≤2Δ(等号仅当 a=−ba = -ba=−b),sasym≤ssyms_{\text{asym}} \leq s_{\text{sym}}sasym≤ssym。
更小的步长意味着更小的颗粒误差 s2/12s^2/12s2/12。□\square□
python
import numpy as np
from numpy.linalg import norm
def quantize_per_tensor(W: np.ndarray, num_bits: int, symmetric: bool = True) -> tuple:
"""逐张量量化。
Args:
W: 权重矩阵
num_bits: 比特数
symmetric: 是否对称
Returns:
W_q: 量化后的权重
scale: 缩放因子
zero_point: 零点
"""
if symmetric:
abs_max = np.max(np.abs(W))
abs_max = max(abs_max, 1e-8)
qmax = 2 ** (num_bits - 1) - 1
scale = abs_max / qmax
zero_point = 0
else:
w_min, w_max = W.min(), W.max()
qmax = 2 ** num_bits - 1
scale = (w_max - w_min) / qmax
scale = max(scale, 1e-10)
zero_point = np.round(-w_min / scale).astype(int)
W_int = np.clip(np.round(W / scale) + zero_point, 0 if not symmetric else -(2**(num_bits-1)),
(2**num_bits - 1) if not symmetric else (2**(num_bits-1) - 1)).astype(int)
W_q = (W_int - zero_point).astype(float) * scale
return W_q, scale, zero_point
def quantize_per_channel(W: np.ndarray, num_bits: int, channel_dim: int = 0) -> tuple:
"""逐通道量化。
Args:
W: 权重矩阵
num_bits: 比特数
channel_dim: 通道维度 (0=按行, 1=按列)
Returns:
W_q: 量化后的权重
scales: 每个通道的缩放因子
"""
qmax = 2 ** (num_bits - 1) - 1
# 计算每个通道的最大绝对值
if channel_dim == 0:
abs_max = np.max(np.abs(W), axis=1, keepdims=True) # (m, 1)
else:
abs_max = np.max(np.abs(W), axis=0, keepdims=True) # (1, n)
abs_max = np.maximum(abs_max, 1e-8)
scales = abs_max / qmax
W_int = np.clip(np.round(W / scales), -qmax, qmax).astype(int)
W_q = W_int.astype(float) * scales
return W_q, scales
def quantize_per_group(W: np.ndarray, num_bits: int, group_size: int = 128) -> tuple:
"""逐组量化。
Args:
W: 权重矩阵 (m, n)
num_bits: 比特数
group_size: 组大小
Returns:
W_q: 量化后的权重
scales: 每组的缩放因子
"""
m, n = W.shape
qmax = 2 ** (num_bits - 1) - 1
# 将权重展平并分组
W_flat = W.flatten()
n_elements = len(W_flat)
n_groups = (n_elements + group_size - 1) // group_size
# 填充到 group_size 的整数倍
padded_len = n_groups * group_size
W_padded = np.pad(W_flat, (0, padded_len - n_elements))
W_groups = W_padded.reshape(n_groups, group_size)
# 每组的缩放因子
abs_max = np.max(np.abs(W_groups), axis=1, keepdims=True)
abs_max = np.maximum(abs_max, 1e-8)
scales = abs_max / qmax
# 量化
W_int = np.clip(np.round(W_groups / scales), -qmax, qmax).astype(int)
W_q_groups = W_int.astype(float) * scales
# 恢复原始形状
W_q = W_q_groups.flatten()[:n_elements].reshape(m, n)
return W_q, scales.flatten()
def demonstrate_granularity_comparison():
"""演示不同量化粒度的对比。"""
np.random.seed(42)
# 构造一个不同通道范围差异很大的权重矩阵
m, n = 128, 256
W = np.random.randn(m, n)
# 人为制造通道间范围差异
for i in range(m):
W[i] *= (0.01 + 10.0 * (i / m)) # 范围从 0.01 到 10
print("=" * 70)
print("量化粒度对比: 逐张量 vs 逐通道 vs 逐组")
print("=" * 70)
print(f"权重矩阵: {m} x {n}")
print(f"通道范围: [{np.min(np.max(np.abs(W), axis=1)):.4f}, {np.max(np.max(np.abs(W), axis=1)):.4f}]")
print()
for bits in [8, 4, 3]:
# 逐张量
W_q_tensor, _, _ = quantize_per_tensor(W, bits)
mse_tensor = norm(W - W_q_tensor, 'fro') ** 2 / (m * n)
# 逐通道
W_q_channel, _ = quantize_per_channel(W, bits)
mse_channel = norm(W - W_q_channel, 'fro') ** 2 / (m * n)
# 逐组
for g in [128, 64]:
W_q_group, _ = quantize_per_group(W, bits, group_size=g)
mse_group = norm(W - W_q_group, 'fro') ** 2 / (m * n)
print(f" {bits}-bit:")
print(f" 逐张量 MSE: {mse_tensor:.8f}")
print(f" 逐通道 MSE: {mse_channel:.8f}")
print(f" 逐组(g=128) MSE: {norm(W - quantize_per_group(W, bits, 128)[0], 'fro')**2/(m*n):.8f}")
print(f" 逐组(g=64) MSE: {norm(W - quantize_per_group(W, bits, 64)[0], 'fro')**2/(m*n):.8f}")
print()
def demonstrate_clip_range_optimization():
"""演示最优裁剪范围的搜索。"""
np.random.seed(42)
# 正态分布权重
W = np.random.randn(256, 256) * 0.5
num_bits = 4
qmax = 2 ** (num_bits - 1) - 1 # 7
print("=" * 70)
print("最优裁剪范围搜索")
print("=" * 70)
print(f"权重分布: N(0, 0.5), 形状 256x256, {num_bits}-bit 量化")
print()
# 搜索不同的裁剪比例
# 裁剪比例 alpha: x_max = alpha * max(|W|)
abs_max = np.max(np.abs(W))
print(f" {'裁剪比例 α':>12} {'x_max':>10} {'步长 s':>10} {'MSE':>15} {'过载比例':>10}")
print(f" {'-'*12} {'-'*10} {'-'*10} {'-'*15} {'-'*10}")
best_alpha = 0
best_mse = np.inf
for alpha_pct in [100, 99, 98, 95, 90, 85, 80, 70, 60]:
alpha = alpha_pct / 100.0
x_max = abs_max * alpha
s = x_max / qmax
# 量化(带裁剪)
W_clipped = np.clip(W, -x_max, x_max)
W_q = s * np.round(W_clipped / s)
mse = np.mean((W - W_q) ** 2)
# 过载比例
overload_ratio = np.mean(np.abs(W) > x_max)
if mse < best_mse:
best_mse = mse
best_alpha = alpha
print(f" {alpha:>12.2f} {x_max:>10.4f} {s:>10.4f} {mse:>15.8f} {overload_ratio:>10.2%}")
print(f"\n 最优裁剪比例: {best_alpha:.2f}")
print(f" 最优 MSE: {best_mse:.8f}")
if __name__ == "__main__":
demonstrate_granularity_comparison()
demonstrate_clip_range_optimization()