1.概述

深度学习的最终目的是要实现模型的部署,通常人们会将模型部署在手机端、开发板,嵌入式设备上,但是这些设备上由于框架的规模,环境依赖,算力的限制,无法直接使用训练好的权重进行推理,因此需要将得到的权重进行变换才能使模型可以成功部署在上述设备上。
在这里插入图片描述

将PyTorch训练好的模型转换为ONNX 格式,然后使用ONNX Runtime运行它进行推理。

  1. 模型部署的整体流程
  2. 使用torch.onnx进行模型格式的转化
  3. 使用ONNX Runtime进行模型推理
  4. 完整的官方代码解释

2.ONNX和ONNX Runtime简介

2.1 ONNX简介

ONNX( Open Neural Network Exchange) 是 Facebook (现Meta) 和微软在2017年共同发布的,用于标准描述计算图的一种格式。ONNX通过定义一组与环境和平台无关的标准格式,使AI模型可以在不同框架和环境下交互使用,ONNX可以看作深度学习框架和部署端的桥梁,就像编译器的中间语言一样。由于各框架兼容性不一,通常只用 ONNX 表示更容易部署的静态图。硬件和软件厂商只需要基于ONNX标准优化模型性能,让所有兼容ONNX标准的框架受益。目前,ONNX主要关注在模型预测方面,使用不同框架训练的模型,转化为ONNX格式后,可以很容易的部署在兼容ONNX的运行环境中。目前,在微软,亚马逊 ,Meta 和 IBM 等公司和众多开源贡献的共同维护下,ONNX 已经对接了下图的多种深度学习框架和多种推理引擎。

在这里插入图片描述

2.2 ONNX Runtime简介

ONNX Runtime 是由微软维护的一个跨平台机器学习推理加速器,它直接对接ONNX,可以直接读取.onnx文件并实现推理,不需要再把 .onnx 格式的文件转换成其他格式的文件。PyTorch借助ONNX Runtime也完成了部署的最后一公里,构建了 PyTorch --> ONNX --> ONNX Runtime 部署流水线,只需要将模型转换为 .onnx 文件,并在 ONNX Runtime 上运行模型即可。

2.3 ONNX和ONNX Runtime的安装

ONNX和ONNX Runtime作为python的一个包与其他包的安装方法相同,可以选择使用conda或者pip进行安装,只需要输入以下命令即可:

# 激活虚拟环境
conda activate env_name # env_name换成环境名称
# 安装onnx
pip install onnx 
# 安装onnx runtime
pip install onnxruntime # 使用CPU进行推理
# pip install onnxruntime-gpu # 使用GPU进行推理

除此之外,还需要注意ONNX和ONNX Runtime之间的适配关系。

ToolRecommended VersionSupported ONNX version(s)
PyTorchLatest stable1.2-1.6
ONNXMLTools
CoreML, LightGBM, XGBoost, LibSVM
Latest stable1.2-1.6
ONNXMLTools
SparkML
Latest stable1.4-1.5
SKLearn-ONNXLatest stable1.2-1.6
Keras-ONNXLatest stable1.2-1.6
Tensorflow-ONNXLatest stable1.2-1.6
WinMLToolsLatest stable1.2-1.6
Paddle2ONNXLatest stable1.6-1.9
AutoML1.0.39+1.5

使用GPU进行推理时,需要先将安装的onnxruntime卸载,再安装onnxruntime-gpu,同时还需要考虑ONNX Runtime与CUDA之间的适配关系,ONNX Runtime和CUDA之间的适配关系如下表所示:
在这里插入图片描述
在这里插入图片描述

3 模型导出为ONNX

3.1 模型转换为ONNX格式

使用torch.onnx.export()把模型转换成 ONNX 格式的函数。模型导成onnx格式前,必须调用model.eval()或者model.train(False)以确保模型处在推理模式下,避免因为dropoutbatchnorm等运算符在推理和训练模式下的不同产生错误。

import torch.onnx 
# 转换的onnx格式的名称,文件后缀需为.onnx
onnx_file_name = "xxxxxx.onnx"
# 需要转换的模型,将torch_model设置为自己的模型
model = torch_model
# 加载权重,将model.pth转换为自己的模型权重
# 如果模型的权重是使用多卡训练出来,需要去除权重中多的module. 
model = model.load_state_dict(torch.load("model.pth"))
# 导出模型前,必须调用model.eval()或者model.train(False)
model.eval()
# dummy_input就是一个输入的实例,仅提供输入shape、type等信息 
batch_size = 1 # 随机的取值,当设置dynamic_axes后影响不大
dummy_input = torch.randn(batch_size, 1, 224, 224, requires_grad=True) 
# 这组输入对应的模型输出
output = model(dummy_input)
# 导出模型
torch.onnx.export(model,        # 模型的名称
                  dummy_input,   # 一组实例化输入
                  onnx_file_name,   # 文件保存路径/名称
                  export_params=True,        #  如果指定为True或默认, 参数也会被导出. 如果你要导出一个没训练过的就设为 False.
                  opset_version=10,          # ONNX 算子集的版本,当前已更新到15
                  do_constant_folding=True,  # 是否执行常量折叠优化
                  input_names = ['input'],   # 输入模型的张量的名称
                  output_names = ['output'], # 输出模型的张量的名称
                  # dynamic_axes将batch_size的维度指定为动态,
                  # 后续进行推理的数据可以与导出的dummy_input的batch_size不同
                  dynamic_axes={'input' : {0 : 'batch_size'},    
                                'output' : {0 : 'batch_size'}})

3.2 ONNX模型的检验

当上述代码运行成功后,会得到一个ONNX 模型文件。需要检测模型文件是否可用,通过onnx.checker.check_model()进行检验,具体方法如下:

import onnx
# 可以使用异常处理的方法进行检验
try:
    # 当模型不可用时,将会报出异常
    onnx.checker.check_model(self.onnx_model)
except onnx.checker.ValidationError as e:
    print("The model is invalid: %s"%e)
else:
    # 模型可用时,将不会报出异常,并会输出“The model is valid!”
    print("The model is valid!")

3.3 ONNX可视化

在将模型导出为onnx格式后,利用Netron可以实现onnx的可视化。

  • Netron下载网址:https://github.com/lutzroeder/netron
    在这里插入图片描述

使用Netron进行可视化后,不仅能看到整体模型的架构,还能看到每一个节点的信息本文。以Netron官方提供的squeezenet为例进行介绍。

在这里插入图片描述

第二幅图显示了第一个Conv的信息包括kernel_size,strides,input,output等信息,同理点击其他节点时也可以显示该节点的信息。

在这里插入图片描述

3.4 使用ONNX Runtime进行推理

通过以上的操作,已经完成了PyTorch的模型到ONNX模型的转换,并通过Netron可视化和onnx.checker.check_model()检查了模型的正确性。在这一步,将使用ONNX Runtime运行一下转化后的模型,看一下推理后的结果。

# 导入onnxruntime
import onnxruntime
# 需要进行推理的onnx模型文件名称
onnx_file_name = "xxxxxx.onnx"

# onnxruntime.InferenceSession用于获取一个 ONNX Runtime 推理器
ort_session = onnxruntime.InferenceSession(onnx_file_name)  

# 构建字典的输入数据,字典的key需要与构建onnx模型时的input_names相同
# 输入的input_img 也需要改变为ndarray格式
ort_inputs = {'input': input_img} 
# 更建议使用下面这种方法,因为避免了手动输入key
# ort_inputs = {ort_session.get_inputs()[0].name:input_img}

# run是进行模型的推理,第一个参数为输出张量名的列表,一般情况可以设置为None
# 第二个参数为构建的输入值的字典
# 由于返回的结果被列表嵌套,因此需要进行[0]的索引
ort_output = ort_session.run(None,ort_inputs)[0]
# output = {ort_session.get_outputs()[0].name}
# ort_output = ort_session.run([output], ort_inputs)[0]

在上述的步骤中,有几个需要注意的点:

  1. PyTorch模型的输入为tensor,而ONNX的输入为array,因此需要对张量进行变换或者直接将数据读取为array格式,可以实现下面的方式进行张量到array的转化。
def to_numpy(tensor):
    return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()
  1. 输入的array的shape应该和导出模型的dummy_input的shape相同,如果图片大小不一样,应该先进行resize操作。
  2. run的结果是一个列表,需要进行索引操作才能获得array格式的结果。
  3. 在构建输入的字典时,需要注意字典的key应与导出ONNX格式设置的input_name相同,因更建议使用上述的第二种方法构建输入的字典。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐