01 技术背景
在大多数情况下,我们都推荐用户在编译处理图像任务的模型时,将 input_type_rt 参数配置 nv12,这是考虑到视频通路传来的数据通常都是 nv12 类型,这样配置可以最大化地节约耗时,提高全流程的处理效率。
但在前期的算法验证阶段,往往用户更希望模型能直接读取 rgb/bgr 数据,以更加直观地检查量化效果。在这种使用场景下,往往需要对图像数据做一些专门的处理,否则会产生错误的输出结果。
本教程会详细介绍如何在 input_type_rt 配置 rgb/bgr 时,正确地处理输入数据。
02 工具链环境
hbdk 3.49.15horizon-nn 1.1.0horizon_tc_ui 1.24.3
复制代码
03 模型说明
这次作为范例使用的模型是 mixvpr,这是一个视觉场景识别算法,输入为 1x3x320x320 的图像,输出是 1x512 的全局描述子。
onnx 模型的输入输出的具体信息如下图所示:
yaml 配置情况如下,浮点模型由 rgb 训练,且希望验证 rgb 输入的效果,因此 input_type_rt 和 input_type_train 都配置为 rgb,同时 input_layout_rt 和 input_layout_train 都配置为 NCHW,也配置了 mean 和 scale 参数。
model_parameters: onnx_model: "mix.onnx" march: "bayes-e" working_dir: 'model_output' output_model_file_prefix: 'mix'input_parameters: input_type_rt: 'rgb' input_layout_rt: 'NCHW' input_type_train: 'rgb' input_layout_train: 'NCHW' norm_type: 'data_mean_and_scale' mean_value: 123.675 116.280 103.530 scale_value: 0.01712475 0.01750700 0.01742919calibration_parameters: cal_data_dir: './calibration_data_rgb_f32' cal_data_type: 'float32'compiler_parameters: optimize_level: 'O3'
复制代码
模型的原始输入为一张 320x320 的彩色 png 图像,pytorch 浮点模型的推理结果如下,可作为量化效果的参考:
模型编译完成后,可以运行 hb_perf mix.bin,查看生成的 png 图,了解 bin 模型的子图结构:
04 推理 quantized.onnx
首先我们需要确认 quantized.onnx 的输入情况,该模型的输入 shape 为 1x3x320x320,数据类型为 int8,并且色彩空间应当是 rgb 类型(对应 input_type_rt)。
但我们知道,opencv 读图后,得到的数据是 bgr,hwc,uint8,且 3 维的,因此需要做一些预处理,才可以得到正确的输入数据。
从读图到预处理到推理 quantized.onnx 的完整代码,及详细注释如下:
from horizon_tc_ui import HB_ONNXRuntimeimport numpy as npimport cv2
def read_img(input_name): # opencv读图,此时数据是bgr,hwc,uint8,且3维的 data = cv2.imread("image.png") # 将bgr转换为rgb data = cv2.cvtColor(data, cv2.COLOR_BGR2RGB) # 将hwc转换为chw data = np.transpose(data,(2,0,1)) # 将3x320x320转换为1x3x320x320,也就是将chw转换为nchw data = data[np.newaxis,:,:,:] print(data) # 将uint8的数据,在数值上-128并且转换为int8 data = (data - 128).astype(np.int8) # 保存此时的数据,可以作为bin模型的输入 data.tofile("rgb_128_int8.bin") print(data) return data
def main(): sess = HB_ONNXRuntime(model_file="./mix_quantized_model.onnx") input_names = [input.name for input in sess.get_inputs()] output_names = [output.name for output in sess.get_outputs()] feed_dict = dict() for input_name in input_names: feed_dict[input_name] = read_img(input_name) outputs = sess.run(output_names, feed_dict) # 保存推理结果,用于对比正确性 print(outputs[0][0]) np.savetxt('output_onnx.txt', outputs[0][0]) if __name__ == '__main__': main()
复制代码
推理完成后查看输出结果,发现和浮点输出较为接近,说明该推理结果可靠。
[-0.05279211 0.01489008 -0.02978017 -0.03113381 -0.07580406 0.02707288 -0.05143847 -0.06091398 -0.01489008 -0.0216583 0.06226762 -0.11506554 0.01759737 0. -0.01082915 -0.00406093 0.00406093 0.04331661 -0.00270729 0.05820669 -0.0121828 -0.01489008 0.11777283 -0.12454105 ...... -0.00270729 -0.01489008 0.05414576 -0.01895102 -0.01353644 -0.06362127 -0.02842652 0.02030466 0.08121864 -0.01759737 -0.08121864 0.00541458 -0.05279211 0.00541458 0.0676822 -0.04196296 0.04737754 -0.05956033 -0.05956033 0.0893405 ]
复制代码
需要特别强调的是 uint8 转换 int8 的方法,代码中写的是 data=(data-128)。astype(np.int8),如果用户不写,那么 HB_ONNXRuntime 会自动做这一操作(相关处理如下图),得到的推理结果是完全一样的。
但如果用户误写成了 data=data-128,或者 data=data.astype(np.int8),那么将得到完全错误的推理结果,这是因为 uint8 转 int8 有两种方法。
方法 1(正确):在数值上,所有数据全部 -128,对应上文代码中的 data=(data-128)。astype(np.int8)
方法 2(错误):在数值上,0 到 127 的值不变,128 到 255 的值 -256,对应 data=data.astype(np.int8)
如果用户只写了 data=data-128,那么此时的数据类型还是 uint8,HB_ONNXRuntime 会继续做 data=(data-128)。astype(np.int8)操作,这两个操作就等效成了 data=data.astype(np.int8)
方法 2 处理后,得到的推理结果也是完全错误的,和浮点输出对不上
[-0.03068066 0.01534323 -0.01227575 -0.03222021 -0.07364288 0. 0.02301195 -0.05062512 0.00766871 -0.04755764 -0.01534323 -0.07364288 -0.01534323 -0.01994446 0.04602389 0.00306749 -0.02301195 0.0214782 0. 0.06290087 0.02761317 -0.02914692 0.10585728 -0.0675021 ...... 0.01534323 -0.01841072 -0.02761317 0.02301195 0.02454569 -0.0214782 0.02761317 -0.02914692 0.01380949 -0.02301195 0.01687698 -0.04755764 -0.04755764 0.03835518 0.07671037 -0.03988893 0.01227575 -0.01994446 0.0352877 0.01841072 0.03988893 -0.05983339 0. -0.01534323 -0.01534323 0.06290087]
复制代码
因此请务必确保,在 uint8 转 int8 这一步,使用 data=(data-128)。astype(np.int8),或者不转,让 HB_ONNXRuntime 自动执行这一操作。
05 推理 bin
可以借助 hrt_model_exec 的 infer 功能推理模型,hrt_model_exec 的源码已经开放,用户可以直接参考/package/board/hrt_tools/src/hrt_model_exec 目录。
hrt_model_exec infer --model-file mix.bin --input-file rgb_128_int8.bin --enable_dump true --dump_format txt
复制代码
其中--input-file 的 rgb_128_int8.bin,就是刚才推理 quantized.onnx 时,保存的预处理后的输入文件。对于 hrt_model_exec 工具来说,推理 RGB 输入且为 int8 的模型,不能直接给 png 图像作为输入数据,而需要提前做好预处理,保存成二进制文件再提供给模型。
对于 rgb_128_int8.bin 输入,模型的推理结果如下,和 quantized.onnx 的输出结果完全一致。
-0.052792113 0.014890083 -0.029780166 -0.031133810 -0.075804062 0.027072879 -0.051438469 -0.060913976 -0.014890083 -0.021658303 ......-0.081218638 0.005414576 -0.052792113 0.005414576 0.067682199 -0.041962963 0.047377538 -0.059560332 -0.059560332 0.089340501
复制代码
评论