0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何将pytorch的模型部署到c++平台上的模型流程

新机器视觉 来源:新机器视觉 作者:新机器视觉 2022-10-26 14:36 次阅读

导读 本文主要讲解如何将pytorch的模型部署到c++平台上的模型流程,按顺序分为四大块详细说明了模型转换、保存序列化模型、C ++中加载序列化的PyTorch模型以及执行Script Module。

最近因为工作需要,要把pytorch的模型部署到c++平台上,基本过程主要参照官网的教学示例,期间发现了不少坑,特此记录。

1.模型转换

libtorch不依赖于python,python训练的模型,需要转换为script model才能由libtorch加载,并进行推理。在这一步官网提供了两种方法: 方法一:Tracing 这种方法操作比较简单,只需要给模型一组输入,走一遍推理网络,然后由torch.ji.trace记录一下路径上的信息并保存即可。示例如下:


importtorch importtorchvision #Aninstanceofyourmodel. model=torchvision.models.resnet18() #Anexampleinputyouwouldnormallyprovidetoyourmodel'sforward()method. example=torch.rand(1,3,224,224) #Usetorch.jit.tracetogenerateatorch.jit.ScriptModuleviatracing. traced_script_module=torch.jit.trace(model,example) 缺点是如果模型中存在控制流比如if-else语句,一组输入只能遍历一个分支,这种情况下就没办法完整的把模型信息记录下来。 方法二:Scripting 直接在Torch脚本中编写模型并相应地注释模型,通过torch.jit.script编译模块,将其转换为ScriptModule。示例如下:

classMyModule(torch.nn.Module): def__init__(self,N,M): super(MyModule,self).__init__() self.weight=torch.nn.Parameter(torch.rand(N,M)) defforward(self,input): ifinput.sum()>0: output=self.weight.mv(input) else: output=self.weight+input returnoutput my_module=MyModule(10,20) sm=torch.jit.script(my_module)

forward方法会被默认编译,forward中被调用的方法也会按照被调用的顺序被编译

如果想要编译一个forward以外且未被forward调用的方法,可以添加@torch.jit.export.

如果想要方法不被编译,可使用[@torch.jit.ignore](https://link.zhihu.com/?target=https%3A//pytorch.org/docs/master/generated/torch.jit.ignore.html%23torch.jit.ignore)或者[@torch.jit.unused](https://link.zhihu.com/?target=https%3A//pytorch.org/docs/master/generated/torch.jit.unused.html%23torch.jit.unused)


#Samebehavioraspre-PyTorch1.2 @torch.jit.script defsome_fn(): return2 #Marksafunctionasignored,ifnothing #evercallsitthenthishasnoeffect @torch.jit.ignore defsome_fn2(): return2 #Aswithignore,ifnothingcallsitthenithasnoeffect. #Ifitiscalledinscriptitisreplacedwithanexception. @torch.jit.unused defsome_fn3(): importpdb;pdb.set_trace() return4 #Doesn'tdoanything,thisfunctionisalready #themainentrypoint @torch.jit.export defsome_fn4(): return2 在这一步遇到好多坑,主要原因可归为一下两点

1. 不支持的操作

TorchScript支持的操作是python的子集,大部分torch中用到的操作都可以找到对应实现,但也存在一些尴尬的不支持操作,详细列表可见unsupported-ops(https://pytorch.org/docs/master/jit_unsupported.html#jit-unsupported),下面列一些我自己遇到的操作: 1)参数/返回值不支持可变个数,例如


def__init__(self,**kwargs): 或者

ifoutput_flag==0: returnreshape_logits else: loss=self.loss(reshape_logits,term_mask,labels_id) returnreshape_logits,loss 2)各种iteration操作 eg1.

layers=[int(a)forainlayers] 报错torch.jit.frontend.UnsupportedNodeError: ListComp aren’t supported 可以改成:

forkinrange(len(layers)): layers[k]=int(layers[k]) eg2.

seq_iter=enumerate(scores) try: _,inivalues=seq_iter.__next__() except: _,inivalues=seq_iter.next() eg3.

line=next(infile) 3)不支持的语句 eg1. 不支持continue torch.jit.frontend.UnsupportedNodeError: continue statements aren’t supported eg2. 不支持try-catch torch.jit.frontend.UnsupportedNodeError: try blocks aren’t supported eg3. 不支持with语句 4)其他常见op/module eg1. torch.autograd.Variable 解决:使用torch.ones/torch.randn等初始化+.float()/.long()等指定数据类型。 eg2. torch.Tensor/torch.LongTensor etc. 解决:同上 eg3. requires_grad参数只在torch.tensor中支持,torch.ones/torch.zeros等不可用 eg4. tensor.numpy() eg5. tensor.bool() 解决:tensor.bool()用tensor>0代替 eg6. self.seg_emb(seg_fea_ids).to(embeds.device) 解决:需要转gpu的地方显示调用.cuda() 总之一句话:除了原生python和pytorch以外的库,比如numpy什么的能不用就不用,尽量用pytorch的各种API

2. 指定数据类型

1)属性,大部分的成员数据类型可以根据值来推断,空的列表/字典则需要预先指定


fromtypingimportDict classMyModule(torch.nn.Module): my_dict:Dict[str,int] def__init__(self): super(MyModule,self).__init__() #Thistypecannotbeinferredandmustbespecified self.my_dict={} #Theattributetypehereisinferredtobe`int` self.my_int=20 defforward(self): pass m=torch.jit.script(MyModule()) 2)常量,使用_Final_关键字

try: fromtyping_extensionsimportFinal except: #Ifyoudon'thave`typing_extensions`installed,youcanusea #polyfillfrom`torch.jit`. fromtorch.jitimportFinal classMyModule(torch.nn.Module): my_constant:Final[int] def__init__(self): super(MyModule,self).__init__() self.my_constant=2 defforward(self): pass m=torch.jit.script(MyModule()) 3)变量。默认是tensor类型且不可变,所以非tensor类型必须要指明

defforward(self,batch_size:int,seq_len:int,use_cuda:bool): 方法三:Tracing and Scriptin混合 一种是在trace模型中调用script,适合模型中只有一小部分需要用到控制流的情况,使用实例如下:

importtorch @torch.jit.script deffoo(x,y): ifx.max()>y.max(): r=x else: r=y returnr defbar(x,y,z): returnfoo(x,y)+z traced_bar=torch.jit.trace(bar,(torch.rand(3),torch.rand(3),torch.rand(3))) 另一种情况是在script module中用tracing生成子模块,对于一些存在script module不支持的python feature的layer,就可以把相关layer封装起来,用trace记录相关layer流,其他layer不用修改。使用示例如下:

importtorch importtorchvision classMyScriptModule(torch.nn.Module): def__init__(self): super(MyScriptModule,self).__init__() self.means=torch.nn.Parameter(torch.tensor([103.939,116.779,123.68]) .resize_(1,3,1,1)) self.resnet=torch.jit.trace(torchvision.models.resnet18(), torch.rand(1,3,224,224)) defforward(self,input): returnself.resnet(input-self.means) my_script_module=torch.jit.script(MyScriptModule())

2.保存序列化模型

如果上一步的坑都踩完,那么模型保存就非常简单了,只需要调用save并传递一个文件名即可,需要注意的是如果想要在gpu上训练模型,在cpu上做inference,一定要在模型save之前转化,再就是记得调用model.eval(),形如


gpu_model.eval() cpu_model=gpu_model.cpu() sample_input_cpu=sample_input_gpu.cpu() traced_cpu=torch.jit.trace(traced_cpu,sample_input_cpu) torch.jit.save(traced_cpu,"cpu.pth") traced_gpu=torch.jit.trace(traced_gpu,sample_input_gpu) torch.jit.save(traced_gpu,"gpu.pth")

3.C++ load训练好的模型

要在C ++中加载序列化的PyTorch模型,必须依赖于PyTorch C ++ API(也称为LibTorch)。libtorch的安装非常简单,只需要在pytorch官网下载对应版本,解压即可。会得到一个结构如下的文件夹。


libtorch/ bin/ include/ lib/ share/ 然后就可以构建应用程序了,一个简单的示例目录结构如下:

example-app/ CMakeLists.txt example-app.cpp example-app.cpp和CMakeLists.txt的示例代码分别如下:

#include//One-stopheader. #include #include intmain(intargc,constchar*argv[]){ if(argc!=2){ std::cerr<< "usage: example-app  "; return-1; } torch::Modulemodule; try{ //DeserializetheScriptModulefromafileusingtorch::load(). module=torch::load(argv[1]); } catch(constc10::Error&e){ std::cerr<< "error loading the model ";     return -1;   }   std::cout << "ok "; }

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/文件夹中构建应用程序啦:

mkdir build cd build cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch .. cmake --build . --config Release 其中/path/to/libtorch是之前下载后的libtorch文件夹所在的路径。这一步如果顺利能够看到编译完成100%的提示,下一步运行编译生成的可执行文件,会看到“ok”的输出,可喜可贺!

4. 执行Script Module

终于到最后一步啦!下面只需要按照构建输入传给模型,执行forward就可以得到输出啦。一个简单的示例如下:


//Createavectorofinputs. std::vectorinputs; inputs.push_back(torch::ones({1,3,224,224})); //Executethemodelandturnitsoutputintoatensor. at::Tensoroutput=module.forward(inputs).toTensor(); std::cout<< output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << ' '; 前两行创建一个torch::IValue的向量,并添加单个输入. 使用torch::ones()创建输入张量,等效于C ++ API中的torch.ones。然后,运行script::Module的forward方法,通过调用toTensor()将返回的IValue值转换为张量。C++对torch的各种操作还是比较友好的,通过torch::或者后加_的方法都可以找到对应实现,例如

torch::tensor(input_list[j]).to(at::kLong).resize_({batch,128}).clone() //torch::tensor对应pytorch的torch.tensor;at::kLong对应torch.int64;resize_对应resize 最后check一下确保c++端的输出和pytorch是一致的就大功告成啦~ 踩了无数坑,薅掉了无数头发,很多东西也是自己一点点摸索的,如果有错误欢迎指正!
审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72900
  • 模型
    +关注

    关注

    1

    文章

    2704

    浏览量

    47685
  • pytorch
    +关注

    关注

    2

    文章

    761

    浏览量

    12831

原文标题:C++平台PyTorch模型部署流程,踩坑心得实录

文章出处:【微信号:vision263com,微信公众号:新机器视觉】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Pytorch模型训练实用PDF教程【中文】

    模型部分?还是优化器?只有这样不断的通过可视化诊断你的模型,不断的对症下药,才能训练出一个较满意的模型。本教程内容及结构:本教程内容主要为在 PyTorch 中训练一个
    发表于 12-21 09:18

    如何将FreeModbus移植STM32平台上

    modbus是什么?如何将FreeModbus移植STM32平台上
    发表于 11-17 07:44

    如何将AI模型部署嵌入式系统中

    本期我们分享主题是如何将 AI 模型部署嵌入式系统中,下一期介绍如何在 RT-Thread 操作系统上运行 Mnist Demo(手写数
    发表于 12-14 07:55

    Pytorch模型如何通过paddlelite部署嵌入式设备?

    Pytorch模型如何通过paddlelite部署嵌入式设备?
    发表于 12-23 09:38

    为什么RK3399Pro平台YOLOv4 pytorch模型转RKNN模型会失败呢

    为什么RK3399Pro平台YOLOv4 pytorch模型转RKNN模型会失败呢?有何解决办法?
    发表于 02-11 09:29

    pytorch模型转化为onxx模型的步骤有哪些

    首先pytorch模型要先转化为onxx模型,然后从onxx模型转化为rknn模型直接转化会出现如下问题,环境都是正确的,论坛询问后也没给出
    发表于 05-09 16:36

    通过Cortex来非常方便的部署PyTorch模型

    的工作。那么,问题是如何将 RoBERTa 部署为一个 JSON API,而不需要手动滚动所有这些自定义基础设施? PyTorch 模型
    发表于 11-01 15:25

    Pytorch模型转换为DeepViewRT模型时出错怎么解决?

    我最终可以在 i.MX 8M Plus 处理器上部署 .rtm 模型。 我遵循了 本指南,我 Pytorch 模型转换为 ONNX
    发表于 06-09 06:42

    如何使用TensorFlow神经网络模型部署移动或嵌入式设备上

    有很多方法可以经过训练的神经网络模型部署移动或嵌入式设备上。不同的框架在各种平台上支持Arm,包括TensorFlow、
    发表于 08-02 06:43

    如何将PyTorch模型与OpenVINO trade结合使用?

    无法确定如何转换 PyTorch 掩码 R-CNN 模型以配合OpenVINO™使用。
    发表于 08-15 07:04

    pytorch模型转换需要注意的事项有哪些?

    和记录张量上的操作,不会记录任何控制流操作。 为什么不能是GPU模型? 答:BMNETP的编译过程不支持。 如何将GPU模型转成CPU模型? 答:在加载
    发表于 09-18 08:05

    如何将Pytorch自训练模型变成OpenVINO IR模型形式

    本文章将依次介绍如何将Pytorch自训练模型经过一系列变换变成OpenVINO IR模型形式,而后使用OpenVINO Python API 对IR
    的头像 发表于 06-07 09:31 1154次阅读
    <b class='flag-5'>如何将</b><b class='flag-5'>Pytorch</b>自训练<b class='flag-5'>模型</b>变成OpenVINO IR<b class='flag-5'>模型</b>形式

    如何部署ML模型到Google云平台

    实践中的机器学习:在 Google 云平台上部署 ML 模型
    的头像 发表于 07-05 16:30 422次阅读
    如何<b class='flag-5'>部署</b>ML<b class='flag-5'>模型</b>到Google云<b class='flag-5'>平台</b>

    TorchVision框架下模型导出并部署到ONNXRUNTIME C++流程解析

    ONNXRUNTIME是主流的深度学习部署框架之一,支持ONNX格式模型在CPU、GPU、ARM等不同硬件平台上加速推理,支持C++、Python、Java、C#、JS等不同语言SDK
    的头像 发表于 07-13 14:46 756次阅读
    TorchVision框架下<b class='flag-5'>模型</b>导出并<b class='flag-5'>部署</b>到ONNXRUNTIME <b class='flag-5'>C++</b>全<b class='flag-5'>流程</b>解析

    基于OpenVINO C++ API部署RT-DETR模型

    应用中,我们为了与当前软件平台集成更多会采用 C++ 平台,因此在本文中,我们将基于 OpenVINO C++ API 向大家展示了不包含后处理的 RT-DETR
    的头像 发表于 11-03 14:30 372次阅读
    基于OpenVINO <b class='flag-5'>C++</b> API<b class='flag-5'>部署</b>RT-DETR<b class='flag-5'>模型</b>