• 将数据预处理嵌入AI模型的常见技巧


    作者:战鹏州  英特尔物联网行业创新大使

    目录

    1.1 用模型优化器实现数据预处理

    1.1.1 模型优化器预处理参数

    1.1.2 将ResNet模型的预处理嵌入模型

    1.2 用OpenVINO™ 预处理API实现数据预处理

    1.3 使用模型缓存技术进一步缩短首次推理时延

    1.4 总结


            本文将介绍基于OpenVINO™ 模型优化器或预处理API将数据预处理嵌入AI模型的常见技巧,帮助读者在硬件投入不变的情况下,进一步提升端到端的AI推理程序的性能。本文所有范例程序已开源:https://gitee.com/ppov-nuc/resnet_ov_ppp.git,并在基于第12 代英特尔® 酷睿™ 处理器的AI开发者套件上完成测试。

            以YOLOv5模型转换为例,使用模型优化器命令:

    mo --input_model yolov5s.onnx --data_type FP16

            将yolov5s.onnx模型转为IR格式模型,并将模型精度从FP32转为FP16——这是最常见的模型优化器使用方式。基于上述IR模型,在编写AI推理程序时,由于图像数据的数值精度和形状,跟模型输入节点要求的数值精度和形状不一样,所以还需要将数据在输入模型前对其进行预处理。

            以YOLOv5模型为例,使用YOLOv5代码仓自带的zidane.jpg图像,打印出图像的数值精度和形状,以及模型输入节点的数值精度和形状,对比如下,如图1-1所示。

    图1-1  OpenCV读入的图像 vs 模型输入节点

             从上图可以看出,通过OpenCV的imread()函数读取的图像数据,在数据形状、数值精度、数值范围等地方,与模型输入节点的要求不一样,如下表所示

    OpenCV读取的图像数据

    YOLOv5模型输入节点

    数据形状(Shape)

    [720, 1280, 3]

    [1, 3, 640, 640]

    数值精度(dtype)

    UINT8

    FP32

    数值范围

    0 - 255

    0.0 - 1.0

    颜色通道顺序

    BGR

    RGB

    数据布局(Layout)

    HWC

    NCHW

            由于存在上述的差异,数据在传入模型前必须进行预处理,以满足模型输入节点的要求。数据预处理可以在推理代码中编程实现,也可以用模型优化器实现,或者用OpenVINO™ 预处理API实现,本文将依次详细介绍。

    1.1 用模型优化器实现数据预处理

    1.1.1 模型优化器预处理参数

            模型优化器可以将颜色通道顺序调整、图像数据归一化等预处理操作嵌入模型,参考《OpenVINO™ 模型转换技术要点解读》。通过指定参数:

    • --mean_values:所有输入数据将减去mean_values, 即 input - mean_values
    • --scale_values:所有输入数据将除以scales_values,当同时指定mean_values和scale_values时,模型优化器执行(input - mean_values)÷scales_values
    • --reverse_input_channels:将输入通道顺序从RGB转换为BGR(反之亦然)

    当上述三个操作同时指定时,预处理顺序为:

    输入数据→reverse_input_channels→mean_values→scale_values→原始模型

            在转换模型时,假设推理程序使用OpenCV库读取图像,则可以在模型优化器中增加mean_valuesscale_valuesreverse_input_channels三个参数,把颜色通道顺序调整和图像数据归一化操作嵌入模型。若推理程序使用非OpenCV库读取图像,例如PIL.Image,则无需添加--reverse_input_channels参数。

            下面本文将以ResNet模型为例,展示使用模型优化器将预处理嵌入模型的完整过程。

    1.1.2 将ResNet模型的预处理嵌入模型

            ResNet不仅是2015ILSVRC大赛冠军,还是产业实践中常用的卷积神经网络模型。PyTorch已将ResNet集成到torchvision中,将PyTorch格式的ResNet模型转为ONNX格式,完整代码如下:

    1. from torchvision.models import resnet50, ResNet50_Weights
    2. import torch
    3. # https://pytorch.org/vision/stable/models/generated/torchvision.models.resnet50.html
    4. weights = ResNet50_Weights.IMAGENET1K_V2
    5. model = resnet50(weights=weights, progress=False).cpu().eval()
    6. # define input and output node
    7. dummy_input = torch.randn(1, 3, 224, 224, device="cpu")
    8. input_names, output_names = ["images"], ['output']
    9. torch.onnx.export(model,
    10. dummy_input,
    11. "resnet50.onnx",
    12. verbose=True,
    13. input_names=input_names,
    14. output_names=output_names,
    15. opset_version=13
    16. )

            在导出PyTorch格式模型为ONNX格式时,需要注意的是算子版本(opset_version)最好≥11。另外,OpenVINO2022.2支持ONNX 1.8.1,即opset_version=13, 所以本文将opset_version设置为13

            基于ImageNet 1k数据集训练的ResNet模型的归一化参数为:

    • mean_values= [123.675,116.28,103.53]
    • scale_values=[58.395,57.12,57.375]

            将ONNX模型转换为OpenVINO IR模型的命令为:

    mo -m resnet50.onnx --mean_values=[123.675,116.28,103.53] --scale_values=[58.395,57.12,57.375]  --data_type FP16 --reverse_input_channels

    当获得ResNet50的IR模型后,可以使用下面的程序,完成推理计算

    1. from openvino.runtime import Core
    2. import cv2
    3. import numpy as np
    4. core = Core()
    5. resnet50 = core.compile_model("resnet50.xml", "CPU")
    6. output_node = resnet50.outputs[0]
    7. # Resize
    8. img = cv2.resize(cv2.imread("cat.jpg"), [224,224])
    9. # Layout: HWC -> NCHW
    10. blob = np.expand_dims(np.transpose(img, (2,0,1)), 0)
    11. result = resnet50(blob)[output_node]
    12. print(np.argmax(result))

            在上面的推理代码中,仍有调整图像尺寸,改变图像数据布局等操作在推理代码中实现,接下来,本文将介绍用OpenVINO™ 预处理API,将更多预处理操作嵌入模型中。

    1.2 用OpenVINO™ 预处理API实现数据预处理

            从OpenVINO™ 2022.1开始,OpenVINO提供一套预处理API,将数据预处理嵌入模型,参考《使用OpenVINO™ 预处理API进一步提升YOLOv5推理性能》。将数据预处理嵌入模型的好处是:

    • 提高AI模型的移植性(推理代码无需考虑编写预处理程序)
    • 提高推理设备(例如,英特尔®集成显卡/独立显卡)的利用率
    • 提高AI程序端到端的性能

            使用OpenVINO™ 预处理API将预处理嵌入模型的完整范例程序export_resnet_ov_ppp.py,如下所示:

    1. from openvino.preprocess import PrePostProcessor, ColorFormat, ResizeAlgorithm
    2. from openvino.runtime import Core, Layout, Type, serialize
    3. # ======== Step 0: read original model =========
    4. core = Core()
    5. model = core.read_model("resnet50.onnx")
    6. # ======== Step 1: Preprocessing ================
    7. ppp = PrePostProcessor(model)
    8. # Declare section of desired application's input format
    9. ppp.input("images").tensor() \
    10. .set_element_type(Type.u8) \
    11. .set_spatial_dynamic_shape() \
    12. .set_layout(Layout('NHWC')) \
    13. .set_color_format(ColorFormat.BGR)
    14. # Specify actual model layout
    15. ppp.input("images").model().set_layout(Layout('NCHW'))
    16. # Explicit preprocessing steps. Layout conversion will be done automatically as last step
    17. ppp.input("images").preprocess() \
    18. .convert_element_type() \
    19. .convert_color(ColorFormat.RGB) \
    20. .resize(ResizeAlgorithm.RESIZE_LINEAR) \
    21. .mean([123.675, 116.28, 103.53]) \
    22. .scale([58.624, 57.12, 57.375])
    23. # Dump preprocessor
    24. print(f'Dump preprocessor: {ppp}')
    25. model = ppp.build()
    26. # ======== Step 2: Save the model with preprocessor================
    27. serialize(model, 'resnet50_ppp.xml', 'resnet50_ppp.bin')

            export_resnet_ov_ppp.py运行结果,如下图所示:

            从上面的代码可见,使用OpenVINO™预处理API,可以将图像尺寸调整、彩色通道转换、数据归一化、数据布局转换全部集成到模型中,并且无需运行模型优化器,即可以将ONNX模型导出为IR模型。

            基于resnet50_ppp.xml的完整推理程序,如下所示:

    1. from openvino.runtime import Core
    2. import cv2
    3. import numpy as np
    4. core = Core()
    5. resnet50_ppp = core.compile_model("resnet50_ppp.xml", "CPU")
    6. output_node = resnet50_ppp.outputs[0]
    7. blob = np.expand_dims(cv2.imread("cat.jpg"),0)
    8. result = resnet50_ppp(blob)[output_node]
    9. print(np.argmax(result))

        如上所示,基于内嵌预处理的IR模型,OpenVINO推理程序变得更加简单清晰,易读易懂了。五行Python核心代码实现了内嵌预处理的ResNet模型推理!

    1.3 使用模型缓存技术进一步缩短首次推理时延

            在《在蝰蛇峡谷上实现YOLOv5模型的OpenVINO异步推理程序》讨论了AI应用程序端到端的性能。对于首次推理时延来说,模型的载入和编译时间,会极大增加首次推理的端到端的运行时间。

            使用模型缓存技术,将极大的缩短首次推理时延,如下图所示。

            使用模型缓存技术,只需要添加一行代码:core.set_property({'CACHE_DIR': './cache/ppp'}),完整范例代码如下所示:

    1. from openvino.runtime import Core
    2. import cv2
    3. import numpy as np
    4. core = Core()
    5. core.set_property({'CACHE_DIR': './cache/ppp'}) # 使用模型缓存技术
    6. resnet50_ppp = core.compile_model("resnet50_ppp.xml", "CPU")
    7. output_node = resnet50_ppp.outputs[0]
    8. blob = np.expand_dims(cv2.imread("cat.jpg"),0)
    9. result = resnet50_ppp(blob)[output_node]
    10. print(np.argmax(result))

            当第二次运行推理程序时,OpenVINO™ 运行时将从缓存文件夹直接加载已编译好的模型,极大的优化了首次推理时延。

    1.4 总结

    本文详细介绍了通过模型优化器和OpenVINO™ 预处理API,将数据预处理嵌入AI模型的技术。将数据预处理嵌入模型,简化了推理程序编写,提升推理计算设备利用率并提升了AI程序端到端的性能。最后,本文还介绍了通过模型缓存技术,进一步优化AI程序端到端的首次推理时延性能。

  • 相关阅读:
    MySQL安装及应用合集(5):如何优雅地写MySQL
    Promise原理、以及Promise.race、Promise.all、Promise.resolve、Promise.reject实现;
    Redis数据持久化
    Python学习笔记——MYSQL,SQL核心
    kafka零拷贝sendfile及mmap简述
    MySQL高级:存储过程
    便捷查询中通快递,详细物流信息轻松获取
    [Qualcomm][Network] 网络拨号失败和netmgrd服务分析
    【源码及课件分享】Java实战项目之新冠疫情统计系统
    ubuntu20.04“E: 软件包 vim 没有可安装候选”“/etc/apt/sources.list:7 中被配置了多次”解决方法
  • 原文地址:https://blog.csdn.net/gc5r8w07u/article/details/128186897