量化——通过降低数值精度来节省内存并可能加速计算——的基本思路是直观的,但在此过程中做出的具体选择会产生重要影响,尤其是对于大型语言模型。在分析高级校准方法或训练感知技术之前,重点在于阐明其基本构成要素:如何映射数值以及应用这些映射的范围。这确立了应对量化庞大Transformer架构复杂性所需的精确术语和理论准备。量化映射本质上,量化是将连续或大量数值(通常是32位或16位浮点数,$FP32$ 或 $FP16/BF16$)映射到更小、离散的数值集合,这些数值通常由低位整数(如 $INT8$, $INT4$)表示。最常见的方法是仿射量化,由比例因子($S$)和零点($Z$)定义。对于给定的实数值输入 $X$,其量化表示 $X_q$ 计算如下:$$ X_q = \text{截断}(\text{取整}(X / S + Z)) $$此处:$X$: 原始高精度值(例如,FP32权重或激活值)。$S$: 比例因子,一个正浮点数,它决定了量化级别之间的步长。它将实数值的范围映射到量化整数的范围。$Z$: 零点,一个整数偏移量,确保实数值零正确映射到量化范围内的值。这对于准确表示不以零为中心的分布很重要。$\text{取整}(\cdot)$: 通常是四舍五入到最近的整数。$\text{截断}(\cdot)$: 将结果限制在目标整数类型可表示的范围内(例如,无符号 $INT8$ 为 $[0, 255]$,或有符号 $INT8$ 为 $[-128, 127]$)。逆运算,即反量化,近似于原始值:$$ X_{dq} = S (X_q - Z) $$$X_{dq}$ 表示反量化值,它近似于原始 $X$。虽然计算通常可以直接在 $X_q$ 上使用整数算术执行,以实现最大效率(运用专用硬件指令),但在与需要更高精度的层交互或进行分析时,反量化是必要的。仿射量化与对称量化存在两种确定 $S$ 和 $Z$ 的主要方案:仿射量化: 使用比例因子($S$)和零点($Z$),如上文所述。这允许进行非对称映射,其中实数值 $0.0$ 可以由其中一个量化整数值(特别是 $Z$)精确表示。这通常优先用于量化激活值(如ReLU或GeLU输出),因为它们通常具有非负或非对称分布。对称量化: 通过将零点 $Z$ 隐式或显式设置为零来简化映射。公式变为: $$ X_q = \text{截断}(\text{取整}(X / S)) $$ 反量化只是 $X_{dq} = S X_q$。这常用于模型权重,因为它们倾向于具有以零为中心的分布。虽然更简单,但如果整数范围不完全以零为中心(例如,有符号 $INT8$ 范围是 $[-128, 127]$),对称量化可能无法精确表示实数值 $0.0$。然而,它有时可以在某些硬件上提供轻微的计算优势。仿射量化和对称量化之间的选择取决于被量化数值的分布以及目标硬件的能力。量化粒度:逐张量量化与更细粒度范围一个重要的决定是量化参数($S$ 和 $Z$)的计算和应用所覆盖的粒度或范围。逐张量量化: 这是最简单的方法。为整个张量计算一对 $(S, Z)$ 值(例如,线性层权重矩阵中的所有权重,或特征图中的所有激活值)。优点: 开销最低;每个张量只需存储一个 $S$ 和一个 $Z$。缺点: 如果张量不同部分的数值范围差异很大,可能会遭受明显的精度损失。张量一小部分中的异常值会大幅缩小量化步长所表示的有效范围,导致低分辨率。逐通道(或逐轴)量化: 一种更精细的方法,特别常用于卷积层和线性层中的权重张量。不是为整个权重矩阵使用一对 $(S, Z)$,而是为每个输出通道(或沿特定轴)计算一个单独的对。对于形状为 [output_channels, input_channels] 的权重矩阵 $W$,您将计算 output_channels 对 $(S, Z)$。优点: 相较于逐张量量化,显著提高了精度,因为它适应通道间可能不同的数值分布。更好地处理变动。缺点: 增加了元数据开销;每个张量需要存储多个 $S$ 和 $Z$ 值。逐组(或块级)量化: 一种更细的粒度,主要应用于权重,特别是在非常低位的量化中(例如,$INT4$, $NF4$)。张量被划分为更小的块或组(例如,64或128个值的组),并为每个块计算一个单独的 $(S, Z)$ 对。优点: 在这些粒度中提供最高的潜在精度,特别有效于隔离张量内异常值的影响。对于像4位这样的激进量化非常重要。缺点: 最高的元数据开销。$S$ 和 $Z$ 值的存储成本相较于量化权重本身变得更高。量化/反量化期间的计算开销也可能增加。digraph G { rankdir=LR; node [shape=box, style=filled, fontname="Arial", fontsize=10]; edge [fontname="Arial", fontsize=9]; subgraph cluster_tensor { label = "逐张量量化"; bgcolor="#e9ecef"; tensor [label="权重张量\n(例如, 2048x2048)", shape=rect, fillcolor="#a5d8ff", width=2, height=1.5]; scale_z_tensor [label="单个(S, Z)对", shape=ellipse, fillcolor="#ffe066"]; tensor -> scale_z_tensor [label="应用于整个张量"]; } subgraph cluster_channel { label = "逐通道量化"; bgcolor="#e9ecef"; channel_tensor [label="权重张量\n(例如, 2048x2048)", shape=rect, fillcolor="#a5d8ff", width=2, height=1.5]; channel_s_z [label="多个(S, Z)对\n(每个输出通道一个,\n例如, 2048对)", shape=ellipse, fillcolor="#ffd8a8"]; channel_tensor -> channel_s_z [label="逐行应用"]; } subgraph cluster_group { label = "逐组量化"; bgcolor="#e9ecef"; group_tensor [label="权重张量\n(例如, 2048x2048)", shape=rect, fillcolor="#a5d8ff", width=2, height=1.5]; group_s_z [label="许多(S, Z)对\n(每块权重一个,\n例如, 块大小为64时有65536对)", shape=ellipse, fillcolor="#ffc9c9"]; group_tensor -> group_s_z [label="逐块应用"]; } }权重张量量化粒度的比较。逐张量量化使用一对比例/零点,逐通道量化对每行(输出通道)使用一对,逐组量化对张量内的较小块使用一对。与LLM量化的相关性重新审视这些基本原理是必要的,因为LLM的庞大规模和具体特点增强了这些选择的重要性:敏感性: Transformer中庞大的参数数量和复杂的相互作用意味着朴素的逐张量量化通常会导致无法接受的精度下降。逐通道量化通常是权重的基准。异常值: LLM激活值,特别是在LayerNorm之后或注意力机制内部,可能表现出极端的异常值。这些异常值对标准量化校准构成重大挑战,通常需要更细的粒度(如权重的逐组量化)或更高级的技术(如PTQ期间的异常值处理或使用QAT),我们将在稍后讨论。硬件支持: 不同的硬件平台(CPU、GPU、TPU、NPU)对不同的量化方案(对称量化与仿射量化)和粒度具有不同程度的优化支持。高效部署需要理解这种关联。混合精度: 不同LLM组件(例如,注意力与前馈网络)的不同敏感性表明,在整个模型中应用统一的量化策略可能不是最优的。这促成了混合精度方法,即对不同层使用不同的位宽和粒度,这是本章稍后讨论的一个议题。理解这些基本选择、仿射与对称映射以及逐张量、逐通道和逐组粒度之间的权衡,为理解高级的训练后量化(PTQ)、量化感知训练(QAT)和极端量化方法提供了所需的基本框架,这些方法对于有效优化大型语言模型非常重要。我们将在此基础上,分析这些技术如何解决LLM带来的独特挑战。