从零开始 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。

optBatchSizecalibrationBatchSizerun
51×
2×
3×
4
41×
2×
3×
4
31
2
3
4

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

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

相关文章

[Mac软件]Adobe Photoshop 2024 v25.5.1 中文激活版

Adobe Photoshop是全球领先的图像处理和编辑软件&#xff0c;自1990年首次发布以来&#xff0c;一直是设计师、摄影师和创意专业人士的首选工具。随着技术的进步和用户需求的变化&#xff0c;Adobe不断更新和改进Photoshop&#xff0c;以提供更强大的功能和更好的用户体验。在这…

安卓开发之资源概述、优劣分析与优化方案

摘要 随着智能手机的普及&#xff0c;Android操作系统已成为全球最广泛使用的移动平台之一。在Android应用开发中&#xff0c;资源管理是构建高效、响应迅速且用户友好的应用程序的关键要素。 本文主要探讨了安卓应用程序开发过程中的资源管理机制&#xff0c;包括其基本结构、…

一键清除JavaScript代码中的注释:使用正则表达式实现

这个正则表达式可以有效地匹配 JavaScript 代码中的各种注释&#xff0c;并且跳过了以 http: 或 https: 开头的链接。 /\/\*[\s\S]*?\*\/|\/\/[^\n]*|<!--[\s\S]*?-->|(?<!http:|https:)\/\/[^\n]*/gvscode 实战&#xff0c;ctrlF 调出查找替换工具&#xff0c;点…

如何把网页调用变为代码调用

1.背景 最近有一个需求&#xff0c;猜测一段十六进制流的校验方式&#xff0c;挨个尝试非常耗时&#xff0c;需要写代码&#xff0c;调用网页上的功能。 2.解决方案 可以使用Python的 requests 库来发起HTTP请求&#xff0c;并通过POST请求将数据发送给服务器进行计算CRC校验和…

【备战蓝桥杯系列】Java组国二选手笔记一:蓝桥杯中的常用语法特性

蓝桥杯Java国二选手笔记一&#xff1a;蓝桥杯中的常用语法特性 前言 参加了好几次蓝桥杯了&#xff0c;C组参加了&#xff0c;Java也参加过&#xff0c;也会用python刷算法。下面给出常用的Java语法特性在蓝桥杯中的使用&#xff0c;以及常见的需要注意的Java语法规范。有准备…

【算法笔记】 LeetCode_209 长度最小的子数组

LeetCode_209 长度最小的子数组 LeetCode_209 长度最小的子数组 题目要求 给定一个含有 n** 个正整数的数组和一个正整数 target 。** 找出该数组中满足其总和大于等于target 的长度最小的 连续子数组[nums<sub>l</sub>, nums<sub>l1</sub>, ..., nums…

【MySQL】-知识点整理

1、存储引擎 -- 查询数据库支持的存储引擎 show engines; -- 查询当前数据库使用的存储引擎 show variables like %storage_engines%; 主要的存储引擎说明&#xff1a; 1&#xff09;MyISAM&#xff1a;无外键、表锁、所有索引都是非聚簇索引、无事务、记录表总条数、删除表…

基于el-tree实现懒加载穿梭条

一、关键代码 <template><div><!-- 左侧待选列表 --><div class"left-box"><p>待选列表</p><el-input placeholder"输入关键词过滤" v-model"leftFilterText" clearable/><el-treeref"tree…

便捷在线导入:完整Axure元件库集合,让你的设计更高效!

Axure元件库包含基本的工具组件&#xff0c;可以使原型绘制节省大量的重复工作&#xff0c;保持整个设计页面的一致性和标准化&#xff0c;同时显得专业。Axure元件库就像我们日常生活中的门把手、自行车踏板和桌子上的螺丝钉&#xff0c;需要组装才能使用。作为一名成熟的产品…

【Web - 框架 - Vue】随笔 - Vue的简单使用(02) - 快速上手

【Web - 框架 - Vue】随笔 - Vue的简单使用(02) - 快速上手 Vue模板代码 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Vue模板</title> </head> <body> <div id"…

redis 性能优化二

前言 性能优化的第二篇文章&#xff0c;将重点讲一下Redis 的响应延迟&#xff0c;响应延迟如何对redis 进行优化。这个延迟不是说一个命令或者几个命令变慢了&#xff0c;延迟了几秒&#xff0c;就说Redis 变慢了。在不同的软硬件环境下&#xff0c;Redis 本身的绝对性能并不…

[go 面试] 一致性哈希:数据分片与负载均衡的黄金法则

在分布式系统中&#xff0c;一致性哈希&#xff08;Consistent Hashing&#xff09;是一项关键算法&#xff0c;为解决数据分片和负载均衡难题提供了强大的支持。本文将深入研究一致性哈希的核心原理&#xff0c;解析其如何超越传统哈希算法&#xff0c;同时详细探讨一个关键问…

基于STM32开路清障车控制系统设计

目 录 摘 要 I Abstract II 引 言 1 1总体方案论证与设计 3 1.1设计方案 3 1.2主控模块的选型和论证 3 2系统硬件电路设计 5 2.1主控模块设计 5 2.1.1 STM32单片机概述 5 2.1.2 STM32单片机最小系统电路 5 2.2 MCU主要实现功能 6 2.3火焰传感器 7 2.4超声波模块 8 2.5驱动模块…

Docker-自定义镜像

目录 1 前言 2 构建java应用的步骤及镜像结构图 2.1 构建步骤 2.2 镜像结构图 3 Dockerfile常用指令 4 Dockerfile的内容举例 4.1 一般形式 4.2 一般形式的优化 5 构建镜像 5.1 指令 5.2 实操 5.2.1 加载jdk镜像(基础镜像) 5.2.2 构建我们的镜像 5.2.3 使用我们的…

SpringCloud微服务-RabbitMQ快速入门

文章目录 RabbitMQ快速入门1、什么是MQ&#xff1f;2、RabbitMQ概述3、RabbitMQ的结构和概念4、常见消息模型5、HelloWorld RabbitMQ快速入门 1、什么是MQ&#xff1f; MQ &#xff08;MessageQueue&#xff09;&#xff0c;中文是消息队列&#xff0c;字面来看就是存放消息的…

Linux nmcli命令使用教程(nmcli指令)

文章目录 先区分两个概念&#xff1a;网络设备和网络连接网络设备网络连接网络连接的UUID nmcli可以为一个网络设备创建多个网络连接&#xff0c;但同一时刻只有一个能生效 Mastering Network Management with nmcli in Linux&#xff08;掌握Linux中使用nmcli进行网络管理&…

掌握 Vue3、Vite 和 SCSS 实现一键换肤的魔法步骤

前言 一个网站的换肤效果算是一个比较常见的功能&#xff0c;尤其是在后台管理系统中&#xff0c;我们几乎都能看到他的身影&#xff0c;这里给大家提供一个实现思路。 搭建项目 vitevue3搭建项目这里就不演示了&#xff0c;vite官网里面讲得很清楚。 注&#xff1a;这里使…

解决跨域问题的FastAPI应用及常见报错解析

介绍&#xff1a; 跨域问题在前后端分离的Web应用中经常会遇到。FastAPI作为一个快速、现代化的Python Web框架&#xff0c;在处理跨域问题上也提供了一些解决方案。本文将介绍如何使用FastAPI来解决跨域问题&#xff0c;并分析一些常见的报错及解决方法。 正文&#xff1a; …

【Java项目介绍和界面搭建】拼图小游戏——作弊码、查看完整图片

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

实现qq音乐歌词滚动效果

闲来无事&#xff0c;听音乐的时候&#xff0c;突发奇想 实现该效果中&#xff0c;包含了根据声音高低生成音波的功能&#xff0c;有兴趣的直接下载代码即可 这是启动后的效果。