一、前言
随着深度学习模型的不断发展,量化(Quantization)作为一种优化手段,已经在嵌入式设备和资源受限平台上得到了广泛应用。特别是在边缘计算和智能硬件领域,量化能够有效减小模型的存储占用并加速推理过程。PTQ(Post-Training Quantization)作为一种无需重新训练模型的量化方式,因其简便性和高效性而成为常用的部署手段。
然而,PTQ过程中常常会遇到模型精度下降的问题,尤其是在部署到特定硬件平台时,精度损失可能会更加显著。地平线J6平台作为一个强大的AI处理平台,支持多种深度学习框架的推理,然而如何确保量化后的模型在J6平台上依然保持较高的精度,是很多开发者面临的挑战。
本博客旨在分享一些在地平线J6平台上进行PTQ模型部署时遇到的精度问题排查经验。通过总结实际操作中的常见问题和排查步骤,希望能为开发者提供一些参考,帮助更顺利地部署量化模型。尽管每个项目的情况可能有所不同,但通过合理的排查和优化,通常能够找到合适的方案来减小精度损失,提升模型在实际应用中的性能
二、 精度下降的常见原因
三、排查方法
校准数据的来源应该是浮点模型的训练集、验证集或测试集中的20~100份数据。以图像数据为例,校准集的图像应具有代表性,避免使用非常少见的异常样本,如纯色图片或不含任何检测或分类目标的图片等,这些样本可能导致量化后的精度偏差。此外,校准数据需要经过与原始浮点模型一致的预处理,包括图像的归一化、尺寸调整等,并以.npy文件格式保存,以确保与模型输入格式一致。
例如YOLOv8的前处理:颜色空间转换(BGR->RGB)RGB)--> 数据排布转换(HWC->CHW)CHW)--> numpy2tensor numpy2tensor --> add batch dim add batch dim --> 归一化(/255)
#YOLOv8前处理
def preprocess(self, im: Union[np.ndarray]) -> torch.Tensor:
"""
Accepts one image in HWC format (np.ndarray), returns tensor (1, 3, H, W)
"""
im = self.pre_transform([im])[0]
if im.shape[-1] == 3:
im = im[..., ::-1] # BGR to RGB
im = im.transpose((2, 0, 1)) # HWC to CHW
im = np.ascontiguousarray(im)
im = torch.from_numpy(im).unsqueeze(0) # Add batch dim: (1, 3, H, W)
im = im.to(self.device)
im = im.half() if self.model.fp16 else im.float()
im /= 255.0
return im # (1, 3, H, W)
例如mean_value, scale_value 等值的配置是否与浮点模型预处理方式一致。
model_parameters:
onnx_model: 'yolov8s.onnx'
march: nash-e
layer_out_dump: False
working_dir: './model_output'
output_model_file_prefix: 'yolov8s_640x640_nv12'
remove_node_type: "Dequantize"
input_parameters:
input_name: ''
input_type_rt: 'nv12'
input_type_train: 'rgb'
input_layout_train: 'NCHW'
input_shape: '1x3x640x640'
norm_type: 'data_scale'
mean_value: ''
scale_value: 0.003921568627451
calibration_parameters:
cal_data_dir: './calibration_data'
calibration_type: 'kl'
compiler_parameters:
optimize_level: 'O3'
1.定位精度下降的阶段
使用 HB_ONNXRuntime工具推理产出的模型(original_float_model.onnx、optimized_float_model.onnx、calibrated_model.onnx、quantized_model.bc等),定位精度下降的阶段。若在original 或 optimized 阶段出现了问题,则检查原始onnx模型是否转换错误。若是在 calibrated_model.onnx 出现问题,则需通过精度debug工具比较余弦相似度,找出敏感算子。
HB_ONNXRuntime示例代码
import numpy as np
# 加载地平线依赖库
from horizon_tc_ui.hb_runtime import HBRuntime
# 准备模型运行的输入,此处`input.npy`为处理好的数据
data = np.load("input.npy")
# 加载模型文件,根据实际模型进行设置
# ONNX模型
sess = HBRuntime("model.onnx")
# HBIR模型
sess = HBRuntime("model.bc")
# HBM模型
sess = HBRuntime("model.hbm")
# 获取输入&输出节点名称
input_names = sess.input_names
output_names = sess.output_names
# 准备输入数据,根据实际输入类型和layout进行准备,配置格式要求为字典形式,输入名称和输入数据组成键值对
# 如模型仅有一个输入
input_feed = {input_names[0]: data}
# 如模型有多个输入
input_feed = {input_names[0]: data1, input_names[1]: data2}
# 进行模型推理,推理的返回值是一个list,依次与output_names指定名称一一对应
output = sess.run(output_names, input_feed)
2.精度debug工具
在上述代码中可以将node_type分别设置为"node"、"weight"和"activation",得到的敏感度分析列表如下。(排序越靠前,说明量化误差越大。)
有以下解决方案:
1.在性能允许的情况下可以将 activation 设置为全int16。仅需在 quant_config 中设置 "all_node_type": "int16" 。若设置为全int16还无法满足精度需求,则需要进行算子拆分(算子拆分可以参考https://developer.horizon.auto/blog/13099)模拟双int16输入。
2.若性能不允许全int16,可以在debug后的敏感度分析列表下,选取误差较大的节点单独设置为int16。