从零开始 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 静态 & 生命周期 应用程序的生命周期&…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
