TensorRT转换onnx的Transpose算子遇到的奇怪问题

2024-02-18 08:04

本文主要是介绍TensorRT转换onnx的Transpose算子遇到的奇怪问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

近来把一个模型导出为onnx并用onnx simplifier化简后转换为TensorRT engine遇到非常奇怪的问题,在我们的网络中有多个检测头时,转换出来的engine的推理效果是正常的,当网络中只有一个检测头时,转换出来的engine的推理效果奇差,类别得分经sigmoid()计算出来的很多很大的值,导致输出上百万个3D box,这明显不正常。

开始以为3D box的C++后处理解码部分有问题,调查折腾了不少时间发现根本没什么大问题,然后想起模型只有一个检测头输出不正常,有多个头时检测头时反而正常,于是去仔细翻看模型网络的实现代码,也没发现什么不对的,很郁闷,想到把用于TensorRT生成engine的onnx可视化出来看有何差异,刚开始看也没发现什么特别的,感觉展示出来的结构都很正确没毛病啊:

那为何多个检测头就正常一个检测头就不行呢?再仔细看代码,发现heatmap这个分支的输出tensor在作permute(onnx里对应的是Transpose)后有这么一句有关:

      result['hm_preds'] =  torch.cat(hm_preds_list, dim=-1)

hm_preds_list是个list,当有多个head时,会有多个hm_preds加入到这个[ ]里,只有一个检测头时就只有一个hm_preds加入这个[ ]里,很显然,当hm_preds_list里只有一个hm_preds时,torch.cat()不会作实质操作,只是依据hm_preds_list里的数据直接输出tensor而已,那我强制只有一个检测头时也作和有多个检测头时一样的算子运算会如何?于是修改代码当网络只配置有一个检测头时,将这个hm_preds tensor自己和自己强制来个Concat,也就是相当于torch.cat([hm_preds, hm_preds], dim=-1),然后为保持输出tensort的维度不变进行了切片,得到的网络结果是这样:

然后导出onnx并用onnx simplifier化简,生成TensorRT engine后推理结果就正常了!

那为何强制加了Concat后输出结果就正常了呢?再回头看从pytorch导出onnx和onnx化简全过程中网络结构上的变化,发现上面有一个检测头和有多个检测头时的网络的onnx图,发现,只有一个检测头时,像上面提到,因为torch.cat()没有实质操作,pytorch导出onnx时虽然在onnx生成了Concat算子节点,但是后面用onnx simplifier化简时,它会把这个Concat算子节点删掉,Transpose算子的输出就直接作为网络的hm_preds输出了,而有多个检测头时,torch.cat()会有实质拼接操作,Concat算子自然被onnx simplifier保留了,所以生成的TensorRT engine的推理结果正常!

于是我修改脚本,当网络只有一个检测头时,在调用onnx simplifier化简网络后,在Transpose算子节点后和网络的输出节点hm_preds之间强制增加Concat算子节点(注意指定attrs={"axis":-1})

然后再生成TensorRT engine,然后用这个engine推理就一切正常了!

再回头仔细想想这个问题的最终原因,当只有一个检测头时,TensorRT在对hm_preds tensor 作Transpose后因为后续没有其他算子了,没有触发输出Transpose后的tensor值而是把原始tensor值输出了?或者因为Transpose输出的hm_preds tensor在加入hm_preds_list后,因为hm_preds_list不是tensor,TensorRT把它丢弃了,直接用的Transpose处理前的tensor值?只能等后面哪天有时间再实验找找根本原因。

附上增加Concat节点的相关代码(:

  hm_node = graph.outputs[1]check_node = hm_node.inputs[0]if check_node.op == 'Transpose':  # only one head, no concat, need to add it forciblytranspose_out = [gs.Variable("transpose_out", shape=hm_node.shape, dtype=hm_node.dtype)]check_node.outputs = transpose_outgraph.layer(name="Concat_199", op= 'Concat', inputs=transpose_out, outputs=[hm_node], attrs={"axis":-1})graph.cleanup().toposort()

我们修改onnx使用的NVIDIA的ONNX GraphSurgeon,它封装后用起来比较简单,感觉比直接使用onnx的API修改网络容易得多,具体文档和examples可参见:

ONNX GraphSurgeonicon-default.png?t=N7T8https://docs.nvidia.com/deeplearning/tensorrt/onnx-graphsurgeon/docs/index.html

 Examplesicon-default.png?t=N7T8https://github.com/NVIDIA/TensorRT/tree/main/tools/onnx-graphsurgeon/examples

这篇关于TensorRT转换onnx的Transpose算子遇到的奇怪问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/720543

相关文章

解决Nginx启动报错Job for nginx.service failed because the control process exited with error code问题

《解决Nginx启动报错Jobfornginx.servicefailedbecausethecontrolprocessexitedwitherrorcode问题》Nginx启... 目录一、报错如下二、解决原因三、解决方式总结一、报错如下Job for nginx.service failed bec

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

MySQ中出现幻读问题的解决过程

《MySQ中出现幻读问题的解决过程》文章解析MySQLInnoDB通过MVCC与间隙锁机制在可重复读隔离级别下解决幻读,确保事务一致性,同时指出性能影响及乐观锁等替代方案,帮助开发者优化数据库应用... 目录一、幻读的准确定义与核心特征幻读 vs 不可重复读二、mysql隔离级别深度解析各隔离级别的实现差异

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

Linux部署中的文件大小写问题的解决方案

《Linux部署中的文件大小写问题的解决方案》在本地开发环境(Windows/macOS)一切正常,但部署到Linux服务器后出现模块加载错误,核心原因是Linux文件系统严格区分大小写,所以本文给大... 目录问题背景解决方案配置要求问题背景在本地开发环境(Windows/MACOS)一切正常,但部署到

MySQL磁盘空间不足问题解决

《MySQL磁盘空间不足问题解决》本文介绍查看空间使用情况的方式,以及各种空间问题的原因和解决方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录查看空间使用情况Binlog日志文件占用过多表上的索引太多导致空间不足大字段导致空间不足表空间碎片太多导致空间不足临时表空间

Mybatis-Plus 3.5.12 分页拦截器消失的问题及快速解决方法

《Mybatis-Plus3.5.12分页拦截器消失的问题及快速解决方法》作为Java开发者,我们都爱用Mybatis-Plus简化CRUD操作,尤其是它的分页功能,几行代码就能搞定复杂的分页查询... 目录一、问题场景:分页拦截器突然 “失踪”二、问题根源:依赖拆分惹的祸三、解决办法:添加扩展依赖四、分页

Java中InputStream重复使用问题的几种解决方案

《Java中InputStream重复使用问题的几种解决方案》在Java开发中,InputStream是用于读取字节流的类,在许多场景下,我们可能需要重复读取InputStream中的数据,这篇文章主... 目录前言1. 使用mark()和reset()方法(适用于支持标记的流)2. 将流内容缓存到字节数组