八. 实战:CUDA-BEVFusion部署分析-分析BEVFusion中各个ONNX

news2024/9/28 5:30:29

目录

    • 前言
    • 0. 简述
    • 1. camera.backbone.onnx(fp16)
    • 2. camera.backbone.onnx(int8)
    • 3. camera.vtransform.onnx(fp16)
    • 4. fuser.onnx(fp16)
    • 5. fuser.onnx(int8)
    • 6. lidar.backbone.xyz.onnx
    • 7. head.bbox.onnx(fp16)
    • 总结
    • 下载链接
    • 参考

前言

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

本次课程我们来学习下课程第八章——实战:CUDA-BEVFusion部署分析,一起来分析 BEVFusion 中各个 ONNX

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

在这里插入图片描述

0. 简述

本小节目标:分析 CUDA-BEVFusion 中各个 onnx 的输入输出,以及网络架构

这节给大家讲解第八章节第 7 小节,分析 BEVFusion 中的各个 onnx,这里我们拿 CUDA-BEVFusion 中导出好的 onnx 先看一看,一共有 5 个 onnx,对比看看 FP16 和 INT8 的 onnx 有什么区别,分析每个 onnx 的输入输出是什么以及它们之间是怎么连接的

1. camera.backbone.onnx(fp16)

我们先看 camera.backbone 部分,backbone 提供了两个,一个是 resnet 另一个是 swin transformer,我们这里以 resnet50 为例来讲解,我们主要看下输入输出就好了,主干部分是 resnet50 的结构,大家已经非常熟悉了

camera backbone 部分 fp16 的情况下需要看的东西并不是很多,需要注意的是 input 有两个,一个是 camera,一个是 LiDAR 到 camera 的 1ch depth map,output 也是有两个,一个是 camera feature(32ch),一个是 depth feature(118ch)

camera.backbone 的第一个输入是环视相机图像,以 nuscenes 数据集为例,相机个数为 6,高度为 256,宽度为 704,所以第一个输入的维度就是 1x6x3x256x704,如下图所示:

在这里插入图片描述

input1:camera
image(RGB)6 camera,3*256*704

通过变换矩阵将点云投影到相机上,这就是 camera.backbone 的第二个输入即 depth map,如下图所示:

在这里插入图片描述

input2:camera depth image
(1ch)6 camera,1*256*704

对于输出也有两个,大家可以回顾下上节课讲解的 BEVPool,它的输入就对应于这里的输出,如下图所示。一个是 camera_feature,维度是 6x32x88x80,分别代表着 NxHxWxC,另一个是 camera_depth_weights 即 depth 的概率图,维度是 6x118x32x88 分别代表着 NxDxHxW,这里的 D 表示对每一个特征像素 D 个 depth 的概率分布,图中 D 是 118,说明我们为图片上每个像素点估计 118 个深度值,之后对 118 个深度值做一个 softmax 看哪个点出现的可能性最大,这就是 camear backbone 的输出,它和普通的图像特征提取网络如 resnet 有所不同,多了一个深度概率分支

在这里插入图片描述

output:
camera feature:80chdepth feature:118ch

2. camera.backbone.onnx(int8)

接着我们再来看下 camera.backbone 的 INT8 的 onnx,camera backbone 部分如果是 int8 的话,我们可以看到在每一个 conv 前面都添加了 Q/DQ 节点,如下图所示,每一个 Q/DQ 节点都有对应的 scale 和 zero_shift,我们可以知道这一部分是经过 QAT 学习的,其余的没有变化(有关 QAT 学习如何添加这些 Q/DQ 节点有时间的话后面会介绍)

在这里插入图片描述

input1:camera
image(RGB)6 camera,3*256*704

可以看到每一个 Conv 前面都加了 Q/DQ 节点,每个 Conv 节点都有两个输入,一个是 activation value,一个是 weight,两个输入都需要加 Q/DQ,其实 Q/DQ 添加的过程并不是很复杂,通过 NVIDIA 提供的 pytorch_quantization 量化工具即可完成,这个我们在 TensorRT量化实战课YOLOv7量化:YOLOv7-PTQ量化(一) 中有提到过,大家感兴趣的可以看下

在这里插入图片描述

我们之前有讲过 TensorRT 里面对输入对 activation value 是 per-tensor 的量化粒度,每一个 tensor 只有一个 scale,这个大家可以从上图中看出来,y_scale 和 y_zero_point 都只有一个值,也就是 6x3x256x704 这整个 tensor 共用这一个 scale 和 zero_point

在这里插入图片描述

对于 weight 而言是 per-channel 的量化粒度,也就是说每个通道共享一个 scale 和 zero_point,这个我们从上图中也能看出来,可以看到 weight 的 Q 节点的 scale 有 64 个,对应的是 Conv 节点的 64 个通道

在这里插入图片描述

同时 zero_point 也是 64 个,只不过全为 0,如上图所示,那为什么都是 0 呢?这个我们在第五章节的时候也讲过,NVIDIA 将量化分为对称量化和非对称量化,NVIDIA 官方说如果要做非对称量化在部署阶段计算量比较大,也不好融合,所以 NVIDIA 在做量化时统一采用的是对称量化,因此 zero_point 就是 0 了

在这里插入图片描述

output:
camera feature:80chdepth feature:118ch

我们可以看其实并不是所有的节点都做了 INT8 量化,输出部分像 softmax、Transpose 就没有做 INT8 了,如上图所示

以上就是 camera.backbone 的 INT8 的 onnx 的整个结构了,值得注意的是在 resnet50int8/build 文件夹下有各种层的信息以及输出的日志文件,我们一起来看下,

在这里插入图片描述

camera.backbone.json

camera.backbone.json 文件中我们可以看到每个 layer 都有关于 INT8 量化的一些描述,我们重点来看下 camera.backbone.log,来看下层融合之后的精度

在这里插入图片描述

camera.backbone.log

我们可以看到 log 里面每个层每个节点的融合信息以及它们的精度的变化,大家可以打开简单看下

3. camera.vtransform.onnx(fp16)

我们看完 camera.backbone 之后我们来看 camera.vtransform,camera vtransform 的部分只有 fp16 的 onnx,需要看的东西并不是很多,需要注意的是,这个 vtransform 是针对 backbone 中输出进行的,三个 conv 将 360x360 大小的 input feature 进行特征学习 downsample 到 180x180

值得注意的是这里跨越了 BEVPool 这个部分,也就是说 camera.backbone 的两个输出经过 BEVPool 投影到 BEV 空间之后的输出才是作为 camera.vtransform 的输入

在这里插入图片描述

output:
80*360*360->80*180*180

4. fuser.onnx(fp16)

我们继续看,下一个是 fuser,fuser.onnx 的 fp16 模型比较简单,相比于 BEVFormer 来说 BEVFusion 的融合部分整体上只有 convolution 而没有像 BEVFormer 的 attention(spatial,temporal)。并且整体上相比于 backbone 而言,模型的深度也很浅,并且只有一个 BN,所有的 kernel 都是 3x3

在这里插入图片描述

input:
投影在 BEV 空间的 feature mapcamera 是 80ch,lidar 是 256ch估计是因为点云是 sparse 的,所以需要更大的 channel size

输入一个是 camera 一个是 lidar,camera 这边是 BEVPool 处理过投影到 BEV 上的 camera feature,维度是 1x80x180x180,lidar 这边是经过 SCN 网络提取后的 lidar feature,维度是 1x256x180x180

在这里插入图片描述

output:
通过多个 conv 将 camera feature 和 lidar feature 融合最终得到 180x180 Grid size 的 BEV 特征,ch 大小是 512

输出是融合后的 BEV 特征,维度是 1x512x180x180

5. fuser.onnx(int8)

fuser.onnx 的 int8 模型会稍微复杂一点,跟 camera.backbone(int8) 一样,每一个 conv 前都有 Q/DQ 节点,所有这里的 fuser 也是经过 QAT 进行学习到的,这里的权重已经能够在某种程度上适应 fp32->int8 的量化误差了

在这里插入图片描述

input:
投影在 BEV 空间的 feature mapcamera 是 80ch,lidar 是 256ch估计是因为点云是 sparse 的,所以需要更大的 channel size

在这里插入图片描述

output:
通过多个 conv 将 camera feature 和 lidar feature 融合最终得到 180x180 Grid size 的 BEV 特征,ch 大小是 512

6. lidar.backbone.xyz.onnx

我们再来看下 lidar.backbone.xyz.onnx 也就是点云特征提取网络的 onnx,这个其实就是 CenterPoint 的 SCN 架构直接导出的 ONNX

值得注意的是 lidar.backbone.xyz 的 onnx 比较特殊,因为这里使用的是自定义 onnx 节点,有两个自定义节点:

  • SparseConvolution
  • ScatterDense

所以在推理的时候会根据自定义 onnx 节点里的信息和输入 tensor 的信息进行推理

在这里插入图片描述

input:点云 tensor

在这里插入图片描述

output:
BEV-Grid:256*180*180

在这里插入图片描述

输入是经过处理后的点云 tensor 维度是 1x5,输出是 lidar feature 维度是 1x256x180x180,这个会输入到 fuser 模块与 camera 部分做融合得到融合后的 BEV 特征

自定义 SparseConvolution 节点里面包含了许多信息,如上图所示,这些信息将会在推理阶段用到,包括 activation,kernel_size,padding,rulebook,stride 等等

7. head.bbox.onnx(fp16)

最后我们看 head,head.bbox 部分是 fp16 推理的,使用 fuser 后的 512x180x180 的 feature map 进行前向推理,这里的 forward 过程的 onnx 详细部分没有必要看,我们只需要知道输出都有哪些:

  • height:[dim,1,200](3D 目标框的高度即 z 方向上的大小)
  • dim:[dim,3,200](3D 目标框的中心点即 center_x,center_y,center_z)
  • rot:[dim,2,200](rotation 即 sin 和 cos)
  • reg:[dim,2,200](3D 目标框的长宽即 x,y 方向上的大小)
  • vel:[dim,2,200](速度即 vx、vy,用来表示在哪个方向移动)
  • score:[dim,10,200](class confidence)

在这里插入图片描述

input:
在 BEV 空间上生成的特征图

在这里插入图片描述

output:
输出在 BEV 空间上的 3D BBox 的各种信息(高度、深度、坐标、得分等等)

在这里插入图片描述

输入是 1x512x180x180,也就是融合后的 BEV 特征,输出有 6 个,相关含义上面已经提到过了

以上就是 BEVFusion 中的各个 onnx 的分析,我们知道了每个 onnx 的输入输出以及如何衔接之后,再去阅读代码会相对简单一些

总结

这节课程我们主要学习了 BEVFusion 中的各个 onnx,分析了每个 onnx 的输入输出以及它们之间是怎么衔接的,主要包括 camera.backbone、camera.vtransform、fuser、lidar.backbone.xyz、head 五个 onnx,我们还分析了不同精度下的 onnx 差异,主要对比了 FP16 和 INT8 两种精度,INT8 下的 onnx 都插入了 Q/DQ 节点来做量化工作。

OK,以上就是第 7 小节有关 BEVFusion 中各个 onnx 分析的全部内容了,下节我们来学习 CUDA-BEVFusion 推理框架设计模式,敬请期待😄

下载链接

  • 论文下载链接【提取码:6463】
  • 数据集下载链接【提取码:data】
  • 代码和安装包下载链接【提取码:cuda】

参考

  • TensorRT量化实战课YOLOv7量化:YOLOv7-PTQ量化(一)

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

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

相关文章

【C++】vector的使用和模拟实现(超级详解!!!!)

文章目录 前言1.vector的介绍及使用1.1 vector的介绍1.2 vector的使用1.2.1 vector的定义1.2.2 vector iterator 的使用1.2.3 vector 空间增长问题1.2.3 vector 增删查改1.2.4 vector 迭代器失效问题。(重点!!!!!!)1.2.5 vector 在OJ中有关的练习题 2.ve…

蓝桥杯倒计时 41天 - KMP 算法

KMP算法 KMP算法是一种字符串匹配算法,用于匹配模式串P在文本串S中出现的所有位置。 例如S“ababac,P“aba”,那么出现的所有位置是13。 在初学KMP时,我们只需要记住和学会使用模板即可,对其原理只需简单理解&#xff…

WiFi模块引领智能家居革命:连接未来的生活

随着科技的快速发展,智能家居正成为现代生活的一部分,极大地改变了我们与家庭环境互动的方式。其中,WiFi模块作为关键的连接技术,在推动智能家居革命中发挥着不可忽视的作用。本文将深入探讨WiFi模块如何驱动智能家居革命。 设备互…

Maven实战(2)之搭建maven私服

一, 背景: 如果使用国外镜像,下载速度比较慢; 如果使用阿里云镜像,速度还算OK,但是假如网速不好的时候,其实也是比较慢的; 如果没有网的情况下更加下载不了. 二, 本地仓库、个人/公司私服、远程仓库关系如下: 三, 下载安装nexus私服 略

如何在Window系统部署VisualSVN服务并结合cpolar实现无公网ip远程访问

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写,是一个开放源代码的版本控制系统…

Mixtral模型解读

Mixtral 8x7B(Mistral MoE) 1.Mistral 7B模型 Mistral 7B模型与Llama2 7B模型结构整体上是相似的,其结构参数如下所示。 细节上来说,他有两点不同。 1.1SWA(Sliding Window Attention) ​ 一般的Attention来说,是Q与KV-Cache做内积&#…

23端口登录的Telnet命令+传输协议FTP命令

一、23端口登录的Telnet命令 Telnet是传输控制协议/互联网协议(TCP/IP)网络(如Internet)的登录和仿真程序,主要用于Internet会话。基本功能是允许用户登录进入远程主机程序。 常用的Telnet命令 Telnet命令的格式为&…

基础算法(四)(递归)

1.递归算法的介绍: 概念:递归是指函数直接或间接调用自身的过程。 解释递归的两个关键要素: 基本情况(递归终止条件):递归函数中的一个条件,当满足该条件时,递归终止,避…

C++11中的auto、基于范围的for循环、指针空值nullptr

目录 auto关键字 使用原因 历史背景 C11中的auto auto的使用案例 auto 指针/引用 同一行定义多个变量 typeid关键字 基于范围的for循环 范围for的语法 范围for的使用条件 指针空值nullptr C98中的指针空值 C11中的指针空值 auto关键字 使用原因 随着程序越…

Decoupled Knowledge Distillation解耦知识蒸馏

Decoupled Knowledge Distillation解耦知识蒸馏 现有的蒸馏方法主要是基于从中间层提取深层特征,而忽略了Logit蒸馏的重要性。为了给logit蒸馏研究提供一个新的视角,我们将经典的KD损失重新表述为两部分,即目标类知识蒸馏(TCKD&a…

JavaSec 基础之五大不安全组件

文章目录 不安全组件(框架)-Shiro&FastJson&Jackson&XStream&Log4jLog4jShiroJacksonFastJsonXStream 不安全组件(框架)-Shiro&FastJson&Jackson&XStream&Log4j Log4j Apache的一个开源项目,是一个基于Java的日志记录框架。 历史…

python学习笔记------元组

元组的定义 定义元组使用小括号,且使用逗号隔开各个数据,数据是不同的数据类型 定义元组字面量:(元素,元素,元素,......,元素) 例如:(1,"hello") 定义元组变量:变量名称(元素,元素,元素,......,元素)…

哈希表是什么?

一、哈希表是什么? 哈希表,也称为散列表,是一种根据关键码值(Key value)直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,从而加快查找速度。这个映射函数叫做散列函数&#xff08…

C#与VisionPro联合开发——单例模式

单例模式 单例模式是一种设计模式,用于确保类只有一个实例,并提供一个全局访问点来访问该实例。单例模式通常用于需要全局访问一个共享资源或状态的情况,以避免多个实例引入不必要的复杂性或资源浪费。 Form1 的代码展示 using System; usi…

初阶数据结构之---栈和队列(C语言)

引言 在顺序表和链表那篇博客中提到过,栈和队列也属于线性表 线性表: 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构,也就是说是连…

c++之拷贝构造和赋值

如果一个构造函数中的第一个参数是类本身的引用,或者是其他的参数都有默认值,则该构造函数为拷贝构造函数。 那么什么是拷贝构造呢?利用同类对象构造一个新对象。 1,函数名和类必须同名。 2,没有返回值。 3&#x…

差分题练习(区间更新)

一、差分的特点和原理 对于一个数组a[],差分数组diff[]的定义是: 对差分数组做前缀和可以还原为原数组: 利用差分数组可以实现快速的区间修改,下面是将区间[l, r]都加上x的方法: diff[l] x; diff[r 1] - x;在修改完成后,需要做前缀和恢复…

4.关联式容器

关联式container STL中一些常见的容器: 序列式容器(Sequence Containers): vector(动态数组): 动态数组,支持随机访问和在尾部快速插入/删除。list(链表)&am…

奇舞周刊第521期:“一切非 Rust 项目均为非法”

奇舞推荐 ■ ■ ■ 拜登:“一切非 Rust 项目均为非法” 科技巨头要为Coding安全负责。这并不是拜登政府对内存安全语言的首次提倡。“程序员编写代码并非没有后果,他们的⼯作⽅式于国家利益而言至关重要。”白宫国家网络总监办公室(ONCD&…

Python3零基础教程之数学运算专题进阶

大家好,我是千与编程,今天已经进入我们Python3的零基础教程的第十节之数学运算专题进阶。上一次的数学运算中我们介绍了简单的基础四则运算,加减乘除运算。当涉及到数学运算的 Python 3 刷题使用时,进阶课程包含了许多重要的概念和技巧。下面是一个简单的教程,涵盖了一些常…