从零开始 TensorRT(7)C++ 篇:解析 ONNX
前言
学习资料:
B站视频配套代码 cookbook
示例
参考源码:cookbook → 04-BuildEngineByONNXParser → pyTorch-ONNX-TensorRT
源码
C++ 代码量较多,已上传 GitHub
OpenCV 安装:
apt install libopencv-dev
(1)按 Python 篇中的方式将 RenNet-18 转为 ONNX
python generate_onnx.py
(2)编译运行
mkdir build
cd build
cmake ..
make
cd ../bin./demo
./demo --fp16
./demo --int8
解析
在 cookbook 中,createCalibrationAndInferenceData.py 将 MNIST 数据存储为 npz 文件,并在 C++ 部分直接读取 Numpy 文件中的数据用于推理和校正,避免了图片解码的相关代码。
本文示例依然是参考 cookbook,使用 ResNet 进行推理。将读取 Numpy 文件的部分改为读取本地图像,并利用 OpenCV 对图像进行预处理,Int8 模式中的校正器部分代码也有所改动。
(1)预处理
std::vector<float> loadImg(const std::string filename, int width, int height, int channel) {cv::Mat image = cv::imread(filename, cv::IMREAD_COLOR);if (image.empty()) {std::cerr << "Error: Unable to read image file." << std::endl;return std::vector<float>();}cv::cvtColor(image, image, cv::COLOR_BGR2RGB);cv::resize(image, image, cv::Size(width, height));image.convertTo(image, CV_32F, 1.0 / 255.0);cv::Scalar meanData(0.485, 0.456, 0.406);cv::Scalar stdData(0.229, 0.224, 0.225);cv::subtract(image, meanData, image);cv::divide(image, stdData, image);"上面图像读取、resize、归一化、标准化都是调用 OpenCV API 与 Python 代码大同小异""下面是对数组维度进行调整: (h,w,3)->(3,h,w)""这里先把图像拆分成三个通道, 依次将三通道中的数据放到data中""通常会直接对图像数据进行遍历放到data中, 效率应该更高"std::vector<cv::Mat> channels;cv::split(image, channels);std::vector<float> data(channel * height * width);int idx = 0;for (int c = 0; c < channel; ++c) {for (int h = 0; h < height; ++h) {for (int w = 0; w < width; ++w) {data[idx++] = channels[c].at<float>(h, w);}}}return data;
}
(2)校准器
"主要是构造函数和 getBatch 与 cookbook 有所不同"
"先看原版"
"这里与 Python 篇中的校准器有所不同"
"Python: 在所有校准数据中随机抽样 batchsize 个循环校正 nCalibration 次"
"C++: 在所有校准数据中依次获取 batchsize 个, 直到剩余数据不足一个 batch, nCalibration 参数并没有用到"
MyCalibrator::MyCalibrator(const std::string &calibrationDataFile, const int nCalibration, const Dims32 dim, const std::string &cacheFile):nCalibration(nCalibration), dim(dim), cacheFile(cacheFile), iBatch(0)
{cnpy::npz_t npzFile = cnpy::npz_load(calibrationDataFile);cnpy::NpyArray array = npzFile[std::string("calibrationData")];pData = array.data<float>();if (pData == nullptr){std::cout << "Failed getting calibration data!" << std::endl;return;}"nBatch 代替 nCalibration"nBatch = array.num_bytes() / bufferSize; "此处源码明显有误, 应该在 bufferSize 计算之后""nElement 计算数组中元素个数, 即 c*h*w"nElement = 1;for (int i = 0; i < dim.nbDims; ++i){nElement *= dim.d[i];}"bufferSize 为数组空间大小"bufferSize = sizeof(float) * nElement;cudaMalloc((void **)&bufferD, bufferSize);return;
}bool MyCalibrator::getBatch(void *bindings[], char const *names[], int32_t nbBindings) noexcept
{if (iBatch < nBatch){cudaMemcpy(bufferD, &pData[iBatch * nElement], bufferSize, cudaMemcpyHostToDevice);bindings[0] = bufferD;iBatch++;return true;}else{return false;}
}
"本文示例将 calibrationDataDir 文件夹内的图像文件作为校准数据, 代替 cookbook 中的 Numpy 数据"
MyCalibrator::MyCalibrator(const std::string &calibrationDataDir, const int nCalibration, const Dims32 dim, const std::string &cacheFile):nCalibration(nCalibration), dim(dim), cacheFile(cacheFile), iBatch(0) {"range-based loop, 用于遍历容器或其他可迭代对象中元素的循环结构""与 Python 中的循环类似 for entry in os.listdir(dir)""const: 变量只读""auto: 自动推导类型""&: 引用, 避免拷贝""fs::directory_iterator: C++17中<filesystem>提供的功能"for (const auto& entry : fs::directory_iterator(calibrationDataDir)) {if (fs::is_regular_file(entry)) {files.push_back(entry.path().string());}}nBatch = files.size() / dim.d[0];nElement = 1;for (int i = 0; i < dim.nbDims; ++i) {nElement *= dim.d[i];}bufferSize = sizeof(float) * nElement;cudaMalloc((void **)&bufferD, bufferSize);return;
}bool MyCalibrator::getBatch(void* bindings[], char const* names[], int32_t nbBindings) noexcept {if (iBatch < nBatch) {for (int i = 0; i < dim.d[0]; ++i) {"逐个读取图像, 并把数据拷贝到 bufferD 中对应位置"std::vector<float> img = loadImg(files[iBatch*dim.d[0]+i], dim.d[3], dim.d[2], dim.d[1]);cudaMemcpy(&bufferD[i*img.size()], img.data(), img.size()*sizeof(float), cudaMemcpyHostToDevice);}bindings[0] = bufferD;iBatch++;return true;}else {return false;}
}
一个奇怪的 Bug
在 int8 模式下,最初设置校正时 BatchSize 为1 calibrationBatchSize {1};,常见输入 BatchSize 为 4 profile->setDimensions(inputTensor->getName(), OptProfileSelector::kOPT, Dims32 {4, {4, nChannel, nHeight, nWidth}}); 时出现如下报错
Succeeded parsing .onnx file!
Failed finding cache file!
ERROR: 1: [calibrator.cpp::add::793] Error Code 1: Cuda Runtime (an illegal memory access was encountered)
ERROR: 1: [executionContext.cpp::commonEmitDebugTensor::1855] Error Code 1: Cuda Runtime (an illegal memory access was encountered)
ERROR: 1: [resizingAllocator.cpp::deallocate::105] Error Code 1: Cuda Runtime (an illegal memory access was encountered)
...
ERROR: 1: [resizingAllocator.cpp::deallocate::105] Error Code 1: Cuda Runtime (an illegal memory access was encountered)
ERROR: 3: [engine.cpp::~Engine::298] Error Code 3: API Usage Error (Parameter check failed at: runtime/api/engine.cpp::~Engine::298, condition: mExecutionContextCounter.use_count() == 1. Destroying an engine object before destroying the IExecutionContext objects it created leads to undefined behavior.
)
ERROR: 1: [cudaDriverHelpers.cpp::operator()::94] Error Code 1: Cuda Driver (an illegal memory access was encountered)
ERROR: 1: [cudaResources.cpp::~ScopedCudaStream::47] Error Code 1: Cuda Runtime (an illegal memory access was encountered)
ERROR: 2: [calibrator.cpp::calibrateEngine::1181] Error Code 2: Internal Error (Assertion context->executeV2(&bindings[0]) failed. )
Failed building serialized engine!
但是反复检查代码感觉没有非法的内存访问,偶然对常用输入的 BatchSize 修改后发现代码能跑通,便做了如下测试。按理来说 optBatchSize 和校正时的 BatchSize 没什么关系,多半是 TensorRT 内部的 Bug。
| optBatchSize | calibrationBatchSize | run |
|---|---|---|
| 5 | 1 | × |
| 2 | × | |
| 3 | × | |
| 4 | √ | |
| 4 | 1 | × |
| 2 | × | |
| 3 | × | |
| 4 | √ | |
| 3 | 1 | √ |
| 2 | √ | |
| 3 | √ | |
| 4 | √ |
相关文章:
从零开始 TensorRT(7)C++ 篇:解析 ONNX
前言 学习资料: B站视频配套代码 cookbook 示例 参考源码:cookbook → 04-BuildEngineByONNXParser → pyTorch-ONNX-TensorRT 源码 C 代码量较多,已上传 GitHub OpenCV 安装: apt install libopencv-dev(1&…...
k8s集群的CA证书过期处理
文章目录 制作延期的CA证书获取CA全名准备签发申请配置生成新CA验证并替换CA 更新master组件的CA配置kube-apiserverkube-controller-managerkube-schedulerkube-admin检查证书过期时间 更新ServiceAccount secret更新node组件配置的CA更新kubelet连接配置签发kubelet自动申请的…...
linuxOPS基础_linux系统注意事项
Linux严格区分大小写 Linux 和Windows不同,Linux严格区分大小写的,包括文件名和目录名、命令、命令选项、配置文件设置选项等。 例如,Win7 系统桌面上有文件夹叫做Test,当我们在桌面上再新建一个名为 test 的文件夹时,…...
《探索虚拟与现实的边界:VR与AR谁更能引领未来?》
引言 在当今数字时代,虚拟现实(VR)和增强现实(AR)技术正以惊人的速度发展,并逐渐渗透到我们的日常生活中。它们正在重新定义人与技术、人与环境之间的关系,同时也为各行各业带来了全新的可能性。然而,究竟是VR还是AR更有潜力改变未来?本文将围绕这一问题展开深入探讨。…...
C++ 获取上一级文件夹路径
我们可能会经常遇到文件所在文件夹路径的问题,虽然各大平台也有提供方便快捷的API来实现,但是如果脱离平台本身,或者想实现跨平台的话,可以考虑用纯C的代码来实现这一需求 示例代码 #include <string> #include <ios…...
Apache Pulsar的分布式集群模式构建
1. 准备环境 6台带jdk8的Linux服务器(CentOS7为例) ip分别为: 主机名IP地址zookeeper1192.168.8.101zookeeper2192.168.8.102zookeeper3192.168.8.103pulsar1192.168.8.108pulsar2192.168.8.109pulsar3192.168.8.110 2. 下载Pulsar最新安…...
第三百八十六回
文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了Snackbar Widget相关的内容,本章回中将介绍TimePickerDialog Widget.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的TimePickerDialog是一种弹出窗口,只不过窗口的内容固定显示…...
Java中介者模式剖析及使用场景
中介者模式 一、介绍二、智能家居系统项目实现三、总结1.优点2.缺点3.使用经验4.Spring框架类似使用思想 一、介绍 介者模式是一种行为型设计模式,它允许对象之间通过一个中介者对象进行通信,而不是直接相互引用。将多对多的关系转化为一对多的关系&…...
ElevenLabs用AI为Sora文生视频模型配音 ,景联文科技提供高质量真人音频数据集助力生成逼真音效
随着Open AI公司推出的Sora文生视频模型惊艳亮相互联网,AI语音克隆创企ElevenLabs又为Sora的演示视频生成了配音,所有的音效均由AI创造,与视频内容完美融合。 ElevenLabs的语音克隆技术能够从一分钟的音频样本中创建逼真的声音。为了实现这一…...
Go语言基础
Go的数据类型定义 //运行第一个程序package main func main(){print("Hello World") }在GO语言中,一个程序只能有一个main包,对应只能有一个main方法,若无法满足这个条件,编译时将会报错。注释方式与PHP相同 import的使…...
IOS覆盖率报告info文件解读
一,IOS覆盖率报告的生成 在做前端精准测试的时候,对于iOS端,通常会做如下操作: (1)合并覆盖率数据 如下操作: xcrun llvm-profdata merge coverage_file1657885040728.profraw coverage_fil…...
爬虫实战——scrapy框架爬取多张图片
scrapy框架的基本使用,请参考我的另一篇文章:scrapy框架的基本使用 起始爬取的网页如下: 点击每张图片,可以进入图片的详情页,如下: 代码实现: 项目文件结构如下 img_download.py文件代码 im…...
LLVM TableGen 系统学习笔记
Basic TableGen 系统可以帮助记录领域特定的信息。它也可以认为是一种小型的编译系统。 TableGen 责负分析文件, 分析结果交给领域特定的后端进行处理。 重要的概念 records 一个 record 有一个独立的名称,一系列值和一系列父类。 它保存了特定领域…...
基于stm32的流水灯设计
1基于stm32的流水灯设计[proteus仿真] 速度检测系统这个题目算是课程设计和毕业设计中常见的题目了,本期是一个基于51单片机的自行车测速系统设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】,赞赏任意文章 2¥,…...
kotlin图片合成和压缩
kotlin图片合成和压缩 之前的方法是继承AsyncTask 在doInBackground 里面去做压缩的操作,然后用 publishProgress 切到主线程里面更新 新方法是在协程里的去做 class ImageService {private fun getSumWidths(bitmaps: ArrayList<Bitmap>): Int {var sumWid…...
Java学习笔记004——接口概念理解及意义
一个类中有抽象方法,则必须声明为abstract(做为抽象类),抽象类不能实例化。子类继承抽象类,必须对所有的抽象方法重写,否则依然有抽象方法,还是抽象的,无法实例化。故抽象类常做为基…...
MT笔试题
前言 某团硬件工程师的笔试题,个人感觉题目的价值还是很高的,分为选择题和编程题,选择题考的是嵌入式基础知识,编程题是两道算法题,一道为简单难度,一道为中等难度 目录 前言选择题编程题 选择题 C语言中变…...
50道SQL面试题
50道SQL面试题 有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家 环境 -- ---------------------------- -- Table structure for teacher -- ---------------------------- DROP TABLE IF EXISTS teacher; CREATE TABLE teacher (t_id varchar(20) …...
2024蓝桥杯每日一题(双指针)
一、第一题:牛的学术圈 解题思路:双指针贪心 仔细思考可以知道,写一篇综述最多在原来的H指数的基础上1,所以基本方法可以是先求出原始的H指数,然后分类讨论怎么样提升H指数。 【Python程序代码】 n,l map(int,…...
Android 开发过程中常见的内存泄漏场景分析
场景1 Static变量存储上下文环境Context public class ClassName {// 定义1个静态变量private static Context mContext;//... // 引用的是Activity的contextmContext context; // 当Activity需销毁时,由于mContext 静态 & 生命周期 应用程序的生命周期&…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
前端开发者常用网站
Can I use网站:一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use:Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站:MDN JavaScript权威网站:JavaScript | MDN...
数据库正常,但后端收不到数据原因及解决
从代码和日志来看,后端SQL查询确实返回了数据,但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离,并且ai辅助开发的时候,很容易出现前后端变量名不一致情况,还不报错,只是单…...
