4、量化原理入门:定点与浮点、对称/非对称量化、量化尺度与零点

在RKNN工具链中,模型量化是将FP32权重与激活映射到低比特整数(如INT8、INT16)的核心过程。理解量化原理是进行精度调优的前提——只有清楚量化误差从何而来,才能准确判断是数据分布问题、校准集问题还是量化参数设置问题。本章从数值表示基础出发,系统讲解定点与浮点差异、对称/非对称量化机制、量化尺度(Scale)与零点(Zero Point)的计算方法,并结合RKNN实际部署场景给出可操作的判断路径。

量化映射关系与参数计算 FP32 浮点域 r ∈ [r_min, r_max] 连续实数 动态范围大 精度高但存储/计算开销大 量化映射公式 q = round(r / scale) + zero_point r = (q - zero_point) × scale scale: 量化尺度 | zero_point: 零点 INT8 整数域 q ∈ [q_min, q_max] 离散整数 INT8: [-128, 127] UINT8: [0, 255] 对称量化 (Symmetric) zero_point = 0,正负范围对称 scale = max(|r_min|, |r_max|) / 127 适合权重(分布近似对称) 非对称量化 (Asymmetric) zero_point ≠ 0,可偏移 scale = (r_max - r_min) / 255 适合激活(ReLU后非负分布) 收集数据分布 校准集前向统计 计算scale/zp 根据量化类型选择公式 量化与反量化 前向/反向传播处理 精度评估与调优 对比FP32基线

4.1 定点与浮点的本质差异

核心原理:浮点数(FP32)用符号位、指数位和尾数位表示实数,动态范围可达约1e-38到3.4e38,但存储开销大(4字节),且NPU中浮点运算单元面积大、功耗高。定点数(如INT8)仅用1字节表示整数,动态范围有限(-128到127),但硬件实现简单、吞吐量高。RKNN NPU采用“权重INT8+激活INT8”的混合定点方案,将浮点运算转换为整数乘加运算,再通过Scale还原为浮点结果。

判断方法:在RKNN模型转换时,可通过rknn.config(quantized_dtype='int8')指定量化类型。若模型包含大量动态范围极大的层(如检测头),可考虑INT16量化(quantized_dtype='int16')以保留更多信息,但NPU算力会下降约一半。

对比维度FP32INT8INT16
存储占用4字节1字节2字节
动态范围±3.4e38-128~127-32768~32767
NPU算力基准约4倍约2倍
典型应用训练/精度基准推理部署高精度推理

4.2 对称量化与非对称量化

核心原理:对称量化强制零点(Zero Point)为0,即浮点0映射到整数0,量化公式简化为q = round(r / scale)。非对称量化允许零点偏移,浮点0可映射到任意整数,公式为q = round(r / scale) + zero_point。对称量化实现简单、推理时无需额外减法操作,但对非对称分布(如ReLU后的激活值全为正)会造成量化范围浪费;非对称量化能充分利用整数范围,但需要额外存储和计算零点。

判断方法:在RKNN中,权重默认使用对称量化(因为权重分布通常近似对称),激活默认使用非对称量化(因为ReLU输出非负)。可通过rknn.config(quantized_algorithm='normal')或'min_max'调整量化算法,但一般不建议手动修改默认策略。

实战场景:以YOLOv5s检测模型为例,其Backbone中Conv层的权重分布近似高斯对称,使用对称量化后Scale=0.0234;而检测头前的激活值经ReLU后全为正,若用对称量化,负半轴127个整数区间全部浪费,实际有效分辨率仅剩一半。改用非对称量化后,Scale=0.0117,量化精度提升一倍,mAP@0.5从0.721提升至0.735。

4.3 量化尺度(Scale)与零点(Zero Point)计算

核心原理:Scale表示每个整数步长代表的浮点大小,Zero Point表示浮点0对应的整数。对于INT8非对称量化:scale = (r_max - r_min) / 255,zero_point = round(-r_min / scale),且需裁剪到[0, 255]范围。对称量化时:scale = max(|r_min|, |r_max|) / 127,zero_point = 0。

操作流程:

  1. 使用校准集(通常500-1000张代表性图片)前向推理,统计每层激活值的r_min和r_max。
  2. 根据量化类型选择公式计算scale和zero_point。
  3. 对超出范围的浮点值进行截断(Clipping),而非直接溢出。
  4. 在RKNN转换时,工具自动完成上述过程,但可通过dataset.txt指定校准集路径。
# RKNN量化配置示例
from rknn.api import RKNN
rknn = RKNN()
rknn.config(mean_values=[[0,0,0]], std_values=[[255,255,255]], 
            quantized_dtype='int8', quantized_algorithm='normal')
rknn.load_rknn('./model.rknn')
rknn.init_runtime(target='rk3588')
# 校准集用于统计激活分布
rknn.accuracy_analysis(inputs=['./calib_001.jpg', './calib_002.jpg'], output_dir='./snapshot')

4.4 量化误差来源与精度优化策略

核心原理:量化误差主要来自三方面:舍入误差(round操作不可避免)、截断误差(超出量化范围的值被强制裁剪)、分布失配(校准集与真实数据分布不一致)。在RKNN中,可通过以下手段降低误差:

  • 优化校准集:确保校准集覆盖各类典型场景,避免单一光照/角度。
  • 混合量化:对敏感层(如检测头、Softmax前)保留FP16或INT16。
  • 量化感知训练(QAT):在训练时模拟量化误差,使权重适应量化噪声。
  • 逐通道量化:对权重按输出通道分别计算scale,提升精度但增加存储。

常见误区:部分开发者认为校准集越多越好,实际上超过2000张图片后精度提升趋于饱和,反而增加转换时间。更关键的是校准集的多样性而非数量。另外,不要直接使用训练集做校准,否则模型对训练数据过拟合,推理时泛化精度下降。

4.5 案例拆解:RK3588上INT8量化精度调优实战

背景与约束:某安防项目需在RK3588上部署人脸关键点检测模型(FP32基线mNME=3.82%),要求INT8量化后mNME<4.5%,推理延迟<8ms。初始直接转换后mNME=5.67%,超出指标。

分析过程:通过rknn.accuracy_analysis()输出逐层精度对比,发现第12层(关键点回归头前的卷积层)余弦相似度仅0.82,明显低于其他层(0.95+)。进一步检查该层激活分布,发现r_min=-0.02,r_max=12.8,分布严重右偏。

操作步骤:

  1. 将校准集从500张增至1200张,并加入夜间红外样本,使分布覆盖更广。
  2. 对第12层启用混合量化:rknn.config(quantized_dtype='int8', custom_quantize_layers=['layer12:int16'])。
  3. 使用QAT微调模型,训练50个epoch,学习率降低至1e-4。

结果对比:优化后mNME=4.12%,延迟7.2ms,满足指标。代价是模型文件增大8%(INT16层),且QAT训练耗时2小时。

复盘经验:量化调优应遵循“先诊断、后优化”原则,优先定位异常层,而非盲目全局调整。混合量化是精度与性能的折中利器,但需控制INT16层数量不超过总层数的10%。

经验提醒:在RKNN工具链中,rknn.accuracy_analysis()输出的逐层余弦相似度是定位量化敏感层的核心指标。若某层相似度低于0.9,优先考虑该层是否可跳过量化(do_quantization=False)或改用INT16。同时,建议在转换前先运行rknn.list_support()确认算子支持情况,避免因算子不支持导致意外量化路径。