4.6.tensorRT基础(1)-实际模型上onnx文件的各种操作

目录

    • 前言
    • 1. onnx
      • 1.1 读取节点
      • 1.2 修改节点
      • 1.3 替换节点
      • 1.4 删除节点
      • 1.5 修改input和output
      • 1.6 预处理的接入
    • 总结

前言

杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。

本次课程学习 tensorRT 基础-实际模型上 onnx 文件的各种操作

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

在这里插入图片描述

1. onnx

本节课程我们来学习 onnx 的补充,拿到一个实际的 onnx 后如何读取到你想要的东西

本次演示使用的是 yolov5s.onnx,这并不重要,可以任意拿一个 onnx,我们的目的是学习如何读取实际的 onnx

1.1 读取节点

我们先来获取第一个卷积 model.0.conv.weight 的权重信息

import onnx
import numpy as npmodel = onnx.load("yolov5s.onnx")for item in model.graph.initializer:if item.name == "model.0.conv.weight":print("shape: ", item.dims)weight = np.frombuffer(item.raw_data, dtype=np.float32).reshape(32, 3, 6, 6)print(weight)

运行效果如下:

在这里插入图片描述

图1-1 model.0.conv.weight

上节课有提到 conv 的权重存储在 initiallizer 中,因此我们可以遍历然后通过 name 进行筛选,获取我们想要得到的 model.0.conv.weight,通过 numpy 将 raw_data 数据转换成 numpy 格式的 float32 的数据,从获取的权重和 onnx 模型的权重对比可看出二者都一一对应,说明我们获取的信息没问题。

1.2 修改节点

我们再来看下 Add 节点中的 Constant 类型:

import onnx
import numpy as npfor item in model.graph.node:if item.op_type == "Constant":if '357' in item.output:t = item.attribute[0].tdata = np.frombuffer(t.raw_data, np.float32).reshape(*t.dims)print(data.shape)print(data)

前面我们提到过对于 anchor grid 类的常量数据,通常会储存在 model.graph.node 中,并指定类型为 Constant,因此我们遍历了 node 节点,通过 name 来筛选,筛选之后打印的 item 数据量非常大,看不出我们想要的东西,我们可以通过调试进一步观察:

在这里插入图片描述

图1-2 constant_debug

可以看到 item 有一个 attribute 方法,它是一个数组,且长度为 1,我们通过访问 item.attribute 可以看到对应的 name、dims、raw_data 信息了,我们可以进一步调试:

在这里插入图片描述

图1-3 constant_debug1

可以看到最终我们想要获取的数据可以通过 item.attribute[0].t.raw_data 获得

在这里插入图片描述

图1-4 constant

可以发现我们最终的 shape 和数据信息都可以对应上,这是关于 constant 的读取

接下来我们尝试修改下 Constant 的值,修改的是 Slice 的 start:

import onnx
import numpy as npmodel = onnx.load("yolov5s.onnx")for item in model.graph.node:if item.op_type == "Constant":if '373' in item.output:t = item.attribute[0].tprint(type(t))print(t)print(np.frombuffer(t.raw_data, dtype=np.int64))t.raw_data = np.array([666], dtype=np.int64).tobytes()onnx.save(model, "new.onnx")

我们先获取了对应的 raw_data 数据,然后将其修改为我们想要的值,运行效果如下:

在这里插入图片描述

图1-5 修改constant

从图中可以看出修改后导出的 onnx 模型的 Constant 的值已经修改为了 666

Slice 中的 Constant 数据类型并不是 float32 而是 int64,我们打印其对应的 data_type 显示数字为 7,然后去 onnx-ml.proto 中可以找到 message TensorProto 可以查询数字 7 对应的数据类型是什么

在这里插入图片描述

图1-6 message TensorProto

1.3 替换节点

这次我们来个高级点的,替换 reshape 节点:

import onnx
import numpy as np
import onnx.helper as helpermodel = onnx.load("yolov5s.onnx")for item in model.graph.node:if item.name == "Reshape_242":# print(item)new_reshape = helper.make_node("Reshape", ["377", "378"], "379", "Reshape_666", myname="Zhou")item.CopyFrom(new_reshape)onnx.save(model, "new.onnx")

导出的 onnx 前后对比图如下:

在这里插入图片描述

图1-7 替换reshape节点

可以看到修改后导出的 onnx 确实是按照我们想要的都修改了,同时还添加了新的属性。值得注意的是,我们在替换节点时不能直接 = 替换,如果想要替换节点,你需要调用 protobuf 提供的函数 CopyFrom

1.4 删除节点

我们接下来演示下如何删除某个节点,以 Transpose 节点为例:

import onnx
import numpy as np
import onnx.helper as helpermodel = onnx.load("yolov5s.onnx")find_node_with_input  = lambda name: [item for item in model.graph.node if name in item.input][0]
find_node_with_output = lambda name: [item for item in model.graph.node if name in item.output][0]remove_nodes = []
for item in model.graph.node:if item.name == "Transpose_201":# 上一个节点的输出是当前节点的输入prev = find_node_with_output(item.input[0])# 下一个节点的输入是当前节点的输出next = find_node_with_input(item.output[0])next.input[0] = prev.output[0]remove_nodes.append(item)for item in remove_nodes[::-1]:model.graph.node.remove(item)onnx.save(model, "new.onnx")

在这里插入图片描述

图1-8 删除transpose节点

我们在删除了某个节点后,你需要将 input 和 output 对接起来,不能让它们直接断开。因此需要将删除节点的上个节点输出和删除节点的下个节点的输入拼接,然后再删除。删除的时候也要注意,最好不要边循环边删除,应该分为两部分来做这个事情

1.5 修改input和output

我们同样可以将输入和输出的动态 batch 修改为静态 batch,代码如下:

import onnx
import numpy as np
import onnx.helper as helpermodel = onnx.load("yolov5s.onnx")static_batch_size = 3
input = model.graph.input[0]
print(type(input))
new_input = helper.make_tensor_value_info("images", 1, [static_batch_size, 3, 640, 640])
print(new_input)
model.graph.input[0].CopyFrom(new_input)new_output = helper.make_tensor_value_info("output", 1, [static_batch_size, 25200, 7])
model.graph.output[0].CopyFrom(new_output)onnx.save(model, "new.onnx")

运行效果如下:

在这里插入图片描述

图1-9 input

我们可以打印 input 节点的信息方便后续我们修改为静态 batch,可以看到其 name 为 images,elem_type 为 1,代表 float32。下图对比了修改前后的 onnx,可以看到确实 input 和 output 发生了修改

在这里插入图片描述

图1-10 动态batch修改为静态batch

同样的,你想静态的修改为动态的,做法也是一样的,因此虽然你的 onnx 导出来是一个确定的东西,但是后期如果你想要修改它,随便改!!!

1.6 预处理的接入

我们同样可以将一些复杂的预处理塞到 onnx 中,其代码如下:

import onnx
import numpy as np
import onnx.helper as helpermodel = onnx.load("yolov5s.onnx")import torchclass Preprocess(torch.nn.Module):def __init__(self) -> None:super().__init__()self.mean = torch.rand(1, 1, 1, 3)self.std  = torch.rand(1, 1, 1, 3)def forward(self, x):# x = B x H x W x C  Uint8# y = B x C x H x W  Float32  减均值除标准差x = x.float()x = (x / 255.0 - self.mean) / self.stdx = x.permute(0, 3, 1, 2)return xpre = Preprocess()
torch.onnx.export(pre, (torch.zeros(1, 640, 640, 3, dtype=torch.uint8)), "preprocess.onnx"
)pre_onnx = onnx.load("preprocess.onnx")# 1. 先把 pre_onnx 的所有节点以及输入输出名称加上前缀,防止命名冲突
# 2. 把 yolov5s.image 为输入的节点,修改为 pre_onnx 的输出节点
# 3. 把 pre_onnx 的 node 全部放到 yolov5s 的 node 中
# 4. 把 pre_onnx 的输入作为 yolov5s 的 input名称for n in pre_onnx.graph.node:n.name = f"pre_{n.name}"for i in range(len(n.input)):n.input[i] = f"pre_{n.input[i]}"for i in range(len(n.output)):n.output[i] = f"pre_{n.output[i]}"for n in model.graph.node:if n.name == "Conv_0":n.input[0] = "pre_" + pre_onnx.graph.output[0].namefor n in pre_onnx.graph.node:model.graph.node.append(n)input_name = "pre_" + pre_onnx.graph.input[0].name
model.graph.input[0].CopyFrom(pre_onnx.graph.input[0])
model.graph.input[0].name = input_nameonnx.save(model, "new.onnx")

首先,我们导出了一个预处理的 onnx,如下图所示:

在这里插入图片描述

图1-11 preprocess.onnx

然后我们需要考虑如何将两个 onnx 对接起来,主要步骤如下:

1、 先把 pre_onnx 的所有节点以及输入输出名称加上前缀,防止命名冲突
2、 把 yolov5s.image 为输入的节点,修改为 pre_onnx 的输出节点
3、 把 pre_onnx 的 node 全部放到 yolov5s 的 node 中
4、 把 pre_onnx 的输入作为 yolov5s 的 input名称

拼接后的 onnx 如下图所示:

在这里插入图片描述

图1-12 拼接onnx

当你的算子很难搞定时,可以考虑 pytorch 生成下来然后塞进去,这样灵活度就很高了。预处理可以接入,同样后处理也可以这样接入,所以你可以把 onnx 当成一个类似可编辑的记事本一样的东西就行,因此无论你遇到多复杂的 onnx,你都可以去编辑修改它,而不至于束手无策。

总结

本节课程我们以实际的 yolov5s.onnx 模型为例,学习了 onnx 文件的各种操作,包括基本的读取某个节点的信息,修改某个节点的值,高级一点的包括替换对应的节点,删除对应的节点,还有将动态 shape 修改为 静态 shape,最后我们还尝试在原有s onnx 的基础上接了一段预处理的代码。

通过本次课程的学习,我们需要知道对于 onnx 而言,我们是可以操作的,读取、修改、删除、增加等等,你把它当成一个可编辑的东西就行,后续无论是多么复杂的 onnx,你都应该要知道如何处理。

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

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

相关文章

【深度学习】神经网络初学者指南

一、说明 这是一篇对神经网络的泛泛而谈的文章,我的意见是,先知道框架,而后知道每一个细节,这是学习人工智能的基本路线。本文就神经网络而言,谈到一些基础概念,适应于初学者建立概念。 二、神经网络定义 神…

计算机网络————网络层

文章目录 网络层设计思路IP地址IP地址分类IP地址与硬件地址 协议ARP和RARPIP划分子网和构造超网划分子网构造超网(无分类编址CIDR) ICMP 虚拟专用网VPN和网络地址转换NATVPNNAT 网络层设计思路 网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数…

【MQTT】Esp32数据上传采集:最新mqtt插件(支持掉线、真机调试错误等问题)

前言 这是我在Dcloud发布的插件-最完整Mqtt示例代码(解决掉线、真机调试错误等问题),经过整改优化和替换Mqtt的js文件使一些市场上出现的问题得以解决,至于跨端出问题,可能原因有很多,例如,合法…

<数据结构>并查集

目录 并查集概念 合并 查找集合的数量 并查集类代码实现 并查集概念 并查集和堆一样,都是通过数组来实现树的节点映射,不过并查集作用是,把一堆数据分为不同的几个小集合 不过并查集是森林的概念,并查集的学习可以帮助我们去更…

IDEA中侧边栏没有git commit模块,如何恢复?

一、修改之前 侧边栏没有git commit模块 二、修改之后 侧边栏恢复了git commit模块 三、下面是恢复教程 1.中文版 打开 文件 -> 设置 -> 版本控制 -> 提交 -> 勾选 【使用非模式提交界面】 -> 点击【确定】 2.英文版 打开 file -> Settings -> Version Co…

同步和异步的区别

同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是处于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令; 异步,执行完函数或方法后,不必阻塞性地等待返回…

Linux与Windows:操作系统的比较与技巧分享

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~&#x1f33…

【数学建模】国赛真题分析 2012 A题 葡萄酒的评价

2012 A题 葡萄酒的评价 优秀论文地址: 链接:https://pan.baidu.com/s/19WGpybgM6RncxTYhx61JRA?pwdvl22 提取码:vl22 –来自百度网盘超级会员V6的分享 确定葡萄酒质量时一般是通过聘请一批有资质的评酒员进行品评。每个评酒员在对葡萄酒进…

计讯物联网关型水利遥测终端机TY910确保闸站自动化监测长效运行

闸站是我国水利建设工程的重要组成部分,具备调度水源、防洪排涝、灌溉等能力,在农业、水路运输、养殖业等行业领域起着关键作用,进而解决区域水资源不均衡的问题,促进水资源多方面的利用。当前,我国闸站存在数量多、分…

线性代数 4 every one(线性代数学习资源分享)

Linear Algebra 4 Every One 版权说明,以下我分享的都是一个名叫Kenji Hiranabe的日本学者,在github上分享的,关于Gilbert Strang教授所撰写的《Linear Algebra for Everyone》一书的总结,更像是一个非常精美的线性代数手册&#…

.net core 2.1 简单部署IIS运行

netcore的项目不像netFramework那么方便部署到iis还是要费点功夫的 比如我想把这个netcore2.1的项目部署到iis并运行: 按照步骤走: 一、确认自己的netcore环境 1、需要安装下面3个环境包(如果电脑已安装请忽略) 检查是否安装cmd命令:cmd&…

go mod vendor简明介绍

Go 语言在 go 1.6 版本以后编译 go 代码会优先从 vendor 目录先寻找依赖包,它具有以下优点: 复制依赖:go mod vendor 会把程序所依赖的所有包复制到项目目录下的vendor 文件夹中,所以即使这些依赖包在外部源(如 GitHu…

OpenCV中掩膜(Mask)、setTo()、copyTo()、clone()、inRange()的定义与使用

文章目录 1、掩膜(Mask)是什么(1)从物理的角度来看:(2)图像处理中的掩膜Mask(3)掩膜的用法:(4)掩膜Mask 的运算: 2、setTo()函数:将图…

flutter开发实战-dio文件下载实现

flutter开发实战-dio文件下载实现 在开发中,需要下载文件,这里使用的是dio dio 是一个强大的 Dart HTTP 请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。 一、引入d…

JS脚本 - 批量给所有指定标签追加Class属性

JS脚本 - 批量给所有指定标签追加Class属性 前言一. 脚本二. 测试运行 前言 公司里我们有个应用引入了UBT埋点,记录了页面上所有的点击操作以及对应的点击按钮。但是我们看下来发现,我们需要给每个按钮加一个唯一标识做区分,并且这个ID是给U…

Flask 使用Flask的session来保存用户登录状态例子

使用Python的Flask库实现的登录接口、查询金额接口和注销接口的示例。 当用户发送POST请求到/login接口时,代码会获取请求中的用户名和密码。如果用户名和密码匹配(在示例中是admin和admin123),则会将用户名保存在session中&…

吴恩达机器学习2022-Jupyter特征缩放

1可选实验室: 特征缩放和学习率(多变量) 1.1 目标 在这个实验室里: 利用前一实验室开发的多变量线性回归模型程序在具有多种功能的数据集上运行梯度下降法探讨学习速度 alpha 对梯度下降法的影响通过使用 z 分数标准化的特征缩放来提高梯度下降法的性能 1.2 工具 您将使用…

Python爬虫学习笔记(三)————urllib

目录 1.使用urllib来获取百度首页的源码 2.下载网页图片视频 3.总结-1 4.请求对象的定制(解决第一种反爬) 5.编解码 (1)get请求方式:urllib.parse.quote() (2)get请求…

RPC分布式网络通信框架(三)—— 服务配置中心Zookeeper模块

文章目录 一、使用Zookeeper的意义二、Zookeeper基础1 文件系统2 通知机制3 原生zkclient API存在的问题4 服务配置中心Zookeeper模块 三、Zk类实现Start方法创建节点、get节点值方法 四、框架应用rpc提供端框架rpc调用端(客户端)框架 总结 一、使用Zook…

flutter开发实战-生日等日期选择器DatePicker

flutter开发实战-生日等日期选择器DatePicker 在开发遇到设置生日等信息需要选择日期,这里用到的是CupertinoDatePicker iOS效果的日期、时间选择器 一、效果图 运行后效果图如下 二、代码实现 我们需要调用底部弹窗显示 //显示底部弹窗static void bottomShe…