pytorch C++ 移植

文章目录

  • 前言
  • 安装 libtorch
  • 安装 opencv(C++)
  • 模型转换
    • 通过跟踪转换为 Torch Script
    • 通过注解转换为 Torch Script
  • 编写 C++ 代码
  • 编译环境搭建
    • C++ 库管理
    • 方法一:手动配置 visual studio 环境
    • 方法二:cmake 配置环境
  • python 调用 C++ 程序

前言

pytorch 神经网络模型来自我的这篇文章:https://blog.csdn.net/weixin_45715159/article/details/108277349

训练好的神经网络模型
链接:https://pan.baidu.com/s/1-cZCLNuYs-1MOu1NI42UOw
提取码:4wsv

pytorch 进行神经网络的搭建和训练固然很方便,但是当部署到实际中来时就显得效率一般。例如网络爬虫需要识别验证码以进行登录,运行 python 程序的话光是加载 torch 库就要花一段时间,这对于不需要一次进行大量图片识别的网络爬虫是一种拖累。所以需要编写 C++ 程序来加载神经网路模型以提高程序运行的效率。

下面以一个 CNN 网络的移植为例介绍 pytorch 的 C++ 移植方法
部分来自官方教程:https://pytorch.org/tutorials/advanced/cpp_export.html

环境

  • 操作系统:Windows 10
  • CPU:Intel i5-9300
  • GPU:GTX 1650
  • 深度学习框架:pytorch 1.6,libtorch 1.6
  • CUDA 11
  • python 3.8
  • opencv 4.4.0(C++)
  • visual studio 2019

安装 libtorch

libtorch 是 torch 的 C++ 版本。libtorch 的版本必须与 pytorch 一致

首先进入官网:https://pytorch.org/

在 pytorch 的安装栏,Package 选择 libtorchLangugae 选择 C++/Java

获得如下的下载链接:

在这里插入图片描述

我选择的是 Release version,点击相应链接下载完毕后解压在合适的目录下。

安装 opencv(C++)

opencv 主要用来进行 C++ 程序的图像处理。

和 libtorch 安装类似,在下载页面 https://opencv.org/releases/ 选择相应的操作系统。例如选择 Windows 系统,跟随安装程序选择合适的安装目录即可完成安装。

模型转换

pytorch 提供了一种统一的模型描述语言 Torch Script 供其他编程语言程序加载,下面介绍两种将我们训练出来的模型转换为 Torch Script 的方法,也可以参考这篇博客:https://blog.csdn.net/xxradon/article/details/86504906

通过跟踪转换为 Torch Script

将模型的实例以及示例输入传递给 torch.jit.trace 函数。

这个方法适用于对任意类型输入有固定格式输出的神经网络。

import torch
from Network import Netnet = Net()
net.load_state_dict(torch.load('.\\model.pt'))
example = torch.rand(1, 1, 45, 45)
scrpit_net = torch.jit.trace(net,example)
script_net.save('.\\model_script.pt')

跟踪器有可能生成警告,因为有就地赋值(torch.rand())。

通过注解转换为 Torch Script

这个方法适用于对不同类型输入有不同格式输出的神经网络。

import torch
from Network import Netnet = Net()
net.load_state_dict(torch.load('.\\model.pt'))
scrpit_net = torch.jit.script(net)
script_net.save('.\\model_script.pt')

编写 C++ 代码

源.cpp 文件内容如下,调用了 opencv 库和 libtorch 库。

  • decode() 解码函数,类似于前面 python 的解码函数。

  • C++ 程序通过主函数的入口参数 argc(参数个数) 和 argv[](参数集) 获取传入程序的参数,例如在windows 命令行中输入:

    program.exe arg1 arg2
    

    即可向程序传入两个参数:arg1arg2,在程序中读取 argv[0]argv[1] 即可知道这两个参数的值。

    在本程序中传入的参数为前面保存的神经网络模型的路径和图片的路径

  • torch::jit::script::Module module = torch::jit::load(argv[1]) 通过模型路径读取模型并创建神经网络,十分简便。

  • cv::imread() 为 opencv 的图像读取函数,输入图片路径即可返回 Mat 类型的图像数据矩阵。

  • cv::resize() 为 opencv 的图像变形函数,在这里使图片变形为 45 x 45。

  • cv::cvtColor() 为 opencv 的图像色域改变函数,opencv 读取的图像通道和我们常见的 RGB 通道不同,它是 BGR,在这里我们只需要将它转变为单通道(即灰度图)。

  • image.convertTo(image, CV_32FC1);Mat 类型的数据类型转换方法,在这里转换为32为浮点型单通道。

  • cv::vconcat() 为 opencv 的图像拼接函数,在这里是在图像个数维上进行拼接。

  • 使用 write 标记 images 变量是否已赋值,因为我们要将所有的输入图片拼接成一个整体再转换为 Tensor 传给神经网络以加快程序运行速度。

  • torch::from_blob() 为 libtorch 提供的 Mat 类型转 Tensor 类型的函数接口,我们可以看到最终输入给神经网络的数据维度为 N x 1 x 45 x 45 (N 为图片个数)。

  • std::vector<torch::jit::IValue> inputs;

    inputs.push_back(input_tensors);

    auto outputs = module.forward(inputs).toTensor();

    这三条语句为我在网上查到的一种固定写法,大概是要将 Tensor 类型的数据放在一种叫向量的数据结构里才能传递给神经网络。

  • 最总通过 std::cout 直接输出识别结果,多图片时输出结果以单空格隔开。

也不知道为什么,发现把模型置于计算模式时(module.eval();)输出结果是错误的。

/*
神经网络调用程序,根据命令行参数直接输出识别结果,可一次识别多张,上限100张
参数顺序:神经网络模型路径,图片1路径,……,图片n路径
*/#include <torch/script.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <memory>std::string decode(at::Tensor code) //解码函数
{int char_num = 4; //字符个数int a;std::string str = ""; //输出字符串std::string table[10] = {"1", "2", "3", "b", "c", "m", "n", "v", "x", "z"}; //编码对照表code = code.view({ -1, 10 });code = torch::argmax(code, 1);for (int i = 0; i < char_num; i++){a = code[i].item().toInt();str += table[a];}return str;
}int main(int argc, const char *argv[])
{if (argc < 3 || argc > 102){std::cerr << "调用错误!" << std::endl;return -1;}try{int image_num = argc - 2; //读取的图片数量bool write = false;torch::jit::script::Module module = torch::jit::load(argv[1]); //读取模型cv::Mat images; //保存图片数据for (int i = 0; i < image_num; i++){cv::Mat image = cv::imread(argv[i + 2]); //读取图片cv::resize(image, image, cv::Size(45, 45)); //变形成为45 x 45cv::cvtColor(image, image, cv::COLOR_BGR2GRAY); //转成灰度图image.convertTo(image, CV_32FC1); //转换为32位浮点型if (!write){images = image;write = true;}else{cv::vconcat(images, image, images); //拼接图像}}auto input_tensors = torch::from_blob(images.data, {image_num, 1, 45, 45 }); //将mat转成tensorstd::vector<torch::jit::IValue> inputs;inputs.push_back(input_tensors);auto outputs = module.forward(inputs).toTensor();std::cout << decode(outputs[0]);for (int i = 1; i < image_num; i++){auto result = decode(outputs[i]);//解码std::cout << ' ' << result;}return 0;}catch (...)  //捕获任意异常{std::cerr << "程序执行出错!" << std::endl;return -1;}
}

编译环境搭建

下面介绍两种编译环境的搭载方法,一种是直接在 visual studio 中配置,一种是官方推荐的用 cmake 配置 visual studio 环境。

确保已安装 visual studio 的 C++ 部件,下面所有方法都以 visual studio 2019 为 IDE,程序编译类型为 x64 release

C++ 库管理

在前面我们已安装了 libtorch 和 opencv,以 libtorch 1.6.0 (release) 和 opencv 4.4.0 为例,假设它们放在一个文件夹:D:\CplusLib\

在这里插入图片描述

程序需要的动态链接库位置:

  • libtorchD:\CplusLib\libtorch\lib
  • opencvD:\CplusLib\opencv\build\x64\vc15\bin

接下来我们需要将这些动态链接库的路径添加进环境变量,以便 C++ 程序通过环境变量找寻这些动态链接库。

以 Windows 10 系统为例,右键 此电脑,选择 属性 -> 高级系统设置 -> 环境变量 打开界面。

在这里插入图片描述

有两种环境变量:用户变量和系统变量,一种只针对一个用户有效,另一种对所有用户都有效。

我们添加系统变量的 path 项,双击 path,点击 新建 输入路径:

在这里插入图片描述

点击 确定 -> 确定

方法一:手动配置 visual studio 环境

直接在 visual studio 环境中配置要一个一个去写包含的库的路径和动态链接库的名称。我是采用属性表的形式直接让工程项目读取,这样每创建一个新工程就不用重复配置了。

以上面的库安装路径为基础提供每个库的属性表:

  • libtorch(release)libtorch.Cpp.x64.user.props

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><ImportGroup Label="PropertySheets" /><PropertyGroup Label="UserMacros" /><PropertyGroup><IncludePath>D:\CplusLib\libtorch\include;D:\CplusLib\libtorch\include\torch;$(IncludePath)</IncludePath><LibraryPath>D:\CplusLib\libtorch\lib;$(LibraryPath)</LibraryPath></PropertyGroup><ItemDefinitionGroup><Link><AdditionalDependencies>asmjit.lib;c10.lib;c10_cuda.lib;caffe2_detectron_ops_gpu.lib;caffe2_module_test_dynamic.lib;caffe2_nvrtc.lib;clog.lib;cpuinfo.lib;dnnl.lib;fbgemm.lib;libprotobuf-lite.lib;libprotobuf.lib;libprotoc.lib;mkldnn.lib;torch.lib;torch_cpu.lib;torch_cuda.lib;%(AdditionalDependencies)</AdditionalDependencies></Link></ItemDefinitionGroup><ItemGroup />
    </Project>
    
  • opencv (release)opencv.Cpp.x64.user.props

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><ImportGroup Label="PropertySheets" /><PropertyGroup Label="UserMacros" /><PropertyGroup><IncludePath>D:\CplusLib\opencv\build\include;D:\CplusLib\opencv\build\include\opencv2;$(IncludePath)</IncludePath><LibraryPath>D:\CplusLib\opencv\build\x64\vc15\lib;$(LibraryPath)</LibraryPath></PropertyGroup><ItemDefinitionGroup><Link><AdditionalDependencies>opencv_world440.lib;%(AdditionalDependencies)</AdditionalDependencies></Link></ItemDefinitionGroup><ItemGroup />
    </Project>
    

其中IncludePath 是库的包含路径,LibraryPath 是链接库路径,AdditionalDependencies 是链接库名称。

如何导入属性表

在 visual studio 中点击 属性管理器,右键 Release|x64 -> 添加现有属性表。最后效果如图:

在这里插入图片描述

新建 C++ 项目,将我们前面编写的 C++ 文件添加进来,按照如图配置:

在这里插入图片描述

点击 本地 Windows调试器 即可完成编译。

方法二:cmake 配置环境

首先安装 cmake:https://cmake.org/download/

比如 Windows 64位系统选择 cmake-3.18.2-win64-x64.msi,跟随安装程序即可完成安装。

新建一个目录 D:\CaptchaRecognize\,在目录下新建 CMakeLists.txt

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(CaptchaRecognize)SET(CMAKE_BUILE_TYPE RELEASE)INCLUDE_DIRECTORIES(
D:/CplusLib/libtorch/include
D:/CplusLib/libtorch/include/torch
D:/CplusLib/opencv/build/include
D:/CplusLib/opencv/build/include/opencv2
)SET(TORCH_LIBRARIES D:/CplusLib/libtorch/lib)
SET(OpenCV_LIBS D:/CplusLib/opencv/build/x64/vc15/lib)LINK_DIRECTORIES(
${TORCH_LIBRARIES}
${OpenCV_LIBS}
)add_executable(CaptchaRecognize 源.cpp)target_link_libraries(CaptchaRecognize
asmjit.lib
c10.lib
c10_cuda.lib
caffe2_detectron_ops_gpu.lib
caffe2_module_test_dynamic.lib
caffe2_nvrtc.lib
clog.lib
cpuinfo.lib
dnnl.lib
fbgemm.lib
libprotobuf-lite.lib
libprotobuf.lib
libprotoc.lib
mkldnn.lib
torch.lib
torch_cpu.lib
torch_cuda.lib
opencv_world440.lib
)set_property(TARGET CaptchaRecognize PROPERTY CXX_STANDARD 14)

该文件中的一些属性和属性表的内容相似。将 源.cpp 复制到该目录下,新建目录 build

在这里插入图片描述

打开 Cmake 进行如下配置:

在这里插入图片描述

点击 configure -> finish,然后点击 Generate 生成 visual studio 工程。

build 目录中找到工程文件并打开,打开 解决方案资源管理器,右键 CaptchaRecognize -> 设为启动项目

在这里插入图片描述

编译属性

在这里插入图片描述
点击 本地 Windows调试器 即可完成编译。

python 调用 C++ 程序

该 C++ 程序的命令行调用示例

CaptchaRecognize.exe model_script.pt pic1.png pic2.png

在 python 中通过 os.popen('command').read() 即可像命令行一样调用 C++ 程序并读取程序的输出结果。

例如

import osresult = os.popen('CaptchaRecognize.exe model_script.pt pic1.png pic2.png').read() #调用 C++ 程序
result = result.split(' ')

由前面可知 C++ 程序输出的结果由单空格隔开,通过 .split(' ') 可得到含所有图片识别结果的列表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118138.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

艾伏尼布:一种用于治疗复发或难治性AML和胆管癌的IDH1抑制剂【医游记】

&#xff08;图片来源于网络) 艾伏尼布&#xff0c;商品名为拓舒沃&#xff0c;是中国首个获批的针对异柠檬酸脱氢酶-1 (IDH1)突变癌症的强效口服靶向抑制剂1。这款药物最初由Agios Pharmaceuticals公司开发&#xff0c;2018年基石药业与Agios达成在大中华区开发及商业化的独家…

RK3399平台开发中安卓系统去除USB权限弹窗

RK3399平台开发中安卓系统去除USB权限弹窗 问题方法 问题 当我们在访问一个插入到Android系统的USB设备的时候往往是需要权限的&#xff0c;此时系统会弹出询问权限的对话框&#xff0c;而我们此时希望让它默认允许访问USB设备并且不希望用户看到这个对话框。 方法 文件目录&…

CSS - 常用属性和布局方式

目录 前言 一、常用属性 1.1、字体相关 1.2、文本相关 1.3、背景相关 1.3.1、背景颜色 1.3.2、背景图片 1.4、圆角边框 二、常用布局相关 2.1、display 2.2、盒子模型 2.2.1、基本概念 2.2.2、border 边框 2.2.3、padding 内边距 2.2.4、margin 外边距 2.3、弹…

gcc和makfile

gcc和makfile gcc预处理(进行宏替换)编译(生成汇编)汇编(生成机器可以识别的代码)连接(生成可以执行的文件或者库文件) makemakefile文件的编写 gcc 没安装的话&#xff0c;按照终端提示的安装命令安装就行 运行的格式&#xff1a; gcc [选项] [要编译的文件] [选项] [目标文件…

SylixOS BSP开发(八)

初始化FPU、MMU和Cache组件 本来想在不初始化这些部件的情况下把SylixOS先启动起来感受下&#xff0c;结果测试发现如果MMU不使能的话&#xff0c;系统启动过程中线程无法进行调度emm。。。所以只好把这一章节提前来讲了。这三个组件的初始化都是在bspInit.c中进行的。 1. FPU初…

Unity游戏开发中打造游戏攻击技能架构与设计

一、技能系统的设计 在 MOBA 游戏中&#xff0c;每个英雄角色都会有多个技能&#xff0c;这些技能可以分为普通攻击和技能攻击两种。普通攻击是英雄角色的基本攻击方式&#xff0c;而技能攻击则需要消耗一定的资源&#xff08;如蓝量&#xff09;才能使用。在设计技能系统时&a…

【面试经典150 | 链表】循环链表

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希集合方法二&#xff1a;快慢指针方法三&#xff1a;计数 拓展其他语言python3 写在最后 Tag 【快慢指针】【哈希集合】【计数】【链表】 题目来源 141. 环形链表 题目解读 判断一个链表中是否存在环。 解题思路 …

(Java)中的数据类型和变量

文章目录 一、字面常量二、数据类型三、变量1.变量的概念2.语法的格式3.整型变量4.长整型变量5.短整型变量6.字节型变量 四、浮点型变量1.双精度浮点数2.单精度浮点数 五、字符型常量六、布尔型变量七、类型转换1.自动类型转换&#xff08;隐式&#xff09;2.强制类型转换(显式…

react的setState做了什么

1、为什么需要setState setState的作用是帮助我们更改数据的同时并且通知视图进行渲染。因为React并不会绑定视图和state&#xff0c;需要我们手动去更新视图。 2、setState什么时候是同步的&#xff0c;什么时候是异步的 setState这个方法在调用的时候是同步的&#xff0c;…

【人工智能Ⅰ】实验1:谓词表示法与产生式知识表示

实验1 谓词表示法与产生式知识表示 一、实验目的 1、熟悉谓词逻辑表示法&#xff1b; 2、理解和掌握产生式知识表示方法&#xff0c;实现产生式系统的规则库。 二、实验内容 要求通过C/C/python语言编程实现&#xff1a; 1、猴子摘香蕉问题 2、动物识别系统 &#xff08…

Spring无法加载静态属性和SpringBoot单元测试指定加载配置文件

一、Spring无法加载静态属性&#xff0c;怎么解决&#xff1f; Spring主要用于管理和注入Bean&#xff08;对象&#xff09;的实例属性&#xff0c;而不是静态属性。静态属性属于类本身&#xff0c;而不是类的实例&#xff0c;因此Spring的依赖注入机制不会处理它们。 看图&a…

项目部署Linux步骤

1、最小化安装centos7-环境准备 安装epel-release 安装epel-release&#xff0c;因为有些rpm包在官方库中找不到。前提是保证可以联网 yum install -y epel-release 修改IP net-tools net-tool&#xff1a;工具包集合&#xff0c;包含ifconfig等命令 yum install -y net-…

【MySQL-->数据操作】

文章目录 前言一、insert1.单行插入2.多行插入3.插入更新/替换 二、select1.全列查询2.指定列插入3.列别名4. 表达式计算5.去重6.where条件查询7.排序8.limit分页显示 三、update四、delete五、插入查询结果六、聚合函数六、聚合分组1.格式2.where和having的区别 前言 一、inse…

个人创业新机遇,零成本开启真人手办定制项目

桌上的日历变薄&#xff0c;2023年已经接近尾声&#xff0c;浅观这一年&#xff0c;您是否发现大家日常关注的重点有明显的变化&#xff0c;诸多社会事件和宏观数字的背后&#xff0c;潜藏着对经济的“不托底”&#xff0c;而当我们真正开始关注起用个人经济积累去对冲未来的不…

大数据Flink(一百零一):SQL 表值函数(Table Function)

文章目录 SQL 表值函数(Table Function) SQL 表值函数(Table Function) Python UDTF,即 Python TableFunction,针对每一条输入数据,Python UDTF 可以产生 0 条、1 条或者多条输出数据,此外,一条输出数据可以包含多个列。比如以下示例,定义了一个名字为 split 的Pyt…

【会员管理系统】篇二之项目搭建、初始化、安装第三方库

一、项目搭建 1.全局安装vue-cli npm install -g vue/cli查看版本信息 vue -V 2.创建项目 vue create 项目名称 回车 回车 剩余选择如下 之后等待项目创建 最后npm run serve 二、初始化配置 1.更改标题 打开public下的index&#xff0c;将title标签里的改成想要设置的…

汽车电子专有名词与相应技术

1.EEA &#xff08;Electronic & Electrical Architecture 电子电气架构&#xff09; EEA在宏观上概括为物理架构与逻辑架构的结合&#xff0c;微观上通过众多电子元器件的协同配合&#xff0c;或集成式或分布式的系统级电子电气架构&#xff0c;具体详见专栏 新能源汽车电…

contenteditable实现文本内容确认提示

功能需求&#xff1a; 列表进行批量查询&#xff0c;需要对输入的值做提交校验&#xff0c;分三种情况&#xff1a; 若部分字符串有误&#xff0c;部分字符串需要变更字体颜色做提示&#xff0c;再次点击确认则对部分正确数据执行批量查询 若全部数据有误则变更字体颜色做提示&…

redis场用命令及其Java操作

目录 1. Redis入门 1.1 Redis简介 1.2 Redis下载与安装 1.2.1 Redis下载 1.2.2 Redis安装 1.3 Redis服务启动与停止 1.3.1 服务启动命令 1.3.2 客户端连接命令 1.3.3 修改Redis配置文件 1.3.4 Redis客户端图形工具 2. Redis数据类型 2.1 五种常用数据类型介绍 2.2 …

docker(1) dockfile制作docker java镜像 并启动

Dockerfile就是利用固定的指令来描述镜像的结构和构建过程&#xff0c;这样Docker才可以依次来构建镜像&#xff1b; 将springboot项目打包上传到指定目录,并且编写一个dockefile文件放在jar包的同级目录 输入命令 打包镜像 成功打包显示 运行镜像