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

在TinyML的工程落地中,模型量化是连接“训练好的高精度浮点模型”与“低功耗、低资源嵌入式设备”之间的关键桥梁。理解量化,首先需要厘清计算机如何表示数值——尤其是浮点数与定点数的本质差异。浮点数(如FP32)动态范围大、精度高,但计算开销和存储占用也大;而定点数(如INT8)虽然范围和精度受限,却能在MCU、DSP或NPU上实现极低延迟的整数运算。本章将深入剖析定点数与浮点数的表示机制,详细拆解量化参数scale(缩放因子)与zero-point(零点)的数学含义,并对比对称量化与非对称量化的适用场景,为后续PTQ(训练后量化)校准集采集与模型转换打下坚实的理论基础。

量化核心概念关系图 浮点数(FP32) 符号位 + 指数位 + 尾数位 动态范围大,精度高 硬件开销大,功耗高 适合训练与高精度推理 定点数(INT8) 纯整数存储与运算 范围有限,精度损失可控 硬件高效,低功耗低延迟 适合边缘设备部署 量化映射 量化参数 scale(缩放因子):浮点步长 zero-point(零点):浮点0的整数映射 公式:r = scale × (q - zero_point) q = round(r / scale) + zero_point 对称量化 zero_point = 0 范围:[-127, 127] 适合权重(分布对称) 实现简单,硬件友好 非对称量化 zero_point ≠ 0 范围:[0, 255] 或自定义 适合激活值(分布偏斜) 精度更高,需额外存储零点

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

在计算机系统中,数值表示方式直接决定了运算精度、动态范围和硬件资源消耗。浮点数(Floating-Point)采用科学计数法的二进制形式,由符号位、指数位和尾数位组成。以FP32为例,其包含1位符号位、8位指数位和23位尾数位,能够表示从约1.18e-38到3.4e38的极大动态范围,且在小数值附近保持较高精度。这种特性使得浮点数非常适合模型训练阶段——梯度更新需要精细的数值变化,权重在初始化时往往接近零,浮点表示可以避免下溢或溢出问题。

然而,浮点运算在硬件实现上代价高昂。FP32的加法或乘法需要复杂的指数对齐和尾数规格化逻辑,在MCU(如Cortex-M系列)或低端NPU上,浮点单元(FPU)要么缺失,要么运算周期长、功耗高。相比之下,定点数(Fixed-Point)将数值表示为纯整数乘以一个固定的缩放因子,例如INT8表示范围为[-128, 127]的整数。定点运算只需要简单的整数加减乘除,硬件逻辑简单,单周期即可完成,功耗和面积都大幅降低。但定点数的精度和范围受限于位宽,例如INT8的精度约为0.01到0.1(取决于scale),无法直接表示极小或极大的数值。

核心理解:量化本质上是在“精度损失”与“硬件效率”之间做权衡。TinyML部署中,我们通常将FP32的权重和激活值映射到INT8或INT16,利用定点运算的高效性换取推理速度的提升和内存占用的减少。关键在于,这种映射必须尽可能保留原始浮点张量的数值分布特征。

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

量化映射的核心公式为:r = scale × (q - zero_point),其中r是原始浮点实数值,q是量化后的整数值,scale是浮点缩放因子(步长),zero_point是整数零点。反向转换公式为:q = round(r / scale) + zero_point。这里的scale决定了量化分辨率——scale越小,相邻整数之间的浮点间隔越小,精度越高;但过小的scale会导致整数范围溢出。zero_point则用于对齐浮点零值,确保浮点0.0能够精确映射到某个整数,避免在填充(padding)或ReLU操作时产生偏差。

在实际PTQ流程中,scale和zero_point的计算依赖于校准集(Calibration Dataset)的统计特征。通常,我们会收集一批有代表性的输入数据,前向推理得到各层激活值的分布(如最小值、最大值、百分位数),然后根据量化方案确定scale和zero_point。例如,对于非对称量化,若激活值范围为[min, max],则scale = (max - min) / (q_max - q_min),zero_point = q_min - round(min / scale)。对于对称量化,zero_point固定为0,scale = max(|min|, |max|) / q_max。

量化参数 数学定义 作用 典型取值
scale(缩放因子) 浮点步长,表示每个整数单位对应的浮点范围 控制量化分辨率,影响精度与溢出风险 0.001 ~ 0.1(取决于数据分布)
zero_point(零点) 浮点0.0映射到的整数值 对齐浮点零,避免填充/ReLU偏差 0(对称)或 0~255(非对称)
q_min / q_max 量化整数的取值范围 定义整数域边界,如INT8为[-128, 127] -128, 127 或 0, 255

2.3 对称量化与非对称量化的对比与选择

对称量化(Symmetric Quantization)要求浮点零值精确映射到整数零,即zero_point = 0。其量化范围关于原点对称,通常取浮点张量的最大绝对值作为量化边界。这种方案的优势在于实现简单,无需存储zero_point,且卷积运算中的乘加操作可以避免额外的零点修正,硬件实现效率高。但缺点是,如果浮点数据的分布偏向某一侧(例如ReLU后的激活值全为非负),对称量化会浪费一半的整数表示范围,导致精度下降。

非对称量化(Asymmetric Quantization)允许zero_point不为零,能够根据浮点数据的实际最小值和最大值动态调整量化范围。例如,若激活值范围为[2.0, 6.0],非对称量化可以将整个INT8范围[0, 255]用于映射这4.0的区间,scale = (6.0 - 2.0) / 255 ≈ 0.0157,精度远高于对称量化(对称量化需映射[-6.0, 6.0],scale = 12.0 / 255 ≈ 0.047)。因此,对于分布偏斜的激活值,非对称量化通常能获得更好的精度。其代价是需要额外存储和计算zero_point,在卷积运算中需要额外的整数加减操作来修正零点。

工程经验:在TinyML实践中,权重(Weight)通常采用对称量化,因为训练后的权重分布近似于均值为零的高斯分布,对称性较好;而激活值(Activation)由于经过ReLU等操作,往往呈现非负偏斜分布,推荐使用非对称量化。这种混合策略(对称权重 + 非对称激活)是TensorFlow Lite和PyTorch Mobile等框架的默认选择。

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

假设我们有一个简单的Conv2D层,其权重张量W的浮点分布范围为[-1.5, 1.5],激活值A(经过ReLU后)的范围为[0.0, 6.0]。我们计划将权重和激活值都量化为INT8(范围[-128, 127])。

步骤1:计算权重的量化参数(对称量化)
取最大绝对值max_abs = 1.5,则scale_w = 1.5 / 127 ≈ 0.01181。zero_point_w = 0。量化后的权重q_w = round(W / 0.01181)。

步骤2:计算激活值的量化参数(非对称量化)
激活值范围[0.0, 6.0],使用INT8的完整范围[0, 255](注意:这里我们使用无符号INT8,即0~255,或者使用有符号INT8的[-128, 127]但调整zero_point)。为简化,采用无符号INT8:scale_a = (6.0 - 0.0) / 255 ≈ 0.02353,zero_point_a = 0 - round(0.0 / 0.02353) = 0。若使用有符号INT8,则scale_a = 6.0 / 255 ≈ 0.02353,zero_point_a = -128 - round(0.0 / 0.02353) = -128。

步骤3:模拟量化误差
原始浮点激活值A = 3.1415,量化后q_a = round(3.1415 / 0.02353) + 0 = 133,反量化回浮点r_a = 0.02353 × (133 - 0) = 3.1295,误差约为0.012。这个误差在可接受范围内。

步骤4:实际部署中的考量
在MCU上,卷积运算将使用整数乘加:q_out = sum(q_w × q_a) + q_bias。其中偏置bias通常使用INT32存储,以避免累加溢出。最终输出需要乘以scale_w × scale_a进行反量化,再传递给下一层。

风险提示:如果校准集选择不当,导致激活值范围估计过小,实际推理时可能出现超出量化范围的数值,造成“饱和”或“截断”误差。例如,若校准集只覆盖了[0.0, 4.0]的范围,而实际输入产生了5.0的激活值,则5.0会被截断到最大量化值,产生较大误差。因此,校准集必须覆盖典型的输入分布,并保留一定的裕量(通常取99.99%的百分位而非绝对最大值)。

2.5 常见误区与总结复盘

误区一:认为量化只是简单的“除以最大值”。实际上,量化需要综合考虑scale、zero_point、数据分布和硬件支持。例如,某些NPU只支持对称量化,此时即使激活值偏斜,也只能使用对称方案,但可以通过调整校准集或使用per-channel量化来缓解精度损失。

误区二:忽略zero_point在卷积中的影响。在非对称量化中,卷积运算需要额外处理zero_point的修正项。若直接使用对称量化的卷积核,会导致输出偏差。正确做法是:q_out = sum(q_w × q_a) - zero_point_a × sum(q_w) + q_bias,其中sum(q_w)可以预先计算。

误区三:校准集越大越好。校准集过大不仅增加PTQ时间,还可能引入冗余数据导致分布估计偏差。通常,1000~5000张具有代表性的图片或样本即可获得稳定的统计特征。关键在于样本的多样性,而非数量。

总结复盘:本章的核心在于理解量化映射的数学本质——用整数近似浮点,用scale和zero_point控制映射关系。对称量化适合权重,非对称量化适合激活值,这是TinyML部署的黄金法则。在实际PTQ流程中,我们需要根据校准集的统计结果,动态计算每层的量化参数,并通过验证集评估量化误差。如果精度损失过大,可以尝试per-channel量化(对每个输出通道单独计算scale)或混合精度量化(部分层保留FP16)。下一阶段,我们将深入探讨如何采集高质量的校准集,确保量化参数能够真实反映模型在目标场景下的数据分布。