本文旨在为大家提供jetson嵌入式系列模型部署两个简单的技术路线,直白的说就是给大家安利两个仓库分别是tensorrtx和tensorRT_Pro。本文采用常见的yolov5(v6.0版本)目标检测算法实现在jetson nano上的模型部署工作(PS:手头只有nano,太穷了,)。假设各位看官的jetson nano环境配置已经完成,能够使用yolov5成功训练自己的数据集。我们重点关注jetson nano上的部署工作。有错误欢迎各位批评指正!!!
本次训练的模型使用yolov5s-6.0,类别数为2,为口罩识别。先看效果图,第一张图为tensorrtx在jetson nano上的推理效果图,可参考Jetson嵌入式系列模型部署-2查看详细流程;第二张图为tensorRT_Pro在jetson nano上的推理效果图,可参考Jetson嵌入式系列模型部署-3查看详细流程。
问题: 什么是深度学习模型部署?为什么需要部署?如何去部署呢?
什么是深度学习模型部署?
简单来说就是将你训练好的深度学习模型应用在不同场景下的不同设备上(即特定环境下运行),这些设备可能是服务器、移动端、嵌入式…
为什么要学习部署呢?
直接将模型放在不同的设备上跑不就完事了吗?其实不然,这样做存在两个问题:模型框架兼容性差以及模型运行速度慢。大家知道目前训练模型都基于深度学习框架如
pytorch、tensorflow、paddle
等,这些框架的兼容性差(直白点说就是环境配置麻烦,一想起深度学习环境配置就头痛),而且这些框架基于python语言其运行速度无法和C++这类语言相比。假设我们需要在jetson nano上去部署属于自己的yolov5模型,难道要求我们在jetson nano上配置pytorch等深度学习环境吗?那未免也太折磨人了,单纯基于
pytorch
等框架去进行模型的推理存在以下几个问题:
- 环境配置繁琐,且不说在arm架构的嵌入式上配置深度学习环境了,光在PC端都要折腾一阵
- 携带的框架太过笨重,训练出的模型太冗余需要优化才能满足实际需求
- 语言问题,框架大多基于python语言,运行速度慢
- 移植问题,框架环境依赖性强,耦合性高,无法更加方便的移植
- 参考自模型部署简介
基于以上问题,我们想能不能将框架隔离呢?即仅通过
pytorch、tensorflow、paddle
等框架训练模型,后续在不同场景下的部署实现仅需要训练好的模型即可,而不需要依赖框架推理。
如何去部署?
即解决方案。怎么利用训练好的模型不依赖框架推理呢?——通过模型推理部署框架,依旧是框架不过这次换成了模型推理框架。目前主流的模型推理部署框架有以下几种:
- NVIDIA的TensorRT。首当其冲的肯定是tensorRT,NVIDIA通过其自家的GPU,CUDA、CUDNN等软件环境形成了一个强大的生态圈。该推理框架主要是针对NVIDIA的显卡和其推出的jetson系列嵌入式设备。
- Intel的OpenVINO。openvino是Intel开发的基于inter CPU计算设备的推理引擎。
- Tencent的NCNN。ncnn是腾讯基于移动端的推理引擎。
- Microsoft的ONNXRuntime。onnx是microsoft开发的一个中间格式,而ort(onnxruntime)是其为onnx开发的推理引擎
- Rockchip的RKNN。rknn是瑞芯微为其NPU设计的nn推理引擎。
- 参考自业界主流模型推理部署框架,RKNN使用
具体使用那种推理框架呢?—看需求,部署的方式取决于需求。如果需要在jetson系列嵌入式平台上推理,那么选择tensorRT再合适不过了;如果需要在手机移动端推理,那么可以腾讯的ncnn推理框架;能做到见招拆招即可。参考自训练好的深度学习模型式怎么部署的?
分享jetson系列嵌入式设备的模型部署,那肯定需要聊聊tensorRT。
tensorRT
是一个SDK(Software Development Kit)即软件开发工具包,用于优化经过训练的深度学习模型以实现高性能推理
TensorRT为什么能加速推理过程,它是如何优化的?主要体现在以下几个方面:
Conv+Bias+ReLU -> CBR
通过tensorRT能够在Nvidia系列GPU上发挥出最好的性能。值得注意的是,tensorRT的模型,需要在目标GPU上以实际运行的方式选择最优算法和配置,也因此tensorRT生成的模型是与其设备强绑定的,与其编译时的trt版本、cuda版本、GPU型号相关联。同时tensorRT支持FP32、FP16、INT8等多种精度,如何查看自身GPU是否支持FP16/INT8精度呢?主要分以下两步
- 1. 访问https://developer.nvidia.com/zh-cn/cuda-gpus#compute查看显卡对应的算力
- 2. 访问https://docs.nvidia.com/deeplearning/tensorrt/support-matrix/index.html#hardware-precision-matrix查看对应算力支持的精度
比如说jetson nano算力是5.3,只支持FP32不支持FP16、INT8。
如果想了解关于tensorRT更多细节请查看tensorRT官方文档
tensorRT是如何构建模型呢?主要通过两种方式
1. 通过TRT API一层层搭建模型
2. NVIDIA官方也提供另外三种途径实现更加方便的封装,如下图所示
UFF格式的文件
,通过libnvparsers.so
可以调用TRT API去解析UFF文件从而构建模型(tensorflow采用的方案)ONNX格式的文件
,通过libnvonnxparser.so
可以调用TRT API去解析ONNX文件从而构建模型(pytorch采用的方案)Caffe格式的文件
,通过libnvcaffe_parser.so
可以调用TRT API去解析Caffe文件从而构建模型(使用较少)Copy自详解TensorRT的C++/Python高性能部署,建议看原视频的详细讲解
repo1为每个模型写硬代码,流程如下所示
gen_wts.py
存储权重gen_wts.py
产生的权重文件repo2为每个算子写Converter,反射Moule.forward捕获输入输出和图结构,流程如下所示
Converter
,为每个操作的forward
反射到自定义函数下torch的forward
操作捕获模块的权重,调用Python API
接口实现模型构建repo3基于ONNX路线,提供C++、Python接口,深度定制ONNXParser,低耦合封装,实现常用模型Yolov7、YoloX、Yolov5、Yolov3、Unet、RetinaFace、Arcface、SCRFD、DeepSORT等等。算子由官方维护,模型直接导出,流程如下所示
单独将这个repo拿出来讲是因为里面有些内容值得深挖,该repo基于ONNX路线完成tensorRT模型构建,需要编译protobuf,下面简单聊聊为什么需要编译protobuf、什么是protobuf以及onnx是什么等相关内容
关于protobuf的相关介绍Copy自赵老师的百度Apollo智能驾驶课程,建议看原视频,关于protobuf的编译请参考here
概念
Protobuf
全称Protocol buffers
,是Google
研发的一种跨语言、跨平台的序列化结构的数据格式,是一个灵活的、高效的用于序列化数据的协议
特点
在序列化数据时常用的数据格式还有XML
、JSON
等,相比较而言,Protobuf
更小、效率更高且使用更为便捷,Protobuf
内置编译器protoc
,可以将protobuf文件
编译成C++
、Python
、Java
、C#
、Go
等多种语言对应的代码,然后可以直接被对应语言使用,轻松实现对数据流的读或写操作而不需要再做特殊解析。
Protobuf
的优点如下:
Protobuf
也有缺点:
现有需求如下
创建一个protobuf文件,在该文件中声明学生的姓名、身高、年龄…等信息,然后分别使用C++和Python实现学生数据的读写操作。
实现流程如下
1.编写proto文件
2.编译生成对应的C++或Python文件
3.在C++或Python中调用
1.编写proto文件,如下所示
// student.proto
// 使用的 proto 版本
syntax = "proto2"
// 包
package person;
//消息 ---message 是关键字,Student 消息名称
message Student{
//字段
//字段格式:字段规则 数据类型 字段名称 字段编号
required string name = 1;
optional unit64 age = 2;
optional double height = 3;
repeated string books = 4;
}
2.编译,指令如下
$ protoc student.proto --cpp_out=./
执行完成后,在当前目录下student.proto
文件会生成student.pb.h
和student.pb.cc
,将.cc
后缀修改为.cpp
可供C++调用
3.C++调用,调用demo如下
// test.cpp
#include
using namespace std;
using namespace person;
int main(int argc, char const *argv[])
{
// 1. create object
person::Student stu;
// 2. wirte data
stu.set_name("zhangsan");
stu.set_age(18);
stu.set_height(1.75);
stu.add_books("c++");
stu.add_books("python");
// 3. read data
std::string name = stu.name();
uint64_t age = stu.age();
double height = stu.height();
std::cout << name << " == " << age << " == " << height << std::endl;
for (int i = 0; i < stu.books_size(); i++)
{
std::cout << stu.books(i) << "-";
}
std::cout << std::endl;
return 0;
}
CMakeLists.txt如下
cmake_minimum_required(VERSION 3.0)
project(test)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread -std=c++11")
set(CMAKE_BUILD_TYPE Debug)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/workspace)
set(PROTOBUF_DIR "/home/zhlab/protobuf")
include_directories(
${PROTOBUF_DIR}/include
${PROJECT_SOURCE_DIR}/src
)
link_directories(
${PROTOBUF_DIR}/lib
)
add_executable(main ${PROJECT_SOURCE_DIR}/src/test.cpp ${PROJECT_SOURCE_DIR}/src/student.pb.cpp)
# add protobuf
target_link_libraries(main protobuf)
target_link_libraries(main pthread)
编译test.cpp
文件,在workspace/
文件夹下运行可执行文件,图解如下所示
给出protobuf的demo演示源码下载链接Baidu Drive[password:yolo]
onnx可以理解为一种通用货币,开发者可以把自己开发训练好的模型保存为onnx文件,而部署工程师可以借助部署框架(如tensorRT、openvino、ncnn等)部署在不同的硬件平台上,而不必关系开发者使用的是哪一种框架
onnx的本质是一种protobuf格式文件
protobuf通过编译onnx-ml.proto
文件得到onnx-ml.pb.h和onnx-ml.pb.cc
用于C++调用或onnx_ml_pb2.py
用于python调用,如下图所示。如果本地python环境下安装了onnx第三方库,则在该库下可以找到onnx_ml_pb2.py
文件
通过编译得到的onnx-ml.pb.cc
和代码就可以操作onnx模型文件,实现对应的增删改
onnx-ml.proto
用于描述onnx文件时如何组成的,具有什么结构,它是onnx经常参照的东西,如下是onnx-ml.proto部分内容,参考自https://github.com/shouxieai/tensorRT_Pro/blob/main/onnx/onnx-ml.proto
onnx文件组成如下图所示
关于jetson nano刷机就不再赘述了,需要各位看官自行配置好相关环境,外网访问较慢,这里提供Jetson nano的几个JetPack镜像下载链接Baidu Drive[password:nano]【更新完毕!!!】(PS:提供4.6和4.6.1两个版本,注意4GB和2GB的区别,不要刷错了),关于Jetson Nano 2GB和4GB的区别可参考链接Jetson NANO是什么?如何选?。(吐槽下这玩意上传忒慢了,超级会员不顶用呀,终于上传完了,折磨!!!),博主使用的jetpack版本为JetPack4.6.1,其详细信息如下所示
本篇博客简单介绍了模型部署的相关工作以及驾驭tensorRT的几种方案。后续通过tesorrtx和tensorRT_Pro两个repo带大家实现在jetson nano上的yolov5模型部署。可参考Jetson嵌入式系列模型部署-2和Jetson嵌入式系列模型部署-3
build src workspace CMakeLists.txt
四个文件build
为博主在jetson nano上编译生成的中间文件(PS:可删除自行编译)src
文件夹下包含student.proto
及其生成的student.pb.h
和student.pb.cc
,test.cpp
用于C++调用workspace
下存放着可执行文件感谢各位看到最后,创作不易,读后有收获的看官请帮忙点个⭐️