C++部署深度学习模型
文章目录
- 前言
- ONNX
- C++
- TensorRT
- 参考
前言
在部署大规模深度学习应用的时候,要想满足应用需求或者压榨模型的性能,C++可能是比python更好的选择方案。基于此,特地记录最近的C++的学习经历。其实以终为始来思考为什么学习C++,首先是为了能够很好地提升模型的性能,满足应用场景中的高可用,高并发,低时延等要求。为了提升模型的性能,需要用到一些推理框架,如TensorRT、NCNN或者Openvino(本文中以TensorRT作为案例)。TensorRT在8.0以上的版本都支持Python的API了,但还是有必要学习C++。另外在模型压缩的时候也会考虑用到C++。
确定推理框架后,然后确定这个框架需要什么格式的模型?这里面可能要提到模型集大成者ONNX,因此需要学习ONNX模型。学习如何将Tensorflow、Pytorch或者keras的模型转换成ONNX,它支持什么算子,这些都是需要学习的。
最后总结这个路线是Tensorflow或pytorch模型转换成ONNX,然后ONNX对模型进行优化,转换成TensorRT模型优化以及C++的推理。
- ONNX模型转换和优化
- TensorRT转换和优化
- C++对ONNX模型的推理和TensorRT推理实现。
ONNX
关于ONNX的转换可以参考我的git仓库:onnx模型转换。当中包括Tensorflow和Pytorch的模型转换Demo。同时包括用onnxruntime
进行推理的过程。在这一块基本的转换过程已经转换了,后续需要更加深化,了解支持的算子,如何转换复杂模型,甚至如何写算子等都要学会。
C++
C++需要学习基础知识,这些都不在话下了。看下书,学习视频,以下用一个C++调用ONNX模型推理作为例子,具体代码可以参考:ONNX C++。另外发现了一个宝藏博主,可以参考他的系列文章:
- https://blog.csdn.net/qq_34124780/article/details/114666312
- 2021.04.15更新 c++下使用opencv部署yolov5模型 (二)
- 2021.09.02更新说明 c++下使用opencv部署yolov5模型 (三)
- 2021.11.01 c++下 opencv部署yolov5-6.0版本 (四)
- 2022.07.25 C++下使用opencv部署yolov7模型(五)
- 代码
在上面博客中使用的是OpenCV自带的DNN推理框架,有时间比较下各种推理框架的优势与劣势。YOLOX的推理主要完成三个步骤:模型加载、图像预处理以及结果后处理。定义如下的头文件:
ONNX
推理头文件:
#include <assert.h>
#include<onnxruntime_cxx_api.h>
#include<ctime>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>class yoloxmodelinference {public:yoloxmodelinference(const wchar_t* onnx_model_path);float* predict_test(std::vector<float>input_tensor_values, int batch_size = 1);cv::Mat predict(cv::Mat& input_tensor, int batch_size = 1, int index = 0);std::vector<float> predict(std::vector<float>& input_data, int batch_size = 1, int index = 0);
private:Ort::Env env;Ort::Session session;Ort::AllocatorWithDefaultOptions allocator;std::vector<const char*>input_node_names;std::vector<const char*>output_node_names;std::vector<int64_t> input_node_dims;std::vector<int64_t> output_node_dims;std::size_t num_output_nodes;std::size_t num_input_nodes;const int netWidth = 640;const int netHeight = 640;const int strideSize = 3;//stride sizefloat boxThreshold = 0.25;
};
#endif // !yoloxmodel
DNN
推理的头文件:
#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>struct Output {//类别int id;//置信度float confidence;//矩形框cv::Rect box;
};class YOLO {public:YOLO() {}~YOLO(){}bool initModel(cv::dnn::Net& net, std::string& netPath, bool isCuda);std::vector<Output>& Detect(cv::Mat& image, cv::dnn::Net& net);private://网络输入的shapeconst int netWidth = 640; //ONNX图片输入宽度const int netHeight = 640; //ONNX图片输入高度const int strideSize = 3; //stride sizefloat boxThreshold = 0.25;float classThreshold = 0.25;float nmsThreshold = 0.45;float nmsScoreThreshold = boxThreshold * classThreshold;};
头文件可以看作是一个“配置文件”,里面声明函数和一些固定的参数。
DNN的读取文件非常简单,如下所示。同时也非常清晰看到DNN是可以使用cuda的。
bool YOLO::initModel(Net& net, string& netPath, bool isCuda)
{try {net = readNet(netPath);}catch (const exception& e) {cout << e.what() << std::endl;return false;}//cuda//if (isCuda) {// net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);// net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);//}net.setPreferableBackend(DNN_BACKEND_DEFAULT);net.setPreferableTarget(DNN_TARGET_CPU);return true;
}
ONNX
会比较麻烦一点,说麻烦一点,其实是没有认真学习当中的API:
yoloxmodelinference::yoloxmodelinference(const wchar_t* onnx_model_path):session(nullptr), env(nullptr) {//初始化环境,每个进程一个环境,环境保留了线程池和其他状态信息this->env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "yolox");//初始化Session选项Ort::SessionOptions session_options;session_options.SetInterOpNumThreads(1);session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);// 创建Session并把模型加载到内存中this->session = Ort::Session(env, onnx_model_path, session_options);//输入输出节点数量和名称this->num_input_nodes = session.GetInputCount();this->num_output_nodes = session.GetOutputCount();for (int i = 0; i < this->num_input_nodes; i++){auto input_node_name = session.GetInputName(i, allocator);this->input_node_names.push_back(input_node_name);Ort::TypeInfo type_info = session.GetInputTypeInfo(i);auto tensor_info = type_info.GetTensorTypeAndShapeInfo();ONNXTensorElementDataType type = tensor_info.GetElementType();this->input_node_dims = tensor_info.GetShape();}for (int i = 0; i < this->num_output_nodes; i++){auto output_node_name = session.GetOutputName(i, allocator);this->output_node_names.push_back(output_node_name);}
}
接着是图像的预处理,当前参考的是上面博主的代码,存在一个问题是博主用的ONNX模型是torch格式的yolo模型转换得到的,而我的yolo模型是tensorflow格式的。两者的不同点在于前者是[batchsize, channel, width, height]
,而后者tensortflow是[batchsize, width, height, channel]
。因此这是当前需要解决的问题之一。通过搜索一些资料,如change-blobfromimage-dimensions-order,发现原来OpenCV的推理框架对Tensorflow支持不太友好,因此为了解决这个问题,到github上下载官方提供的通过pytorch转换的ONNX模型。
TensorRT
TensorRT支持三种网络的结构和参数:
- TF-TRT,要求的是Tensorflow模型。该API集成在Tensorflow框架当中,开发成本是最小的
- ONNX模型格式,该方法开发周期相对比较短,对用户挺友好
- 使用TensorRT API手动把模型搭建,这是骨灰大神级别的任务操作。
综上第二点是比较经济实惠的,使用ONNX的模型的时候是尚未被优化的,因此需要用TensorRT优化特定的参数,得到TensorRT Engine模型,最后使用该模型进行推理。
(1)使用trtexec.exe
trtexec 是 TensorRT sample 里的一个例子,把 TensorRT 许多方法包装成了一个可执行文件。它可以把模型优化成 TensorRT Engine ,并且填入随机数跑 inference 进行速度测试。命令./trtexec --onnx=model.onnx
把 onnx 模型优化成 Engine ,然后多次 inference 后统计并报时。也可以将ONNX模型转换成trt格式的TensorRT模型: /trtexec --onnx=model.onnx --saveEngine=xxx.trt
。trtexec作用是看模型最快能跑多快,它是不管精度的,如果真想实际部署上又快又好的模型还是要自己调 TensorRT 的 API。
一般而言,大家写的模型内都是float32的运算,TensorRT 会默认开启 TF32 数据格式,它是截短版本的 FP32,只有 19 bit,保持了 fp16 的精度和 fp32 的指数范围。另外,TensorRT 可以额外指定精度,把模型内的计算转换成 float16 或者 int8 的类型,可以只开一个也可以两个都开,trtexec会倾向于速度最快的方式(有些网络模块不支持 int8)。--best
参数,这个参数相当于 --int8 --fp16
同时开。
要注意一点的是int8 优化涉及模型量化,需要校准(calibrate)提升精度。TensorRT 有两种量化方法:训练后量化和训练中量化。二者的校准方法不同,精度也不同,后者更高一些。具体参考NVIDIA Deep Learning TensorRT Documentation。
(2)量化校准
这里用的还是训练后校准。逻辑是:搞一些真实输入数据(不需要输出),告诉 TensorRT,它会根据真实输入数据的分布来调整量化的缩放幅度,以最大程度保证精度合适。理论上校准数据越多,精度越高,但实际上不需要太多数据,TensorRT 官方说 500 张图像就足以校准 ImageNet 分类网络。
在这里我觉得要做到这一步校准,首先你得保证你得ONNX模型也是准确的,换言之用训练框架保存的模型转换成ONNX模型应先进行校准。下面就介绍实践教程|实现 PyTorch-ONNX 精度对齐工具。
在把深度学习框架模型转换成中间表示模型后,部署工程师们要做的第一件事就是精度对齐,确保模型的计算结果与之前相当。精度对齐时最常用的方法,就是使用测试集评估一遍中间表示模型,看看模型的评估指标(如准确度、相似度)是否下降。
为了把 PyTorch 和 ONNX 模块对应起来,我们可以使用一种储存了调试信息的自定义算子,如下图所示:
可以定义一个叫做Debug
的ONNX
算子,它有一个属性调试名name
。而由于每一个ONNX
算子节点又自带了输出张量的名称,这样一来,ONNX
节点的输出名和调试名绑定在了一起。可以顺着PyTorch
里的调试名,找到对应ONNX
里的输出,完成PyTorch
和ONNX
的对应。详情可参考原文,原文大致思路是与在Linux上调试一样,写一些print
,cout
的语句来一个个对比输出结果的差异。同时也可以从尾部出发,一步步向前调试进行优化,查看两者精度的差异。如果发现有差异的算子,就要具体了解到哪一步出现问题,重新编写ONNX模型了。
参考
- Nvidia TensorRT
- 踩坑实录 TensorRT傻瓜式部署流程
- ONNX
C++部署深度学习模型相关推荐
- 实践教程 | TensorRT部署深度学习模型
作者 | ltpyuanshuai@知乎 来源 | https://zhuanlan.zhihu.com/p/84125533 编辑 | 极市平台 本文仅作学术分享,版权归原作者所有,如有侵权请联系删 ...
- TensorRT部署深度学习模型
1.背景 目前主流的深度学习框架(caffe,mxnet,tensorflow,pytorch等)进行模型推断的速度都并不优秀,在实际工程中用上述的框架进行模型部署往往是比较低效的.而通过Nvidia ...
- 实战 | 深度学习轻松学:如何用可视化界面来部署深度学习模型 转载 2017年12月27日 00:00:00 109 翻译 | AI科技大本营 参与 | 王赫 上个月,我有幸结识了 DeepCogn
实战 | 深度学习轻松学:如何用可视化界面来部署深度学习模型 转载 2017年12月27日 00:00:00 标签: 109 编辑 删除 翻译 | AI科技大本营 参与 | 王赫 上个月,我有幸结识了 ...
- 深度学习模型保存_Web服务部署深度学习模型
本文的目的是介绍如何使用Web服务快速部署深度学习模型,虽然TF有TFserving可以进行模型部署,但是对于Pytorch无能为力(如果要使用的话需要把torch模型进行转换,有些麻烦):因此,本文 ...
- amd 深度学习模型部署_Web服务部署深度学习模型-续集
在上一篇中,本人介绍了如何使用Web服务部署深度学习模型,见知乎链接:刘聪NLP:Web服务部署深度学习模型. 有同学提问:"是否可以在web上有输入数据的接口,通过深度学习模型的计算数据的 ...
- 收藏 | TensorRT部署深度学习模型
点上方计算机视觉联盟获取更多干货 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:作者 | ltpyuanshuai@知乎 来源 | https://zhuanlan.zhihu.com/p/ ...
- C++环境下部署深度学习模型方案
目录 一.问题背景 二.解决方案 2.1 C++调用python 2.2 Python服务接口 2.3 Python转c++(不推荐) 2.4 深度学习部署框架(推荐) 三.总结 3.1 接口形式分类 ...
- 通过MACE在Android手机上部署深度学习模型
1. MACE的环境搭建 参考我的博客:MACE的环境搭建--conda实现 2. 构建项目 (1)下载MACE项目到本地 git clone https://github.com/XiaoMi/ma ...
- Transfer Learning Toolkit (TLT) + DeepStream (DS)快速部署深度学习模型(以口罩检测为例)
文章目录 简介 TLT DS 基于TLT进行迁移学习 环境准备 模型训练 基于DS的模型部署 总结 最近在做一个深度学习的横向,被实时性搞的很头疼,遂打算研究研究新的技术路线,做点技术储备.TLT+D ...
- 在英特尔硬件上部署深度学习模型的无代码方法 OpenVINO 深度学习工作台的三部分系列文章 - CPU AI 第一部
作者 Taylor, Mary, 翻译 李翊玮 关于该系列 了解如何转换.微调和打包推理就绪的 TensorFlow 模型,该模型针对英特尔®硬件进行了优化,仅使用 Web 浏览器.每一步都在云中使用 ...
最新文章
- Windows Azure Storage (25) Azure Append Blob
- HTTP错误汇总及其解决方法
- Git初始配置【一】
- LeetCode141 Linked List Cycle. LeetCode142 Linked List Cycle II
- Python与C语言的区别是什么?
- python中的continue和break
- elasticsearch_spring-data-elasticsearch 快速入门-Spring Boot+Elasticsearch
- 逻辑思维训练500题(修订版)
- 从googleDriver下载大数据集
- openwrt的源码下载及其编译 (一)
- html悬浮客服代码,js QQ客服悬浮效果实现代码
- 智能家居-斐讯N1安装篇
- 博客管理系统测试用例设计——XMind版和网页版
- 证明三角形中cosA+cosB+cosC=1+4sin(A/2)sin(B/2)sin(C/2)
- c语言案例六 速算24,参阅:C语言速算24数据结构课程设计最终版
- 转发页面,并且传参数,@click@dblclick冲突问题
- 八键电话号码的字母组合
- [Err] 23000 - [SQL Server]不能在具有唯一索引 'IX_student_info_2' 的对象 'dbo.student_info' 中插入重复键的行
- 【PMP认证考试之个人总结】 第 13 章 PMP计算题汇总
- 台式计算机idc数据排名,2019年电脑销量排行_IDC:2019年中国PC市场预测销量持续走低...
热门文章
- R | RColorBrewer颜色设置
- 501 5.1.7 Invalid address
- 输入6名学生的学号,姓名和三门课程的成绩,三门课程的名称:(programming,databse,network)。。。。。。。
- 利用wangEditor富文本上传图片及文本并回显
- iOS 多选相册图片上传,添加、删除图片
- 天源财富:“中国天眼”发现201颗新脉冲星
- 【北上广招聘】中企高呈找人:内容营销经理、SEM运营经理、KA客户经理...
- 计算机基础知识比赛主持稿,我校举办第一届计算机基础知识抢答赛
- XP sp3共享最大连接数修改工具 EvID4226Patch.exe
- 有N个灯放在一排,N个人进行操作,求灯泡最后的状态