1.1 引言

PyTorch作为一款端到端的深度学习框架,在1.0版本之后已具备较好的生产环境部署条件。除了在web端撰写REST API进行部署之外(参考),软件端的部署也有广泛需求。尤其是最近发布的1.5版本,提供了更为稳定的C++前端API。

工业界与学术界最大的区别在于工业界的模型需要落地部署,学界更多的是关心模型的精度要求,而不太在意模型的部署性能。一般来说,我们用深度学习框架训练出一个模型之后,使用Python就足以实现一个简单的推理演示了。但在生产环境下,Python的可移植性和速度性能远不如C++。所以对于深度学习算法工程师而言,Python通常用来做idea的快速实现以及模型训练,而用C++作为模型的生产工具。目前PyTorch能够完美的将二者结合在一起。实现PyTorch模型部署的核心技术组件就是TorchScript和libtorch。

所以基于PyTorch的深度学习算法工程化流程大体如下图所示:

1.2 TorchScript

TorchScript可以视为PyTorch模型的一种中间表示,TorchScript表示的PyTorch模型可以直接在C++中进行读取。PyTorch在1.0版本之后都可以使用TorchScript的方式来构建序列化的模型。TorchScript提供了Tracing和Script两种应用方式。

Tracing应用示例如下:

class MyModel(torch.nn.Module):def __init__(self):super(MyModel, self).__init__()self.linear = torch.nn.Linear(4, 4)def forward(self, x, h):new_h = torch.tanh(self.linear(x) + h)return new_h, new_h# 创建模型实例
my_model = MyModel()
# 输入示例
x, h = torch.rand(3, 4), torch.rand(3, 4)
# torch.jit.trace方法对模型构建TorchScript
traced_model = torch.jit.trace(my_model, (x, h))
# 保存转换后的模型
traced_model.save('model.pt')

在这段代码中,我们先是定义了一个简单模型并创建模型实例,然后给定输入示例,Tracing方法最关键的一步在于使用torch.jit.trace方法对模型进行TorchScript转化。我们可以获得转化后的traced_model对象获得其计算图属性和代码属性。计算图属性:

print(traced_model.graph)
graph(%self.1 : __torch__.torch.nn.modules.module.___torch_mangle_1.Module,%input : Float(3, 4),%h : Float(3, 4)):%19 : __torch__.torch.nn.modules.module.Module = prim::GetAttr[name="linear"](%self.1)%21 : Tensor = prim::CallMethod[name="forward"](%19, %input)%12 : int = prim::Constant[value=1]() # /var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0%13 : Float(3, 4) = aten::add(%21, %h, %12) # /var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0%14 : Float(3, 4) = aten::tanh(%13) # /var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0%15 : (Float(3, 4), Float(3, 4)) = prim::TupleConstruct(%14, %14)return (%15)

代码属性:

print(traced_cell.code)
def forward(self,input: Tensor,h: Tensor) -> Tuple[Tensor, Tensor]:_0 = torch.add((self.linear).forward(input, ), h, alpha=1)_1 = torch.tanh(_0)return (_1, _1)

这样我们就可以将整个模型都保存到硬盘上了,并且经过这种方式保存下来的模型可以加载到其他其他语言环境中。

TorchScript的另一种实现方式是Script的方式,可以算是对Tracing方式的一种补充。当模型代码中含有if或者for-loop等控制流程序时,使用Tracing方式是无效的,这时候可以采用Script方式来进行实现TorchScript。实现方法跟Tracing差异不大,关键在于把jit.tracing换成jit.script方法,示例如下。

scripted_model = torch.jit.script(MyModel)
scripted_model.save('model.pt')

除了Tracing和Script之外,我们也可以混合使用这两种方式,这里不做详述。总之,TorchScript为我们提供了一种表示形式,可以对代码进行编译器优化以提供更有效的执行。

1.3 libtorch

在Python环境下对训练好的模型进行转换之后,我们需要C++环境下的PyTorch来读取模型并进行编译部署。这种C++环境下的PyTorch就是libtorch。因为libtorch通常用来作为PyTorch模型的C++接口,libtorch也称之为PyTorch的C++前端。

我们可以直接从PyTorch官网下载已经编译好的libtorch安装包,当然也可以下载源码自行进行编译。这里需要注意的是,安装的libtorch版本要与Python环境下的PyTorch版本一致。

安装好libtorch后可简单测试下是否正常。比如我们用TorchScript转换一个预训练模型,示例如下:

import torch
import torchvision.models as models
vgg16 = models.vgg16()
example = torch.rand(1, 3, 224, 224).cuda()
model = model.eval()
traced_script_module = torch.jit.trace(model, example)
output = traced_script_module(torch.ones(1,3,224,224).cuda())
traced_script_module.save('vgg16-trace.pt')
print(output)

输出为:

tensor([[ -0.8301, -35.6095, 12.4716]], device='cuda:0',grad_fn=<AddBackward0>)

然后切换到C++环境,编写CmakeLists文件如下:

cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(libtorch_test)
find_package(Torch REQUIRED)
message(STATUS "Pytorch status:")
message(STATUS "libraries: ${TORCH_LIBRARIES}")
add_executable(libtorch_test test.cpp)
target_link_libraries(libtorch_test "${TORCH_LIBRARIES}")
set_property(TARGET libtorch_test PROPERTY CXX_STANDARD 11)

继续编写test.cpp代码如下:

#include "torch/script.h"
#include "torch/torch.h"
#include <iostream>
#include <memory>
using namespace std;int main(int argc, const char* argv[]){if (argc != 2) {std::cerr << "usage: example-app <path-to-exported-script-module>\n";return -1;}// 读取TorchScript转化后的模型torch::jit::script::Module module;try {module = torch::jit::load(argv[1]);}catch (const c10::Error& e) {std::cerr << "error loading the model\n";return -1;}module->to(at::kCUDA);assert(module != nullptr);std::cout << "ok\n";// 构建示例输入std::vector<torch::jit::IValue> inputs;inputs.push_back(torch::ones({1, 3, 224, 224}).to(at::kCUDA));// 执行模型推理并输出tensorat::Tensor output = module->forward(inputs).toTensor();std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n';}

编译test.cpp并执行,输出如下。对比Python环境下的的运行结果,可以发现基本是一致的,这也说明当前环境下libtorch安装没有问题。

ok
-0.8297, -35.6048, 12.4823
[Variable[CUDAFloatType]{1,3}]

1.4 完整部署流程

通过前面对TorchScript和libtorch的描述,其实我们已经基本将PyTorch的C++部署已经基本讲到了,这里我们再来完整的理一下整个流程。基于C++的PyTorch模型部署流程如下。

第一步:

通过torch.jit.trace方法将PyTorch模型转换为TorchScript,示例如下:

import torch
from torchvision.models import resnet18
model =resnet18()
example = torch.rand(1, 3, 224, 224)
tracing.traced_script_module = torch.jit.trace(model, example)

第二步:

将TorchScript序列化为.pt模型文件。

traced_script_module.save("traced_resnet_model.pt")

第三步:

在C++中导入序列化之后的TorchScript模型,为此我们需要分别编写包含调用程序的cpp文件、配置和编译用的CMakeLists.txt文件。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 14)

包含模型调用程序的example-app.cpp示例编码如下:

#include <torch/script.h> // torch头文件.
#include <iostream>#include <memory>int main(int argc, const char* argv[]) {if (argc != 2) {std::cerr << "usage: example-app <path-to-exported-script-module>\n";return -1;}torch::jit::script::Module module;try {// 反序列化:导入TorchScript模型module = torch::jit::load(argv[1]);}catch (const c10::Error& e) {std::cerr << "error loading the model\n";return -1;}std::cout << "ok\n";}

两个文件编写完成之后便可对其执行编译:

mkdir example_test
cd example_test
cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..
cmake --example_test . --config Release

第四步:

给example-app.cpp添加模型推理代码并执行:

std::vector<torch::jit::IValue> inputs;inputs.push_back(torch::ones({1, 3, 224, 224}));
// 执行推理并将模型转化为Tensor
output = module.forward(inputs).toTensor();std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n';

以上便是C++中部署PyTorch模型的全过程,相关教程可参考PyTorch官方:https://pytorch.org/tutorials/

总结

模型部署对于算法工程师而言非常重要,关系到你的工作能否产生实际价值。相应的也需要大家具备足够的工程能力,比如MySQL、Redis、C++、前端和后端的一些知识和开发技术,需要各位算法工程师都能够基本了解和能够使用。

基于C++的深度学习模型部署相关推荐

  1. 基于TensorRT的深度学习模型部署实战教程!

    应用背景介绍 早在遥远的1989年,一家叫做ALVIVN的公司首次将神经网络用在汽车上,进行车道线检测和地面分割.时至今日,深度学习已经应用在自动驾驶系统的多个分支领域.首先是感知领域,常用的传感器有 ...

  2. 【深度学习】基于web端和C++的两种深度学习模型部署方式

    深度学习 Author:louwill Machine Learning Lab 本文对深度学习两种模型部署方式进行总结和梳理.一种是基于web服务端的模型部署,一种是基于C++软件集成的方式进行部署 ...

  3. pytorch基于web端和C++的两种深度学习模型部署方式

    本文对深度学习两种模型部署方式进行总结和梳理.一种是基于web服务端的模型部署,一种是基于C++软件集成的方式进行部署. 基于web服务端的模型部署,主要是通过REST API的形式来提供接口方便调用 ...

  4. 基于web端和C++的两种深度学习模型部署方式

    深度学习Author:louwillMachine Learning Lab 本文对深度学习两种模型部署方式进行总结和梳理.一种是基于web服务端的模型部署,一种是基... 深度学习 Author:l ...

  5. 基于web端的深度学习模型部署

    1.1 web服务与技术框架 下面以ResNet50预训练模型为例,旨在展示一个轻量级的深度学习模型部署,写一个较为简单的图像分类的REST API.主要技术框架为Keras+Flask+Redis. ...

  6. 深度学习模型部署简要介绍

    一.模型部署简介 近几年来,随着算力的不断提升和数据的不断增长,深度学习算法有了长足的发展.深度学习算法也越来越多的应用在各个领域中,比如图像处理在安防领域和自动驾驶领域的应用,再比如语音处理和自然语 ...

  7. 如何将深度学习模型部署到实际工程中?(分类+检测+分割)

    应用背景介绍 早在遥远的1989年,一家叫做ALVIVN的公司首次将神经网络用在汽车上,进行车道线检测和地面分割.时至今日,深度学习已经应用在自动驾驶系统的多个分支领域.首先是感知领域,常用的传感器有 ...

  8. 为什么将表格的method改为post后就无法工作_用Python将Keras深度学习模型部署为Web应用程序...

    构建一个很棒的机器学习项目是一回事,但归根结底,你希望其他人能够看到你的辛勤工作.当然,你可以将整个项目放在GitHub上,但是怎么让你的祖父母也看到呢?我们想要的是将深度学习模型部署为世界上任何人都 ...

  9. 面向生产环境!深度学习模型部署资源全辑

    点上方计算机视觉联盟获取更多干货 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:机器学习实验室   Author:louwill Machine Learning Lab AI博士笔记系列推 ...

  10. 深度学习模型部署技术方案

    深度学习模型部署技术方案 训练好的深度学习模型如何进行部署的相关技术方案 1 什么是模型部署? 2 数据科学项目整个开发流程 3 使用flask 将 Keras深度学习模型部署为Web应用程序 4 T ...

最新文章

  1. 关于querySelector 和 document.getElementsByTagName 选中集合问题
  2. 永洪Desktop安装——windows版
  3. golang float浮点型精度丢失问题解决办法:使用decimal包;float与int的相互转换
  4. 添加请求头 retrofit_RxJava 与 Retrofit 结合的最佳实践
  5. python-描述符的操作
  6. 网课老师凭什么年薪200万?
  7. Web3 是去中心化的“骗局”?
  8. MySQL 基础 —— DQL(数据查询)
  9. git 取消head的游离状态
  10. mac下的git的安装与简单的配置
  11. Mapbar 地图 API 概念   技术文档
  12. 编译原理:有穷自动机(DFA与NFA)
  13. cp命令强制覆盖方式实现
  14. 交换机通过port-channel互联
  15. 易语言 mysql操作_易语言数据库应用手册
  16. 无人车系统(七):Udacity ‘s无人驾驶仿真环境(社区介绍)
  17. 制作TTF格式的字体
  18. 经历3个失败的游戏创业公司,我所亲历的痛与教训
  19. Win32应用程序开发:完整的开发流程
  20. Jenkins基础:API:10:使用API更新进行节点的连接和断开

热门文章

  1. pythonRuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing
  2. 微信小程序引入字体图标 1
  3. 蒟蒻朱的 CSP2020 J/S 游记
  4. 线性代数及矩阵论(八)
  5. 人到中年,没事多休息,有空多赚钱!
  6. 密码学——变色龙哈希
  7. 2019上半年软考 | 报名时间汇总(持续更新中)
  8. va_buffersharing example
  9. 【算法】网球循环赛比赛日程表
  10. 苦禅箜mm让我帮她做的作业