4. 量化核心原理

TinyML 模型量化,本质上是在精度与资源之间做一次“翻译”。深度学习模型在训练时通常使用 32 位浮点数(FP32)来存储权重和激活值,但在微控制器、边缘传感器等资源受限设备上,FP32 的存储和计算开销往往难以承受。量化就是将 FP32 的连续数值映射到低比特离散数值(如 INT8、INT16)的过程。要理解这一过程,必须先掌握三个核心概念:定点与浮点表示、量化尺度(Scale)与零点(Zero Point)、对称与非对称量化。这三者共同决定了量化误差的来源、大小以及部署后的实际表现。

定点表示 浮点表示 量化映射 整数 + 固定小数位 例:Q7.8 → 1符号位+7整数+8小数 范围有限,精度均匀 硬件友好,计算快 适合MCU/FPGA 符号位 + 指数 + 尾数 例:FP32 → 1+8+23 位 动态范围大,精度高 硬件开销大,功耗高 适合训练/服务器 r = S × (q - Z) S:尺度(Scale) Z:零点(Zero Point) 对称:Z=0,非对称:Z≠0 核心:误差最小化 FP32 权重/激活 计算 S 和 Z INT8 量化张量 部署推理 量化核心公式:r = S × (q - Z),其中 r 为真实浮点值,q 为量化整数值 对称量化 Z=0,非对称量化 Z≠0,两者影响量化精度与计算复杂度 量化误差来源:舍入误差、截断误差、范围溢出 误差控制策略:校准数据集、MinMax/Percentile/Entropy 方法

上图展示了从浮点表示到定点/量化表示的完整映射关系。在 TinyML 部署中,我们通常将 FP32 模型转换为 INT8 或 INT16 模型,以换取 4 倍到 8 倍的存储压缩和更快的推理速度。但这一转换并非无损,理解量化尺度与零点的数学含义,是控制精度损失的第一步。

4.1 定点与浮点表示:从数学本质到硬件代价

浮点表示(Floating Point) 采用科学计数法的二进制形式,由符号位、指数位和尾数位组成。以 FP32 为例,其数值范围约为 ±3.4×10³⁸,精度可达 7 位有效十进制数字。这种表示方式动态范围极大,非常适合训练阶段梯度传播和参数更新的需求。然而,FP32 运算需要专门的浮点单元(FPU),在 ARM Cortex-M 系列等低功耗 MCU 上,FPU 要么不存在,要么频率受限,导致计算延迟和功耗显著上升。

定点表示(Fixed Point) 则使用整数配合固定的小数点位置来表示数值。例如 Q7.8 格式表示 1 位符号、7 位整数、8 位小数,其范围有限但精度均匀。定点运算只需要整数乘法器和移位操作,在硬件上极其高效。TinyML 中常用的 INT8 量化,本质上就是一种特殊的定点表示——将浮点数值映射到 [-128, 127] 的整数区间。

关键对比:FP32 适合训练,INT8 适合推理。在 MCU 上,INT8 矩阵乘法的速度通常比 FP32 快 4~8 倍,内存占用减少 75%,功耗降低 5~10 倍。但代价是精度损失,尤其是对分布极不均匀的权重或激活值,量化误差可能被放大。

4.2 量化尺度与零点:数学映射的核心

量化过程可以抽象为一个仿射变换。给定浮点张量 r,量化后的整数值 q 满足:

r = S × (q - Z)

其中:

  • S(Scale,尺度):浮点数值与整数步长的比例关系,计算公式为 S = (r_max - r_min) / (q_max - q_min)。S 是一个浮点数,决定了量化的分辨率。
  • Z(Zero Point,零点):浮点数值 0 对应的量化整数值。Z 的作用是保证浮点 0 在量化后能被精确表示,避免因偏移产生误差。

实际量化时,我们首先统计张量的最小值 r_min 和最大值 r_max,然后计算 S 和 Z,最后将每个浮点值映射到最近的整数:

q = round(r / S + Z)

反量化(Dequantization)则使用 r = S × (q - Z) 恢复近似浮点值。这一过程在推理时通常被融合到算子内部,以减少额外开销。

4.3 对称与非对称量化:适用场景与精度权衡

对称量化(Symmetric Quantization) 将浮点范围映射到以 0 为中心的整数区间,即 Z = 0。此时 q = round(r / S),实现简单,无需存储零点。对称量化适合权重分布近似对称的层(如经过 BatchNorm 后的卷积层),且 INT8 的符号位天然支持负值。但若激活值分布偏向正数(如 ReLU 输出),对称量化会浪费一半的量化范围,导致精度下降。

非对称量化(Asymmetric Quantization) 允许 Z ≠ 0,将浮点范围 [r_min, r_max] 完整映射到 [q_min, q_max]。对于 ReLU 等非负激活函数,非对称量化能充分利用全部整数范围,显著降低量化误差。代价是需要额外存储和计算 Z,且推理时需多一次减法操作。

对比维度 对称量化 非对称量化
零点 Z 恒为 0 非 0,需计算和存储
计算复杂度 低(无减法) 中(多一次减法)
适用张量 权重(分布近似对称) 激活值(ReLU 后非负)
精度表现 对偏态分布误差较大 对偏态分布更友好
硬件支持 多数 NPU/加速器原生支持 部分硬件需额外指令

在实际工程中,权重通常使用对称量化,激活值通常使用非对称量化。这是因为权重经过训练后分布相对稳定且近似高斯分布,而激活值受 ReLU 等非线性函数影响,往往呈现明显的单侧分布。

4.4 案例拆解:一个语音唤醒词的 INT8 量化实战

假设我们有一个基于 DS-CNN 的语音唤醒词模型,输入为 49 帧 Mel 频谱特征,模型大小 450KB,在 Cortex-M4 上 FP32 推理延迟为 320ms。目标是将模型量化到 INT8 并部署到资源更紧张的 Cortex-M0+ 上。

初始现象:直接使用 TensorFlow Lite 默认的量化配置(对称权重、非对称激活),模型精度从 96.2% 下降到 91.5%,误唤醒率上升了 3 倍。用户无法接受。

分析过程:

  1. 首先检查各层的权重分布。通过直方图工具发现,第一层卷积权重存在少量极端值(outliers),导致 S 值偏大,量化步长粗糙。
  2. 检查激活值分布。发现中间层的激活值范围在 [-0.5, 6.8] 之间,非对称量化虽然比对称量化好,但仍有部分通道的分布差异极大。
  3. 进一步定位到 BatchNorm 层融合后的缩放因子影响了激活分布。

优化步骤:

  • 对第一层权重采用 Per-Channel 量化(每个输出通道独立计算 S 和 Z),而非默认的 Per-Tensor 量化。这一步将精度恢复到 94.8%。
  • 对激活值使用 Percentile 校准(取 99.99% 分位数作为 r_max),排除极端噪声值的影响。精度提升至 95.6%。
  • 将最后一层全连接层保留为 FP32(混合精度量化),因为该层参数少但对最终分类置信度影响大。最终精度达到 96.0%,仅损失 0.2%。

结果对比:

配置 模型大小 推理延迟 精度
FP32 原始 450KB 320ms 96.2%
默认 INT8 113KB 85ms 91.5%
优化 INT8 118KB 88ms 96.0%

风险与代价:Per-Channel 量化增加了约 4KB 的元数据存储,且部分底层算子(如 CMSIS-NN)对 Per-Channel 支持不完善,需要手动实现。混合精度量化导致推理流程中需要切换数据类型,增加了约 3ms 的调度开销。

复盘经验:量化不是“一键开启”的魔法。精度下降时,优先检查权重分布是否有极端值,其次检查激活值是否充分利用了量化范围。Per-Channel 量化是解决权重分布不均的利器,而 Percentile 校准是处理激活值噪声的标准方案。

4.5 常见误区与总结复盘

误区一:认为量化尺度 S 越小越好。实际上,S 过小会导致量化范围不足,大数值被截断(Clipping),产生严重溢出误差。S 的选择需要在分辨率和范围之间平衡。

误区二:忽略零点 Z 的作用。在对称量化中 Z=0 简化了计算,但若强行对非负激活值使用对称量化,会浪费一半的整数编码空间,导致有效分辨率减半。

误区三:对所有层使用相同的量化配置。不同层对量化的敏感度差异巨大。第一层和最后一层通常对精度影响最大,应优先考虑更高比特或 Per-Channel 量化。

误区四:校准数据集选择不当。校准集应覆盖真实部署场景的输入分布。若只用干净语音校准,遇到噪声环境时激活值分布偏移,量化误差会急剧增大。

工程提醒:在 MCU 上部署时,务必验证目标硬件是否支持非对称量化的运行时计算。部分低端 DSP 或 NPU 只支持对称量化,此时需要将非对称量化转换为对称量化(通过调整输入偏移),否则推理结果会出错。

总结而言,定点与浮点表示决定了硬件计算的效率边界,量化尺度与零点构成了浮点到整数的数学桥梁,对称与非对称量化则提供了针对不同张量分布的精度优化手段。掌握这三者的原理,是进行后续精度调优、误差分析和部署验证的基石。在实际项目中,建议按照“先默认配置跑通,再逐层分析分布,最后针对性优化”的流程推进,避免盲目追求低比特而牺牲模型可用性。