文章目录

  • 前言
  • 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++的推理。

  1. ONNX模型转换和优化
  2. TensorRT转换和优化
  3. C++对ONNX模型的推理和TensorRT推理实现。

ONNX

关于ONNX的转换可以参考我的git仓库:onnx模型转换。当中包括Tensorflow和Pytorch的模型转换Demo。同时包括用onnxruntime进行推理的过程。在这一块基本的转换过程已经转换了,后续需要更加深化,了解支持的算子,如何转换复杂模型,甚至如何写算子等都要学会。

C++

C++需要学习基础知识,这些都不在话下了。看下书,学习视频,以下用一个C++调用ONNX模型推理作为例子,具体代码可以参考:ONNX C++。另外发现了一个宝藏博主,可以参考他的系列文章:

  1. https://blog.csdn.net/qq_34124780/article/details/114666312
  2. 2021.04.15更新 c++下使用opencv部署yolov5模型 (二)
  3. 2021.09.02更新说明 c++下使用opencv部署yolov5模型 (三)
  4. 2021.11.01 c++下 opencv部署yolov5-6.0版本 (四)
  5. 2022.07.25 C++下使用opencv部署yolov7模型(五)
  6. 代码

在上面博客中使用的是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支持三种网络的结构和参数:

  1. TF-TRT,要求的是Tensorflow模型。该API集成在Tensorflow框架当中,开发成本是最小的
  2. ONNX模型格式,该方法开发周期相对比较短,对用户挺友好
  3. 使用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 模块对应起来,我们可以使用一种储存了调试信息的自定义算子,如下图所示:

可以定义一个叫做DebugONNX算子,它有一个属性调试名name。而由于每一个ONNX算子节点又自带了输出张量的名称,这样一来,ONNX节点的输出名和调试名绑定在了一起。可以顺着PyTorch里的调试名,找到对应ONNX里的输出,完成PyTorchONNX的对应。详情可参考原文,原文大致思路是与在Linux上调试一样,写一些printcout的语句来一个个对比输出结果的差异。同时也可以从尾部出发,一步步向前调试进行优化,查看两者精度的差异。如果发现有差异的算子,就要具体了解到哪一步出现问题,重新编写ONNX模型了。

参考

  1. Nvidia TensorRT
  2. 踩坑实录 TensorRT傻瓜式部署流程
  3. ONNX

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

  1. 实践教程 | TensorRT部署深度学习模型

    作者 | ltpyuanshuai@知乎 来源 | https://zhuanlan.zhihu.com/p/84125533 编辑 | 极市平台 本文仅作学术分享,版权归原作者所有,如有侵权请联系删 ...

  2. TensorRT部署深度学习模型

    1.背景 目前主流的深度学习框架(caffe,mxnet,tensorflow,pytorch等)进行模型推断的速度都并不优秀,在实际工程中用上述的框架进行模型部署往往是比较低效的.而通过Nvidia ...

  3. 实战 | 深度学习轻松学:如何用可视化界面来部署深度学习模型 转载 2017年12月27日 00:00:00 109 翻译 | AI科技大本营 参与 | 王赫 上个月,我有幸结识了 DeepCogn

    实战 | 深度学习轻松学:如何用可视化界面来部署深度学习模型 转载 2017年12月27日 00:00:00 标签: 109 编辑 删除 翻译 | AI科技大本营 参与 | 王赫 上个月,我有幸结识了 ...

  4. 深度学习模型保存_Web服务部署深度学习模型

    本文的目的是介绍如何使用Web服务快速部署深度学习模型,虽然TF有TFserving可以进行模型部署,但是对于Pytorch无能为力(如果要使用的话需要把torch模型进行转换,有些麻烦):因此,本文 ...

  5. amd 深度学习模型部署_Web服务部署深度学习模型-续集

    在上一篇中,本人介绍了如何使用Web服务部署深度学习模型,见知乎链接:刘聪NLP:Web服务部署深度学习模型. 有同学提问:"是否可以在web上有输入数据的接口,通过深度学习模型的计算数据的 ...

  6. 收藏 | TensorRT部署深度学习模型

    点上方计算机视觉联盟获取更多干货 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:作者 | ltpyuanshuai@知乎 来源 | https://zhuanlan.zhihu.com/p/ ...

  7. C++环境下部署深度学习模型方案

    目录 一.问题背景 二.解决方案 2.1 C++调用python 2.2 Python服务接口 2.3 Python转c++(不推荐) 2.4 深度学习部署框架(推荐) 三.总结 3.1 接口形式分类 ...

  8. 通过MACE在Android手机上部署深度学习模型

    1. MACE的环境搭建 参考我的博客:MACE的环境搭建--conda实现 2. 构建项目 (1)下载MACE项目到本地 git clone https://github.com/XiaoMi/ma ...

  9. Transfer Learning Toolkit (TLT) + DeepStream (DS)快速部署深度学习模型(以口罩检测为例)

    文章目录 简介 TLT DS 基于TLT进行迁移学习 环境准备 模型训练 基于DS的模型部署 总结 最近在做一个深度学习的横向,被实时性搞的很头疼,遂打算研究研究新的技术路线,做点技术储备.TLT+D ...

  10. 在英特尔硬件上部署深度学习模型的无代码方法 OpenVINO 深度学习工作台的三部分系列文章 - CPU AI 第一部

    作者 Taylor, Mary, 翻译 李翊玮 关于该系列 了解如何转换.微调和打包推理就绪的 TensorFlow 模型,该模型针对英特尔®硬件进行了优化,仅使用 Web 浏览器.每一步都在云中使用 ...

最新文章

  1. Windows Azure Storage (25) Azure Append Blob
  2. HTTP错误汇总及其解决方法
  3. Git初始配置【一】
  4. LeetCode141 Linked List Cycle. LeetCode142 Linked List Cycle II
  5. Python与C语言的区别是什么?
  6. python中的continue和break
  7. elasticsearch_spring-data-elasticsearch 快速入门-Spring Boot+Elasticsearch
  8. 逻辑思维训练500题(修订版)
  9. 从googleDriver下载大数据集
  10. openwrt的源码下载及其编译 (一)
  11. html悬浮客服代码,js QQ客服悬浮效果实现代码
  12. 智能家居-斐讯N1安装篇
  13. 博客管理系统测试用例设计——XMind版和网页版
  14. 证明三角形中cosA+cosB+cosC=1+4sin(A/2)sin(B/2)sin(C/2)
  15. c语言案例六 速算24,参阅:C语言速算24数据结构课程设计最终版
  16. 转发页面,并且传参数,@click@dblclick冲突问题
  17. 八键电话号码的字母组合
  18. [Err] 23000 - [SQL Server]不能在具有唯一索引 'IX_student_info_2' 的对象 'dbo.student_info' 中插入重复键的行
  19. 【PMP认证考试之个人总结】 第 13 章 PMP计算题汇总
  20. 台式计算机idc数据排名,2019年电脑销量排行_IDC:2019年中国PC市场预测销量持续走低...

热门文章

  1. R | RColorBrewer颜色设置
  2. 501 5.1.7 Invalid address
  3. 输入6名学生的学号,姓名和三门课程的成绩,三门课程的名称:(programming,databse,network)。。。。。。。
  4. 利用wangEditor富文本上传图片及文本并回显
  5. iOS 多选相册图片上传,添加、删除图片
  6. 天源财富:“中国天眼”发现201颗新脉冲星
  7. 【北上广招聘】中企高呈找人:内容营销经理、SEM运营经理、KA客户经理...
  8. 计算机基础知识比赛主持稿,我校举办第一届计算机基础知识抢答赛
  9. XP sp3共享最大连接数修改工具 EvID4226Patch.exe
  10. 有N个灯放在一排,N个人进行操作,求灯泡最后的状态