当前位置:   article > 正文

ONNX系列一:ONNX的使用,从转化到推理_onnx模型推理

onnx模型推理
onnx模型在算法岗位上属于接触到比较多的一款框架,随着接触到的框架越来越多,不免会产生一些遗忘。因此,写下这篇文章,记录下来学习onnx框架的一些心得体会。

1.pytorch转化onnx

这一步属于比较简单的步骤,代码中详细的参数参考下列表格。

  1. import torch
  2. # 模型保存时,只保存了模型的参数,官方推荐
  3. # 必须先实例化你的网络架构,然后再加载模型
  4. model = YourNet()
  5. model.load_state_dict(torch.load("path_to_yourModel.pt"))
  6. # 如果保存pt模型时保存了完成的模型,则直接使用torch.load("path_to_yourModel.pt")
  7. # 模型的输入输出名,有几个就在列表里写几个
  8. input_list = ['input']
  9. output_list=['output1', 'output2']
  10. # 模拟模型的输入
  11. x = torch.randn(1, 3, 256, 256)
  12. with torch.no_grad():
  13. torch.onnx.export(
  14. model,
  15. x,
  16. "./model/srcnn.onnx",
  17. opset_version=11,
  18. input_names=input_list,
  19. output_names=output_list)
  20. # 如果需要动态输入输出,则可以使用dynamic_axes,如:
  21. #torch.onnx.export(
  22. # model,
  23. # x,
  24. # "./model/srcnn.onnx",
  25. # opset_version=11,
  26. # input_names=input_list,
  27. # output_names=output_list,
  28. #        dynamic_axes={
  29. #        'output1': [0, 1, 2, 3]
  30. #       ' output2': [0, 1, 2, 3]}
  31. #)

参数列表如下:

参数

用法

model

pytorch模型

x

模型的任意一组输入(模拟实际输入数据的大小,比如三通道的512*512大小的图片,就可以设置为torch.randn(1, 3, 512, 512))

opset_version

onnx算子集的版本

input_names

模型的输入名称(自己定义的),如果不写,默认输出数字类型的名称

output_name

模型的输出名称(自己定义的),如果不写,默认输出数字类型的名称

dynamic_axes

设置动态输入输出,用法:"输入输出名:[支持动态的纬度]",如“支持动态的纬度设置为[0, 2, 3]”则表示第0纬,第2纬,第3维支持动态输入输出。

参数讲解:

(1)为什么需要模型的一组输入:

pytorch有两种方式转onnx:一种是trace(跟踪)(不会考虑诸如if-else的控制流),另一种是script(记录)(会记录诸如if-else的控制流)。

torch.onnx.export使用的是trace方法导出onnx,即onnx记录不考虑控制流的静态图。

trace方式的特点是:任意给点一组输入,再运行一遍模型,在模型运行的过程中,把计算图保存下来,导出模型的静态图。

script方式的特点是:通过解析模型来记录所有的控制流。

由于推理引擎对静态图比较友好,所以通常会直接使用torch.onnx.export导出静态模型。

(2)为什么ONNX 模型的每个输入和输出张量都有一个名字。

很多推理引擎在运行 ONNX 文件时,都需要以“名称-张量值”的数据对来输入数据,并根据输出张量的名称来获取输出数据。

在进行跟张量有关的设置(比如添加动态维度)时,也需要知道张量的名字。

2.简化得到的ONNX模型

经过上述步骤得到的ONNX模型可能会存在很多冗余的算子,因此对ONNX模型进行简化是非常重要的。用onnx-simplify作者的话说:“ONNX牛逼但啰嗦”,不得不说,“雀食”如此,你瞅。

2.1安装onnx-simplify

pip3 install -U pip && pip3 install onnxsim

2.2使用onnx-sim

  1. cd /path_to_your_model.onnx
  2. onnxsim your_origin_model.onnx simplified_model.onnx

3.使用ONNXRuntime进行推理

  1. import numpy as np
  2. import onnx
  3. import onnxruntime as rt
  4. import cv2
  5. input_image_path = 'path_to_test_image'
  6. ONNX_Model_Path = 'path_to_onnx_model'
  7. def onnx_infer(im, onnx_model):
  8. # InferenceSession获取onnxruntime解释器
  9. sess = rt.InferenceSession(onnx_model)
  10.     # 模型的输入输出名,必须和onnx的输入输出名相同,可以通过netron查看,如何查看参考下文
  11. input_name = "input"
  12. output_name = ['boxes', 'labels', 'scores', 'masks']
  13. # run方法用于模型推理,run(输出张量名列表,输入值字典)
  14. output = sess.run(output_name, {input_name: im})
  15. boxes = output[0]
  16. labels = output[1]
  17. scores = output[2]
  18. masks = output[3]
  19. if __name__ == '__main__':
  20. # 图片的预处理
  21. img = cv2.imread(input_image_path)
  22. img = cv2.resize(img, (256, 256))
  23. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  24. img = img.transpose(2, 0, 1)
  25. img = img.astype(np.float32) / 255
  26. img = np.expand_dims(img, axis=0) # 3维转4维
  27. output = onnx_infer(img, ONNX_Model_Path)

转换后的onnx模型可以通过netron查看,其中模型的输入输出名以你的模型上的INPUTS和OUTPUTS为主,如下:

具体模型使用OnnxRuntime进行推理,可以参考我的另一篇博客:Yolov5使用OnnxRuntime进行推理

4.ONNXRuntime 使用多核进行推理

  1. import onnxruntime as rt
  2. import onnx
  3. import numpy as np
  4. import time
  5. ONNX_Model_Path = 'path_to_onnx_model'
  6. img = np.ones((1, 3, 640, 640)).astype(np.float32)
  7. # 加载模型,传入模型路径
  8. model = onnx.load_model(ONNX_Model_Path)
  9. # 创建一个SessionOptions对象
  10. rtconfig = rt.SessionOptions()
  11. # 设置CPU线程数为4
  12. cpu_num_thread = 4
  13. # 设置执行模式为ORT_SEQUENTIAL(即顺序执行)
  14. rtconfig.intra_op_num_threads = cpu_num_thread
  15. rtconfig.execution_mode = rt.ExecutionMode.ORT_SEQUENTIAL
  16. # 设置使用的ExecutionProvider为CPUExecutionProvider
  17. providers = ['CPUExecutionProvider']
  18. # 创建一个InferenceSession对象
  19. sess = rt.InferenceSession(model.SerializeToString(), providers=providers, sess_options=rtconfig)
  20. # 模型的输入和输出节点名,可以通过netron查看
  21. input_name = 'images'
  22. outputs_name = ['output']
  23. # 模型推理:模型输出节点名,模型输入节点名,输入数据(注意节点名的格式!!!!!)
  24. net_outs = sess.run(outputs_name, {input_name: img})
  25. result = np.array(net_outs)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/371520
推荐阅读
  

闽ICP备14008679号