2、量化基础:什么是模型量化、定点数与浮点数、量化参数(scale、zero-point)、对称与非对称量化

在进入 QAT(量化感知训练)实战之前,必须先建立对量化基础概念的清晰认知。很多初学者直接调用 PyTorch 的 torch.quantization 接口,却对 scale、zero-point 的含义一知半解,导致模型精度异常时无从排查。本章将从模型量化的本质出发,逐步拆解定点数与浮点数的差异、量化参数的计算逻辑,以及对称/非对称两种量化策略的适用场景。这些内容不仅是 QAT 的理论基石,更是后续调试量化模型时定位误差来源的关键工具。

量化基础概念关系图 浮点数 (FP32) 符号位 1bit + 指数 8bit + 尾数 23bit 表示范围: ±3.4×10³⁸ 精度: ~7位十进制小数 映射 量化参数 scale (缩放因子) zero-point (零点) q = round(r/scale) + zp r = (q - zp) × scale 决定 量化策略 对称量化: zp = 0 非对称量化: zp ≠ 0 int8 范围: [-128, 127] uint8 范围: [0, 255] 量化输出 (int8) 权重: 对称量化 (权重分布近似零均值) 激活: 非对称量化 (ReLU后全为正) QAT: 在训练中模拟量化误差 推理: 仅整数运算,加速部署

2.1 模型量化的核心原理

模型量化本质上是一种数值精度压缩技术,其目标是将神经网络中高精度的浮点数参数(权重、激活值)转换为低比特的整数表示,从而在保持模型功能基本不变的前提下,显著降低模型的存储体积和推理时的计算开销。以最常见的 INT8 量化为例,一个 FP32 模型(约 4 字节/参数)转换为 INT8(1 字节/参数)后,模型体积直接缩减为原来的 1/4。在推理阶段,INT8 矩阵乘法可以利用硬件提供的 SIMD(单指令多数据)指令集并行执行,吞吐量通常可提升 2~4 倍。

量化的数学本质是一个区间映射问题:我们需要找到一个从浮点数区间 [r_min, r_max] 到整数区间 [q_min, q_max] 的双射关系。这个映射不是简单的截断或取整,而是需要保证映射后的整数能够尽可能精确地还原原始浮点数值。映射过程中必然引入量化误差,QAT 的核心思想就是让模型在训练阶段就"感知"到这种误差,通过反向传播调整权重,使模型对量化误差具有鲁棒性。

从信息论的角度看,量化是一个有损压缩过程。FP32 提供约 23 位二进制精度,而 INT8 仅提供 8 位。这意味着每个数值的表示精度从约 7 位十进制有效数字下降到约 2~3 位。对于神经网络而言,这种精度损失通常是可以接受的,因为网络本身具有一定的容错能力,且权重和激活值的分布往往集中在某个有限区间内,并非均匀分布在整个 FP32 表示范围内。

量化收益与代价的权衡:量化的核心收益是存储压缩(4倍)和计算加速(2~4倍),但代价是精度损失。QAT 的目标就是在训练阶段模拟量化误差,让模型权重适应这种精度限制,从而将精度损失控制在 1%~2% 以内。如果精度损失超过 5%,通常需要检查量化参数设置或考虑混合精度方案。

2.2 定点数与浮点数的本质差异

要理解量化,必须先厘清定点数(Fixed-point)与浮点数(Floating-point)的差异。浮点数(如 FP32)采用科学计数法的二进制形式,由符号位、指数位和尾数位三部分组成。以 FP32 为例,其 32 位被划分为:1 位符号位(S)、8 位指数位(E)和 23 位尾数位(M)。这种设计使得浮点数能够表示极大和极小的数值范围(约 ±3.4×10³⁸),且在整个范围内保持相对精度(约 7 位十进制有效数字)。

定点数则完全不同,它使用固定的二进制位数来表示整数部分和小数部分。例如,Q7.8 定点格式表示 7 位整数位和 8 位小数位,总共 16 位。定点数的表示范围是固定的,由整数位决定;精度也是固定的,由小数位决定。定点数的优势在于硬件实现简单、计算速度快、功耗低,但缺点是动态范围有限,容易溢出或精度不足。

在神经网络中,权重和激活值的分布通常具有以下特征:权重往往集中在零附近的小范围内(如 [-0.1, 0.1]),而激活值经过 ReLU 后通常为非负值(如 [0, 6])。这些分布特征使得我们不需要浮点数那样巨大的动态范围,只需要针对实际分布选择合适的定点格式即可。模型量化正是基于这一观察,将浮点数映射到定点整数,同时通过 scale 和 zero-point 两个参数来适配不同的数值分布。

对比维度 FP32 浮点数 INT8 定点数 量化后的影响
存储空间 4 字节 1 字节 模型体积减少 75%
表示范围 ±3.4×10³⁸ [-128, 127] 或 [0, 255] 需要 scale 映射
精度 ~7 位十进制有效数字 ~2 位十进制有效数字 引入量化误差
计算速度 慢(需浮点运算单元) 快(整数运算) 推理加速 2~4 倍
硬件支持 通用 CPU/GPU 移动端 NPU/DSP 友好 边缘部署可行

2.3 量化参数:scale 与 zero-point 的数学定义

量化参数是连接浮点数和整数的桥梁。给定浮点数 r 和量化后的整数 q,它们之间的转换关系由以下公式定义:

量化(浮点 → 整数):
q = round(r / scale) + zero_point

反量化(整数 → 浮点):
r = (q - zero_point) × scale

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

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

zero_point(零点)是一个整数,表示浮点数值 0 对应的量化整数。它的计算公式为:

zero_point = round(q_min - r_min / scale)

zero_point 的存在是为了处理浮点区间不对称的情况。例如,当激活值经过 ReLU 后全为非负数(如 [0, 6])时,如果直接映射到 [-128, 127] 的 INT8 区间,会浪费一半的表示能力。通过设置合适的 zero_point,可以将 [0, 6] 完整映射到 [0, 255] 的 UINT8 区间,充分利用所有整数编码。

在实际的量化实现中,scale 和 zero_point 的确定有两种主要方法:校准(Calibration)和训练中统计。校准方法在模型训练完成后,使用一小部分验证数据统计激活值的分布范围,从而确定 scale 和 zero_point。而 QAT 则在训练过程中动态统计这些值,并让模型适应量化误差。

经验提醒:在 QAT 中,scale 和 zero_point 通常被当作常量处理,不参与梯度更新。但它们的计算依赖于当前批次的激活值统计,因此需要在训练循环中定期更新。PyTorch 的 FakeQuantize 模块会自动处理这一过程,但理解其内部机制对调试至关重要。

2.4 对称量化与非对称量化

根据 zero_point 是否为零,量化策略分为对称量化和非对称量化两种。

对称量化强制 zero_point = 0,即浮点数的 0 映射到整数的 0。此时量化公式简化为:

q = round(r / scale)
r = q × scale

对称量化的优点是实现简单,不需要额外存储 zero_point,且整数运算时无需考虑零点偏移,计算效率更高。其缺点是当浮点数据分布不对称时(如全为正的激活值),会浪费一部分整数表示范围。对称量化通常适用于权重,因为训练良好的网络权重分布近似于零均值的正态分布。

非对称量化允许 zero_point 不为零,能够更灵活地适配不对称的数据分布。例如,ReLU 后的激活值范围为 [0, 6],使用非对称量化可以将该区间完整映射到 [0, 255],充分利用所有整数编码,从而获得更高的精度。非对称量化的代价是需要额外存储 zero_point,且在推理时需要进行一次额外的加法运算。

对比维度 对称量化 非对称量化
zero_point 恒为 0 可为非零整数
适用场景 权重(分布近似零均值) 激活值(ReLU 后全为正)
表示效率 可能浪费部分整数范围 充分利用整数范围
计算复杂度 低(无需零点偏移) 略高(需处理零点)
精度表现 对不对称分布精度较低 对不对称分布精度较高

2.5 案例拆解:一个卷积层的量化参数计算

为了将上述概念具体化,我们以一个实际场景来演示量化参数的计算过程。假设我们有一个训练好的卷积层,其权重分布经统计为 [-0.15, 0.12],激活值(ReLU 后)分布为 [0.0, 5.8]。我们计划将权重量化为 INT8(对称量化),激活值量化为 UINT8(非对称量化)。

第一步:计算权重的 scale

权重范围: r_min = -0.15, r_max = 0.12
INT8 范围: q_min = -128, q_max = 127
scale_w = (0.12 - (-0.15)) / (127 - (-128)) = 0.27 / 255 ≈ 0.001059
zero_point_w = 0 (对称量化)

第二步:计算激活值的 scale 和 zero_point

激活范围: r_min = 0.0, r_max = 5.8
UINT8 范围: q_min = 0, q_max = 255
scale_a = (5.8 - 0.0) / (255 - 0) = 5.8 / 255 ≈ 0.022745
zero_point_a = round(0 - 0.0 / 0.022745) = 0

在这个例子中,由于激活值的最小值恰好为 0,zero_point 恰好为 0。但如果激活值的最小值为 0.5,则 zero_point 将不为零。例如:

激活范围: r_min = 0.5, r_max = 5.8
scale_a = (5.8 - 0.5) / 255 ≈ 0.020784
zero_point_a = round(0 - 0.5 / 0.020784) = round(-24.06) = -24

第三步:验证量化-反量化误差

以权重值 r = 0.05 为例:

q = round(0.05 / 0.001059) = round(47.21) = 47
r' = 47 × 0.001059 = 0.049773
量化误差 = |0.05 - 0.049773| = 0.000227

这个误差在可接受范围内。如果误差过大,说明 scale 选择不当,或者数据分布中存在极端离群值,需要采用 clipping(截断)策略来牺牲少量范围换取更高的精度。

风险提示:在计算 scale 时,如果直接使用数据的绝对最大值(r_max 和 r_min),可能会被离群值主导,导致大部分数据的量化精度下降。实践中通常采用百分位截断法(如 99.9% 分位数)来排除极端值,或者使用 KL 散度校准方法(TensorRT 采用的方法)来寻找最优截断点。

2.6 常见误区与总结复盘

在理解量化基础时,初学者常犯以下错误,需要特别警惕:

  • 误区一:认为量化就是简单的四舍五入。实际上,量化涉及区间映射、scale 计算和零点偏移,是一个系统性的数值变换过程。简单的四舍五入会导致严重的精度损失。
  • 误区二:对称量化一定比非对称量化差。对于权重这种零均值分布,对称量化不仅精度相当,而且计算效率更高。选择哪种策略取决于数据分布特征,而非一概而论。
  • 误区三:忽略 zero_point 的作用。zero_point 不是可有可无的,它决定了浮点 0 在整数域中的位置。如果 zero_point 计算错误,会导致整个量化映射偏移,产生系统性误差。
  • 误区四:认为量化误差只影响精度。量化误差还会影响模型的数值稳定性,特别是在深层网络中,误差会逐层累积,最终导致输出严重偏离。

总结本章内容,模型量化的核心在于理解浮点到整数的映射关系,掌握 scale 和 zero-point 的计算方法,并根据数据分布特征选择合适的量化策略。对称量化适合权重,非对称量化适合激活值,这是 QAT 中最常见的组合方案。在后续章节中,我们将基于这些基础概念,深入探讨如何在 PyTorch 中实现 QAT 训练流程,以及如何评估和优化量化模型的精度。

本章核心要点回顾:
  1. 量化是将 FP32 映射到 INT8/UINT8 的有损压缩过程,核心收益是存储减少 75%、推理加速 2~4 倍。
  2. scale 决定每个整数单位代表的浮点大小,zero-point 决定浮点 0 对应的整数位置。
  3. 对称量化(zero-point=0)适合权重,非对称量化(zero-point≠0)适合激活值。
  4. QAT 的核心思想是在训练中模拟量化误差,让模型权重适应精度限制。
  5. 计算 scale 时需注意离群值影响,必要时采用百分位截断或 KL 散度校准。