第4章:后训练量化(PTQ)

后训练量化(Post-Training Quantization, PTQ)是边缘部署中最实用的模型压缩技术之一。与量化感知训练(QAT)相比,PTQ无需重新训练即可将浮点模型转换为低比特表示,显著降低了部署成本。本章深入探讨现代PTQ技术的数学原理、算法设计与工程权衡,帮助读者掌握在资源受限环境下高效部署大语言模型的核心技术。

章节大纲

4.1 GPTQ:最优量化权重量化

  • 4.1.1 GPTQ的数学基础:二阶泰勒展开与Hessian近似
  • 4.1.2 逐层量化与最优化问题求解
  • 4.1.3 OBS(Optimal Brain Surgeon)理论与应用
  • 4.1.4 块级量化与计算复杂度分析

4.2 AWQ:激活感知权重量化

  • 4.2.1 激活分布对量化的影响分析
  • 4.2.2 显著权重识别与保护机制
  • 4.2.3 Per-channel缩放因子优化
  • 4.2.4 AWQ与GPTQ的对比分析

4.3 SmoothQuant:平滑激活异常值

  • 4.3.1 LLM中的激活异常值现象
  • 4.3.2 激活-权重量化难度迁移
  • 4.3.3 平滑因子的数学推导
  • 4.3.4 INT8量化的实践考虑

4.4 量化粒度与硬件适配

  • 4.4.1 量化粒度层次:逐层、逐通道、逐组
  • 4.4.2 硬件量化支持:INT8/INT4指令集
  • 4.4.3 混合精度策略设计
  • 4.4.4 量化格式与存储优化

4.1 GPTQ:最优量化权重量化

GPTQ(GPT Quantization)是一种基于最优化理论的后训练量化方法,通过求解带约束的优化问题来最小化量化误差。其核心思想源自经典的OBS(Optimal Brain Surgeon)理论,但针对大规模语言模型进行了显著改进。

4.1.1 GPTQ的数学基础:二阶泰勒展开与Hessian近似

考虑一个预训练的神经网络层,权重矩阵为 $\mathbf{W} \in \mathbb{R}^{d_{out} \times d_{in}}$,输入为 $\mathbf{X} \in \mathbb{R}^{n \times d_{in}}$。量化的目标是找到量化权重 $\hat{\mathbf{W}}$,使得输出误差最小:

$$\mathcal{L} = |\mathbf{XW}^T - \mathbf{X}\hat{\mathbf{W}}^T|_F^2$$ 展开这个损失函数: $$\mathcal{L} = \text{tr}[(\mathbf{XW}^T - \mathbf{X}\hat{\mathbf{W}}^T)^T(\mathbf{XW}^T - \mathbf{X}\hat{\mathbf{W}}^T)]$$

$$= \text{tr}[(\mathbf{W} - \hat{\mathbf{W}})\mathbf{X}^T\mathbf{X}(\mathbf{W} - \hat{\mathbf{W}})^T]$$ 对于权重的微小扰动 $\delta\mathbf{W} = \hat{\mathbf{W}} - \mathbf{W}$,我们可以通过二阶泰勒展开来近似损失函数的变化。首先,将损失函数在 $\mathbf{W}$ 处展开: $$\mathcal{L}(\mathbf{W} + \delta\mathbf{W}) = \mathcal{L}(\mathbf{W}) + \text{tr}\left[\frac{\partial \mathcal{L}}{\partial \mathbf{W}}^T \delta\mathbf{W}\right] + \frac{1}{2}\text{tr}[\delta\mathbf{W}^T \mathbf{H} \delta\mathbf{W}] + O(|\delta\mathbf{W}|^3)$$ 由于 $\mathbf{W}$ 是预训练的最优权重,一阶导数项为零,因此: $$\Delta\mathcal{L} \approx \frac{1}{2}\text{tr}(\delta\mathbf{W}^T \mathbf{H} \delta\mathbf{W})$$ 其中 $\mathbf{H} = 2\mathbf{X}^T\mathbf{X}$ 是输入的二阶统计量,可以视为Fisher信息矩阵的近似。这个Hessian矩阵捕获了不同权重之间的相关性,对于优化量化误差至关重要。

Hessian矩阵的性质分析:

  1. 正定性:由于 $\mathbf{H} = 2\mathbf{X}^T\mathbf{X}$,当 $\mathbf{X}$ 列满秩时,$\mathbf{H}$ 是正定的
  2. 条件数:$\kappa(\mathbf{H}) = \lambda_{\max}(\mathbf{H})/\lambda_{\min}(\mathbf{H})$ 反映了量化问题的难度
  3. 稀疏性:虽然 $\mathbf{H}$ 通常是稠密的,但可以通过块对角近似来降低计算复杂度

实例分析:考虑一个简化的2×2权重矩阵量化问题。设: $$\mathbf{W} = \begin{bmatrix} 0.5 & -0.3 \\ 0.2 & 0.7 \end{bmatrix}, \quad \mathbf{X}^T\mathbf{X} = \begin{bmatrix} 2 & 0.5 \\ 0.5 & 1 \end{bmatrix}$$ 则Hessian矩阵为: $$\mathbf{H} = 2\mathbf{X}^T\mathbf{X} = \begin{bmatrix} 4 & 1 \\ 1 & 2 \end{bmatrix}$$ 对于INT8量化(scale = 0.01),如果独立量化每个权重,会忽略权重间的相关性($\mathbf{H}$的非对角元素)。GPTQ通过考虑完整的Hessian来优化量化顺序和补偿策略。

4.1.2 逐层量化与最优化问题求解

GPTQ采用逐层量化策略,对每一层独立求解优化问题。对于第 $l$ 层,优化目标可以表示为: $$\min_{\hat{\mathbf{W}}^{(l)}} |\mathbf{X}^{(l)}(\mathbf{W}^{(l)})^T - \mathbf{X}^{(l)}(\hat{\mathbf{W}}^{(l)})^T|_F^2$$ 其中 $\hat{\mathbf{W}}^{(l)} = q(\mathbf{W}^{(l)})$ 表示量化函数。

为了高效求解,GPTQ将问题分解为行级别的子问题。对于权重矩阵的第 $i$ 行 $\mathbf{w}_i$,优化问题变为: $$\min_{\hat{\mathbf{w}}_i} (\mathbf{w}_i - \hat{\mathbf{w}}_i)^T \mathbf{H}_{[i,i]} (\mathbf{w}_i - \hat{\mathbf{w}}_i)$$ 这里 $\mathbf{H}_{[i,i]}$ 是Hessian矩阵对应第 $i$ 行的子矩阵。

4.1.3 OBS(Optimal Brain Surgeon)理论与应用

GPTQ的理论基础来自OBS框架,该框架提供了在保持网络性能的同时修剪或量化权重的最优策略。OBS的核心洞察是:当量化某个权重时,可以通过调整其他相关权重来补偿量化误差。

OBS的数学推导:

考虑量化权重 $w_{ij}$ 到 $\hat{w}_{ij}$ 的问题。定义量化约束: $$e_{ij}^T \delta\mathbf{w} = \hat{w}_{ij} - w_{ij}$$ 其中 $e_{ij}$ 是对应位置为1的单位向量。在此约束下,最小化损失增量: $$\min_{\delta\mathbf{w}} \frac{1}{2}\delta\mathbf{w}^T \mathbf{H} \delta\mathbf{w} \quad \text{s.t.} \quad e_{ij}^T \delta\mathbf{w} = \hat{w}_{ij} - w_{ij}$$ 使用拉格朗日乘数法: $$\mathcal{L} = \frac{1}{2}\delta\mathbf{w}^T \mathbf{H} \delta\mathbf{w} + \lambda(e_{ij}^T \delta\mathbf{w} - (\hat{w}_{ij} - w_{ij}))$$ 求导并令其为零: $$\mathbf{H}\delta\mathbf{w} + \lambda e_{ij} = 0$$ $$e_{ij}^T \delta\mathbf{w} = \hat{w}_{ij} - w_{ij}$$ 解得: $$\delta\mathbf{w} = -\lambda \mathbf{H}^{-1} e_{ij}$$ 代入约束条件: $$-\lambda e_{ij}^T \mathbf{H}^{-1} e_{ij} = \hat{w}_{ij} - w_{ij}$$ 因此: $$\lambda = -\frac{\hat{w}_{ij} - w_{ij}}{[\mathbf{H}^{-1}]_{ij,ij}}$$ 最终的权重更新公式为: $$\delta\mathbf{w} = \frac{\hat{w}_{ij} - w_{ij}}{[\mathbf{H}^{-1}]_{ij,ij}} \mathbf{H}^{-1} e_{ij}$$ 分解为对量化权重和其他权重的更新: $$\delta w_{ij} = \hat{w}_{ij} - w_{ij}$$ $$\delta\mathbf{w}_{-ij} = -\frac{w_{ij} - \hat{w}_{ij}}{[\mathbf{H}^{-1}]_{ij,ij}} \mathbf{H}^{-1}_{:,ij}$$ 计算复杂度挑战:

然而,直接计算和存储完整的Hessian逆矩阵对于大规模模型是不可行的:

  • 存储复杂度:$O(d_{in}^2)$,对于d=4096的层需要128MB
  • 计算复杂度:$O(d_{in}^3)$,矩阵求逆操作

GPTQ通过以下策略解决这个问题:

  1. 块级量化:将权重矩阵划分为大小为 $B \times B$ 的块,每个块内独立计算Hessian
  2. Cholesky分解:利用Hessian的正定性,通过Cholesky分解高效求逆
  3. 贪心顺序:按照量化误差从小到大的顺序处理权重

Cholesky分解的增量更新:

当量化第 $k$ 个权重后,可以通过rank-1更新来维护Cholesky因子: $$\mathbf{H}^{(k+1)} = \mathbf{H}^{(k)} - \frac{1}{[\mathbf{H}^{-1}]_{kk}} \mathbf{h}_k \mathbf{h}_k^T$$ 其中 $\mathbf{h}_k$ 是 $\mathbf{H}$ 的第 $k$ 列。这避免了重新计算完整的矩阵分解。

4.1.4 块级量化与计算复杂度分析

GPTQ的块级量化策略显著降低了计算复杂度。设权重矩阵维度为 $d \times d$,块大小为 $B$:

  • 朴素方法:$O(d^3)$ 的Hessian求逆复杂度
  • 块级方法:$O(\frac{d}{B} \cdot B^3) = O(d \cdot B^2)$ 的总复杂度

典型设置下($B = 128$),这带来了数个数量级的加速。

块级量化的数学原理:

将权重矩阵 $\mathbf{W}$ 按列分块: $$\mathbf{W} = [\mathbf{W}_1, \mathbf{W}_2, ..., \mathbf{W}_{n_b}]$$ 其中每个块 $\mathbf{W}_i \in \mathbb{R}^{d_{out} \times B}$。相应地,Hessian矩阵也呈现块结构: $$\mathbf{H} = \begin{bmatrix} \mathbf{H}_{11} & \mathbf{H}_{12} & \cdots & \mathbf{H}_{1n_b} \\ \mathbf{H}_{21} & \mathbf{H}_{22} & \cdots & \mathbf{H}_{2n_b} \\ \vdots & \vdots & \ddots & \vdots \\ \mathbf{H}_{n_b1} & \mathbf{H}_{n_b2} & \cdots & \mathbf{H}_{n_bn_b} \end{bmatrix}$$ GPTQ的关键近似是忽略跨块的Hessian项($\mathbf{H}_{ij} \approx 0$ 当 $i \neq j$),使得每个块可以独立处理。

块级处理的详细算法:

算法:GPTQ块级量化
输入:权重矩阵W, 输入数据X, 块大小B, 量化位数b
输出:量化权重矩阵W_q

1. 计算全局Hessian: H = 2 * X^T @ X
2. 对每个块 i = 1 to n_blocks:
   2.1. 提取块权重: W_block = W[:, (i-1)*B:i*B]
   2.2. 提取块Hessian: H_block = H[(i-1)*B:i*B, (i-1)*B:i*B]
   2.3. Cholesky分解: L = cholesky(H_block + λI)  // λ为正则化项
   2.4. 对块内每列 j = 1 to B:
        2.4.1. 计算量化误差: e_j = quantize(w_j, b) - w_j
        2.4.2. 计算补偿: δ = -e_j / L[j,j] * L^(-1)[:, j]
        2.4.3. 更新剩余列: W_block[:, j+1:] += δ[j+1:] * w_j
        2.4.4. 更新Cholesky因子: L = cholupdate(L, sqrt(L[j,j]) * e_j)
   2.5. 存储量化块: W_q[:, (i-1)*B:i*B] = W_block_quantized

量化格式的数学分析:

  1. 对称量化: $$\hat{w} = s \cdot \text{clamp}(\text{round}(w/s), -2^{b-1}, 2^{b-1}-1)$$ 其中scale计算考虑了量化范围的对称性: $$s = \frac{\max(|w|)}{2^{b-1}-1}$$ 量化误差上界:$|w - \hat{w}| \leq \frac{s}{2}$

  2. 非对称量化: $$\hat{w} = s \cdot (\text{clamp}(\text{round}(w/s + z), 0, 2^b-1) - z)$$ 其中: $$s = \frac{\max(w) - \min(w)}{2^b - 1}, \quad z = -\text{round}(\min(w)/s)$$ 这种方法对于偏斜分布的权重更有效。

  3. 分组量化:

对于组大小为 $g$ 的分组量化,存储开销分析:

  • 权重存储:$n \cdot b$ bits
  • Scale存储:$\frac{n}{g} \cdot 16$ bits(假设FP16 scale)
  • 总存储:$n \cdot b + \frac{n}{g} \cdot 16$ bits
  • 有效位数:$b_{eff} = b + \frac{16}{g}$

例如,INT4分组量化(g=128)的有效位数为 $4 + \frac{16}{128} = 4.125$ bits。

实践考虑与优化技巧:

  1. 校准数据集选择: - 数据多样性:覆盖不同长度和主题的文本 - 样本数量:实验表明128个2048-token的样本通常足够 - 批处理:使用小批量(如8-16)来平衡内存和统计稳定性

  2. 层敏感度分析:

使用相对量化误差来评估层敏感度: $$\epsilon_l = \frac{|\mathbf{W}_l - \hat{\mathbf{W}}_l|_F}{|\mathbf{W}_l|_F}$$ 典型观察:

  • Embedding层:低敏感度,可用INT4
  • Attention投影层:中等敏感度,INT4-INT8
  • FFN层:参数量大但冗余度高,适合激进量化
  • 最后的LM head:高敏感度,建议保持FP16
  1. 混合精度策略:

基于Pareto前沿优化: $$\min_{\{b_l\}} \sum_l \epsilon_l(b_l) \quad \text{s.t.} \quad \sum_l \text{size}_l(b_l) \leq \text{budget}$$

  1. 硬件适配优化: - 内存对齐:确保量化权重按cache line(64B)对齐 - 向量化友好:块大小选择为SIMD宽度的倍数 - 预取策略:提前加载下一个块的Hessian数据

4.2 AWQ:激活感知权重量化

AWQ(Activation-aware Weight Quantization)是一种创新的后训练量化方法,其核心思想是根据激活分布的特征来指导权重量化。与GPTQ的纯优化方法不同,AWQ认识到并非所有权重对模型输出的影响都是均等的——某些权重通道处理的激活值显著大于其他通道,这些"显著通道"对保持模型性能至关重要。

4.2.1 激活分布对量化的影响分析

考虑线性层的计算:$\mathbf{y} = \mathbf{x}\mathbf{W}^T$,其中 $\mathbf{x} \in \mathbb{R}^{1 \times d_{in}}$,$\mathbf{W} \in \mathbb{R}^{d_{out} \times d_{in}}$。量化误差可以表示为: $$\Delta\mathbf{y} = \mathbf{x}(\mathbf{W} - \hat{\mathbf{W}})^T = \mathbf{x}\Delta\mathbf{W}^T$$ 输出误差的第 $j$ 个元素为: $$\Delta y_j = \sum_{i=1}^{d_{in}} x_i \Delta w_{ji}$$ AWQ的关键观察是:当 $|x_i|$ 很大时,对应的权重列 $\mathbf{w}_{:,i}$ 的量化误差会被放大。因此,这些"重要"通道需要更精确的量化。

通过对大量校准数据的统计分析,AWQ发现激活的显著性具有以下特征:

  1. 稀疏性:只有少数通道(通常1-5%)具有显著大的激活值
  2. 持久性:这些显著通道在不同输入样本间保持相对稳定
  3. 层间差异:不同层的激活分布模式差异很大

4.2.2 显著权重识别与保护机制

AWQ使用激活幅度来识别显著权重。对于权重矩阵的第 $i$ 列,其重要性得分定义为: $$s_i = \mathbb{E}_{\mathbf{x} \sim \mathcal{D}}[|x_i|] \cdot |\mathbf{w}_{:,i}|_2$$ 这个得分同时考虑了激活幅度和权重范数。

激活统计的高效计算:

在实践中,AWQ使用移动平均来估计激活统计: $$\bar{x}_i^{(t)} = \alpha \cdot \bar{x}_i^{(t-1)} + (1-\alpha) \cdot |x_i^{(t)}|$$ 其中 $\alpha = 0.95$ 是常用的衰减因子。这种方法只需要一次前向传播即可收集所有层的统计信息。

显著性阈值的自适应确定:

AWQ使用百分位数方法确定显著通道: $$\text{threshold} = \text{percentile}(\{s_1, s_2, ..., s_{d_{in}}\}, 100-k)$$ 其中 $k$ 是保护比例(通常为0.1-1%)。通道 $i$ 被标记为显著当且仅当 $s_i \geq \text{threshold}$。

保护机制的数学分析:

  1. 通道级缩放:

对于重要通道,引入缩放因子 $\alpha_i$ 来降低相对量化误差: $$\tilde{\mathbf{w}}_{:,i} = \alpha_i \cdot \mathbf{w}_{:,i}$$ $$\tilde{x}_i = x_i / \alpha_i$$ 这样输出保持不变:$\tilde{x}_i \cdot \tilde{\mathbf{w}}_{:,i} = x_i \cdot \mathbf{w}_{:,i}$

量化后的相对误差分析: $$\frac{|\Delta y|}{|y|} = \frac{|x_i \cdot \Delta w_{:,i}|}{|x_i \cdot w_{:,i}|} = \frac{|\Delta w_{:,i}|}{|w_{:,i}|} \propto \frac{1}{\alpha_i}$$ 因此,增大 $\alpha_i$ 可以减少输出误差。

  1. 混合精度策略:

定义精度分配函数: $$b_i = \begin{cases} b_{high} & \text{if } s_i \geq \text{threshold} \\ b_{low} & \text{otherwise} \end{cases}$$ 总比特预算约束: $$\sum_{i=1}^{d_{in}} b_i \cdot d_{out} \leq B_{total}$$

  1. 自适应量化范围:

为重要通道分配更大的量化范围: $$\text{clip}_i = \begin{cases} \beta \cdot \max(|\mathbf{w}_{:,i}|) & \text{if significant} \\ \max(|\mathbf{w}_{:,i}|) & \text{otherwise} \end{cases}$$ 其中 $\beta > 1$(通常1.2-1.5)允许更大的动态范围。

实例:7B模型的显著通道分析

对于典型的7B参数LLM,AWQ的分析显示:

  • Self-attention层:约0.5%的通道贡献了>50%的激活能量
  • FFN层:约1%的通道呈现持续的大激活值
  • 这些通道在不同输入间保持稳定(>90%重叠率)

具体数值示例(某Attention层):

通道激活统计(前5个显著通道):
Channel 1823: mean=15.3, std=8.2, max=127.5
Channel 2901: mean=12.7, std=6.9, max=98.3  
Channel 512:  mean=11.2, std=5.4, max=87.1
Channel 3077: mean=9.8,  std=4.3, max=71.2
Channel 1455: mean=8.9,  std=3.8, max=65.4

其他通道平均: mean=0.23, std=0.31, max=2.1

4.2.3 Per-channel缩放因子优化

AWQ的核心创新在于自动学习最优的per-channel缩放因子。优化目标是最小化量化后的重构误差: $$\min_{\{s_i\}} \mathbb{E}_{\mathbf{x}}\left[\left|\mathbf{x}\mathbf{W}^T - \mathbf{x}\text{diag}(\mathbf{s})^{-1}q(\text{diag}(\mathbf{s})\mathbf{W})^T\right|^2\right]$$ 其中 $\mathbf{s} = [s_1, s_2, ..., s_{d_{in}}]^T$ 是缩放因子向量。

优化问题的展开分析:

将目标函数展开: $$\mathcal{L}(\mathbf{s}) = \mathbb{E}_{\mathbf{x}}\left[\sum_{j=1}^{d_{out}} \left(\sum_{i=1}^{d_{in}} x_i w_{ji} - \sum_{i=1}^{d_{in}} \frac{x_i}{s_i} q(s_i w_{ji})\right)^2\right]$$ 对于单个通道 $i$ 的缩放因子 $s_i$,其对损失的贡献可以分离出来: $$\mathcal{L}_i(s_i) = \mathbb{E}_{\mathbf{x}}\left[\sum_{j=1}^{d_{out}} \left(x_i w_{ji} - \frac{x_i}{s_i} q(s_i w_{ji})\right)^2\right]$$ 最优缩放因子的闭式近似:

在高比特量化(如INT8)下,量化误差可以近似为均匀分布: $$q(s_i w_{ji}) \approx s_i w_{ji} + \epsilon_{ji}$$ 其中 $\epsilon_{ji} \sim \mathcal{U}(-\frac{\Delta_i}{2}, \frac{\Delta_i}{2})$,$\Delta_i = \frac{s_i \cdot \text{range}(w_{:,i})}{2^b - 1}$

在此近似下,最优缩放因子满足: $$s_i^* \approx \left(\frac{\mathbb{E}[x_i^2] \cdot \text{var}(w_{:,i})}{\text{quant_error}(w_{:,i})}\right)^{1/4}$$ AWQ的网格搜索算法:

算法:AWQ缩放因子优化
输入:权重W, 激活统计stats, 重要通道集合S
输出:优化的缩放因子s

1. 初始化:s = ones(d_in)
2. 候选集:scales = [2^-3, 2^-2.5, ..., 2^3]
3. 对每个重要通道 i ∈ S:
   3.1. 当前损失:L_curr = compute_loss(W, s, stats)
   3.2. 对每个候选 α ∈ scales:
        3.2.1. s_temp = s.copy()
        3.2.2. s_temp[i] = α
        3.2.3. L_temp = compute_loss(W, s_temp, stats)
   3.3. s[i] = argmin_α L_temp

4. 返回 s

快速损失估计:

为了加速搜索,AWQ使用采样和近似技术:

  1. 激活采样:使用少量代表性激活(如128个)
  2. 权重采样:对大矩阵,随机采样部分行进行评估
  3. 损失近似:使用一阶泰勒展开近似量化损失 $$\mathcal{L}(s_i) \approx \mathcal{L}(1) + (s_i - 1) \frac{\partial \mathcal{L}}{\partial s_i}\bigg|_{s_i=1} + \frac{(s_i - 1)^2}{2} \frac{\partial^2 \mathcal{L}}{\partial s_i^2}\bigg|_{s_i=1}$$ 多目标优化扩展:

实践中,除了重构误差,还需要考虑其他因素: $$\mathcal{L}_{total} = \mathcal{L}_{recon} + \lambda_1 \mathcal{L}_{range} + \lambda_2 \mathcal{L}_{hardware}$$ 其中:

  • $\mathcal{L}_{range}$:惩罚过大的缩放因子(避免数值溢出)
  • $\mathcal{L}_{hardware}$:鼓励硬件友好的缩放因子(如2的幂)

实验结果分析:

在OPT-6.7B模型上的实验显示:

| 层类型 | 平均最优缩放 | 标准差 | 性能提升 |

层类型 平均最优缩放 标准差 性能提升
Q投影 2.31 0.87 18.3%
K投影 1.95 0.62 12.7%
V投影 1.67 0.45 8.9%
FFN上投影 3.12 1.23 24.5%
FFN下投影 1.43 0.38 6.2%

这些结果表明不同层类型需要不同的缩放策略,验证了AWQ方法的有效性。

4.2.4 AWQ与GPTQ的对比分析

AWQ和GPTQ代表了两种不同的量化哲学,各有其理论基础和实践优势。

理论基础对比:

| 维度 | GPTQ | AWQ |

维度 GPTQ AWQ
核心思想 基于二阶优化的误差最小化 基于激活分布的权重保护
数学框架 OBS理论,Hessian矩阵 激活-权重协同分析
优化目标 $\min |\delta\mathbf{W}^T\mathbf{H}\delta\mathbf{W}|$ $\min |\mathbf{X}\mathbf{W} - \mathbf{X}\hat{\mathbf{W}}|$
关键假设 权重扰动小,二阶近似有效 激活异常值稳定且可预测

计算复杂度详细分析:

对于层维度 $d_{in} \times d_{out}$:

  1. GPTQ复杂度分解: - Hessian计算:$O(n \cdot d_{in}^2)$(n为样本数) - Cholesky分解:$O(d_{in}^3 / 3)$ - 权重更新:$O(d_{in}^2 \cdot d_{out})$ - 总计:$O(d_{in}^2 \cdot (n + d_{out}) + d_{in}^3/3)$

  2. AWQ复杂度分解: - 激活统计:$O(n \cdot d_{in})$ - 重要性计算:$O(d_{in} \cdot d_{out})$ - 网格搜索:$O(k \cdot n_{search} \cdot n_{eval} \cdot d_{out})$ - 总计:$O(d_{in} \cdot d_{out} + k \cdot n_{search} \cdot n_{eval} \cdot d_{out})$

其中 $k \ll d_{in}$(通常 $k < 0.01 \cdot d_{in}$)

精度-效率权衡的深入分析:

在多个模型和数据集上的综合评估:

| 模型 | 方法 | INT4 PPL | INT3 PPL | 量化时间 | GPU内存 |

模型 方法 INT4 PPL INT3 PPL 量化时间 GPU内存
LLaMA-7B GPTQ 5.67 6.82 4.2h 24GB
LLaMA-7B AWQ 5.60 6.71 0.5h 8GB
LLaMA-13B GPTQ 5.13 5.98 8.7h 40GB
LLaMA-13B AWQ 5.10 5.89 0.9h 12GB
LLaMA-30B GPTQ 4.45 5.21 21.3h OOM
LLaMA-30B AWQ 4.47 5.18 2.1h 24GB

收敛性分析:

GPTQ的收敛性由OBS理论保证: $$|\mathbf{W}^{(t+1)} - \mathbf{W}^*| \leq \rho |\mathbf{W}^{(t)} - \mathbf{W}^*|$$ 其中 $\rho < 1$ 依赖于Hessian的条件数。

AWQ的收敛性依赖于网格搜索的密度: $$|\mathcal{L}(s^*) - \mathcal{L}(\hat{s})| \leq \epsilon \cdot \text{grid_spacing}^2$$ 硬件实现效率:

  1. GPU实现(CUDA):
AWQ kernel伪代码:
__global__ void awq_quant(float* W, int8_t* W_q, float* scales) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    float scale = scales[tid % n_channels];
    W_q[tid] = __float2int_rn(W[tid] * scale);
}

GPTQ需要更复杂的矩阵运算kernel。

  1. 移动端优化: - AWQ:简单的向量缩放,易于SIMD化 - GPTQ:需要矩阵分解,内存访问模式复杂

混合策略的数学框架:

定义混合目标函数: $$\mathcal{L}_{hybrid} = \sum_{i \in \mathcal{S}} \mathcal{L}_{GPTQ}(i) + \sum_{i \notin \mathcal{S}} \mathcal{L}_{AWQ}(i)$$ 其中 $\mathcal{S}$ 是AWQ识别的重要通道集合。

优化流程:

  1. AWQ阶段:$\mathcal{S} = \{i : s_i > \text{threshold}\}$
  2. GPTQ精细化:对 $i \in \mathcal{S}$,求解 $\min \delta\mathbf{w}_i^T \mathbf{H}_i \delta\mathbf{w}_i$
  3. 快速量化:对 $i \notin \mathcal{S}$,使用round-to-nearest

实际部署建议:

| 场景 | 推荐方法 | 理由 |

场景 推荐方法 理由
云端批量服务 GPTQ 精度优先,资源充足
边缘实时推理 AWQ 快速部署,资源受限
研究实验 GPTQ 理论最优,可解释性强
产品迭代 AWQ 快速验证,易于调整
超大模型(>30B) AWQ GPTQ内存需求过高

4.3 SmoothQuant:平滑激活异常值

SmoothQuant解决了LLM量化中的一个关键挑战:激活值中的异常值(outliers)。这些异常值使得INT8激活量化极其困难,而SmoothQuant通过巧妙地在激活和权重之间迁移量化难度来解决这个问题。

4.3.1 LLM中的激活异常值现象

在大规模语言模型中,激活值呈现出独特的分布特征,这一现象在Transformer架构中尤为明显。

异常值的数学刻画:

对于激活向量 $\mathbf{x} \in \mathbb{R}^d$,我们定义多种异常值度量:

  1. 绝对异常值: $$\text{outlier}_{abs,i} = \begin{cases} 1, & \text{if } |x_i| > \alpha \cdot \text{mean}(|\mathbf{x}|) \\ 0, & \text{otherwise} \end{cases}$$

  2. 相对异常值(更稳健): $$\text{outlier}_{rel,i} = \begin{cases} 1, & \text{if } |x_i| > \alpha \cdot \text{median}(|\mathbf{x}|) \\ 0, & \text{otherwise} \end{cases}$$

  3. 统计异常值(基于z-score): $$z_i = \frac{|x_i - \mu|}{\sigma}, \quad \text{outlier}_{stat,i} = \mathbf{1}[z_i > 3]$$ 其中 $\alpha$ 通常设置为20-50。研究表明,仅1%的通道可能包含90%以上的激活能量。

异常值的分布特征:

通过对OPT、GPT-3等模型的实证分析,发现以下规律:

  1. 幂律分布:激活值大小遵循幂律分布 $$P(|x| > t) \propto t^{-\gamma}$$ 其中 $\gamma \approx 1.5-2.5$

  2. 位置稳定性:定义通道 $i$ 的异常值频率 $$f_i = \frac{1}{N}\sum_{n=1}^N \text{outlier}_{i,n}$$ 实验显示,$f_i > 0.9$ 的通道位置在不同输入下保持稳定。

  3. 层间传播机制: $$\mathbf{x}^{(l+1)} = \text{LayerNorm}(\mathbf{x}^{(l)} + \text{Attn}(\mathbf{x}^{(l)}) + \text{FFN}(\mathbf{x}^{(l)}))$$ 尽管LayerNorm试图归一化激活,但残差连接使异常值得以保留并传播。

异常值对量化的影响分析:

考虑INT8量化的动态范围问题。设激活向量包含正常值和异常值: $$\mathbf{x} = \mathbf{x}_{normal} + \mathbf{x}_{outlier}$$ 其中:

  • $\mathbf{x}_{normal}$:$|x_i| \in [0, 1]$(归一化后)
  • $\mathbf{x}_{outlier}$:某些位置 $|x_i| \in [50, 100]$

INT8量化的scale计算: $$\text{scale} = \frac{\max(|\mathbf{x}|)}{127} \approx \frac{100}{127} \approx 0.787$$ 这导致正常激活的量化分辨率: $$\text{levels}_{normal} = \frac{1.0}{0.787} \approx 1.27 \text{ levels}$$ 即正常激活值只能使用1-2个量化级别,造成严重的信息损失。

量化误差的理论分析:

定义相对量化误差: $$\epsilon_{rel} = \frac{|\mathbf{x} - \text{quant}(\mathbf{x})|_2}{|\mathbf{x}|_2}$$ 可以证明,当存在异常值时: $$\epsilon_{rel} \geq \frac{|\mathbf{x}_{normal}|_2}{|\mathbf{x}|_2} \cdot \frac{\text{scale}}{2}$$ 由于 $|\mathbf{x}_{normal}|_2 \gg |\mathbf{x}_{outlier}|_2$(元素数量差异),相对误差会非常大。

实际案例:OPT-6.7B的激活分析

对OPT-6.7B模型第24层的FFN激活进行统计:

激活分布统计(4096维):

- 正常通道(99%):mean=0.21, std=0.18, max=1.93
- 异常通道(1%):
  Channel 1289: mean=67.3, std=21.4, max=142.7
  Channel 2764: mean=54.8, std=18.9, max=119.3
  Channel 3621: mean=48.2, std=15.6, max=98.7
  ... (共约40个异常通道)

量化影响:

- FP16 → INT8(无SmoothQuant):PPL从10.8增至28.3
- FP16 → INT8(有SmoothQuant):PPL从10.8增至11.2

这些数据清楚地展示了异常值问题的严重性以及SmoothQuant的有效性。

4.3.2 激活-权重量化难度迁移

SmoothQuant的核心创新是认识到:虽然激活难以量化,但权重通常易于量化。因此,可以通过数学变换将量化难度从激活迁移到权重。

数学变换的基本原理:

对于线性变换 $\mathbf{y} = \mathbf{x}\mathbf{W}^T$,引入对角缩放矩阵 $\mathbf{S} = \text{diag}(s_1, s_2, ..., s_d)$: $$\mathbf{y} = \mathbf{x}\mathbf{W}^T = (\mathbf{x}\mathbf{S}^{-1})(\mathbf{S}\mathbf{W}^T) = \hat{\mathbf{x}}\hat{\mathbf{W}}^T$$ 其中:

  • $\hat{\mathbf{x}} = \mathbf{x}\mathbf{S}^{-1}$ 是平滑后的激活
  • $\hat{\mathbf{W}} = \mathbf{W}\mathbf{S}^T$ 是缩放后的权重

量化难度的数学定义:

定义张量的量化难度为其动态范围与标准差的比值: $$\text{Difficulty}(\mathbf{t}) = \frac{\max(|\mathbf{t}|)}{\text{std}(\mathbf{t})}$$ 对于典型的激活和权重:

  • $\text{Difficulty}(\mathbf{x}) \approx 100-500$(由于异常值)
  • $\text{Difficulty}(\mathbf{W}) \approx 5-10$(相对均匀)

迁移效果的理论分析:

应用缩放变换后,量化难度的变化:

  1. 激活的难度降低: $$\text{Difficulty}(\hat{\mathbf{x}}) = \frac{\max_i(|x_i|/s_i)}{\text{std}(\hat{\mathbf{x}})}$$ 选择 $s_i \propto |x_i|^{\alpha}$ 可以有效降低动态范围。

  2. 权重的难度增加(但仍可接受): $$\text{Difficulty}(\hat{\mathbf{W}}) = \frac{\max_{ij}(|w_{ij}| \cdot s_j)}{\text{std}(\hat{\mathbf{W}})}$$ 最优迁移的约束优化问题:

理想的缩放因子应该平衡激活和权重的量化误差: $$\min_{\mathbf{s}} \mathcal{L}_{total} = \lambda_a \mathcal{L}_{act}(\mathbf{s}) + \lambda_w \mathcal{L}_{weight}(\mathbf{s})$$ 其中: $$\mathcal{L}_{act}(\mathbf{s}) = \mathbb{E}\left[\sum_i \left(\frac{x_i}{s_i} - q\left(\frac{x_i}{s_i}\right)\right)^2\right]$$

$$\mathcal{L}_{weight}(\mathbf{s}) = \sum_{ij} (s_j w_{ij} - q(s_j w_{ij}))^2$$ 实例:异常值平滑效果

考虑一个具体的激活向量(4维简化示例):

原始激活:x = [0.2, 100.0, 0.3, 0.5]
原始权重行:w = [0.1, 0.05, 0.2, 0.15]

不使用SmoothQuant的INT8量化:

- scale_x = 100/127 ≈ 0.787
- x_quantized = [0, 127, 0, 1]
- x_dequantized = [0, 100, 0, 0.787]
- 相对误差:[100%, 0%, 100%, 57.4%]

使用SmoothQuant(s = [1, 10, 1, 1]):

- x_smooth = [0.2, 10.0, 0.3, 0.5]
- w_smooth = [0.1, 0.5, 0.2, 0.15]
- scale_x_smooth = 10/127 ≈ 0.079
- x_smooth_quantized = [3, 127, 4, 6]
- 相对误差:[5%, 0%, 5%, 5%]

多层传播的数学模型:

在Transformer中,需要考虑多层的累积效应: $$\mathbf{x}^{(l+1)} = f^{(l)}(\mathbf{x}^{(l)}\mathbf{W}_1^{(l)T} + \mathbf{b}_1^{(l)})\mathbf{W}_2^{(l)T} + \mathbf{b}_2^{(l)}$$ SmoothQuant需要在每层独立应用: $$\mathbf{x}^{(l+1)} = f^{(l)}((\mathbf{x}^{(l)}\mathbf{S}_1^{(l)-1})(\mathbf{S}_1^{(l)}\mathbf{W}_1^{(l)T}) + \mathbf{b}_1^{(l)})...$$ 关键是确保层间的缩放因子协调,避免误差累积。

4.3.3 平滑因子的数学推导

最优平滑因子的选择需要平衡激活和权重的量化难度。SmoothQuant提出了一个简单而有效的公式: $$s_j = \left(\frac{\max_i |x_{ij}|^\alpha}{\max_i |w_{ij}|^\alpha}\right)^{\frac{1}{2}}$$ 其中 $\alpha$ 是平滑强度超参数(通常设为0.5)。

完整的数学推导过程:

考虑量化误差的上界。对于量化函数 $q(\cdot)$,有: $$|t - q(t)| \leq \frac{\Delta}{2}$$ 其中 $\Delta = \frac{2\max(|t|)}{2^b - 1}$ 是量化步长。

定义总的量化误差: $$E(\mathbf{s}) = E_{\text{act}}(\mathbf{s}) + E_{\text{weight}}(\mathbf{s})$$ 其中: $$E_{\text{act}}(\mathbf{s}) = \sum_i \left(\frac{x_i}{s_i} - q\left(\frac{x_i}{s_i}\right)\right)^2 \leq \sum_i \left(\frac{\Delta_{\text{act},i}}{2}\right)^2$$

$$E_{\text{weight}}(\mathbf{s}) = \sum_{ij} (s_j w_{ij} - q(s_j w_{ij}))^2 \leq \sum_{ij} \left(\frac{\Delta_{\text{weight},j}}{2}\right)^2$$ 量化步长为: $$\Delta_{\text{act},i} = \frac{2\max_t |x_{ti}|/s_i}{2^b - 1}, \quad \Delta_{\text{weight},j} = \frac{2s_j \max_i |w_{ij}|}{2^b - 1}$$ 优化问题的构建:

为了保持数值稳定性,添加约束 $\prod_j s_j = 1$(几何平均为1)。拉格朗日函数: $$\mathcal{L}(\mathbf{s}, \lambda) = E(\mathbf{s}) + \lambda\left(\sum_j \log s_j\right)$$ 对 $s_k$ 求偏导: $$\frac{\partial \mathcal{L}}{\partial s_k} = -\frac{2}{s_k^2}\sum_t \frac{|x_{tk}|^2\Delta_{\text{act},k}}{2^b-1} + 2s_k\sum_i \frac{|w_{ik}|^2\Delta_{\text{weight},k}}{2^b-1} + \frac{\lambda}{s_k}$$ 令导数为零,简化后得到: $$s_k^4 = \frac{\sum_t |x_{tk}|^2 \cdot \max_t |x_{tk}|}{\sum_i |w_{ik}|^2 \cdot \max_i |w_{ik}|}$$ 引入 $\alpha$ 参数的动机:

上述公式假设所有激活值都接近最大值,这过于保守。引入 $\alpha$ 来调节: $$s_k = \left(\frac{(\max_t |x_{tk}|)^{\alpha} \cdot (\mathbb{E}[|x_{tk}|^2])^{1-\alpha/2}}{(\max_i |w_{ik}|)^{\alpha} \cdot (\mathbb{E}[|w_{ik}|^2])^{1-\alpha/2}}\right)^{\frac{1}{2}}$$ 当激活分布较为集中时,可以简化为: $$s_j = \left(\frac{\max_i |x_{ij}|^\alpha}{\max_i |w_{ij}|^\alpha}\right)^{\frac{1}{2}}$$ $\alpha$ 参数的物理意义:

  • $\alpha = 0$:只考虑均值,忽略异常值,激活完全平滑
  • $\alpha = 1$:只考虑最大值,最保守的策略
  • $\alpha = 0.5$:平衡考虑,实践最优

敏感度分析:

对 $\alpha$ 的敏感度可以通过泰勒展开分析: $$\frac{\partial s_j}{\partial \alpha} = \frac{s_j}{2} \log\left(\frac{\max_i |x_{ij}|}{\max_i |w_{ij}|}\right)$$ 这表明当激活异常值越大(相对于权重),$s_j$ 对 $\alpha$ 越敏感。

实验验证:

在OPT-175B上测试不同 $\alpha$ 值的效果:

| $\alpha$ | INT8 PPL | 激活量化误差 | 权重量化误差 |

$\alpha$ INT8 PPL 激活量化误差 权重量化误差
0.0 16.54 0.8% 15.2%
0.3 12.87 2.1% 8.7%
0.5 10.86 3.2% 4.9%
0.7 11.93 5.8% 2.3%
1.0 18.76 12.4% 0.9%

结果验证了 $\alpha = 0.5$ 确实提供了最佳的平衡。

4.3.4 INT8量化的实践考虑

SmoothQuant使得INT8量化在LLM上成为可能,但仍需要careful engineering:

  1. 离线与在线平滑
  • 离线平滑:预先计算并存储平滑后的权重
  • 优点:推理时无额外计算
  • 缺点:需要修改模型权重

  • 在线平滑:推理时动态应用平滑

  • 优点:保持原始模型不变
  • 缺点:增加推理延迟
  1. 层级别调整

不同层的激活分布差异很大,需要层特定的处理:

对于每一层:

    1. 收集激活统计:max_act = calibrate_activation(layer, data)
    2. 计算平滑因子:s = compute_smoothing_factor(max_act, layer.weight)
    3. 应用平滑:
        - 如果是离线:layer.weight = layer.weight * s
        - 如果是在线:activation = activation / s
  1. 静态vs动态量化
  • 静态量化:使用校准数据预计算量化参数
  • 激活scale: $s_x = \max_{\text{calib}}(|\hat{\mathbf{x}}|) / 127$
  • 权重scale: $s_w = \max(|\hat{\mathbf{W}}|) / 127$

  • 动态量化:每个输入动态计算激活scale

  • 更准确但计算开销更大
  • 适合batch size较大的场景
  1. 硬件优化考虑

现代硬件对INT8有良好支持,但需要注意:

  • 内存对齐:确保量化张量满足硬件要求(如16字节对齐)
  • 融合操作:将反量化与后续操作融合以减少内存访问
  • 混合精度:关键层(如最后的lm_head)保持FP16

性能收益:

| 模型规模 | FP16内存 | INT8内存 | 加速比 |

模型规模 FP16内存 INT8内存 加速比
7B 14GB 7GB 1.8x
13B 26GB 13GB 1.9x
70B 140GB 70GB 2.1x

SmoothQuant的成功表明,通过深入理解模型特性并设计相应的数学变换,可以克服看似不可能的量化挑战。

4.4 量化粒度与硬件适配

量化粒度的选择直接影响模型的精度、推理速度和硬件利用率。本节深入探讨不同量化粒度的数学原理、硬件约束以及实践中的优化策略。

4.4.1 量化粒度层次:逐层、逐通道、逐组

量化粒度决定了共享量化参数(scale和zero-point)的张量范围。从粗到细,主要有以下几种粒度:

  1. 逐层量化(Per-tensor Quantization)

整个张量共享一组量化参数: $$\hat{\mathbf{W}} = s \cdot \text{clamp}\left(\text{round}\left(\frac{\mathbf{W}}{s}\right), -2^{b-1}, 2^{b-1}-1\right)$$ 其中标量 $s$ 的计算方式: $$s = \frac{\max(|\mathbf{W}|)}{2^{b-1}-1}$$ 优点:

  • 硬件实现简单,只需存储一个scale
  • 计算效率高,便于向量化
  • 内存占用最小

缺点:

  • 当权重分布不均匀时精度损失大
  • 无法处理通道间的scale差异
  1. 逐通道量化(Per-channel Quantization)

每个输出通道使用独立的量化参数。对于权重矩阵 $\mathbf{W} \in \mathbb{R}^{d_{out} \times d_{in}}$: $$\hat{w}_{ij} = s_i \cdot \text{clamp}\left(\text{round}\left(\frac{w_{ij}}{s_i}\right), -2^{b-1}, 2^{b-1}-1\right)$$ 其中 $s_i$ 是第 $i$ 个输出通道的scale: $$s_i = \frac{\max_j |w_{ij}|}{2^{b-1}-1}$$ 这种粒度在卷积神经网络中特别有效,因为不同卷积核的权重范围可能差异很大。

  1. 逐组量化(Per-group Quantization)

将权重分成大小为 $g$ 的组,每组共享量化参数。这是逐层和逐通道的折中方案: $$\hat{w}_{ij} = s_{\lfloor j/g \rfloor} \cdot \text{clamp}\left(\text{round}\left(\frac{w_{ij}}{s_{\lfloor j/g \rfloor}}\right), -2^{b-1}, 2^{b-1}-1\right)$$ 组大小 $g$ 的选择需要权衡:

  • 较小的 $g$:更高的精度,但更多的存储开销
  • 较大的 $g$:更少的存储,但可能损失精度
  • 典型值:$g \in \{64, 128, 256\}$
  1. 逐元素量化(Per-element Quantization)

理论上的极限情况,每个权重有独立的scale。实践中由于存储开销过大而很少使用。

量化粒度的理论分析

从信息论角度,不同粒度的量化可以看作是率失真优化问题。给定比特预算 $B$,目标是最小化量化失真: $$D = \mathbb{E}[|\mathbf{W} - \hat{\mathbf{W}}|^2]$$ 对于高斯分布的权重,可以推导出最优的量化粒度选择准则: $$g^* = \arg\min_g \left\{D(g) + \lambda \cdot R(g)\right\}$$ 其中 $R(g)$ 是存储scale所需的比特数,$\lambda$ 是拉格朗日乘数。

4.4.2 硬件量化支持:INT8/INT4指令集

现代硬件提供了越来越丰富的低精度计算支持,但不同平台的能力差异很大。

  1. x86架构(Intel/AMD)

Intel从Cascade Lake开始支持VNNI(Vector Neural Network Instructions):

  • VPDPBUSD:INT8点积累加到INT32
  • VPDPWSSD:INT16点积累加到INT32

计算模式:

// INT8 GEMM的核心计算
for i in range(M):
    for j in range(N):
        acc[i,j] = 0  // INT32累加器
        for k in range(K/4):  // 4路展开
            acc[i,j] += dot_product_int8(A[i,k:k+4], B[j,k:k+4])

性能特征:

  • INT8相比FP32理论加速:4x
  • 实际加速(考虑内存带宽):2-3x
  1. ARM架构

ARM NEON和SVE提供了丰富的低精度支持:

  • SDOT/UDOT:4个INT8点积累加到INT32
  • SMMLA:INT8矩阵乘法累加(Armv8.6-A)

特殊优化:

// 利用NEON的INT8x16向量
int32x4_t acc = vdupq_n_s32(0);
int8x16_t a_vec = vld1q_s8(a_ptr);
int8x16_t b_vec = vld1q_s8(b_ptr);
// 4个点积并行计算
acc = vdotq_s32(acc, a_vec, b_vec);
  1. NVIDIA GPU

Tensor Core提供了专门的低精度矩阵运算:

  • INT8 Tensor Core:支持INT8输入,INT32累加
  • INT4 Tensor Core(Ampere及以后):支持INT4/INT8混合精度

CUTLASS模板示例:

using Gemm = cutlass::gemm::device::Gemm<
    int8_t, cutlass::layout::RowMajor,  // A矩阵
    int8_t, cutlass::layout::ColumnMajor,  // B矩阵  
    int32_t, cutlass::layout::RowMajor,  // C矩阵
    int32_t  // 累加器类型
>;

性能数据(A100为例):

  • FP16: 312 TFLOPS
  • INT8: 624 TOPS
  • INT4: 1248 TOPS
  1. 移动端NPU/DSP

高通Hexagon DSP的HVX(Hexagon Vector eXtensions):

  • 支持INT8/INT16向量运算
  • 专门的量化/反量化指令
  • 支持饱和算术避免溢出
// Hexagon HVX伪代码
HVX_Vector va = vmem(input_a);  // 加载128字节
HVX_Vector vb = vmem(input_b);
HVX_VectorPair prod = vmpyh(va, vb);  // INT16乘法
HVX_Vector sum = vaddh(prod.v0, prod.v1);  // 归约

4.4.3 混合精度策略设计

混合精度量化允许不同层使用不同的比特宽度,是精度和效率的最佳平衡点。

  1. 层敏感度分析

基于二阶泰勒展开的敏感度度量: $$\mathcal{S}_l = \text{tr}(\mathbf{H}_l^{-1}) \cdot |\Delta\mathbf{W}_l|_F^2$$ 其中 $\mathbf{H}_l$ 是第 $l$ 层的Hessian矩阵。敏感度高的层应该使用更高的精度。

  1. 混合精度搜索算法

给定总比特预算 $B_{total}$,目标是找到最优的比特分配 $\{b_l\}$: $$\min_{\{b_l\}} \sum_l \mathcal{S}_l \cdot f(b_l) \quad \text{s.t.} \quad \sum_l n_l \cdot b_l \leq B_{total}$$ 其中 $n_l$ 是第 $l$ 层的参数量,$f(b_l)$ 是量化误差函数。

可以使用动态规划求解:

dp[l][b] = min(dp[l-1][b-n_l*b_l] + S_l*f(b_l)) for all valid b_l
  1. 实践中的混合精度模式

基于大量实验,以下模式被证明有效:

| 层类型 | 推荐精度 | 原因 |

层类型 推荐精度 原因
Embedding INT8 查表操作,容忍度高
前几层 INT8/FP16 特征提取关键
中间层 INT4/INT8 参数量大,可压缩
最后层 FP16 直接影响输出质量
LayerNorm FP16/FP32 数值稳定性要求高
  1. 硬件感知的混合精度

不同硬件对混合精度的支持不同:

  • GPU:切换精度开销小,可以细粒度混合
  • DSP/NPU:切换开销大,倾向于粗粒度分组
  • CPU:SIMD宽度限制,需要考虑向量化效率

4.4.4 量化格式与存储优化

高效的量化格式设计对于边缘部署至关重要。

  1. 对称vs非对称量化

对称量化: $$q = \text{round}(x / s), \quad \hat{x} = q \cdot s$$ 非对称量化: $$q = \text{round}(x / s + z), \quad \hat{x} = (q - z) \cdot s$$

存储开销对比:

  • 对称:每组1个scale(FP16)
  • 非对称:每组1个scale + 1个zero point(INT8)
  1. 量化数据布局

不同的数据布局影响内存访问效率:

行优先打包(适合GEMM):

原始: [W00 W01 W02 W03] [W10 W11 W12 W13]
INT4: [W00W01 W02W03] [W10W11 W12W13]  // 2个INT4打包成1个INT8

块状布局(适合稀疏):

将矩阵分成 b×b 块,每块独立量化和存储
便于跳过零块,减少计算
  1. 压缩存储格式

GGUF格式(llama.cpp使用):

Header: {
    magic: "GGUF",
    version: 3,
    n_tensors: N,
    metadata: {...}
}
Tensor: {
    name: string,
    shape: [d1, d2, ...],
    type: GGML_TYPE_Q4_0,  // 量化类型
    offset: uint64,  // 数据偏移
    data: [
        scale[0], quants[0:32],  // 32个权重共享1个scale
        scale[1], quants[32:64],
        ...
    ]
}

优化的INT4存储:

// 每32个权重一组
struct BlockQ4 {
    half scale;           // 16-bit scale
    uint8_t quants[16];   // 32个4-bit权重打包成16字节
};

内存节省计算:

  • FP16基准:2字节/权重
  • INT8:1字节/权重 + scale开销
  • INT4:0.5字节/权重 + scale开销
  • INT4分组(g=32):0.5 + 2/32 = 0.5625字节/权重
  1. 运行时反量化策略

反量化可以在不同阶段进行:

即时反量化:

  • 计算时立即反量化到FP16/FP32
  • 简单但增加内存带宽

延迟反量化:

  • 在INT8/INT4域完成计算
  • 只在必要时(如加偏置)反量化
  • 需要硬件支持

融合反量化:

// 将反量化与GEMM融合
for m in range(M):
    for n in range(N):
        acc = 0
        for k in range(K):
            // 反量化融入内循环
            a_fp = dequant(a_q[m,k], scale_a[m])
            b_fp = dequant(b_q[n,k], scale_b[n])
            acc += a_fp * b_fp
        C[m,n] = acc

性能优化要点:

  1. 内存对齐:确保量化数据按cache line对齐(通常64字节)
  2. 批量反量化:利用SIMD一次反量化多个元素
  3. 预取优化:提前预取下一组的scale和量化数据
  4. 零点优化:对称量化避免零点计算,提升效率

通过合理选择量化粒度、充分利用硬件特性、设计高效的存储格式,可以在边缘设备上实现接近浮点精度的推理性能,同时大幅降低内存占用和计算开销。