深度模型在训练的时候,为了得到最好的推理精度,通常都不会把图像数据(0,255)直接送进去,而是会对数据做归一化处理。比如把数据从(0,255)的8bit整数转到(0,1)或者(-1,1)的32bit浮点之间;同时对于RGB数据,不同的推理框架在训练时对于彩色通道的排列RGB/BGR也不同。因此对应的在推理的时候, 也需要把摄像头抓到的数据也做对应的预处理, 把数据做一个归一化,以及色彩通道的调整。
OpenVINO的用MO转换模型的时候,也考虑到了这个问题,可以在mo转换的时候加一些对应的参数来帮忙做数据预处理。具体的做法是在IE runtime在推理的时候,在模型的最上层先加一个ScaleShift层来做归一化;对于RGB通道的调整,会再加一个reorder层来做数据的交换。这么做的好处是
- OpenVINO在实现ScaleShift计算的时候会自动用SIMD指令加TBB或者OpenMP来做数据并行的优化,经过Intel工程师优化过的代码远比我们自己写的C代码实现效率要高了不知道几条马路
- 用MKLDNN做卷积的时候,前面也需要一个Reoder来调整内存数据排列顺序,这个reorder操作可以捎带手把RGB<->BGR的转换也做了,不需要消耗额外的时间开销。
下面用Resnet的ONNX模型来举个例子,演示一下怎么在MO的时候设置preprocess参数
先看看ONNX Open Model Zoo上对Resnet 输入数据和预处理的介绍
从网站上这部分的描述可以看到输入数据要求是RGB排列
归一化数据的算法是
RGB 3个通道的mean, stddev分别为
mean_vec = np.array([0.485, 0.456, 0.406])
stddev_vec = np.array([0.229, 0.224, 0.225])
对应R通道的img_data, 应该是
对应OpenVINO的ScaleShift层的Mean和Scale,计算公式是
对比一下上面Resnet ONNX模型预处理的公式
所以OpenVINO MO的Preprocess里Mean/Scale的值应该是
#Resnet ONNX preprocess
mean_vec = np.array([0.485, 0.456, 0.406])
stddev_vec = np.array([0.229, 0.224, 0.225])
#OpenVINO
mean_values = mean_vec * 255
scale_values = stddev_vec * 255
#mean_value = [123.675,116.28,103.53]
#scale_value = [58.395,57.12,57.375]
算出了Mean/Scale的值, 接下来开始MO转换了
C:\Program Files (x86)\IntelSWTools\openvino\deployment_tools\model_optimizer>python mo_onnx.py --input_shape=[1,3,224,224] --input=data --reverse_input_channels --mean_values=data[123.675,116.28,103.53] --scale_values=data[58.395,57.12,57.375] --input_model .\resnet34-v2-7.onnx -o .\
这里用了几个参数
--input_shape=[1,3,224,224] 告诉IE runtime 输入的图像分辨率为 224x224 3通道
--reverse_input_channels 告诉IE runtime 预处理时把颜色通道交换一下,因为我用的是OpenCV做的图像处理,图像数据默认是BGR排列的,需要转成RGB再去推理
--mean_values=data[123.675,116.28,103.53] --scale_values=data[58.395,57.12,57.375] 前面算出来的归一化参数
最后把转换出来的模型用accuracy_check一下精度, 输入的图像只做一个224x224的缩放,剩下的都交给IE推理的runtime去做
得到结果
很不错 :)