网上充斥着ONNX Runtime的简单科普,却没有一个系统介绍ONNX Runtime的博客,因此本博客旨在基于官方文档进行翻译与进一步的解释。ONNX runtime的官方文档:https://onnxruntime.ai/docs/
如果尚不熟悉ONNX格式,可以参照该博客专栏,本专栏对onnx 1.16文档进行翻译与进一步解释,
ONNX 1.16学习笔记专栏:https://blog.csdn.net/qq_33345365/category_12581965.html
如果觉得有收获,点赞收藏关注,目前仅在CSDN发布,本博客会分为多个章节,目前尚在连载中。
开始编辑时间:2024/3/12;最后编辑时间:2024/3/12
所有资料均来自书写时的最新官方文档内容。
本专栏链接如下所示,所有相关内容均会在此处收录。
https://blog.csdn.net/qq_33345365/category_12589378.html
介绍
参考:https://onnxruntime.ai/docs/get-started/with-python.html
本教程第一篇:介绍ONNX Runtime(ORT)的基本概念。
本教程第二篇:是一个快速指南,包括安装ONNX Runtime;安装ONNX进行模型输出;Pytorch, TensorFlow和SciKit的快速开始例子
本教程第三篇:CUDA Execution Provider
本教程第四篇:介绍EP的相关概念,包括相关的架构。
本教程第五篇:介绍Pytorch加速相关知识,首先介绍Pytorch 推理相关知识。包括比较pytorch推理,torchscipt推理和ONNXRuntime推理。
推理Pytorch模型
PyTorch领导着深度学习领域,其易于理解和灵活的API;大量现成的模型,尤其是在自然语言(NLP)领域;以及其领域特定库。一个不断增长的开发者和应用生态系统希望使用PyTorch构建的模型,本文提供了一个关于如何对PyTorch模型进行推理的快速介绍。有许多不同的方式可以进行PyTorch模型的推理,这些方式如下所示。本文假设您正在寻找有关如何对PyTorch模型进行推理的信息,而不是如何训练PyTorch模型。
目录:
- Pytorch回顾
- 推理选项
- 使用ONNXRuntime推理
Pytoch回顾
在PyTorch的核心是nn.Module
,这是一个代表整个深度学习模型或单个层的类。模块可以组合或扩展以构建模型。要编写自己的模块,您需要实现一个forward
函数,该函数根据模型输入和训练好的权重计算输出。如果您正在编写自己的PyTorch模型,那么您可能也在对其进行训练。或者,您可以使用PyTorch自身或其他库(如HuggingFace)中的预训练模型。
PyTorch本身编写图像处理模型的代码:
import torch
import torch.nn as nn
import torchvision.transforms as T
from torchvision.models import resnet18, ResNet18_Weights
class Predictor(nn.Module):
def __init__(self):
super().__init__()
weights = ResNet18_Weights.DEFAULT
self.resnet18 = resnet18(weights=weights, progress=False).eval()
self.transforms = weights.transforms()
def forward(self, x: torch.Tensor) -> torch.Tensor:
with torch.no_grad():
x = self.transforms(x)
y_pred = self.resnet18(x)
return y_pred.argmax(dim=1)
HuggingFace 库的语言模型代码:
model_name = "bert-large-uncased-whole-word-masking-finetuned-squad"
tokenizer = transformers.BertTokenizer.from_pretrained(model_name)
model = transformers.BertForQuestionAnswering.from_pretrained(model_name)
创建或导入经过训练的模型后,如何运行它来执行推理? 下面我们描述了几种可用于在 PyTorch 中执行推理的方法。
推理选项
源生Pytorch推理
如果您对性能或大小不敏感并且在包含 Python 可执行文件和库的环境中运行,则可以在本机 PyTorch 中运行应用程序。
一旦您拥有经过训练的模型,您可以使用两种方法来保存和加载模型以进行推理:
1 保存和加载整个模型
# 保存整个模型到PATH
torch.save(model, PATH)
# 从PATH加载模型,为推理设置eval模式
model = torch.load(PATH)
model.eval()
2 保存模型参数,重新声明模型,加载模型
# 保存整个模型到PATH
torch.save(model.state_dict(), PATH)
# Redeclare the model and load the saved parameters
model = TheModel(...)
model.load_state_dict(torch.load(PATH))
model.eval()
这取决于您的配置。保存和加载整个模型意味着您无需重新声明模型,甚至无需访问模型代码本身。但是,这样做的代价是保存环境和加载环境必须匹配,包括可用的类、方法和参数(因为这些直接序列化和反序列化)。
保存模型的训练参数(状态字典state_dict)比第一种方法更灵活,只要您可以访问原始模型代码即可。
有两个主要原因可能不想使用原生的PyTorch在模型上执行推理。第一个原因是您必须在包含Python运行时和PyTorch库及其相关依赖项的环境中运行 - 这些文件总共达到数千兆字节。如果您想在移动电话、网络浏览器或专用硬件等环境中运行,使用原生PyTorch进行推理将不起作用。第二个原因是性能:使用原始的PyTorch模型,性能可能不符合您应用程序的需求。
使用Torchscript推理
如果你在一个更受限制的环境中运行,无法安装PyTorch或其他Python库,你可以选择使用已转换为TorchScript的PyTorch模型执行推理。TorchScript是Python的一个子集,允许你创建可序列化的模型,在非Python环境中加载和执行。
# 输出为TorchScript格式
script = torch.jit.script(model, example)
# 保存模型为文件
script.save(PATH)
加载可以使用多种语言:
# Load scripted model
model = torch.jit.load(PATH)
model.eval()
C++实现
#include <torch/script.h>
...
torch::jit::script::Module module;
try {
// Deserialize the ScriptModule
module = torch::jit::load(PATH);
}
catch (const c10::Error& e) {
...
}
...
虽然在使用TorchScript方法对PyTorch模型进行推理时不需要在环境中安装Python运行时,但是你需要安装libtorch二进制文件,而这些文件可能太大而无法适应你的环境。此外,你可能也无法获得你应用程序所需的性能。
使用ONNXRuntime进行推理
当性能和可移植性至关重要时,您可以使用ONNXRuntime来执行PyTorch模型的推理。使用ONNXRuntime,您可以减少延迟和内存占用,并增加吞吐量。您还可以使用ONNXRuntime提供的语言绑定和库,在云端、边缘、Web或移动设备上运行模型。
第一步是使用PyTorch ONNX导出器将您的PyTorch模型导出为ONNX格式。
# Specify example data
example = ...
# Export model to ONNX format
torch.onnx.export(model, PATH, example)
一旦将模型导出为ONNX格式,您可以选择在Netron查看器中查看模型图,了解模型图以及输入和输出节点的名称和形状,以及哪些节点具有可变大小的输入和输出(动态轴)。
然后,您可以在所选环境中运行ONNX模型。ONNXRuntime引擎是用C++实现的,并且在C++、Python、C#、Java、Javascript、Julia和Ruby中具有API。ONNXRuntime可以在Linux、Mac、Windows、iOS和Android上运行您的模型。例如,以下代码片段展示了一个C++推理应用程序的框架。
// 创建ONNXRuntime会话
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
Ort::Env env;
Ort::Session session{env, ORT_TSTR("model.onnx"), Ort::SessionOptions{nullptr}};
// Allocate model inputs: fill in shape and size
std::array<float, ...> input{};
std::array<int64_t, ...> input_shape{...};
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input.data(), input.size(), input_shape.data(), input_shape.size());
const char* input_names[] = {...};
// Allocate model outputs: fill in shape and size
std::array<float, ...> output{};
std::array<int64_t, ...> output_shape{...};
Ort::Value output_tensor = Ort::Value::CreateTensor<float>(memory_info, output.data(), output.size(), output_shape.data(), output_shape.size());
const char* output_names[] = {...};
// Run the model
session_.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
ONNXRuntime在默认情况下,会对ONNX图应用一系列优化,尽可能地合并节点并将常量值提取出来(常量折叠)。ONNXRuntime还通过其执行提供者接口与许多硬件加速器集成,包括CUDA、TensorRT、OpenVINO、CoreML和NNAPI,具体取决于您要针对的硬件平台。
您可以通过量化ONNX模型进一步提高性能。
如果应用程序在受限环境中运行,例如移动设备和边缘设备,您可以基于应用程序运行的模型或一组模型构建一个减小尺寸的运行时。
要开始使用您选择的语言和环境,请参阅本专题其他博客。