2、量化基础:定点与浮点表示、量化参数(scale/zero-point)、对称与非对称量化

TinyML 模型在部署到 MCU、DSP 或 NPU 等资源受限设备时,几乎无一例外地需要将训练好的 32 位浮点(FP32)权重和激活值转换为低比特整数表示(如 INT8、INT16)。这一转换过程称为量化(Quantization),而 PTQ(训练后量化)则是其中工程代价最低、落地最广的路径。要正确实施 PTQ 并完成功能验证,首先必须透彻理解量化背后的数学本质:浮点与定点如何映射、scale 与 zero-point 如何决定映射关系、对称与非对称量化各自适用何种场景。本章将围绕这三个核心问题展开,为后续的校准、误差分析和功能验证打下坚实的理论基础。

量化映射关系总览:从 FP32 到 INT8 浮点域 (FP32) 实数范围: [r_min, r_max] 权重/激活/偏置 连续分布 量化 Q = round(r/scale) + zero_point 量化参数 scale (缩放因子) zero_point (零点) bit-width (位宽) 对称/非对称 反量化 r = (Q - zero_point) × scale 整数域 (INT8) 离散范围: [-128, 127] 或 [0, 255] 硬件高效计算 内存占用减少 4× 对称量化 zero_point = 0 范围: [-127, 127] (INT8) 适合: 权重 (分布近似对称) 实现简单, 推理速度快 缺点: 非对称分布时精度损失 非对称量化 zero_point ≠ 0 范围: [0, 255] (INT8) 适合: 激活 (ReLU后非负) 精度更高, 动态范围利用充分 缺点: 计算稍复杂

2.1 定点与浮点表示:为什么 TinyML 必须转向整数运算

浮点数(FP32)由符号位、指数位和尾数位组成,能够以极高的动态范围表达实数,但其硬件实现需要专门的 FPU(浮点运算单元),且单次乘加运算的功耗和面积远高于整数运算。在 Cortex-M 系列 MCU 上,FP32 乘法通常需要 3-5 个时钟周期,而 INT8 乘法仅需 1 个周期,且功耗降低约 10 倍。定点数则通过固定的小数点位置,用整数表示实数,其运算完全由 ALU 完成,无需额外硬件支持。

在 TinyML 场景中,模型推理的瓶颈往往不是算力而是内存带宽和功耗。一个 100 万参数的模型,FP32 存储需要 4MB,而 INT8 仅需 1MB,这意味着 Flash 占用减少 75%,SRAM 占用同样大幅降低。更重要的是,绝大多数 MCU 不具备 FPU(如 Cortex-M0+),若强行运行浮点推理,只能通过软件模拟,速度慢 20-50 倍。因此,将模型转换为定点或整数表示,是 TinyML 落地的先决条件。

定点表示的核心思想是:用整数配合缩放因子(scale)来表达实数。例如,定点数 Q7.8 表示 7 位整数位、8 位小数位,其精度为 2⁻⁸ = 0.00390625。但现代量化框架(如 TFLite Micro、CMSIS-NN)更倾向于使用统一的整数表示 + 浮点 scale 的组合,即"伪定点"方式——存储和计算用整数,但 scale 以浮点形式保存,在反量化或融合运算时使用。

关键认知:量化不是简单的"四舍五入",而是建立浮点域到整数域的仿射映射。映射关系由 scale 和 zero_point 两个参数唯一确定,它们共同决定了量化的精度和动态范围。

2.2 量化参数:scale 与 zero-point 的数学定义与计算

量化映射的数学表达式为:

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

其中,scale(缩放因子)是一个正浮点数,表示整数每变化 1 个单位对应的浮点步长;zero_point(零点)是一个整数,表示浮点值 0.0 对应的量化值。这两个参数的计算方法如下:

  1. 确定浮点范围:统计张量(权重或激活)的最小值 r_min 和最大值 r_max。对于权重,通常直接取训练后的实际 min/max;对于激活,则需要通过校准数据集(Calibration Dataset)统计得出。
  2. 计算 scale:scale = (r_max - r_min) / (q_max - q_min),其中 q_max 和 q_min 是量化后的整数范围。对于 INT8 非对称量化,q_min=0, q_max=255;对于对称量化,q_min=-127, q_max=127(或 -128~127,取决于实现)。
  3. 计算 zero_point:zero_point = round(q_min - r_min / scale),并需要裁剪到 [q_min, q_max] 范围内。

以实际案例说明:假设某层激活值经校准后统计得到 r_min = -2.5,r_max = 5.5,采用非对称 INT8 量化(0~255):

scale = (5.5 - (-2.5)) / (255 - 0) = 8.0 / 255 ≈ 0.0313725
zero_point = round(0 - (-2.5) / 0.0313725) = round(79.69) = 80

验证:浮点值 0.0 对应的量化值为 80,反量化时 (80 - 80) × 0.0313725 = 0.0,映射正确。浮点值 5.5 对应 round(5.5/0.0313725) + 80 = round(175.3) + 80 = 255,达到最大值边界。

参数符号作用典型值/范围注意事项
缩放因子scale确定量化步长正浮点数,通常为 2⁻¹⁰ ~ 2⁻³过小导致溢出,过大量化误差增大
零点zero_point对齐浮点 0 值INT8 范围 [-128, 127] 或 [0, 255]非对称量化时通常非零
位宽bit-width决定量化精度8/16/4 位位宽越低,精度损失越大
量化范围[q_min, q_max]整数表示边界INT8: [-128,127] 或 [0,255]对称量化通常用 [-127,127] 避免溢出

2.3 对称量化与非对称量化:原理、对比与选型

对称量化强制 zero_point = 0,即浮点 0 映射到整数 0。此时映射简化为 q = round(r / scale),反量化为 r = q × scale。对称量化的优势在于:

  • 计算路径更短,无需额外的零点减法操作,在硬件上可减少一次加法运算。
  • 对于权重张量(其分布通常近似关于 0 对称),对称量化不会浪费量化范围。
  • 卷积运算中的乘加累加可以完全使用整数运算,无需处理零点偏移。

但对称量化的缺点是:若浮点分布明显不对称(如 ReLU 后的激活值全为非负),则量化范围会有一半被浪费。例如,激活值范围 [0, 6.0],若采用对称量化,则实际映射范围是 [-6.0, 6.0],但负半轴完全未使用,导致有效精度减半。

非对称量化允许 zero_point ≠ 0,能够将浮点范围 [r_min, r_max] 完整映射到整数范围 [q_min, q_max],充分利用所有量化级别。对于激活值(尤其是经过 ReLU 后的非负分布),非对称量化通常能获得更低的量化误差。其代价是:

  • 推理时需要额外处理 zero_point 的偏移,增加少量计算开销。
  • 在卷积层融合时,zero_point 需要被纳入偏置项,增加了实现复杂度。

工程实践中的选型原则:权重优先使用对称量化,激活优先使用非对称量化。这是因为权重分布通常对称,而激活分布往往偏置。TFLite Micro 默认采用这种混合策略,在精度和计算效率之间取得最佳平衡。

经验提示:在 PTQ 功能验证阶段,如果发现量化后模型精度下降超过 2%,首先检查激活值的量化方式。将激活从对称改为非对称,往往能恢复大部分精度损失。对于权重,若分布严重不对称(如某些深度可分离卷积),也可尝试非对称量化。

2.4 案例拆解:一个 Conv2D 层的量化参数计算与验证

假设我们有一个 3×3 的卷积层,权重张量(4 个输出通道,每个通道 3×3×3)的统计值如下:w_min = -0.85,w_max = 0.92。激活值(输入特征图)经校准统计为 a_min = 0.0,a_max = 7.5。我们采用权重对称 INT8、激活非对称 INT8 的混合方案。

步骤 1:计算权重量化参数(对称)

scale_w = max(|w_min|, |w_max|) / 127 = 0.92 / 127 ≈ 0.007244
zero_point_w = 0
量化权重: q_w = round(w / 0.007244)

步骤 2:计算激活量化参数(非对称)

scale_a = (7.5 - 0.0) / 255 = 0.029412
zero_point_a = round(0 - 0.0 / 0.029412) = 0
量化激活: q_a = round(a / 0.029412) + 0

注意:由于激活最小值为 0,zero_point 恰好为 0,此时非对称退化为对称。但若激活范围为 [1.2, 7.5],则 zero_point_a = round(0 - 1.2/0.029412) = round(-40.8) = -41,非对称特性显现。

步骤 3:卷积计算的整数实现

浮点卷积:y = Σ (w × a) + b。量化后,整数卷积为:

q_y = zero_point_y + scale_w * scale_a / scale_y × (Σ (q_w × q_a) - zero_point_a × Σ q_w + q_b)

其中 q_b 是偏置的量化值,scale_y 是输出激活的 scale。实际部署时,scale_w * scale_a / scale_y 会被预先计算为一个浮点乘数,并在累加后一次性缩放。

步骤 4:功能验证要点

  • 验证 zero_point 是否在合法范围内(INT8 为 [-128, 127]),若超出则裁剪。
  • 验证反量化后的浮点值与原始浮点值的最大绝对误差(MAE)和均方根误差(RMSE)。
  • 使用随机输入数据,对比量化模型与浮点模型的输出差异,确认误差在可接受范围(通常 < 1% 相对误差)。
风险警示:在计算 zero_point 时,若 r_min / scale 恰好接近整数边界,round 操作可能产生 1 的偏差。这种偏差在深层网络中会被逐层放大。建议在验证时检查每一层的 zero_point 是否与理论值一致,并观察激活值是否出现系统性偏移。

2.5 常见误区与总结复盘

误区 1:认为对称量化一定比非对称量化精度差。实际上,对于权重这种对称分布,对称量化反而更优,因为它避免了 zero_point 引入的额外噪声。选型应基于数据分布,而非一概而论。

误区 2:忽略 scale 的溢出风险。当浮点范围过大而位宽较小时,scale 会变得很大,导致量化步长粗糙,小数值被截断为 0。例如,若权重范围 [-100, 100] 用 INT8 对称量化,scale = 100/127 ≈ 0.787,则小于 0.39 的权重全部变为 0,精度严重受损。此时应考虑使用 INT16 或进行权重裁剪。

误区 3:zero_point 计算后不检查范围。在极端情况下(如 r_min 远大于 0),zero_point 可能超出 [q_min, q_max]。例如,r_min = 10.0,r_max = 20.0,scale = 10/255 ≈ 0.0392,zero_point = round(0 - 10/0.0392) = round(-255.1) = -255,超出 INT8 范围。此时必须裁剪到 -128,但会导致部分浮点值无法映射,需要重新调整 r_min 或使用更大的位宽。

总结复盘:量化参数的正确性是 PTQ 功能验证的第一道关卡。在编写验证脚本时,应至少包含以下检查项:

  1. 所有 scale 必须为正浮点数,且非 NaN/Inf。
  2. 所有 zero_point 必须在量化范围内。
  3. 反量化后的张量与原始张量的形状、数值范围一致。
  4. 对每个量化层,计算量化误差(MAE/RMSE),并设定阈值(如 MAE < 0.01 × 原始范围)。
  5. 端到端验证:使用至少 100 张校准图片,对比量化模型与浮点模型的输出 logits 或 softmax 概率分布。

只有通过上述验证,才能确保量化后的模型在目标硬件上具备可靠的功能正确性。下一阶段将进入校准数据集的选择与 PTQ 流程设计,但在此之前,请务必确保对 scale 和 zero_point 的理解无任何偏差。