0


Torch 模型 onnx 文件的导出和调用

Open Neural Network Exchange (ONNX,开放神经网络交换) 格式,是一个用于表示深度学习模型的标准,可使模型在不同框架之间进行转移

Torch 所定义的模型为动态图,其前向传播是由类方法定义和实现的

但是 Python 代码的效率是比较底下的,试想把动态图转化为静态图,模型的推理速度应当有所提升

Torch 框架中,torch.onnx.export 可以将父类为 nn.Module 的模型导出到 onnx 文件中,最重要的有三个参数:

  • model:父类为 nn.Module 的模型
  • args:传入 model 的 forward 方法的变量列表,类型应为 tuple
  • f:onnx 文件名称的字符串
  1. import torch
  2. from torchvision.models import resnet50
  3. file = 'resnet.onnx'
  4. # 声明模型
  5. resnet = resnet50(pretrained=False).eval()
  6. image = torch.rand([1, 3, 224, 224])
  7. # 导出为 onnx 文件
  8. torch.onnx.export(resnet, (image,), file)

onnx 文件可被 Netron 打开,以查看模型结构

基本用法

要在 Python 中运行 onnx 模型,需要下载 onnxruntime

  1. # 选其一即可
  2. pip install onnxruntime # CPU 版本
  3. pip install onnxruntime-gpu # GPU 版本

推理时需要借助其中的 InferenceSession,其中较为重要的实例方法有:

  • get_inputs():得到输入变量的列表 (变量属性:name、shape、type)
  • get_outputs():得到输入变量的列表 (变量属性:name、shape、type)
  • run(output_names, input_feed):输入变量为 numpy.ndarray (注意 dtype 应为 float32),使用模型推理并返回输出

可得出 onnx 模型的基本用法:

  1. import onnxruntime as ort
  2. import numpy as np
  3. file = 'resnet.onnx'
  4. # 找到 GPU / CPU
  5. provider = ort.get_available_providers()[
  6. 1 if ort.get_device() == 'GPU' else 0]
  7. print('设备:', provider)
  8. # 声明 onnx 模型
  9. model = ort.InferenceSession(file, providers=[provider])
  10. # 参考: ort.NodeArg
  11. for node_list in model.get_inputs(), model.get_outputs():
  12. for node in node_list:
  13. attr = {'name': node.name,
  14. 'shape': node.shape,
  15. 'type': node.type}
  16. print(attr)
  17. print('-' * 60)
  18. # 得到输入、输出结点的名称
  19. input_node_name = model.get_inputs()[0].name
  20. ouput_node_name = [node.name for node in model.get_outputs()]
  21. image = np.random.random([1, 3, 224, 224]).astype(np.float32)
  22. print(model.run(output_names=ouput_node_name,
  23. input_feed={input_node_name: image}))

高级 API

为了简化使用步骤,使用类进行封装:

  1. class Onnx_Module(ort.InferenceSession):
  2. ''' onnx 推理模型
  3. provider: 优先使用 GPU'''
  4. provider = ort.get_available_providers()[
  5. 1 if ort.get_device() == 'GPU' else 0]
  6. def __init__(self, file):
  7. super(Onnx_Module, self).__init__(file, providers=[self.provider])
  8. # 参考: ort.NodeArg
  9. self.inputs = [node_arg.name for node_arg in self.get_inputs()]
  10. self.outputs = [node_arg.name for node_arg in self.get_outputs()]
  11. def __call__(self, *arrays):
  12. input_feed = {name: x for name, x in zip(self.inputs, arrays)}
  13. return self.run(self.outputs, input_feed)

在 Torch 中,对于卷积神经网络 model 与图像 image,推理的代码为 "model(image)",而使用这个封装的类也是类似:

  1. import numpy as np
  2. file = 'resnet.onnx'
  3. model = Onnx_Module(file)
  4. image = np.random.random([1, 3, 224, 224]).astype(np.float32)
  5. print(model(image))

为了方便观察 Torch 模型与 onnx 模型的速度差异,同时检查两个模型的输出是否一致,又编写了 test 函数

test 方法的参数与 torch.onnx.export 一致,其基本流程为:

  • 得到 Torch 模型的输出,并 print 推断耗时
  • 将 Torch 模型导出为 onnx 文件,将输入变量中的 torch.tensor 转化为 numpy.ndarray
  • 初始化 onnx 模型,得到 onnx 模型的输出,并 print 推断耗时
  • 计算 Torch 模型与 onnx 模型输出的绝对误差的均值
  • 将 onnx 模型 return
  1. class Timer:
  2. repeat = 3
  3. def __new__(cls, fun, *args, **kwargs):
  4. import time
  5. start = time.time()
  6. for _ in range(cls.repeat): fun(*args, **kwargs)
  7. cost = (time.time() - start) / cls.repeat
  8. return cost * 1e3 # ms
  9. class Onnx_Module(ort.InferenceSession):
  10. ''' onnx 推理模型
  11. provider: 优先使用 GPU'''
  12. provider = ort.get_available_providers()[
  13. 1 if ort.get_device() == 'GPU' else 0]
  14. def __init__(self, file):
  15. super(Onnx_Module, self).__init__(file, providers=[self.provider])
  16. # 参考: ort.NodeArg
  17. self.inputs = [node_arg.name for node_arg in self.get_inputs()]
  18. self.outputs = [node_arg.name for node_arg in self.get_outputs()]
  19. def __call__(self, *arrays):
  20. input_feed = {name: x for name, x in zip(self.inputs, arrays)}
  21. return self.run(self.outputs, input_feed)
  22. @classmethod
  23. def test(cls, model, args, file, **export_kwargs):
  24. # 测试 Torch 的运行时间
  25. torch_output = model(*args).data.numpy()
  26. print(f'Torch: {Timer(model, *args):.2f} ms')
  27. # model: Torch -> onnx
  28. torch.onnx.export(model, args, file, **export_kwargs)
  29. # data: tensor -> array
  30. args = tuple(map(lambda tensor: tensor.data.numpy(), args))
  31. onnx_model = cls(file)
  32. # 测试 onnx 的运行时间
  33. onnx_output = onnx_model(*args)
  34. print(f'Onnx: {Timer(onnx_model, *args):.2f} ms')
  35. # 计算 Torch 模型与 onnx 模型输出的绝对误差
  36. abs_error = np.abs(torch_output - onnx_output).mean()
  37. print(f'Mean Error: {abs_error:.2f}')
  38. return onnx_model

对于 ResNet50 而言,Torch 模型的推断耗时为 172.67 ms,onnx 模型的推断耗时为 36.56 ms,onnx 模型的推断耗时仅为 Torch 模型的 21.17%


本文转载自: https://blog.csdn.net/qq_55745968/article/details/125965503
版权归原作者 荷碧TongZJ 所有, 如有侵权,请联系我们删除。

“Torch 模型 onnx 文件的导出和调用”的评论:

还没有评论