在C++中加载TorchScript模型
本教程已更新为可与PyTorch 1.2一起使用
顾名思义,PyTorch的主要接口是Python编程语言。尽管Python是合适于许多需要动态性和易于迭代的场景,并且是首选的语言,但同样的,在 许多情况下,Python的这些属性恰恰是不利的。后者通常适用的一种环境是要求生产-低延迟和严格部署。对于生产场景,即使只将C ++绑定到Java, Rust或Go之类的另一种语言中,它也是经常选择的语言。以下各段将概述PyTorch提供的从现有Python模型到可以完全从C ++加载和执行的序 列化表示形式的路径,而无需依赖Python。
步骤1:将PyTorch模型转换为Torch脚本
PyTorch模型从Python到C ++的旅程由Torch Script启动,Torch Script是PyTorch模型的一种表示形式,可以由Torch Script编译器理解, 编译和序列化。如果是从使用vanilla“eager” API编写的现有PyTorch模型开始的,则必须首先将模型转换为Torch脚本。在最常见的情况 下(如下所述),这只需要花费很少的功夫。如果已经有了Torch脚本模块,则可以跳到本教程的下一部分。
有两种将PyTorch模型转换为Torch脚本的方法。第一种称为跟踪,一种机制,其中通过使用示例输入对模型的结构进行一次评估,并记录这些 输入在模型中的流量,从而捕获模型的结构。这适用于有限使用控制流的模型。第二种方法是在模型中添加显式批注,以告知Torch Script编 译器可以根据Torch Script语言施加的约束直接解析和编译模型代码。
提示:可以在官方Torch脚本参考中找到有关这两种方法的完整文档,以及使用方法的进一步指导。
方法1:通过跟踪转换为Torch脚本
要将PyTorch模型通过跟踪转换为Torch脚本,必须将模型的实例以及示例输入传递给torch.jit.trace函数。这将产生一个torch.jit.ScriptModule 对象,该对象的模型评估痕迹将嵌入模块的forward方法中:
import torch
import torchvision

模型的一个实例.

model = torchvision.models.resnet18()

通常会提供给模型的forward()方法的示例输入。

example = torch.rand(1, 3, 224, 224)

使用torch.jit.trace来通过跟踪生成torch.jit.ScriptModule

traced_script_module = torch.jit.trace(model, example)
现在可以对跟踪的ScriptModule进行评估,使其与常规PyTorch模块相同:
In[1]: output = traced_script_module(torch.ones(1, 3, 224, 224))
In[2]: output[0, :5]
Out[2]: tensor([-0.2698, -0.0381, 0.4023, -0.3010, -0.0448], grad_fn=)
方法2:通过注释转换为Torch脚本
在某些情况下,例如,如果模型采用特定形式的控制流,则可能需要直接在Torch脚本中编写模型并相应地注释模型。例如,假设具有以下 vanilla Pytorch模型:
import torch

class MyModule(torch.nn.Module):
def init(self, N, M):
super(MyModule, self).init()
self.weight = torch.nn.Parameter(torch.rand(N, M))

def forward(self, input):if input.sum() > 0:output = self.weight.mv(input)else:output = self.weight + inputreturn output

因为此模块的前向方法使用取决于输入的控制流,所以它不适合跟踪。相反,可以将其转换为ScriptModule。为了将模块转换为 ScriptModule,需要使用torch.jit.script编译模块,如下所示:
class MyModule(torch.nn.Module):
def init(self, N, M):
super(MyModule, self).init()
self.weight = torch.nn.Parameter(torch.rand(N, M))

def forward(self, input):if input.sum() > 0:output = self.weight.mv(input)else:output = self.weight + inputreturn output

my_module = MyModule(10,20)
sm = torch.jit.script(my_module)
如果需要在nn.Module中排除某些方法,因为它们使用了TorchScript尚不支持的Python功能,则可以使用@torch.jit.ignore对其进行注释
my_module是ScriptModule的实例,可以序列化。
步骤2:将脚本模块序列化为文件
一旦有了ScriptModule(通过跟踪或注释PyTorch模型),就可以将其序列化为文件了。稍后,将可以使用C ++从此文件加载模块并执行它, 而无需依赖Python。假设要序列化先前在跟踪示例中显示的ResNet18模型。要执行此序列化,只需在模块上调用save并传递一个文件名即可:
traced_script_module.save(“traced_resnet_model.pt”)
这将在工作目录中生成traced_resnet_model.pt文件。如果还想序列化my_module,请调用my_module.save(“my_module_model.pt”) 现在已经正式离开Python领域,并准备跨入C ++领域。
步骤3:在C ++中加载脚本模块
要在C ++中加载序列化的PyTorch模型,应用程序必须依赖于PyTorch C ++ API(也称为LibTorch)。LibTorch发行版包含共享库,头文件 和CMake构建配置文件的集合。虽然CMake不是依赖LibTorch的要求,但它是推荐的方法,并且将来会得到很好的支持。 对于本教程,将 使用CMake和LibTorch构建一个最小的C ++应用程序,该应用程序简单地加载并执行序列化的PyTorch模型。
最小的C ++应用程序
让从讨论加载模块的代码开始。以下将已经做:
include <torch/script.h> // One-stop header.

#include
#include

int main(int argc, const char* argv[]) {
if (argc != 2) {
std::cerr << “usage: example-app \n”;
return -1;
}

torch::jit::script::Module module;
try {
// 使用以下命令从文件中反序列化脚本模块: torch::jit::load().
module = torch::jit::load(argv[1]);
}
catch (const c10::Error& e) {
std::cerr << “error loading the model\n”;
return -1;
}

std::cout << “ok\n”;
}
<torch/script.h>标头包含运行示例所需的LibTorch库中的所有相关包含。的应用程序接受序列化的PyTorch ScriptModule的文件路径 作为其唯一的命令行参数,然后使用torch::jit::load()函数继续对该模块进行反序列化,该函数将此文件路径作为输入。作为返回, 收到一个Torch::jit::script::Module对象。将稍后讨论如何执行它。
取决于LibTorch和构建应用程序
假设将以上代码存储在名为example-app.cpp的文件中。最小的CMakeLists.txt可能看起来很简单:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(custom_ops)

find_package(Torch REQUIRED)

add_executable(example-app example-app.cpp)
target_link_libraries(example-app “${TORCH_LIBRARIES}”)
set_property(TARGET example-app PROPERTY CXX_STANDARD 11)
建立示例应用程序的最后一件事是LibTorch发行版。可以随时从PyTorch网站的下载页面上获取最新的稳定版本。 如果下载并解压缩最新的归档文件,则应收到具有以下目录结构的文件夹:
libtorch/
bin/
include/
lib/
share/
• lib/ 文件夹包含必须链接的共享库,
• include/ 文件夹包含程序需要包含的头文件,
• share/ 文件夹包含必要的CMake配置,以启用上面的简单find_package(Torch)命令。
提示;在Windows上,调试和发行版本不兼容ABI。 如果打算以调试模式构建项目,请尝试使用LibTorch的调试版本。
最后一步是构建应用程序。为此,假定示例目录的布局如下:
example-app/
CMakeLists.txt
example-app.cpp
现在,可以运行以下命令从example-app/文件夹中构建应用程序:
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch …
make
/path/to/libtorch应该是解压缩的LibTorch发行版的完整路径。如果一切顺利,它将看起来像这样:
root@4b5a67132e81:/example-app# mkdir build
root@4b5a67132e81:/example-app# cd build
root@4b5a67132e81:/example-app/build# cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch …
– The C compiler identification is GNU 5.4.0
– The CXX compiler identification is GNU 5.4.0
– Check for working C compiler: /usr/bin/cc
– Check for working C compiler: /usr/bin/cc – works
– Detecting C compiler ABI info
– Detecting C compiler ABI info - done
– Detecting C compile features
– Detecting C compile features - done
– Check for working CXX compiler: /usr/bin/c++
– Check for working CXX compiler: /usr/bin/c++ – works
– Detecting CXX compiler ABI info
– Detecting CXX compiler ABI info - done
– Detecting CXX compile features
– Detecting CXX compile features - done
– Looking for pthread.h
– Looking for pthread.h - found
– Looking for pthread_create
– Looking for pthread_create - not found
– Looking for pthread_create in pthreads
– Looking for pthread_create in pthreads - not found
– Looking for pthread_create in pthread
– Looking for pthread_create in pthread - found
– Found Threads: TRUE
– Configuring done
– Generating done
– Build files have been written to: /example-app/build
root@4b5a67132e81:/example-app/build# make
Scanning dependencies of target example-app
[ 50%] Building CXX object CMakeFiles/example-app.dir/example-app.cpp.o
[100%] Linking CXX executable example-app
[100%] Built target example-app
如果提供了之前创建的到示例应用程序二进制文件的跟踪ResNet18模型traced_resnet_model.pt的路径,则应该以友好的“ ok” 作为奖励。 请注意,如果尝试使用my_module_model.pt运行此示例,则会收到一条错误消息,提示输入的形状不兼容。my_module_model.pt 需要1D而不是4D。
root@4b5a67132e81:/example-app/build# ./example-app <path_to_model>/traced_resnet_model.pt
ok
步骤4:在C ++中执行脚本模块
成功用C ++加载了序列化的ResNet18之后,现在只需执行几行代码即可!让将这些行添加到C ++应用程序的main()函数中:
// 创建输入向量
std::vectortorch::jit::IValue inputs;
inputs.push_back(torch::ones({1, 3, 224, 224}));

// 执行模型并将输出转化为张量
at::Tensor output = module.forward(inputs).toTensor();
std::cout << output.slice(/dim=/1, /start=/0, /end=/5) << ‘\n’;
前两行设置了模型的输入。创建一个torch::jit::IValue的向量(类型为type-erased的值Script::Module方法接受并返回), 并添加单个输入。要创建输入张量,使用torch::ones(),等效于C ++ API中的torch.ones。然后,运行script::Module的 forward方法,并向其传递创建的输入向量。作为回报,得到一个新的IValue,通过调用toTensor()将其转换为张量。
提示:要总体上了解有关torch::ones和PyTorch C ++ API之类的功能的更多信息,请参阅其文档,网址为https://pytorch.org/cppdocs。 PyTorch C ++ API提供了与Python API几乎相同的功能奇偶校验,使可以像在Python中一样进一步操纵和处理张量。
在最后一行中,打印输出的前五个条目。由于在本教程前面的部分中,向Python中的模型提供了相同的输入,因此理想情况下, 应该看到相同的输出。让通过重新编译的应用程序并以相同的序列化模型运行它来进行尝试:
root@4b5a67132e81:/example-app/build# make
Scanning dependencies of target example-app
[ 50%] Building CXX object CMakeFiles/example-app.dir/example-app.cpp.o
[100%] Linking CXX executable example-app
[100%] Built target example-app
root@4b5a67132e81:/example-app/build# ./example-app traced_resnet_model.pt
-0.2698 -0.0381 0.4023 -0.3010 -0.0448
[ Variable[CPUFloatType]{1,5} ]
作为参考,Python以前的输出为:
tensor([-0.2698, -0.0381, 0.4023, -0.3010, -0.0448], grad_fn=)
看来匹配得很好!
提示:要将模型移至GPU内存,可以编写model.to(at::kCUDA);。通过调用tensor.to(at::kCUDA),确保模型的输入也位于CUDA内存中, 这将在CUDA内存中返回新的张量。
步骤5:获取帮助并探索API
本教程有望使对PyTorch模型从Python到C ++的路径有一个大致的了解。使用本教程中描述的概念,应该能够从vanilla, “eager” PyTorch模型, 到Python中的已编译ScriptModule,再到磁盘上的序列化文件,以及–结束循环–到可执行脚本: C ++中的模块。
当然,有许多没有介绍的概念。例如,可能会发现自己想要使用以C ++或CUDA实现的自定义运算符扩展ScriptModule,并在加载到纯 C ++生产环境中的ScriptModule中执行此自定义运算符。好消息是:这是可能的,并且得到了很好的支持!现在,可以浏览此文件夹中的示例, 将很快提供一个教程。 目前,以下链接通常可能会有所帮助:
• Torch Script参考:https://pytorch.org/docs/master/jit.html
• PyTorch C ++ API文档:https://pytorch.org/cppdocs/
• PyTorch Python API文档:https://pytorch.org/docs/
与往常一样,如果遇到任何问题或疑问,可以使用的论坛或GitHub issues进行联系。

在C++中加载TorchScript模型相关推荐

  1. xBIM 实战01 在浏览器中加载IFC模型文件

    系列目录    [已更新最新开发文章,点击查看详细]  一.创建Web项目 打开VS,新建Web项目,选择 .NET Framework 4.5  选择一个空的项目 新建完成后,项目结构如下: 二.添 ...

  2. 用launch文件在rviz中加载机器人模型

    1.创建功能包:"catkin_creak_pkg 功能包名 依赖"(catkin_create_pkg mrobot_description xacro urdf) 2.urdf ...

  3. 通过超图在网页中加载3dmax模型

    接此: https://blog.csdn.net/bcbobo21cn/article/details/116806866 在场景中加载了3dmax模型后,保存场景: 场景节点下面出来一个场景: 保 ...

  4. 网页中加载obj模型比较慢_R语言估计时变VAR模型时间序列的实证研究分析案例...

    原文 http://tecdat.cn/?p=3364​tecdat.cn 加载R包和数据集 上述症状数据集包含在R-package 中,并在加载时自动可用. 加载包后,我们将此数据集中包含的12个心 ...

  5. PyTorch 1.0 中文官方教程:在 C++ 中加载 PYTORCH 模型

    译者:talengu PyTorch的主要接口为Python.虽然Python有动态编程和易于迭代的优势,但在很多情况下,正是Python的这些属性会带来不利.我们经常遇到的生产环境,要满足低延迟和严 ...

  6. 网页中加载obj模型比较慢_Web前端优化技巧分享,让你的网页显示的更流畅

    如果我们在打开一个网站时速度很慢,势必会影响体验,甚至会造成用户流失.浏览量下降的情况.想要解决这个问题自然就需要Web前端开发人员对前端页面进行优化,众所周知,前端的页面主要包括HTML.CSS.J ...

  7. CesiumJs 地图中加载模型,cesium 地图模型的加载,cesium 加载建筑模型, cesium 加载3Dmodels

    最近接手cesium 地图. . . 自我感觉cesium地图很屌的.  真的很强大. 基础的一些东西跟着教程多看几遍就好了. 下面我们着重说的是cesium 地图模型的加载. . 1. cesium ...

  8. cesium加载BIM模型

    自己尝试用cesium引擎加载BIM模型,操作步骤如下: 第一步: 下载一个BIM模型 第二步: 将BIM模型转换成FBX格式 转成gltf格式 在如下网站进行转换: https://products ...

  9. gazebo加载world模型

    使用launch文件启动gazebo加载world模型,或者说是将world模型加入gazebo仿真器中作为环境. first,我们应该告诉gazebo 要加载的world文件放在哪里.并通过arg标 ...

最新文章

  1. jQuery Alert Dialogs (Alert, Confirm, Prompt Replacements)(翻译)
  2. Android之TableLayout表格布局
  3. 成功解决WARNING:tensorflow:From :read_data_sets (from tensorflow.contrib.learn.python.learn.
  4. 汇编语言——16位寄存器实现32位二进制数左移4位。
  5. 香港虚拟主机怎么转服务器,香港虚拟主机好慢怎么办
  6. 中低频量化交易策略研发04_ 简单的择时策
  7. 我国物联网发展如何跳出“窄巷”?
  8. AttributeError: ‘torch.return_types.max‘ object has no attribute ‘dim‘
  9. linux上压缩pdf文件,如何压缩PDF文件?
  10. [济南考勤机专题]考勤机类型(五)打卡考勤机
  11. 《回话的技术》读后感
  12. laravel集成谷歌验证_如何将Google的两因素身份验证添加到Laravel
  13. div内嵌网页ajax,Div里面载入另一个页面的实现(取代框架)(AJax)(转)
  14. 职场“女神”,绝不会有的12个习惯
  15. 落球法测量液体粘滞系数
  16. 超好用的抓包工具——whistle
  17. UBTC7月份升级的温馨提示
  18. 数据结构练习题---先序遍历二叉树
  19. 网上怎么样可以挣钱,分享几种可以让你在家赚钱的兼职项目
  20. 应广PMC131 SOP16 16pin八位单片机

热门文章

  1. 【springboot】入门
  2. 微信支付回调重复通知,正确的响应
  3. 【PyTorch学习笔记】4:在Tensor上的索引和切片
  4. LeetCode简单题之汇总区间
  5. Apollo 自动驾驶开发套件(D-KIT)
  6. 嵌入式Linux设备驱动程序:发现硬件配置
  7. 激光雷达模块支持提高高速公路速度
  8. CVPR2020:三维点云无监督表示学习的全局局部双向推理
  9. 计算机视觉一些项目实战技术
  10. 高精地图中导航标识识别