量化基础:量化定义、量化类型与量化参数

2.1 什么是量化:从连续域到离散域的映射

在深度学习推理中,模型权重和激活值通常以 FP32(32位浮点数)存储。FP32 能表达约 10⁻⁴⁵ 到 10³⁸ 的动态范围,精度极高,但代价是内存占用大、计算吞吐低。vLLM 等推理引擎在服务大模型时,显存带宽往往比算力更早成为瓶颈——每个 token 生成都需要读取全部权重,权重位宽直接决定显存读取量。

量化的本质是:用低精度整数(如 INT8、INT4)近似表示高精度浮点数,同时尽量保留原始数值的分布信息。数学上,这是一个从实数域 ℝ 到有限整数集合 ℤ 的映射:

量化:x_q = clamp(round(x / scale) + zero_point, q_min, q_max)
反量化:x ≈ (x_q - zero_point) × scale

其中 scale 控制映射的步长(浮点数),zero_point 控制映射的偏移(整数),q_min/q_max 是目标整数类型的表示范围(如 INT8 为 [-128, 127])。量化过程必然引入舍入误差和截断误差,但大模型权重通常呈现近似正态分布,绝大多数值集中在均值附近,因此合理的量化方案可以将精度损失控制在可接受范围内。

关键认知:量化不是“压缩”而是“重新编码”。压缩(如 ZIP)在解码后能无损还原,而量化是有损的——信息在映射到离散整数的瞬间已经丢失,反量化无法完全恢复原始值。
FP32 → INT8 量化映射示意 FP32 权重分布 μ ≈ 0 -σ +σ scale + zero_point INT8 量化后 -128 0 127 舍入误差 ≈ ±scale/2 截断误差(超出范围)

2.2 量化类型:PTQ 与 QAT

根据量化过程发生在训练前还是训练后,量化分为两大类:训练后量化(PTQ, Post-Training Quantization)和量化感知训练(QAT, Quantization-Aware Training)。两者在精度、成本和适用场景上有显著差异。

对比维度 PTQ(训练后量化) QAT(量化感知训练)
是否需要训练 不需要,直接对预训练模型量化 需要,在训练/微调过程中模拟量化
所需数据 少量校准数据(几百张样本) 完整训练数据集
精度损失 较大(尤其低比特时) 较小,可恢复大部分精度
计算成本 分钟级~小时级 需要完整训练周期
适用场景 快速部署、无训练资源 精度敏感、长期部署

PTQ 流程

  1. 校准(Calibration):从训练集或真实推理数据中采样少量代表性样本,前向传播收集每层激活值的分布统计(min/max、百分位数、KL散度等)。
  2. 计算量化参数:根据收集的统计量,为每个张量(权重或激活)计算最优的 scale 和 zero_point。
  3. 量化与验证:将 FP32 权重转换为 INT8/INT4,在验证集上评估精度,必要时调整校准策略。

QAT 流程

  1. 在模型中插入伪量化节点(fake-quant),前向传播时模拟量化-反量化过程。
  2. 反向传播时使用直通估计器(STE, Straight-Through Estimator),将舍入操作的梯度近似为恒等映射,使梯度能够正常回传。
  3. 训练若干 epoch 后,模型权重逐渐适应量化噪声,最终移除伪量化节点,导出真正的低精度模型。
vLLM 中的实践:vLLM 主要采用 PTQ 路线,因为大模型(如 Llama-70B)重新训练成本极高。vLLM 支持通过 quantization 参数指定量化方法(如 awq、gptq、fp8),这些方法本质上都是 PTQ 的变体,区别在于校准策略和量化粒度(per-tensor / per-channel / per-group)。

2.3 量化参数:scale 与 zero-point 的深入剖析

scale 和 zero_point 是量化的两个核心参数,它们共同定义了浮点数到整数的仿射变换。理解这两个参数是掌握所有量化算法的基础。

scale(缩放因子)

scale 是一个正浮点数,表示整数中每个单位对应的浮点数值大小。计算公式为:

scale = (r_max - r_min) / (q_max - q_min)

其中 r_max 和 r_min 是原始浮点数据的最大值和最小值(或根据分布截断后的边界),q_max 和 q_min 是量化后整数的取值范围。scale 越小,量化精度越高,但能表示的浮点范围也越小。

zero-point(零点偏移)

zero_point 是一个整数,表示浮点数值 0 映射到哪个整数。它的作用是确保浮点 0 能被精确表示——这在神经网络中至关重要,因为 padding、ReLU 输出等大量场景都涉及 0 值。计算公式为:

zero_point = clamp(round(-r_min / scale) + q_min, q_min, q_max)

对于对称量化(symmetric),zero_point = 0,即浮点 0 映射到整数 0;对于非对称量化(asymmetric),zero_point 通常不为 0,可以更充分地利用整数表示范围。

量化粒度

量化参数可以按不同粒度计算,粒度越细,精度越高,但计算开销也越大:

  • Per-tensor:整个张量共享一组 scale/zero_point,最简单但精度最低。
  • Per-channel:每个输出通道独立计算参数,常用于权重量化,精度显著提升。
  • Per-group:将通道进一步分组(如每 128 个元素一组),是 GPTQ、AWQ 等主流方法的默认选择。
# 伪代码:per-group 量化参数计算
for group in groups:
    r_min = min(group)
    r_max = max(group)
    scale[group] = (r_max - r_min) / (q_max - q_min)
    zero_point[group] = round(-r_min / scale[group]) + q_min

2.4 案例:以 Llama-7B 为例的 INT8 PTQ 实践

假设我们要对 Llama-7B 进行 INT8 权重量化,使用 per-channel 粒度。具体步骤如下:

  1. 加载模型:使用 HuggingFace Transformers 加载 FP32 预训练权重。
  2. 校准数据收集:从 C4 数据集中随机采样 128 条文本,每条截断为 512 token。
  3. 逐层量化:对每个 Linear 层的权重矩阵,按输出通道计算 scale 和 zero_point。
  4. 替换算子:将 FP32 的 torch.nn.Linear 替换为支持 INT8 的量化版本(如 bitsandbytes 的 Linear8bitLt)。
  5. 验证精度:在 WikiText-2 上计算困惑度(perplexity),对比量化前后差异。
常见误区:很多人认为“量化就是简单地把 FP32 除以 255 映射到 INT8”。实际上,校准数据的质量对量化精度影响极大。如果校准数据分布与真实推理数据差异过大,会导致 scale 估计偏差,出现“校准集上精度正常、真实场景精度骤降”的问题。此外,激活值量化比权重量化更难,因为激活值分布随输入动态变化,需要更精细的校准策略(如动态量化、混合精度)。

2.5 复盘:量化方案选择的决策要点

在实际项目中,选择量化方案需要综合考虑以下因素:

  • 精度敏感度:如果模型用于医疗、金融等对错误容忍度极低的场景,优先选择 QAT 或高比特量化(FP8/INT8);如果只是普通对话场景,INT4 配合 GPTQ/AWQ 通常足够。
  • 硬件支持:不同 GPU 对低精度计算的支持不同。例如,A100 支持 INT8 Tensor Core,但 INT4 需要依赖反量化到 INT8 再计算;H100 则原生支持 FP8。
  • 推理引擎兼容性:vLLM 对 GPTQ、AWQ、FP8 等有专门优化,而某些量化格式可能需要额外的 kernel 支持,否则无法发挥加速效果。
  • 部署成本:QAT 需要训练资源和时间,PTQ 则几乎零成本。对于快速迭代的项目,PTQ 是首选。