系列导读:上一篇打通了三大框架到 RKNN 的转换路径,过程中我们一直在用 do_quantization=True 做量化,但量化到底做了什么、为什么会掉精度、出问题怎么排查——这些细节这一篇彻底讲清楚。量化是 NPU 推理性能的核心杠杆,理解原理才能在精度和性能之间做出正确取舍。


一、为什么要量化:一笔简单的算力账

RK3588S 的 NPU 算力规格写的是 6 TOPS INT8,这个数字的含义是:NPU 硬件电路是按 INT8(8位整数)运算设计的,FP16/FP32 浮点运算需要额外的转换开销,实际等效算力会打折扣。

数据类型 单次乘加运算占用晶体管 RK3588S 等效算力 内存占用(vs FP32)
FP32 最多 不直接支持,需软件模拟 100%
FP16 中等 ~3 TOPS 50%
INT8 最少 6 TOPS(满载) 25%

简单说:用 INT8 代替 FP32,算力翻倍,内存占用降到四分之一,代价是精度损失。 这就是量化要做的取舍。


二、量化的数学原理:从浮点到整数

2.1 核心公式

量化的本质是把一段连续的浮点数值范围,映射到 256 个离散的整数(INT8 范围是 -128~127 或 0~255):

量化公式:q = round(r / scale) + zero_point
反量化公式:r = (q - zero_point) × scale

其中:

  • r:原始浮点值(real value)
  • q:量化后的整数值(quantized value)
  • scale:缩放因子,决定每个整数台阶代表多大的浮点范围
  • zero_point:零点偏移,让浮点 0 能精确映射为某个整数

2.2 一个具体例子

假设某一层的激活值范围是 [-2.5, 3.8]

# 对称量化(zero_point = 0)的简化示例
import numpy as np

r_min, r_max = -2.5, 3.8
scale = max(abs(r_min), abs(r_max)) / 127   # 0.0299...

# 原始浮点值
r = 1.234

# 量化
q = round(r / scale)   # = round(1.234 / 0.0299) = 41

# 反量化(用于验证误差)
r_recovered = q * scale   # = 41 × 0.0299 = 1.2259

print(f"原始值: {r}, 反量化后: {r_recovered:.4f}, 误差: {abs(r - r_recovered):.4f}")
# 误差约 0.008,这就是量化引入的精度损失

RKNN-Toolkit2 默认用的是非对称量化asymmetric_quantized-8),即 zero_point 不固定为 0,能更好地适应激活值分布不对称的情况(比如经过 ReLU 后值域是 [0, +∞))。

2.3 量化误差从哪里来

误差主要来自两点:取整误差(round 操作丢失精度)和截断误差(如果某个值超出校准时统计的 [r_min, r_max] 范围,会被截断到边界值)。

这就是为什么校准数据集的代表性直接决定量化精度——如果校准集没有覆盖到实际推理时会出现的数值分布,截断误差就会很大。


三、校准数据集:精度的命门

3.1 校准集的作用机制

PTQ(Post-Training Quantization,训练后量化)的流程是:

1. 模型结构 + FP32 权重(已训练好,不再改变)
        ↓
2. 喂入校准数据集(100~300张代表性图片)
        ↓
3. 统计每一层激活值的实际分布范围 [r_min, r_max]
        ↓
4. 根据分布范围计算每层的 scale / zero_point
        ↓
5. 生成量化后的 INT8 模型

关键认知:校准集不参与训练,只用来"摸清"网络各层的激活值分布。如果校准集的数据分布和真实推理场景差异很大,统计出来的 scale 就会不准,导致推理时大量数值被截断。

3.2 校准集怎么选:四条原则

原则一:数量上 100-300 张通常够用。更多数据边际收益递减,超过 500 张基本没有提升,反而拖慢转换时间。

原则二:必须覆盖真实场景的分布。如果模型部署在工厂产线做缺陷检测,校准集就该用产线实拍图,而不是网上下载的通用数据集。

# ❌ 错误做法:用 ImageNet 随机图片校准一个专用工业检测模型
dataset.txt 内容:随便下载的猫狗图片

# ✅ 正确做法:用实际部署场景采集的图片校准
dataset.txt 内容:产线摄像头实拍的工件图片(含正常+缺陷样本)

原则三:覆盖边缘场景。如果实际使用中会遇到逆光、模糊、不同光照条件,校准集里也要包含这些"困难样本",否则这些场景下的推理精度会明显下降。

原则四:类别要均衡。多分类任务中,如果某个类别在校准集里几乎没有,对应的激活值分布统计不准,该类别的识别精度会明显劣化。

3.3 生成校准数据集列表

# generate_dataset_list.py
import os

img_dir = "./calibration_images"
output_file = "./dataset.txt"

img_files = [f for f in os.listdir(img_dir)
             if f.lower().endswith(('.jpg', '.jpeg', '.png'))]

with open(output_file, 'w') as f:
    for img in img_files:
        f.write(os.path.join(img_dir, img) + '\n')

print(f"✅ 生成 {len(img_files)} 张图片的校准列表")

四、量化算法选择:normal vs mmse vs kl_divergence

RKNN-Toolkit2 提供多种量化校准算法,在 config() 中通过 quantized_algorithm 指定:

rknn.config(
    quantized_algorithm="normal",   # 可选:normal / mmse / kl_divergence
    ...
)
算法 原理 速度 精度 适用场景
normal 直接取 min/max 范围 最快 一般 快速验证、分布均匀的模型
mmse 最小化均方误差搜索最优 scale 较慢(约慢3-5倍) 较好 大多数生产场景推荐
kl_divergence KL散度最小化(类似 TensorRT 的做法) 最慢 通常最好 精度要求高、有充足调试时间

实用建议:开发阶段用 normal 快速验证流程通通是否正常,确定要上生产环境的模型,换成 mmse 重新转换一次,通常能挽回 1-2% 的精度损失。

# 生产环境推荐配置
rknn.config(
    mean_values=[[123.675, 116.28, 103.53]],
    std_values=[[58.395, 57.12, 57.375]],
    target_platform="rk3588",
    quantized_dtype="asymmetric_quantized-8",
    quantized_algorithm="mmse",      # 生产环境用 mmse
    optimization_level=3
)

五、精度诊断:accuracy_analysis 实战

当量化后模型精度明显下降时,需要逐层定位问题出在哪一层。

5.1 运行精度分析

# accuracy_diagnosis.py
from rknn.api import RKNN
import numpy as np

rknn = RKNN(verbose=True)
rknn.config(
    mean_values=[[123.675, 116.28, 103.53]],
    std_values=[[58.395, 57.12, 57.375]],
    target_platform="rk3588",
)
rknn.load_onnx(model="model.onnx")
rknn.build(do_quantization=True, dataset="./dataset.txt")

# 准备一个测试输入(.npy 格式)
test_input = np.load("test_sample.npy")

# 逐层精度分析,输出每层 FP32 vs INT8 的余弦相似度
rknn.accuracy_analysis(
    inputs=[test_input],
    output_dir="./snapshot",
    target=None   # 不连板子,纯 PC 端模拟分析
)
rknn.release()

5.2 解读分析结果

分析完成后,./snapshot 目录下会生成逐层报告,关键看每层的 cosine similarity(余弦相似度):

layer_name                 cos_similarity
conv1                      0.9998
bn1                        0.9997
layer1.0.conv1             0.9995
...
layer3.2.conv3             0.8721   ← 明显偏低,问题层
...
fc                         0.9201   ← 偏低

判断标准

  • cos_similarity > 0.99:量化误差可忽略
  • 0.95 ~ 0.99:轻微误差,通常不影响最终精度
  • < 0.95:明显误差,需要重点关注,特别是接近输出层的位置(误差会累积放大)

5.3 定位到问题层后的三种处理方式

方式一:混合量化——让问题层保持 FP16

rknn.config(
    ...
    quantized_dtype="asymmetric_quantized-8",
)

# 指定特定层不参与 INT8 量化,保留 FP16 精度
rknn.build(
    do_quantization=True,
    dataset="./dataset.txt",
    rknn_batch_size=1,
)

# 通过 hybrid_quantization 两阶段流程精细控制
rknn.hybrid_quantization_step1(dataset="./dataset.txt")
# 此时会生成 model.quantization.cfg,手动编辑该文件
# 找到问题层对应的配置行,将 dtype 改为 float16
rknn.hybrid_quantization_step2(
    model_input="model.quantization.json",
    data_input="model.quantization.data",
    model_quantization_cfg="model.quantization.cfg"
)

方式二:扩充校准集,针对性补充该层激活值分布异常的样本

如果问题层是对某类特定输入(如暗光图片)敏感,在校准集中增加这类样本的比例。

方式三:调整该层的量化算法

部分版本的 RKNN-Toolkit2 支持逐层指定量化算法,对问题层单独使用 kl_divergence


六、量化前后性能与精度对比实测

以 YOLOv8n 在 COCO 数据集上的实测数据为例(仅供参考,实际数值因模型和数据集而异):

配置 mAP@0.5 推理耗时(单核NPU) 模型体积
FP32(原始) 37.3% 不支持直接跑(需CPU模拟,约150ms) 12.4 MB
FP16 37.1% 约 9ms 6.3 MB
INT8(normal) 35.8% 约 4.5ms 3.2 MB
INT8(mmse) 36.6% 约 4.5ms 3.2 MB

可以看到:INT8 量化让推理速度提升约 2 倍,模型体积缩小到近四分之一,而 mmse 算法能把精度损失从 1.5% 收窄到 0.7%。这就是为什么生产环境强烈建议用 mmse 而不是 normal。


七、常见问题排查表

现象 可能原因 解决方案
量化后精度暴跌(>10%) 校准集与实际场景分布严重不符 重新采集贴近真实场景的校准集
某几类识别率特别低 校准集中该类别样本过少 补充该类别样本,保持类别均衡
整体精度小幅下降但可接受 正常的 INT8 量化损失 尝试 mmse 算法,或对关键层做混合量化
accuracy_analysis 跑不出结果 测试输入格式不匹配模型要求 检查 npy 文件的 shape 是否与模型输入一致
转换后输出全是异常值 mean/std 配置错误 核对原始模型的预处理参数(见第4篇速查表)

八、量化决策流程图(实战速查)

开始量化
   │
   ▼
用 normal 算法快速转换,跑通流程
   │
   ▼
精度是否满足要求?
   │
   ├─ 是 → 完成,可以部署
   │
   └─ 否 → 换 mmse 算法重新转换
            │
            ▼
        精度是否满足要求?
            │
            ├─ 是 → 完成,可以部署
            │
            └─ 否 → accuracy_analysis 定位问题层
                     │
                     ▼
                 对问题层做混合量化(FP16)
                     │
                     ▼
                 精度是否满足要求?
                     │
                     ├─ 是 → 完成,可以部署
                     │
                     └─ 否 → 检查校准集代表性,重新采集

九、总结与下篇预告

本篇把量化从原理到实战彻底讲透:

  • 理解了量化的数学本质(scale/zero_point映射)
  • 掌握了校准数据集选择的四条原则
  • 学会了 normal/mmse/kl_divergence 三种算法的取舍
  • 掌握了 accuracy_analysis 逐层精度诊断方法
  • 学会了混合量化处理问题层的具体操作

至此,模型转换三部曲(转换→量化→精度诊断)全部打通。

下一篇(系列第 6 篇)继续深入精度调优,讲解 accuracy_analysis 更多高级用法、逐层精度对比的可视化方法,以及生产环境下的精度回归测试方案。


本系列文章列表(持续更新)

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐