4. 动态量化与静态量化对比
任务目标
在RKNN工具链中分别完成YOLOv5s模型的动态量化与静态量化,对比两种方式在模型大小、推理耗时、精度(mAP)三个维度的差异,并给出选择建议。
环境依赖
- RKNN-Toolkit2(≥1.6.0)
- Rockchip Linux SDK(或模拟器环境)
- YOLOv5s.onnx(输入640×640,80类COCO)
- 校准数据集:COCO val2017中随机200张图片(仅静态量化需要)
核心实现
以下代码完成两种量化rknn模型的生成、推理与精度评估。动态量化直接调用build接口(不提供量化数据集);静态量化通过quantize_dataset传入校准数据。两者共用同一推理与评估函数,保证对比公平。
import os
import cv2
import numpy as np
from rknn.api import RKNN
# ========== 配置 ==========
ONNX_PATH = "yolov5s.onnx"
DYNAMIC_RKNN = "yolov5s_dynamic.rknn"
STATIC_RKNN = "yolov5s_static.rknn"
CALIB_DIR = "calib_imgs/" # 200张校准图片
TEST_DIR = "test_imgs/" # 验证图片(含GT标注)
IMG_SIZE = 640
CLASSES = 80
IOU_THRESH = 0.45
CONF_THRESH = 0.25
# ========== 1. 构建RKNN引擎(共用) ==========
def build_engine(platform="rk3588"):
rknn = RKNN(verbose=False)
rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform=platform)
assert rknn.load_onnx(model=ONNX_PATH) == 0, "ONNX加载失败"
return rknn
# ========== 2. 动态量化(无需校准数据) ==========
def build_dynamic():
rknn = build_engine()
# 关键:build时不传dataset,即动态量化
assert rknn.build(do_quantization=True) == 0, "动态量化build失败"
rknn.export_rknn(DYNAMIC_RKNN)
rknn.release()
print(f"[动态量化] 模型已保存: {DYNAMIC_RKNN}")
# ========== 3. 静态量化(需校准数据) ==========
def build_static():
rknn = build_engine()
# 关键:传入dataset参数,即静态量化
assert rknn.build(do_quantization=True, dataset="./calib_dataset.txt") == 0, "静态量化build失败"
rknn.export_rknn(STATIC_RKNN)
rknn.release()
print(f"[静态量化] 模型已保存: {STATIC_RKNN}")
# ========== 4. 推理与精度评估(共用) ==========
def evaluate(rknn_path, label="量化模型"):
rknn = RKNN(verbose=False)
rknn.load_rknn(rknn_path)
rknn.init_runtime()
tp, fp, total_gt = 0, 0, 0
for img_name in os.listdir(TEST_DIR):
img = cv2.imread(os.path.join(TEST_DIR, img_name))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))
outputs = rknn.inference(inputs=[img])
# 此处简化:实际需NMS+box解码,与GT计算mAP
# 假设已实现yolo_postprocess()返回boxes, scores, classes
boxes, scores, classes = yolo_postprocess(outputs, CONF_THRESH)
gt_boxes, gt_classes = load_gt(img_name) # 读取GT
tp_i, fp_i = calc_tp_fp(boxes, scores, classes, gt_boxes, gt_classes, IOU_THRESH)
tp += tp_i; fp += fp_i; total_gt += len(gt_boxes)
precision = tp / (tp + fp + 1e-6)
recall = tp / (total_gt + 1e-6)
ap = (precision + recall) / 2 # 简化mAP,实际应逐类计算
rknn.release()
return ap
# ========== 5. 主流程 ==========
if __name__ == "__main__":
# 生成两种量化模型
build_dynamic()
build_static()
# 对比评估
ap_dyn = evaluate(DYNAMIC_RKNN, "动态量化")
ap_sta = evaluate(STATIC_RKNN, "静态量化")
size_dyn = os.path.getsize(DYNAMIC_RKNN) / 1024
size_sta = os.path.getsize(STATIC_RKNN) / 1024
print("\n========== 对比结果 ==========")
print(f"模型大小: 动态 {size_dyn:.1f}KB | 静态 {size_sta:.1f}KB")
print(f"mAP(简化): 动态 {ap_dyn:.4f} | 静态 {ap_sta:.4f}")
print("注:推理耗时需在板端用RKNN Benchmark工具实测")
运行与验证
- 准备校准列表:在
calib_dataset.txt中逐行写入校准图片路径(仅静态量化需要)。 - 执行量化:运行上述脚本,生成
yolov5s_dynamic.rknn与yolov5s_static.rknn。 - 板端耗时对比:将两个rknn模型分别推送到RK3588开发板,使用
rknn_benchmark工具各运行100次取平均耗时。
判定标准
- 功能通过:脚本无报错,两个rknn文件均成功生成且可被
init_runtime()正常加载。 - 效果差异:静态量化模型大小通常比动态量化小10%~20%(因权重与激活均定点化);静态量化mAP通常比动态量化高1~3个百分点(因动态量化仅权重量化,激活仍为浮点)。
- 耗时结论:板端实测静态量化推理耗时通常比动态量化低15%~30%(因激活定点化后计算更快)。
选择建议:若追求精度且校准数据易得,优先静态量化;若需快速部署且无校准数据,动态量化是合理降级方案。两者均比FP16模型体积减少约75%,但静态量化精度损失更小。