五. TensorRT API的基本使用-TensorRT-network-structure

news2024/11/15 10:15:52

目录

    • 前言
    • 0. 简述
    • 1. 案例运行
    • 2. 代码分析
      • 2.1 main.cpp
      • 2.2 model.cpp
    • 总结
    • 下载链接
    • 参考

前言

自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》,链接。记录下个人学习笔记,仅供自己参考

本次课程我们来学习课程第五章—TensorRT API 的基本使用,一起来学习打印网络模型结构

课程大纲可以看下面的思维导图

在这里插入图片描述

0. 简述

本小节目标:学习如何打印优化前后网络模型结构

今天我们来讲第五章节第四小节—5.4-print-structure 这个案例,来看下经过 TensorRT 优化前后网络的结构有什么样的变化

下面我们开始本次课程的学习🤗

1. 案例运行

在正式开始课程之前,博主先带大家跑通 5.4-print-structure 这个小节的案例🤗

源代码获取地址:https://github.com/kalfazed/tensorrt_starter.git

首先大家需要把 tensorrt_starter 这个项目给 clone 下来,指令如下:

git clone https://github.com/kalfazed/tensorrt_starter.git

也可手动点击下载,点击右上角的 Code 按键,将代码下载下来。至此整个项目就已经准备好了。也可以点击 here 下载博主准备好的源代码(注意代码下载于 2024/7/14 日,若有改动请参考最新

整个项目后续需要使用的软件主要有 CUDA、cuDNN、TensorRT、OpenCV,大家可以参考 Ubuntu20.04软件安装大全 进行相应软件的安装,博主这里不再赘述

假设你的项目、环境准备完成,下面我们来一起运行 5.4 小节案例代码

开始之前我们需要创建几个文件夹,在 tensorrt_starter/chapter5-tensorrt-api-basics/5.4-print-structure 小节中创建一个 models 文件夹,接着在 models 文件夹下创建 onnx 和 engine 文件夹,总共三个文件夹需要创建

创建完后 5.4 小节整个目录结构如下:

在这里插入图片描述

接着我们需要执行 python 文件创建一个 ONNX 模型,先进入到 5.4 小节中:

cd tensorrt_starter/chapter5-tensorrt-api-basics/5.4-print-structure

执行如下指令:

python src/python/generate_onnx.py

Note:大家需要准备一个虚拟环境,安装好 torch、onnx、onnxsim 等第三方库

输出如下:

在这里插入图片描述

生成好的 onnx 模型文件保存在 models/onnx 文件夹下,大家可以查看

接着我们需要利用 ONNX 生成对应的 engine,在此之前我们需要修改下整体的 Makefile.config,指定一些库的路径:

# tensorrt_starter/config/Makefile.config
# CUDA_VER                    :=  11
CUDA_VER                    :=  11.6
    
# opencv和TensorRT的安装目录
OPENCV_INSTALL_DIR          :=  /usr/local/include/opencv4
# TENSORRT_INSTALL_DIR        :=  /mnt/packages/TensorRT-8.4.1.5
TENSORRT_INSTALL_DIR        :=  /home/jarvis/lean/TensorRT-8.6.1.6

Note:大家查看自己的 CUDA 是多少版本,修改为对应版本即可,另外 OpenCV 和 TensorRT 修改为你自己安装的路径即可

然后我们还要简单修改下源码,在 src/cpp/main.cpp 中默认打印的 ONNX 是 vgg16.onnx,我们修改为 sample.onnx,修改如下所示:

# src/cpp/main.cpp
int main(int argc, char const *argv[])
{
    Model model("models/onnx/sample.onnx");
    // Model model("models/onnx/resnet50.onnx");
    // Model model("models/onnx/vgg16.onnx");
    ...
}

接着我们就可以来执行编译,指令如下:

make -j64

输出如下:

在这里插入图片描述

接着执行:

./trt-infer

输出如下:

在这里插入图片描述

我们这里读取一个模型,然后将模型优化前后信息都打印出来了,我们的模型是使用的上节课的 onnx,就是一个 Linear 层

Note:博主这里也准备了 resnet50 和 vgg16 的 ONNX 模型,大家可以点击 here 下载,然后运行代码看下 resnet50 和 vgg16 在 TensorRT 优化前后有什么区别

如果大家能够看到上述输出结果,那就说明本小节案例已经跑通,下面我们就来看看具体的代码实现

2. 代码分析

2.1 main.cpp

我们先从 main.cpp 看起:

#include <iostream>
#include <memory>

#include "utils.hpp"
#include "model.hpp"

using namespace std;

int main(int argc, char const *argv[])
{
    Model model("models/onnx/sample.onnx");
    // Model model("models/onnx/resnet50.onnx");
    // Model model("models/onnx/vgg16.onnx");

    if(!model.build()){
        LOGE("fail in building model");
        return 0;
    }
    // if(!model.infer()){
    //     LOGE("fail in infering model");
    //     return 0;
    // }
    return 0;
}

与之前 build 的案例一样,这里只提供 build 的接口,然后在 build 函数中我们对模型的结构进行打印,除了 sample.onnx 外这边还有 resnet50.onnx 和 vgg16.onnx,大家可以都测试下

博主这边还测试了 resnet50.onnx 经过 tensorRT 优化前后的网络结构打印:

在这里插入图片描述

resnet50.onnx优化前

在这里插入图片描述

resnet50.onnx优化后

可以看到优化前有 126 层,都是 conv+relu,每一层的输入输出维度以及精度都有打印;优化后只有 79 层,可以看懂 tensorRT 做了很多层融合,比如 conv+relu 融合,conv+add+relu 融合等等

我们再看下 vgg16.onnx:

在这里插入图片描述

vgg16.onnx优化前

在这里插入图片描述

vgg16.onnx优化后

可以看到 vgg16 优化前有 49 个 layer,基本上就是 conv+relu+maxpool 的组合,优化后只有 25 层,tensorRT 做了一些层融合操作

2.2 model.cpp

相比于之前的 build 案例代码,它新增了如下代码:

bool Model::build(){
	...

    // 把优化前和优化后的各个层的信息打印出来
    LOG("Before TensorRT optimization");
    print_network(*network, false);
    LOG("");
    LOG("After TensorRT optimization");
    print_network(*network, true);
    return true;
};

那它通过调用 print_network 函数打印网络结构信息,其中该函数接受两个参数传递,一个是 network,一个是 bool 变量,用来指定打印优化前还是优化后的网络结构

我们重点来看下该函数的实现:

int inputCount = network.getNbInputs();
int outputCount = network.getNbOutputs();
string layer_info;

for (int i = 0; i < inputCount; i++) {
    auto input = network.getInput(i);
    LOG("Input info: %s:%s", input->getName(), printTensorShape(input).c_str());
}

for (int i = 0; i < outputCount; i++) {
    auto output = network.getOutput(i);
    LOG("Output info: %s:%s", output->getName(), printTensorShape(output).c_str());
}

首先我们通过 network 获取模型输入和输出的数量,接着对于每个 input 和 output,我们都把它的 name 和 shape 给打印出来。network 的 getInputgetOutput 方法返回的是一个 ITensor* 类型的变量,从 ITensor 中我们可以获取它的 dimension、name、type 等信息

其中的 printTensorShape 函数的内容如下:

string printTensorShape(nvinfer1::ITensor* tensor){
    string str;
    str += "[";
    auto dims = tensor->getDimensions();
    for (int j = 0; j < dims.nbDims; j++) {
        str += to_string(dims.d[j]);
        if (j != dims.nbDims - 1) {
            str += " x ";
        }
    }
    str += "]";
    return str;
}

通过 getDimensions 获取 ITensor 的维度信息,之后遍历 nbDims 将每个维度的信息给打印出来就行

OK,我们接着往下看:

int layerCount = optimized ? mEngine->getNbLayers() : network.getNbLayers();
LOG("network has %d layers", layerCount);

通过 optimized 变量我们选择不同的 API 接口获取 layer 总数量,如果 optimized 为 true 那么我们从 mEngine 中去拿 layer 总层数,如果 optimized 为 false,那么我们从 network 中去拿 layer 总层数,接着将网络总层数打印出来

其中的 IEngine 我们可以认为它是已经用 tensorR 优化好的模型,而 INetwork 我们可以认为它是还没有用 tensorRT 优化,刚刚通过 parser 给 parse 出来的模型

我们下面看优化前后模型结构如何打印:

if (!optimized) {
    for (int i = 0; i < layerCount; i++) {
        char layer_info[1000];
        auto layer   = network.getLayer(i);
        auto input   = layer->getInput(0);
        int n = 0;
        if (input == nullptr){
            continue;
        }
        auto output  = layer->getOutput(0);

        LOG("layer_info: %-40s:%-25s->%-25s[%s]", 
            layer->getName(),
            printTensorShape(input).c_str(),
            printTensorShape(output).c_str(),
            getPrecision(layer->getPrecision()).c_str());
    }
}

如果打印没有被优化的模型结构,则先遍历 layerCount,然后通过 getLayer 方法拿到层信息,接着通过 layer 的 getInput 和 getOutput 方法拿到对应层的输入输出,接着把层的 name、input shape、output shape、precision 给打印出来

我们再来看优化后:

auto inspector = make_unique<nvinfer1::IEngineInspector>(mEngine->createEngineInspector());
for (int i = 0; i < layerCount; i++) {
    string info = inspector->getLayerInformation(i, nvinfer1::LayerInformationFormat::kONELINE);

    info = info.substr(0, info.size() - 1);
    LOG("layer_info: %s", info.c_str());
}

这里我们通过 createEngineInspector 创建了一个 inspector 来查看 engine 内部信息,接着通过 inspector 的 getLayerInformation 获取每一层的信息,它直接返回的就是一个字符串。它有两个参数,第一个参数代表获取的是第几个 layer,第二个参数代表想获取的 layer info 的格式,可以有 JSON、ONELINE 格式

OK,这就是 print network structure 的全部内容,内容不多,比较简单

总结

本次课程我们主要学习了如何打印 tensorRT 优化前后的网络结构,可以方便我们对比 tensorRT 都做了哪些优化,优化前我们主要是通过 INetwork 获取 layer 信息,优化后我们主要是 ICudaEngine 获取 layer 信息,此外一些类的定义包括 ITensor、ILayer、INetwork、ICudaEngine、IExecutionContext、IBuilder 等等大家可以看下官方文档理解其含义

OK,以上就是 5.4 小节案例的全部内容了,下节我们来学习 5.5 小节来利用 C++ API 手动搭建网络模型,敬请期待😄

下载链接

  • tensorrt_starter源码
  • 5.4-TensorRT-network-structure案例文件

参考

  • Ubuntu20.04软件安装大全
  • https://github.com/kalfazed/tensorrt_starter.git

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

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

相关文章

java面向对象进阶进阶篇--《接口和接口与抽象类综合案例》(附带全套源代码)

个人主页→VON 收录专栏→java从入门到起飞 抽象类→抽象类和抽象方法 目录 一、初识接口 特点和用途 示例&#xff1a; Animal类 Dog类 Frog类 Rabbit类 Swim接口 text测试类 结果展示&#xff1a; 二、接口的细节 接口中的成员特点&#xff1a; 成员特点与接口的关…

【通信模块】WiFi&Bluetooth简介与对比

学习云里物里科技文章及结合CSDN优秀作者Edison Tao总结笔记&#xff0c;侵权联删&#xff01; 云里物里科技&#xff1a; https://www.minewtech.com/news/industry-2019-01-25-01.html CSDN&#xff1a; https://blog.csdn.net/taotongning/article/details/95215927 WIFI…

EXCEL 排名(RANK,COUNTIFS)

1.单列排序 需求描述&#xff1a;如有下面表格&#xff0c;需要按笔试成绩整体排名。 解决步骤&#xff1a; 我们使用RANK函数即可实现单列整体排名。 Number 选择第一列。 Ref 选择这一整列&#xff08;CtrlShift向下箭头、再按F4&#xff09;。 "确定"即可计算…

C++ | Leetcode C++题解之第284题窥视迭代器

题目&#xff1a; 题解&#xff1a; template <class T> class PeekingIterator : public Iterator<T> { public:PeekingIterator(const vector<T>& nums) : Iterator<T>(nums) {flag Iterator<T>::hasNext();if (flag) {nextElement Ite…

AUTOSAR从入门到精通-CAN-FD

目录 几个高频面试题目 CAN与CAN FD的区别是什么? 一、CAN与CAN FD的概念 二、CAN与CANFD的比较 三、CAN与CANFD的优劣势 为何CANFD还不能大面积取代CAN总线? 算法原理 什么是CAN FD CAN FD的特点 为什么会出现CAN FD? CAN FD和CAN总线协议帧异同 Can FD报文讲解…

调用python-docx 提示出错

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

【Django】 读取excel文件并在前端以网页形式显示-安装使用Pandas

文章目录 安装pandas写views写urls安装openpyxl重新调试 安装pandas Pandas是一个基于NumPy的Python数据分析库&#xff0c;可以从各种文件格式如CSV、JSON、SQL、Excel等导入数据&#xff0c;并支持多种数据运算操作&#xff0c;如归并、再成形、选择等。 更换pip源 pip co…

C#开发的全屏图片切换效果应用 - 开源研究系列文章 - 个人小作品

这天无聊&#xff0c;想到上次开发的图片显示软件《 PhotoNet看图软件 》&#xff0c;然后想到开发一个全屏图片切换效果的应用&#xff0c;类似于屏幕保护程序&#xff0c;于是就写了此博文。这个应用比较简单&#xff0c;主要是全屏切换换图片效果的问题。 1、 项目目录&…

Boost搜索引擎项目相关介绍

Boost搜索引擎相关介绍&#xff1a; 首先&#xff0c;Boost库不具备搜索条件&#xff0c;所以我们这个项目借此实现搜索功能。 项目的核心就是以用户搜索的相关内容在目标数据中进行查找。 首先&#xff0c;我们面临的第一大难题就是目标数据&#xff0c;在这里目标数据就是Boo…

气膜羽毛球馆的维护和运营成本解析—轻空间

随着人们对健康生活方式的追求不断增加&#xff0c;羽毛球这项运动也愈发受到欢迎。然而&#xff0c;传统的羽毛球馆往往存在建设周期长、成本高、维护复杂等问题。气膜羽毛球馆作为一种新型的运动场馆解决方案&#xff0c;因其快速搭建、环保节能、舒适环境等优势而逐渐被广泛…

整合StarRocks更新表全部知识点

总结StarRocks更新表的全部内容的集合&#xff08;V3.2版本&#xff09; 一、基本功能 聚合函数replace的聚合表主键被主键表替代采用Merge-On-Read的策略&#xff0c;读取时需要在线Merge多个版本的数据文件&#xff0c;谓词和索引无法下推至底层数据&#xff0c;会严重影响…

开源AI智能名片O2O商城小程序在私域流量营销中的应用与实践

摘要 在数字化时代&#xff0c;私域流量营销已成为企业构建长期客户关系、实现可持续增长的关键策略。本文深入探讨了开源AI智能名片O2O商城小程序如何以其独特的智能化、个性化及高效性&#xff0c;在私域流量营销领域发挥重要作用。通过详细分析其在扩大公域影响力、打造微信…

c生万物系列(封装)

为了对c语言进行封装&#xff0c;笔者参考了lw_oopc等开源库&#xff0c;决定使用宏对结构体进行封装。 先说一下大致思想&#xff1a;通过宏&#xff0c;结构体和文件来实现封装。 大概步骤&#xff1a;抽象出类-> 使用lw_oopc库进行封装->定义接口封装底层实现 ->…

SQL labs-SQL注入(六)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 本文为SQL labs-SQL注入&#xff08;四&#xff09;的续作&#xff0c;依据sqllabs靶场&#xff0c;详细讲解手工post注入。 一&#xff0c;观察页面&#xff08;SQL labs-less11&a…

网站基本布局CSS

代码 <!DOCTYPE html> <html> <head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title></title><style type"text/css">body {margi…

FullCalendar日历组件集成系列5——高级配置1

高级配置 调整单元格高度 默认情况下&#xff0c;单元格高度会自动扩展&#xff0c;如下图所示&#xff0c;留下不小的空白&#xff0c;既浪费空间又不美观。解决方式就是为高度height属性指定值auto&#xff0c;如下&#xff1a; // 高度自动调整 height: auto效果如下&…

设计模式 之 —— 单例模式

目录 什么是单例模式&#xff1f; 定义 单例模式的主要特点 单例模式的几种设计模式 1.懒汉式&#xff1a;线程不安全 2.懒汉式&#xff1a;线程安全 3.饿汉式 4.双重校验锁 单例模式的优缺点 优点&#xff1a; 缺点&#xff1a; 适用场景&#xff1a; 什么是单例模…

Golang | Leetcode Golang题解之第283题移动零

题目&#xff1a; 题解&#xff1a; func moveZeroes(nums []int) {left, right, n : 0, 0, len(nums)for right < n {if nums[right] ! 0 {nums[left], nums[right] nums[right], nums[left]left}right} }

压测实操--produce压测方案

作者&#xff1a;九月 环境信息&#xff1a; 操作系统centos7.9&#xff0c;kafka版本为hdp集群中的2.0版本。 Producer相关参数 使用Kafka自带的kafka-producer-perf-test.sh脚本进行压测&#xff0c;该脚本参数为&#xff1a; 在producer涉及到性能的关键因素可能会存在如…

Golang实现免费天气预报获取(OpenWeatherMap)

最近接到公司的一个小需求&#xff0c;需要天气数据&#xff0c;所以就做了一个小接口&#xff0c;供前端调用 这些数据包括六个元素&#xff0c;如降水、风、大气压力、云量和温度。有了这些&#xff0c;你可以分析趋势&#xff0c;知道明天的数据来预测天气。 1.1 工具简介 …