onnx模型量化掉点以及hb_onnxruntime使用对齐问题

1.芯片型号:X3派

2.天工开物开发包OpenExplorer版本

hbdk version 3.39.2

horizon_nn version 0.14.9

hb_mapper version 1.11.2

3.问题定位:模型转换

4.问题具体描述:量化模型精度下降的很厉害,以及关于**_original_float_model.onnx 的使用问题对齐

您好,这里再模型量化转换当中遇到了一些问题:

onnx原始模型测评进度再一个角度回归的问题上下降了10多个点,(精度的评定按照小于10°的误差为tp,算所有结果的accuracy),从原始模型的97%下降到83%

分类分支下降了3个点。

掉点为bin模型上板测量的结果。

一下sigmoid为角度向量的回归分支,conv为分类分支,网络的预处理为cv.imread,之后resize 128,128 , /256

从模型量化loge当中看量化结果应该挺正常的才对

2023-04-13 03:45:23,527 INFO The quantify model output:

======================================================

Node Cosine Similarity L1 Distance L2 Distance Chebyshev Distance

-------------------------------------------------------------------------------------------

Conv_97 0.999954 0.056883 0.027743 0.134689

Sigmoid_101 1.000000 0.001775 0.001278 0.004256

然后开始查找预处理操作可能出现的错误,

然后分别使用

*_quant_model.onnx

*_original_float_model.onnx

用于量化网络的一个pc上的模拟验证和预处理操作是否有误的判断。

这里俩个模型和bin文件的测试结果比较接近(但是quant_model.onnx和bin文件的结果还是有一定差异的不知道是否算正常)

定位可能是量化数据处理和量化预处理设置有问题。

检查配置感觉没有啥问题,于是按照makertbin的执行方式做了一个 1层卷积的网络输入输出完全相等的

用来验证量化工程当中的预处理设置问题,onnx模型本身是bgr输入,bin模型希望设置为nv12输入,所以这里给生成的**_original_float_model.onnx的时候,输入为yuv444_128格式(参考了用户手册当中的 input_type_r对应中间类型表格)应该得到得到正确的预处理的bgr的结果。结果做去归一化 确实也回到了bgr图片的值(np.allclose判断输入输出相似度,1个像素值误差我们就认为预处理本身是没有问题的)

那在预处理确认无误之后,按照用户文档的说法_original_float_model.onnx模型应该就只加了一个预处理,这里我也查看了源模型的weight和_original_float_model的weight ,也是完全一致的。(下图这个三到五的对齐应该也是因为input_type_r在pc上用中间类型表达没有办法完全做到绝对一致,但像上面的实验以及足够验证它是正确的了

于是这里去掉了量化当中的makertbin环节的预处理操作,所有的操作都放到网络外面完成,也就是input_type_rt 设置为’featuremap’,input_type_train: ‘featuremap’,我理解这个时候得到的_original_float_model.onnx就应该和原始onnx输入数据也是完全等价。

这里出现了:

在同为HB_ONNXRuntime作为后端的时候,_original_float_model.onnx 和原始onnx喂同样的数据的时候,结果完全一致 ,但是开源onnxruntime 的测试结果不一致。

于是,以上过程当中有三个问题不太理解:

1.HB_ONNXRuntime可以调用原始的onnx文件进行推理吗(得到的结果和自己环境的onnxruntime结果不一致)?

2.**_original_float_model.onnx 是否除了预处理步骤,以后的算子完全等价onnx文件本身?

3.bin文件和quant_model.onnx结果存在一定误差是否合理?

如果有其他需要提供表述的log和模型或者测试图像,这边都可以提供的,感谢答疑

感谢您使用地平线芯片算法工具链,最近我们在收集大家的满意度反馈,欢迎您填写问卷,详细情况可见:https://developer.horizon.ai/forumDetail/146177053698464782

逐个回答一下你的问题哈

关于问题1:HB_ONNXRuntime可以推理自己刚导出的onnx,也可以推理模型转换中间产物:original_float_model.onnx、optimized_float_model.onnx、quantized_model.onnx,它的推理结果和自己环境的onnxruntime是一致的,只是onnxruntime只能推理自己刚导出的onnx,关于如何推理,建议参考社区文章:https://developer.horizon.ai/forumDetail/71036815603174578。

关于问题2:original_float_model.onnx只是在模型首部插入了预处理节点,建议将归一化、图像格式转换在yaml中配置,该部分解读,可以看这篇社区文章:https://developer.horizon.ai/forumDetail/118363912788935513。

关于问题3:bin和quantized.onnx存在一点误差是正常的,但这个误差很小,可以使用hb_verifier/hb_model_verifier来进行一致性验证,也可以参考问题1中的链接。

此外,建议您使用最新版OE2.5.2来进行转换哈,获取链接如下:https://developer.horizon.ai/forumDetail/136488103547258769

感谢回复,不过在使用上问题一这里还是有一些问题:

这里图中demo为hb_onnxruntime调用原始模型,demo_1为个人环境通过onnxruntime调用模型,共同使用input.npy作为输入数据,前后都完全没有预处理和后处理,我的理解是这里得到的结果应该是一样的才对,但是结果无法对上,这边稍后会用新版本的工具进行量化测试

float32的数据,在demo中,需要使用sess.run_feature()哈,后期我们会出一个关于这块的社区文章,专门介绍一下这个

你好,在float32数据下,再预处理什么都不做的 feature 作为输入的模型转换当中,demo 使用sess.run_feature 跑出来的结果与个人环境的onnxruntime的结果完全一致,这里对齐了。回到nv12转bgr 的模型,使用original_float_model.onnx 使用yuv444_128数据作为输入的时候,结果没有对齐,差异如下,结果为上述sigmoid层的输出对比,是否再可以接受范围之内?

下面是makebin 的yaml文件操作其中的数据输入设置

model_parameters:-
onnx_model: ‘../car_angle_test/model_weight/car_angle_cls_model_20230406.onnx’-
march: “bernoulli2”-
working_dir: 'model_output-
output_model_file_prefix: ‘car_angle_cls_model_20230406’-
input_parameters:-
input_name: “”-
input_type_rt: ‘nv12’-
input_type_train: ‘bgr’-
input_layout_train: ‘NCHW’-
input_shape: ‘’-
norm_type: ‘data_scale’-
mean_value: ‘’-
scale_value: 0.00390625

这个转换得到的float_orginal_model.onnx 调用手段如下

def infer_transformers(input_layout=“NCHW”):-
transformers = [-
ResizeTransformer(target_size=(128,128)),-
BGR2NV12Transformer(data_format=“HWC”),-
NV12ToYUV444Transformer((128, 128),-
yuv444_output_layout=input_layout[1:]),-
]-
return transformers

其中yuv444 减少128的操作放在 input_offset当中,操作如下

outputs = self.sess.run(self.output_names, feed_dict, input_offset=128)

用yuv444_128的数据测试original_float_model.onnx掉点了10个点,想问一下该怎么调查这个误差的原因-

您好,您配置的板端输入是nv12,给original_model就应该是nv12哈。

如果您实在不放心,建议您先尝试将板端输入与训练输入都使用一样的数据,熟悉之后,再尝试使用nv12的板端数据。

抱歉这里不太理解为什么板端输入是nv12的时候,original_float_model.onnx为啥是nv12呢,这里先对齐一下说法哈,板端模型是指bin模型,然后根据用户手册当中的 转换内部过程解读 相当于original_float_model.onnx相较于训练原始模型onnx多了一个input_type_rt*到bgr的预处理。然后bin模型是完整做了nv12到bgr的预处理融合到模型当中。一下是转换内部过程解读的部分节选

我是根据这个对应表格来判断我再使用original_float_model.onnx的时候应该使用nv12对应的中间类型来使用yuv444_128的数据作为输入的。(即使我想输入nv12数据长度也对应不上)。

然后由于无论是bin模型文件还是original_float_model.onnx模型文件,他们输入和原始模型同样的数据应该都是结果没有办法对齐的把,他们不是给模型增加了预处理模块吗?感谢答疑

嗯嗯,您的理解是没问题的哈,板端时nv12的时候,original_model确实应该处理到nv12的中间类型,也就是yuv444_128,这里的减去128通过配置onnx模型推理API的input_offset参数实现。

关于在模型中加了预处理模块,从您的yaml配置看,主要是scale的影响,请问你的0.00390625是满足下面公式的计算嘛?

还有就是,做了归一化,外面的数据处理时,别重复了哈。

实在不放心,建议先尝试将板端输入与训练输入都使用bgr,都不要配置scale参数,熟悉之后,再尝试使用nv12和scale,用来提升性能。