• pytorch模型(.pt)转onnx模型(.onnx)的方法详解(1)


    1. pytorch模型转换到onnx模型

    2.运行onnx模型

    3.比对onnx模型和pytorch模型的输出结果

     我这里重点是第一点和第二点,第三部分  比较容易

    首先你要安装 依赖库:onnx 和 onnxruntime

    1. pip install onnx
    2. pip install onnxruntime 进行安装

    也可以使用清华源镜像文件安装  速度会快些。

    开始:

    1. pytorch模型转换到onnx模型

    pytorch 转 onnx 仅仅需要一个函数 torch.onnx.export 

    torch.onnx.export(model, args, path, export_params, verbose, input_names, output_names, do_constant_folding, dynamic_axes, opset_version)
    

    参数说明:

    • model——需要导出的pytorch模型
    • args——模型的输入参数,满足输入层的shape正确即可。
    • path——输出的onnx模型的位置。例如‘yolov5.onnx’。
    • export_params——输出模型是否可训练。default=True,表示导出trained model,否则untrained。
    • verbose——是否打印模型转换信息。default=False。
    • input_names——输入节点名称。default=None。
    • output_names——输出节点名称。default=None。
    • do_constant_folding——是否使用常量折叠,默认即可。default=True。
    • dynamic_axes——模型的输入输出有时是可变的,如Rnn,或者输出图像的batch可变,可通过该参数设置。如输入层的shape为(b,3,h,w),batch,height,width是可变的,但是chancel是固定三通道。
      格式如下 :
      1)仅list(int) dynamic_axes={‘input’:[0,2,3],‘output’:[0,1]}
      2)仅dict dynamic_axes={‘input’:{0:‘batch’,2:‘height’,3:‘width’},‘output’:{0:‘batch’,1:‘c’}}
      3)mixed dynamic_axes={‘input’:{0:‘batch’,2:‘height’,3:‘width’},‘output’:[0,1]}
    • opset_version——opset的版本,低版本不支持upsample等操作。

    转化代码:参考1:

    1. import torch
    2. import torch.nn
    3. import onnx
    4. model = torch.load('best.pt')
    5. model.eval()
    6. input_names = ['input']
    7. output_names = ['output']
    8. x = torch.randn(1,3,32,32,requires_grad=True)
    9. torch.onnx.export(model, x, 'best.onnx', input_names=input_names, output_names=output_names, verbose='True')

     参考2:PlainC3AENetCBAM 是网络模型,如果你没有自己的网络模型,可能成功不了

    1. import io
    2. import torch
    3. import torch.onnx
    4. from models.C3AEModel import PlainC3AENetCBAM
    5. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    6. def test():
    7. model = PlainC3AENetCBAM()
    8. pthfile = r'/home/joy/Projects/models/emotion/PlainC3AENet.pth'
    9. loaded_model = torch.load(pthfile, map_location='cpu')
    10. # try:
    11. # loaded_model.eval()
    12. # except AttributeError as error:
    13. # print(error)
    14. model.load_state_dict(loaded_model['state_dict'])
    15. # model = model.to(device)
    16. #data type nchw
    17. dummy_input1 = torch.randn(1, 3, 64, 64)
    18. # dummy_input2 = torch.randn(1, 3, 64, 64)
    19. # dummy_input3 = torch.randn(1, 3, 64, 64)
    20. input_names = [ "actual_input_1"]
    21. output_names = [ "output1" ]
    22. # torch.onnx.export(model, (dummy_input1, dummy_input2, dummy_input3), "C3AE.onnx", verbose=True, input_names=input_names, output_names=output_names)
    23. torch.onnx.export(model, dummy_input1, "C3AE_emotion.onnx", verbose=True, input_names=input_names, output_names=output_names)
    24. if __name__ == "__main__":
    25. test()

    直接将PlainC3AENetCBAM替换成需要转换的模型,然后修改pthfile,输入和onnx模型名字然后执行即可。

    注意:上面代码中注释的dummy_input2,dummy_input3,torch.onnx.export对应的是多个输入的例子。

    在转换过程中遇到的问题汇总

    RuntimeError: Failed to export an ONNX attribute, since it's not constant, please try to make things (e.g., kernel size) static if possible

    在转换过程中遇到RuntimeError: Failed to export an ONNX attribute, since it's not constant, please try to make things (e.g., kernel size) static if possible的错误。

    我成功的案例,我直接把我训练的网络贴上,成功转换,没有from **   import 模型名词这么委婉,合法,我的比较粗暴

    1. import torch
    2. import torch.nn
    3. import onnx
    4. from torchvision import transforms
    5. import torch.nn as nn
    6. from torch.nn import Sequential
    7. # 添加模型
    8. # 设置数据转换方式
    9. preprocess_transform = transforms.Compose([
    10. transforms.ToTensor(), # 把数据转换为张量(Tensor)
    11. transforms.Normalize( # 标准化,即使数据服从期望值为 0,标准差为 1 的正态分布
    12. mean=[0.5, ], # 期望
    13. std=[0.5, ] # 标准差
    14. )
    15. ])
    16. class CNN(nn.Module): # 从父类 nn.Module 继承
    17. def __init__(self): # 相当于 C++ 的构造函数
    18. # super() 函数是用于调用父类(超类)的一个方法,是用来解决多重继承问题的
    19. super(CNN, self).__init__()
    20. # 第一层卷积层。Sequential(意为序列) 括号内表示要进行的操作
    21. self.conv1 = Sequential(
    22. nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=1),
    23. nn.BatchNorm2d(64),
    24. nn.ReLU(),
    25. nn.MaxPool2d(kernel_size=2, stride=2)
    26. )
    27. # 第二卷积层
    28. self.conv2 = Sequential(
    29. nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
    30. nn.BatchNorm2d(128),
    31. nn.ReLU(),
    32. nn.MaxPool2d(kernel_size=2, stride=2)
    33. )
    34. # 全连接层(Dense,密集连接层)
    35. self.dense = Sequential(
    36. nn.Linear(7 * 7 * 128, 1024),
    37. nn.ReLU(),
    38. nn.Dropout(p=0.5),
    39. nn.Linear(1024, 10)
    40. )
    41. def forward(self, x): # 正向传播
    42. x1 = self.conv1(x)
    43. x2 = self.conv2(x1)
    44. x = x2.view(-1, 7 * 7 * 128)
    45. x = self.dense(x)
    46. return x
    47. # 训练
    48. # 训练和参数优化
    49. # 定义求导函数
    50. def get_Variable(x):
    51. x = torch.autograd.Variable(x) # Pytorch 的自动求导
    52. # 判断是否有可用的 GPU
    53. return x.cuda() if torch.cuda.is_available() else x
    54. # 判断是否GPU
    55. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    56. # device1 = torch.device('cpu')
    57. # 定义网络
    58. model = CNN()
    59. loaded_model = torch.load('save_model/model.pth', map_location='cuda:0')
    60. model.load_state_dict(loaded_model)
    61. model.eval()
    62. input_names = ['input']
    63. output_names = ['output']
    64. # x = torch.randn(1,3,32,32,requires_grad=True)
    65. x = torch.randn(1, 1, 28, 28, requires_grad=True) # 这个要与你的训练模型网络输入一致。我的是黑白图像
    66. torch.onnx.export(model, x, 'save_model/model.onnx', input_names=input_names, output_names=output_names, verbose='True')

    前提是你要准备好*.pth模型保持文件

    输出结果:

    1. graph(%input : Float(1, 1, 28, 28, strides=[784, 784, 28, 1], requires_grad=1, device=cpu),
    2. %dense.0.weight : Float(1024, 6272, strides=[6272, 1], requires_grad=1, device=cpu),
    3. %dense.0.bias : Float(1024, strides=[1], requires_grad=1, device=cpu),
    4. %dense.3.weight : Float(10, 1024, strides=[1024, 1], requires_grad=1, device=cpu),
    5. %dense.3.bias : Float(10, strides=[1], requires_grad=1, device=cpu),
    6. %33 : Float(64, 1, 3, 3, strides=[9, 9, 3, 1], requires_grad=0, device=cpu),
    7. %34 : Float(64, strides=[1], requires_grad=0, device=cpu),
    8. %36 : Float(128, 64, 3, 3, strides=[576, 9, 3, 1], requires_grad=0, device=cpu),
    9. %37 : Float(128, strides=[1], requires_grad=0, device=cpu)):
    10. %input.4 : Float(1, 64, 28, 28, strides=[50176, 784, 28, 1], requires_grad=1, device=cpu) = onnx::Conv[dilations=[1, 1], group=1, kernel_shape=[3, 3], pads=[1, 1, 1, 1], strides=[1, 1]](%input, %33, %34) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\modules\conv.py:443:0
    11. %21 : Float(1, 64, 28, 28, strides=[50176, 784, 28, 1], requires_grad=1, device=cpu) = onnx::Relu(%input.4) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\functional.py:1442:0
    12. %input.8 : Float(1, 64, 14, 14, strides=[12544, 196, 14, 1], requires_grad=1, device=cpu) = onnx::MaxPool[kernel_shape=[2, 2], pads=[0, 0, 0, 0], strides=[2, 2]](%21) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\functional.py:797:0
    13. %input.16 : Float(1, 128, 14, 14, strides=[25088, 196, 14, 1], requires_grad=1, device=cpu) = onnx::Conv[dilations=[1, 1], group=1, kernel_shape=[3, 3], pads=[1, 1, 1, 1], strides=[1, 1]](%input.8, %36, %37) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\modules\conv.py:443:0
    14. %25 : Float(1, 128, 14, 14, strides=[25088, 196, 14, 1], requires_grad=1, device=cpu) = onnx::Relu(%input.16) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\functional.py:1442:0
    15. %26 : Float(1, 128, 7, 7, strides=[6272, 49, 7, 1], requires_grad=1, device=cpu) = onnx::MaxPool[kernel_shape=[2, 2], pads=[0, 0, 0, 0], strides=[2, 2]](%25) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\functional.py:797:0
    16. %27 : Long(2, strides=[1], device=cpu) = onnx::Constant[value= -1 6272 [ CPULongType{2} ]]() # E:/paddle_project/Pytorch_Imag_Classify/zifu_fenlei/CNN/pt模型转onnx模型.py:51:0
    17. %28 : Float(1, 6272, strides=[6272, 1], requires_grad=1, device=cpu) = onnx::Reshape(%26, %27) # E:/paddle_project/Pytorch_Imag_Classify/zifu_fenlei/CNN/pt模型转onnx模型.py:51:0
    18. %input.20 : Float(1, 1024, strides=[1024, 1], requires_grad=1, device=cpu) = onnx::Gemm[alpha=1., beta=1., transB=1](%28, %dense.0.weight, %dense.0.bias) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\modules\linear.py:103:0
    19. %input.24 : Float(1, 1024, strides=[1024, 1], requires_grad=1, device=cpu) = onnx::Relu(%input.20) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\functional.py:1442:0
    20. %output : Float(1, 10, strides=[10, 1], requires_grad=1, device=cpu) = onnx::Gemm[alpha=1., beta=1., transB=1](%input.24, %dense.3.weight, %dense.3.bias) # D:\ProgramData\Anaconda3\envs\openmmlab\lib\site-packages\torch\nn\modules\linear.py:103:0
    21. return (%output)

    输出结果的device  是CPU,模型加载的时候是GPU。这就是转换的意义吧

    2.运行onnx模型

    1. import onnx
    2. import onnxruntime as ort
    3. model = onnx.load('best.onnx')
    4. onnx.checker.check_model(model)
    5. session = ort.InferenceSession('best.onnx')
    6. x=np.random.randn(1,3,32,32).astype(np.float32) # 注意输入type一定要np.float32!!!!!
    7. # x= torch.randn(batch_size,chancel,h,w)
    8. outputs = session.run(None,input = { 'input' : x })

    参考:

    Pytorch模型转onnx模型实例_python_脚本之家 (jb51.net)

    pytorch模型转onnx模型的方法详解_python_脚本之家 (jb51.net)

  • 相关阅读:
    【无标题】
    JVM相关面试题及常用命令参数
    论文解读:《DeepKla:一种基于注意力机制的深度神经网络,用于蛋白质赖氨酸乳酸化位点预测》
    查看双翌视觉软件版本号
    Python零基础入门篇 · 21】:构造函数、类属性和实例属性的访问
    Maven常用命令、坐标、依赖管理、依赖范围
    嵌入式面经111题答案汇总(含技术答疑)_嵌入式项目源码分享
    Go 与 Rust:导航编程语言景观
    工具篇 | 基于SpringBoot的H2数据库入门实战
    《Neo4j全站开发》笔记
  • 原文地址:https://blog.csdn.net/Vertira/article/details/127601368