第十二章 YOLO的部署实战篇(上篇)

21 篇文章 17 订阅 ¥49.90 ¥99.00
7 篇文章 0 订阅

前言

学习我的教程专栏,你将绝对能实现CUDA工程化,实现环境安装、index计算、kernel核函数编程、内存优化与steam性能优化、原子操作、nms的cuda算子、yolov5的cuda部署等内容,并开源教程源码。

恭喜朋友们,你们能跟着我的cuda教程学到这里,你们已然掌握cuda编写自己算子,也学了具有代表意义的NMS算子。你们应当已走进cuda编程世界了,后期各位技术高低全然在于各自熟练与进一步深度挖掘,我也不在过多列举太多无意义的列子分享大家。于是,我将带领大家如何使用cuda部署yolov5的算法,也会涉及到tensorrt相关内容,介于我不太清楚各位现有技术能力,我将介绍基于tensorrt的yolov5模型部署,也会涉及到一些tensorrt相关知识,主要分为三个部分内容,内容一基于onnx的yolov5部署的tensorrt介绍,内容二基于cpu部署onnx的yolov5模型介绍,主要介绍如何使用C++构建前后处理逻辑,内容三基于cuda部署的yolov5模型介绍,主要介绍如何使用cuda构建前后处理逻辑及device端内部处理过程,也是最重要内容。介于内容独创,前2个内容代码开源,后一个内容链接可在文章中获取

专栏概括

1、cuda教程目录

第一章 指针篇–>点击这里
第二章 CUDA原理篇–>点击这里
第三章 CUDA编译器环境配置篇–>点击这里
第四章 kernel函数基础篇–>点击这里
第五章 kernel索引(index)篇–>点击这里
第六章 kenel矩阵计算实战篇–>点击这里-上篇点击这里-下篇

第七章 kenel实战强化篇–>点击这里
第八章 CUDA内存应用与性能优化篇–>点击这里-上篇点击这里-中篇点击这里-下篇
第九章 CUDA原子(atomic)实战篇–>点击这里
第十章 CUDA流(stream)实战篇–>点击这里
第十一章 CUDA的NMS算子实战篇–>点击这里-上篇点击这里-下篇
第十二章 YOLO的部署实战篇–>点击这里-上篇点击这里-下篇

第十三章 基于CUDA的YOLO部署实战篇–>点击这里

2、cuda教程背景

随着人工智能的发展与人才的内卷,很多企业已将深度学习算法的C++部署能力作为基本技能之一。面对诸多arm相关且资源有限的设备,往往想更好的提速,满足更高时效性,必将更多类似矩阵相关运算交给CUDA处理。同时,面对市场诸多教程与诸多博客岑子不起的教程或高昂教程费用,使读者(特别是小白)容易迷糊,无法快速入手CUDA编程,实现工程化。
因此,我将结合我的工程实战经验,我将在本专栏实现CUDA系列教程,帮助读者(或小白)实现CUDA工程化,掌握CUDA编程能力。学习我的教程专栏,你将绝对能实现CUDA工程化,完全从环境安装到CUDA核函数编程,从核函数到使用相关内存优化与流stream优化,从内存、stream优化到深度学习算子开发(如:nms),从算子优化到模型(以yolo系列为基准)部署。最重要的是,我的教程将简单明了直切主题,CUDA理论与实战实例应用,并附相关代码,可直接上手实战。我的想法是掌握必要CUDA相关理论,去除非必须繁杂理论,实现CUDA算法应用开发,待进一步提高,将进一步理解更高深理论。

3、cuda教程内容

第一章到第三章探索指针在cuda函数中的作用与cuda相关原理及环境配置;

第四章初步探索cuda相关函数编写(globaldevice、__host__等),实现简单入门;

第五章探索不同grid与block配置,如何计算kernel函数的index,以便后续通过index实现各种运算;

第六、七章由浅入深探索核函数矩阵计算,深入探索grid、block与thread索引对kernel函数编写作用与影响,并实战多个应用列子(如:kernel函数实现图像颜色空间转换);

第八章探索cuda内存纹理内存、常量内存、全局内存等分配机制与内存实战应用(附代码),通过不同内存的使用来优化cuda计算性能;

第九章探索cuda原子(atomic)相关操作,并实战应用(如:获得某些自加索引等);

第十章探索cuda流stream相关应用,并给出相关实战列子(如:多流操作等);

第十一到十三章探索基于tensorrt部署yolo算法,我们首先将给出通用tensorrt的yolo算法部署,该部署的前后处理基于C++语言的host端实现,然后给出基于cuda的前后处理的算子核函数编写,最后数据无需在gpu与host间复制操作,实现gpu处理,提升算法性能。

目前,以上为我们的cuda教学全部内容,若后续读者有想了解知识,可留言,我们将根据实际情况,更新相关教学内容。

大神忽略

源码链接地址点击这里
yolov5部署代码链接点击这里
yolov5的cuda部署代码链接:文中链接源码

基于我的代码实测,cuda部署yolov5加速10倍,只想说cuda太香了!!!

一、基于onnx的tensorrt部署

我将在这里简单介绍基于onnx方式部署tensorrt相关内容,而具体也可参考链接点击这里
说明:模型onnx文件在构建network中使用!!!

1、构建engine文件

构建engine文件实际 构建glogging、builder、network、config、模型序列化为engine文件,glogging为日志,builder依赖glogging,network是builder部分、config也是builder一部分、序列化只为保存模型文件格式。当然也可以使用trtexec.exe该文件直接构建engine文件,这里不做介绍。

构建glogging

构建glogging,为创建builder做准备,简单创建代码如下:

class Logger : public nvinfer1::ILogger
{
    void log(Severity severity, const char* msg) noexcept override
    {
        // suppress info-level messages
        if (severity != Severity::kINFO)
            std::cout << msg << std::endl;
    }
} gLogger;

构架builder

创建builder,使用gLogger,代码如下:

IBuilder* builder = createInferBuilder(gLogger); // Create builder with the help of logger   构建builder 

构建network

构建网络,代码如下:

INetworkDefinition* network = builder->createNetworkV2(0U); //创造网络

然搭建网络可有多种方法,会根据不同格式转换成相应网络,我这里大概介绍自行搭建于onnx转网络方法,其中onnx转网络方法是我们重点关注内容。
①、自行搭建

网络构建完毕后,需为网络添加结构,可以使用onnx/caffe/uft解析添加网络,但本篇博客使用C++ API 构建网络,如下:

ITensor* data = network->addInput("data", DataType::kFLOAT, Dims3{ 1, 1, 1 });// Create an input with proper *name 创建输入,参数:名称  类型  维度
IFullyConnectedLayer* fc1 = network->addFullyConnected(*data, 1, weightMap["linear.weight"], weightMap["linear.bias"]);    // Add layer for MLP 参数:输入 输出 w权重 b权重
fc1->getOutput(0)->setName("out");  // set output with *name 设置fc1层的输出,(对特殊的网络层通过ITensor->setName()方法设定名称,方便后面的操作);指定网络的output节点,tensorrt必须指定输出节点,否则有可能会在优化过程中将该节点优化掉
network->markOutput(*fc1->getOutput(0)); //设为网络的输出,防止被优化掉

其中weightMap为权重保存变量,类似一个字典
②、onnx转换
onnx转网络较为简单,代码如下:

IParser* parser = createParser(*network, gLogger);
parser->parseFromFile(onnx_path, static_cast<int32_t>(ILogger::Severity::kWARNING));

config配置

调用TensorRT的builder来创建优化的runtime。 builder的其中一个功能是搜索其CUDA内核目录以获得最快的实现,因此用来构建优化的engine的GPU设备和实际跑的GPU设备一定要是相同的才行,这也是为什么无法适应其它环境原因。

builder具有许多属性,可以通过设置这些属性来控制网络运行的精度,以及自动调整参数。还可以查询builder以找出硬件本身支持的降低的精度类型。

有个特别重要的属性,最大batch size :大batch size指定TensorRT将要优化的batch大小。在运行时,只能选择比这个值小的batch。

config有个workspace size:各种layer算法通常需要临时工作空间。这个参数限制了网络中所有的层可以使用的最大的workspace空间大小。 如果分配的空间不足,TensorRT可能无法找到给定层的实现。

IBuilderConfig* config = builder->createBuilderConfig(); // Create hardware configs为builder分配内存,默认全部分配builder->setMaxBatchSize(1); // Set configurations
config->setMaxWorkspaceSize(1 << 20); // Set workspace size

其它参数配置,后期读者自己根据需求设置

构建engine

构建engine也较为简单,代码如下:

ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);// Build CUDA Engine using network and configurations<br>network->destroy();//顺带销毁网络,释放内存

序列化engine

IHostMemory** modelStream;//引擎变量声明,并保存序列化结果
(*modelStream) = engine->serialize(); //调用序列化方法

// Open the file and write the contents there in binary format
   std::ofstream p(file_engine, std::ios::binary);
   if (!p) {
       std::cerr << "could not open plan output file" << std::endl;
       return;
   }
   p.write(reinterpret_cast<const char*>(modelStream->data()), modelStream->size());

其中modelStream为序列化的变量,file_engine为保存engine的地址,如:“C:\Users\Administrator\Desktop\code\tensorrt-code\mlp\mlp.wts”

以上为tensorrt C++ API 将网络编译成engine,并保存的全部流程,若后续更改不同网络,主要更改步骤③构建网络模块。

2、部署engine模型

在1中已根据模型创建的engine模型文件,推理部署engine大致为读取engine文件,将其反序列后,构建context即可推理。

读取engine

char* trtModelStream{ nullptr }; //指针函数,创建保存engine序列化文件结果
size_t size{ 0 };
 
// read model from the engine file
std::ifstream file(file_engine, std::ios::binary);
if (file.good()) {
    file.seekg(0, file.end);
    size = file.tellg();
    file.seekg(0, file.beg);
    trtModelStream = new char[size];
    assert(trtModelStream);
    file.read(trtModelStream, size);
    file.close();
}

其中file_engine为:file_engine = “C:\Users\Administrator\Desktop\code\tensorrt-code\mlp\mlp.engine”

反序列化engine

// create a runtime (required for deserialization of model) with NVIDIA's logger
 IRuntime* runtime = createInferRuntime(gLogger); //反序列化方法
 assert(runtime != nullptr);
 // deserialize engine for using the char-stream
 ICudaEngine* engine = runtime->deserializeCudaEngine(trtModelStream, size, nullptr);
 assert(engine != nullptr);
 /*
 一个engine可以有多个execution context,并允许将同一套weights用于多个推理任务。可以在并行的CUDA streams流中按每个stream流一个engine和一个context来处理图像。每个context在engine相同的GPU上创建。
 */
 runtime->destroy(); //顺道销毁runtime,释放内存

其中gLogger来源创建引擎构建的glogging

以上为初始化过程,从以下为实施推理过程

构建context

IExecutionContext* context = engine->createExecutionContext(); // create execution context -- required for inference executions

经过上述步骤,即可使用context在gpu上实现推理,我将不在这里列举源码解析,后期2篇文章有具体源码。

二、yolov5转onnx

这里我将重点说明,我使用官方export.py能成功导出onnx文件,也能使用python的onnx runtime预测出正确结果,且也能转rknn模型完成测试,但使用tensorrt的onnx解析构建engine时候,便会出错。这里不必过多纠结,基本是导出数据格式问题,我将罗列以下2个方法,帮助实现onnx转换。
第一个源码:点击这里 成功转onnx,能被tensorrt的onnx解析,实现网络构建。
第二个源码:点击这里 成功转onnx,能被tensorrt的onnx解析,实现网络构建。同时也测试了yolov7转换,任然可运行。

当然,也可参考我的github,有yolov5实现方法也有转onnx源码,链接这里 ,其文件如下图:
在这里插入图片描述

三.CMakeLists.txt编写

以上代码我是使用visual studio编译器构架yolov5部署代码,然如何使用编译命令在ubuntu(linux)环境中运行呢?我是使用cmakelist文件编译,以下也将给出yolov5的tensorrt部署编译文件内容,其中target_link_libraries(yolo /home/ubuntu/soft/TensorRT-8.2.5.1/lib/stubs/libnvonnxparser.so),此库的onnx需要调用。

engine的CMakeLists.txt构建:


cmake_minimum_required(VERSION 2.6)

project(yolo)

add_definitions(-std=c++11)

option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)

include_directories(${PROJECT_SOURCE_DIR}/include)
# include and link dirs of cuda and tensorrt, you need adapt them if yours are different
# cuda
include_directories(/usr/local/cuda/include)
link_directories(/usr/local/cuda/lib64)
# tensorrt
include_directories(/home/ubuntu/soft/TensorRT-8.2.5.1/include/)
link_directories(/home/ubuntu/soft/TensorRT-8.2.5.1/lib/)

include_directories(/home/ubuntu/soft/TensorRT-8.2.5.1/samples/common/)
#link_directories(/home/ubuntu/soft/TensorRT-8.2.5.1/lib/stubs/)

# opencv
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(yolo ${PROJECT_SOURCE_DIR}/main.cpp)
target_link_libraries(yolo nvinfer)
target_link_libraries(yolo cudart)
target_link_libraries(yolo ${OpenCV_LIBS})
target_link_libraries(yolo /home/ubuntu/soft/TensorRT-8.2.5.1/lib/stubs/libnvonnxparser.so)

add_definitions(-O2 -pthread)

四、总结

本章大概介绍tensorrt构建于调用方法,至于很多细节,读者需要自行挖掘,随后本文介绍如何使用yolov5转onnx方法,这个关系后面直接使用onnx转tensorrt内容,希望读者能掌握,也能通过我的源码深入理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tangjunjun-owen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值