《YOLO11的ONNX推理部署:多语言多架构实践指南》

引言:YOLO11 与 ONNX 的相遇

在计算机视觉的广袤星空中,目标检测始终是一颗耀眼的明星,其在自动驾驶、智能安防、工业检测、医疗影像分析等诸多领域都有着举足轻重的应用。想象一下,自动驾驶汽车需要实时准确地检测出道路上的车辆、行人、交通标志;智能安防系统要快速识别出监控画面中的异常行为和可疑人员;工业生产线上,需要精准检测产品的缺陷;医疗影像分析中,辅助医生检测病变区域。这些场景都对目标检测技术的准确性和实时性提出了极高的要求。

YOLO(You Only Look Once)系列算法,无疑是目标检测领域中最为璀璨的星座之一。自诞生以来,YOLO 就以其独特的 “一次检测” 理念,彻底颠覆了传统目标检测方法需要对图像进行多次处理的繁琐流程,在速度上占据了绝对优势 ,并且随着版本的不断迭代,从最初的 YOLOv1 到如今的 YOLO11,每一次更新都像是一场华丽的蜕变,在性能、速度与准确度上实现了跨越式的提升。

YOLO11,作为这一系列的最新杰作,更是站在了巨人的肩膀上,将目标检测技术推向了新的高度。它采用了基于 Transformer 的骨干网络,能够捕捉长程依赖关系,在小物体检测上尤为显著;动态头部设计根据图像复杂度自适应分配资源,优化了计算效率,处理速度更快;无 NMS(非极大值抑制)训练用更高效的算法取代了传统 NMS,从而在不牺牲准确性的情况下减少了推理时间;双重标签分配针对重叠和密集物体的检测,采用了一对一和一对多的标签分配方式,提高了识别效果 ;大核卷积在减少计算资源消耗的同时,实现了更好的特征提取,进一步提升模型性能;部分自注意力(PSA)机制有选择性地对特定部分的特征图施加注意力机制,在不增加计算成本的情况下提高了全局表示学习能力。这些创新使得 YOLO11 在保持快速检测速度的同时,大幅提高了检测的准确性,对于不同光照、视角和尺度的图像,都具有较强的鲁棒性,并且可以方便地部署在各种硬件平台上,包括嵌入式设备、服务器等,为实际应用提供了极大的便利。

然而,当我们想要将 YOLO11 模型部署到实际应用中时,往往会面临诸多挑战。不同的硬件平台和软件框架可能对模型的格式和运行环境有不同的要求,这就使得模型的部署变得复杂起来。这时,ONNX(Open Neural Network Exchange)应运而生,它就像是一座桥梁,连接了不同的深度学习框架和硬件平台。ONNX 是一个开放的神经网络交换格式,它允许机器学习框架之间进行模型的互操作性。通过使用 ONNX,我们可以将训练好的模型从一个深度学习框架(如 TensorFlow 或 PyTorch)转换为另一个框架(如 CNTK 或 Caffe2),使得模型在不同平台或设备上能够运行,同时保留其精度和性能 。ONNX 已被广泛应用于推理引擎、运行时优化、自动化代码生成等领域,为机器学习社区提供了一种通用的模型表示方式。

在接下来的文章中,我们将深入探讨如何利用多种语言和架构进行 YOLO11 基于 ONNX 的推理部署。我们将详细介绍不同语言(如 Python、C++、Java 等)和架构(如 CPU、GPU、嵌入式设备等)下的部署方法和完整例子程序,帮助大家更好地将 YOLO11 模型应用到实际项目中,感受目标检测技术的魅力和强大。

一、YOLO11 与 ONNX 基础入门

(一)YOLO11 简介

YOLO11 作为目标检测领域的佼佼者,在 YOLO 系列的基础上实现了重大突破,它的诞生为实时目标检测任务带来了前所未有的高效性和准确性。

在网络结构方面,YOLO11 采用了基于 Transformer 的骨干网络,这一创新设计使得模型能够捕捉长程依赖关系,尤其在小物体检测上展现出了强大的优势。以往的目标检测模型在处理小物体时,常常因为感受野的限制而难以准确识别,而 YOLO11 的 Transformer 骨干网络通过自注意力机制,能够对图像中的全局信息进行建模,从而更好地捕捉小物体的特征 。比如在智能安防监控中,对于远处的行人、车辆等小目标,YOLO11 能够更精准地检测和识别,大大提高了监控系统的可靠性。

动态头部设计也是 YOLO11 的一大亮点。它能够根据图像的复杂度自适应地分配资源,优化计算效率,使得模型在处理不同难度的图像时都能保持快速的处理速度。当遇到简单背景下的目标检测时,动态头部会自动减少不必要的计算,提高检测速度;而在面对复杂背景、多目标重叠等情况时,又能合理分配更多资源,确保检测的准确性 。这一特性使得 YOLO11 在不同场景下都能游刃有余地完成目标检测任务,无论是在交通监控中的车辆检测,还是在工业生产线上的零部件检测,都能发挥出色。

在训练算法上,YOLO11 引入了无 NMS(非极大值抑制)训练,用更高效的算法取代了传统 NMS,在不牺牲准确性的情况下减少了推理时间。传统的 NMS 算法在处理密集目标时,容易出现误删正确检测框的问题,而 YOLO11 的无 NMS 训练算法通过改进,能够更合理地处理重叠和密集物体的检测,提高了识别效果 。例如在人群密集的场景中,能够准确地检测出每个人的位置和身份,避免了漏检和误检的情况。

双重标签分配策略也是 YOLO11 的一项重要创新。它针对重叠和密集物体的检测,采用了一对一和一对多的标签分配方式,进一步提高了模型对复杂场景的适应能力。在实际应用中,很多场景下物体之间存在重叠和遮挡的情况,传统的标签分配方式难以准确标注,而 YOLO11 的双重标签分配策略能够更好地处理这些情况,提升了检测的精度 。比如在停车场中,车辆之间可能存在部分重叠,YOLO11 能够准确地识别出每辆车的位置和类型。

此外,YOLO11 还采用了大核卷积,在减少计算资源消耗的同时,实现了更好的特征提取,进一步提升了模型性能。部分自注意力(PSA)机制有选择性地对特定部分的特征图施加注意力机制,在不增加计算成本的情况下提高了全局表示学习能力。这些创新使得 YOLO11 在保持快速检测速度的同时,大幅提高了检测的准确性,对于不同光照、视角和尺度的图像,都具有较强的鲁棒性,并且可以方便地部署在各种硬件平台上,包括嵌入式设备、服务器等,为实际应用提供了极大的便利。

(二)ONNX 介绍

ONNX,即开放式神经网络交换(Open Neural Network Exchange)格式,是一个旨在促进深度学习模型在不同框架和硬件平台间无缝迁移的开放标准。它为深度学习和传统机器学习模型定义了一个统一的文件格式,以及一组标准的运算符和数据类型 。

ONNX 最大的特点之一就是其卓越的互操作性。在深度学习领域,存在着众多不同的框架,如 PyTorch、TensorFlow、MXNet 等,每个框架都有其独特的优势和应用场景。然而,这也导致了模型在不同框架之间的迁移变得异常困难。ONNX 的出现打破了这一壁垒,它允许开发者在自己熟悉的框架中进行模型的开发和训练,然后将模型导出为 ONNX 格式,再在其他支持 ONNX 的框架或平台上进行部署和推理 。比如,一个使用 PyTorch 训练的模型,可以轻松地转换为 ONNX 格式,然后在 TensorFlow Serving 中进行部署,大大提高了开发效率和模型的可移植性。

ONNX 还具备强大的硬件加速支持能力。随着人工智能技术的发展,各种硬件加速器不断涌现,如 GPU、TPU、FPGA 等。ONNX 使得开发者能够更方便地利用这些硬件加速器的性能优势,通过使用 ONNX 兼容的运行时和库,模型可以在不同的硬件平台上实现高效运行 。例如,在 NVIDIA 的 GPU 上,通过 ONNX Runtime 结合 CUDA 加速,可以显著提高模型的推理速度;在 Google 的 TPU 上,也可以利用 ONNX 模型实现快速的推理计算,为大规模的深度学习应用提供了有力的支持。

作为一个开源项目,ONNX 得到了广泛的社区支持。众多科技公司和研究机构都积极参与到 ONNX 的发展和完善中来,不断推动其功能的扩展和性能的优化。这种开放的协作模式确保了 ONNX 能够持续演进,满足 AI 领域不断变化的需求 。在 ONNX 的官方社区中,开发者们可以分享经验、交流技术、提交代码和报告问题,共同促进 ONNX 生态系统的繁荣发展。

ONNX 模型通常包含图(Graph)、节点(Node)、张量(Tensor)和属性(Attribute)等关键组件。图描述了模型的整体结构,包括输入、输出和中间计算节点;节点代表模型中的各个操作,如卷积、池化等;张量用于存储和传递数据的多维数组;属性则定义了节点的特定参数 。这种结构使得 ONNX 模型具有很好的可读性和可移植性,便于开发者理解和修改模型,也为模型在不同平台上的部署提供了便利。

(三)为什么选择 ONNX 部署 YOLO11

使用 ONNX 部署 YOLO11 具有多方面的显著优势,这些优势使得 ONNX 成为 YOLO11 模型部署的理想选择。

首先,跨平台兼容性是 ONNX 的一大核心优势。在实际应用中,我们往往需要将 YOLO11 模型部署到不同的硬件平台和软件环境中。无论是在高性能的服务器上,还是在资源受限的嵌入式设备中,亦或是在不同操作系统的移动设备上,ONNX 都能确保 YOLO11 模型能够顺利运行 。例如,将训练好的 YOLO11 模型转换为 ONNX 格式后,可以在 Windows、Linux、MacOS 等多种操作系统上进行部署,也可以在 NVIDIA Jetson 系列的嵌入式设备上实现高效推理,大大拓宽了 YOLO11 模型的应用范围。

ONNX 还为 YOLO11 模型带来了强大的性能优化潜力。许多深度学习框架和硬件加速器都对 ONNX 模型提供了良好的支持,这意味着在将 YOLO11 模型转换为 ONNX 格式后,可以充分利用这些框架和加速器的性能优化功能 。比如,通过 ONNX Runtime 结合 NVIDIA 的 TensorRT 技术,可以对 YOLO11 模型进行优化,实现更快的推理速度和更低的内存占用。在实际应用中,这对于需要实时处理大量图像数据的场景,如自动驾驶中的实时目标检测,具有至关重要的意义。

ONNX 的生态系统非常丰富,包括多个重要组件,如 ONNX 格式、ONNX Runtime、ONNX 工具以及来自全球开发者的各种工具、库和扩展 。这些组件为 YOLO11 模型的部署和应用提供了全方位的支持。ONNX Runtime 作为一个高性能的推理引擎,可以在多种平台上运行 ONNX 模型,并且提供了丰富的 API 和工具,方便开发者进行模型的加载、推理和优化;ONNX 工具则包括模型转换、优化和可视化等工具,帮助开发者更好地管理和调试 YOLO11 模型 。此外,ONNX 社区的活跃也使得开发者可以轻松获取到各种资源和技术支持,加速 YOLO11 模型的开发和应用。

使用 ONNX 部署 YOLO11 还可以简化模型的部署流程,降低开发成本。由于 ONNX 提供了统一的模型表示方式,开发者无需针对不同的框架和平台进行繁琐的模型适配工作,只需要将 YOLO11 模型转换为 ONNX 格式,就可以在支持 ONNX 的环境中进行部署 。这不仅减少了开发工作量,还提高了模型的稳定性和可维护性。在一个大型的项目中,涉及到多个团队协作开发和部署 YOLO11 模型,使用 ONNX 可以大大提高团队之间的协作效率,降低沟通成本。

二、准备工作

(一)环境搭建

  1. 硬件环境

进行 YOLO11 推理部署,硬件的选择对推理速度起着关键作用。在 CPU 方面,建议选择英特尔酷睿 i7 或 AMD 锐龙 7 系列及以上的处理器,这些处理器具有较高的核心数和主频,能够在一定程度上满足复杂计算需求 。例如,英特尔酷睿 i7 - 13700K 处理器,拥有 24 核心 32 线程,睿频可达 5.4GHz,在处理图像数据时,多核心可以并行处理不同的任务,如数据预处理、模型推理等,提高整体效率。而 AMD 锐龙 7 7800X3D 则凭借其独特的 3D V - Cache 技术,在缓存性能上表现出色,对于频繁访问数据的深度学习推理任务,能够减少数据读取时间,提升推理速度。

对于 GPU,NVIDIA 的 RTX 系列是不错的选择,如 RTX 3060、RTX 4090 等 。RTX 3060 拥有 12GB 显存,能够存储大量的图像数据和模型参数,其 CUDA 核心数量众多,可以并行执行大量的计算任务,在处理高分辨率图像时,能够快速完成卷积、池化等操作,显著提高推理速度。RTX 4090 更是性能强劲,24GB 的高速显存和高达 16384 个 CUDA 核心,使其在面对复杂场景和大规模数据集时,依然能够保持高效的推理性能,无论是实时视频流的处理,还是对大量图像的批量分析,都能轻松应对。

在内存方面,16GB 及以上的 DDR4 或 DDR5 内存是必要的,足够的内存可以保证模型和数据在运行过程中有充足的空间存储和交换 。如果内存不足,在处理大尺寸图像或复杂模型时,会频繁出现内存交换,导致推理速度大幅下降。例如,在处理分辨率为 4K 的图像时,16GB 内存可以确保图像数据能够完整加载,并为模型推理提供足够的运行空间,避免因内存不足而引发的卡顿现象。

硬盘则建议使用高速的固态硬盘(SSD),如三星 980 PRO、西部数据 SN850X 等 。SSD 具有快速的读写速度,能够快速读取模型文件和图像数据,减少数据加载时间。以三星 980 PRO 为例,其顺序读取速度可达 7000MB/s,顺序写入速度也能达到 5000MB/s 左右,在加载 YOLO11 模型和大量图像数据时,能够迅速将数据传输到内存中,为推理过程提供高效的数据支持,相比传统机械硬盘,大大缩短了推理的准备时间。

  1. 软件环境

在软件环境搭建方面,Python 是必不可少的,建议安装 Python 3.8 及以上版本 。Python 具有丰富的库和工具,如 NumPy、PyTorch 等,能够方便地进行数据处理和深度学习模型的开发与部署。在安装 Python 时,可以选择从官方网站下载安装包进行安装,也可以使用 Anaconda 等工具进行安装和环境管理。使用 Anaconda 可以方便地创建虚拟环境,隔离不同项目的依赖,避免版本冲突。

如果选择使用 C++ 进行推理部署,还需要安装 C++ 编译器,如 GCC(在 Linux 系统中)或 Visual Studio(在 Windows 系统中) 。GCC 是 Linux 系统中广泛使用的开源编译器,具有高效、稳定的特点,能够将 C++ 代码编译成可执行文件。在 Ubuntu 系统中,可以通过命令 “sudo apt - get install build - essential” 来安装 GCC 及其相关依赖。Visual Studio 则是 Windows 系统下强大的集成开发环境,提供了丰富的调试工具和代码智能提示功能,对于 C++ 开发非常友好。在安装 Visual Studio 时,可以选择安装 C++ 开发相关的组件,如 C++ 编译器、Windows SDK 等。

对于深度学习框架,PyTorch 是训练 YOLO11 模型的首选框架,建议安装 PyTorch 1.10 及以上版本 。可以从 PyTorch 官方网站根据自己的 CUDA 版本选择合适的安装命令进行安装。如果你的显卡支持 CUDA 11.6,并且安装了相应的 CUDA Toolkit 和 cuDNN,可以使用以下命令安装 PyTorch:

pip install torch torchvision torchaudio --index - url https://download.pytorch.org/whl/cu116

这样可以确保安装的 PyTorch 版本与你的硬件环境相匹配,充分发挥 GPU 的加速性能。

如果还需要使用 TensorFlow,也需要安装相应版本 。TensorFlow 在工业界应用广泛,具有强大的分布式训练和模型部署能力。在安装 TensorFlow 时,同样要注意版本的选择,根据自己的需求和硬件环境进行安装。例如,如果需要使用 TensorFlow 的 GPU 加速功能,需要安装与 CUDA 版本兼容的 TensorFlow GPU 版本。

为了更好地管理项目依赖,建议使用虚拟环境 。在 Python 中,可以使用 venv 或 conda 来创建虚拟环境。使用 venv 创建虚拟环境的命令如下:

python3 - m venv myenv
source myenv/bin/activate # 在Windows系统中使用myenv\Scripts\activate

这样就创建了一个名为 myenv 的虚拟环境,并激活了它。在激活的虚拟环境中安装的包不会影响系统全局环境,避免了不同项目之间的依赖冲突。使用 conda 创建虚拟环境的命令为:

conda create - n myenv python = 3.8
conda activate myenv

conda 不仅可以创建虚拟环境,还可以方便地管理环境中的包,通过 conda install 命令可以安装各种依赖包,并且能够自动解决包之间的依赖关系。

(二)获取 YOLO11 模型与数据集

  1. 下载 YOLO11 预训练模型

获取 YOLO11 预训练模型可以从官方网站或其他可靠的开源代码仓库进行下载。以从 Ultralytics 官方仓库获取为例,首先需要确保已经安装了 Git 工具,然后在命令行中执行以下命令克隆仓库:

git clone https://github.com/ultralytics/ultralytics.git

克隆完成后,进入 ultralytics 目录,在该目录下可以找到不同版本的 YOLO11 预训练模型文件,通常以.pt 为后缀 。这些模型文件包含了训练好的权重和网络结构信息,是进行推理部署的关键。

YOLO11 有不同的模型版本,如 yolo11s、yolo11m 等 。yolo11s 是小型版本,其模型参数较少,模型体积小,推理速度快,适合在资源受限的设备上运行,如嵌入式设备或移动设备 。在智能安防摄像头中,由于设备的计算资源和存储资源有限,yolo11s 可以在保证一定检测精度的前提下,快速对监控画面进行目标检测,实现实时预警功能。yolo11m 则是中型版本,在模型大小和检测精度之间取得了较好的平衡,适用于大多数通用场景 。在交通监控系统中,需要对道路上的车辆、行人等目标进行准确检测,yolo11m 能够满足这一需求,同时在普通服务器或具有一定计算能力的设备上也能高效运行。而 yolo11l 等大型版本,模型参数更多,检测精度更高,但推理速度相对较慢,适用于对检测精度要求极高的场景,如医学影像分析中的病灶检测 。在医学领域,对于微小病灶的检测需要极高的精度,yolo11l 可以通过学习大量的医学图像数据,准确识别出病灶的位置和类型,为医生的诊断提供有力支持。

  1. 准备测试数据集

准备用于推理测试的数据集是验证 YOLO11 模型性能的重要环节。数据集的格式通常为图像文件,如常见的.jpg、.png 等格式 。标注方法一般采用边界框标注,即使用矩形框标记出图像中目标物体的位置,并标注出目标物体的类别 。在实际操作中,可以使用专业的标注工具,如 LabelImg、Labelme 等。以 LabelImg 为例,打开工具后,选择要标注的图像,然后使用矩形框工具框选目标物体,输入对应的类别标签,保存标注结果,标注文件通常以.xml 或.txt 格式保存 。

为了全面评估模型的性能,需要将数据集划分为训练集、验证集和测试集 。一般按照 8:1:1 的比例进行划分,即将 80% 的数据用于训练模型,让模型学习目标物体的特征和模式;10% 的数据用于验证集,在训练过程中,通过验证集来调整模型的超参数,防止模型过拟合;剩下 10% 的数据用于测试集,在模型训练完成后,使用测试集来评估模型的泛化能力和准确性 。在划分数据集时,可以使用 Python 的相关库,如 scikit - learn 中的 train_test_split 函数来实现。例如:

from sklearn.model_selection import train_test_split

import os

import shutil

image_files = [f for f in os.listdir('images') if f.endswith(('.jpg', '.png'))]

train_images, test_images = train_test_split(image_files, test_size = 0.2, random_state = 42)

train_images, val_images = train_test_split(train_images, test_size = 0.125, random_state = 42) # 0.125是0.1 / 0.8得到,确保划分比例正确

def move_files(files, source_dir, target_dir):

for file in files:

shutil.move(os.path.join(source_dir, file), os.path.join(target_dir, file))

move_files(train_images, 'images', 'train/images')

move_files(val_images, 'images', 'val/images')

move_files(test_images, 'images', 'test/images')

这样就完成了数据集的划分,并将不同部分的图像文件移动到相应的目录中。同时,标注文件也需要按照相同的方式进行划分和移动,确保图像文件和标注文件的对应关系。

(三)安装 ONNX 及相关依赖

  1. ONNX 安装

在不同操作系统下安装 ONNX 的方法略有不同。在 Windows 系统中,如果已经安装了 Python 和 pip,可以使用以下命令进行安装:

pip install onnx

在安装过程中,pip 会自动下载 ONNX 及其依赖包,并将其安装到 Python 的环境中 。如果安装过程中出现网络问题,可以尝试更换 pip 源,如使用清华大学的镜像源:

pip install onnx - i https://pypi.tuna.tsinghua.edu.cn/simple

在 Linux 系统中,同样可以使用 pip 进行安装,命令与 Windows 系统类似 。如果系统中安装了 Anaconda,也可以在 Anaconda 环境中使用 conda 进行安装:

conda install - c conda - forge onnx

conda 会从 conda - forge 渠道下载并安装 ONNX,conda - forge 是一个社区维护的 conda 包仓库,提供了丰富的科学计算和机器学习相关的包 。

在安装 ONNX 时,可能会遇到一些问题。例如,依赖包冲突问题,可能是由于系统中已经安装了其他版本的相关依赖包,与 ONNX 的依赖要求不兼容 。解决方法可以尝试升级或降级相关依赖包,或者在虚拟环境中重新安装 ONNX,以避免依赖冲突。如果出现安装失败的情况,可能是网络问题或安装源不可用,可以检查网络连接,更换安装源,或者手动下载 ONNX 的安装包进行离线安装 。

  1. 相关依赖库安装

与 ONNX 配合使用的重要依赖库之一是 ONNX - Runtime,它是一个高性能的推理引擎,用于运行 ONNX 模型 。在 Windows 系统中,可以使用 pip 安装 ONNX - Runtime:

pip install onnxruntime

如果需要使用 GPU 加速,可以安装 ONNX - Runtime - GPU 版本:

pip install onnxruntime - gpu

安装 ONNX - Runtime - GPU 版本时,需要确保系统中已经安装了相应的 CUDA Toolkit 和 cuDNN,并且版本与 ONNX - Runtime - GPU 的要求相匹配 。在 Linux 系统中,安装方法类似,可以根据需要选择安装 CPU 版本或 GPU 版本。

ONNX - Runtime 的作用是提供高效的推理运行时环境,它能够优化 ONNX 模型的执行,利用硬件加速(如 GPU)来提高推理速度 。在将 YOLO11 模型转换为 ONNX 格式后,使用 ONNX - Runtime 可以快速加载模型并进行推理计算,实现高效的目标检测。除了 ONNX - Runtime,还可能需要安装其他一些辅助库,如 numpy 用于数值计算,opencv - python 用于图像处理等 。这些库可以通过 pip 进行安装:

pip install numpy opencv - python

numpy 提供了强大的数组操作和数学计算功能,在数据预处理和后处理过程中经常用到 。在将图像数据转换为模型输入格式时,需要使用 numpy 进行数组的转换和计算。opencv - python 则是一个广泛使用的计算机视觉库,提供了丰富的图像处理函数,如图像读取、裁剪、缩放等操作,在 YOLO11 的推理部署中,用于对输入图像进行预处理,使其符合模型的输入要求 。

三、Python 语言下基于 ONNX 的 YOLO11 推理部署

(一)模型转换:PyTorch 模型转 ONNX

  1. 导出 ONNX 模型

在 Python 环境中,使用 PyTorch 将 YOLO11 模型导出为 ONNX 格式,关键代码如下:

import torch

from ultralytics import YOLO

# 加载YOLO11模型

model = YOLO("yolo11s.pt")

model.eval()

# 创建虚拟输入张量,尺寸需与模型输入要求匹配

dummy_input = torch.randn(1, 3, 640, 640)

# 导出为ONNX文件

output_onnx_file = "yolo11s.onnx"

torch.onnx.export(model,

dummy_input,

output_onnx_file,

input_names=["input"],

output_names=["output"],

opset_version=11)

print(f"Model has been successfully converted to {output_onnx_file}.")

在这段代码中,torch.onnx.export函数是实现模型转换的核心。model参数传入待转换的 YOLO11 模型;dummy_input是一个虚拟的输入张量,它的形状为(1, 3, 640, 640),其中 1 表示批量大小,3 表示图像的通道数(RGB 图像),640x640 是图像的分辨率,这个尺寸需要与模型训练时的输入尺寸一致,以确保模型的正确性 。output_onnx_file指定了导出的 ONNX 模型文件的保存路径和文件名;input_names和output_names分别定义了模型输入和输出的名称,这些名称在后续的推理过程中会被用到,用于标识输入和输出数据 。opset_version设置为 11,它指定了 ONNX 操作集的版本,不同的版本支持不同的操作,选择合适的版本可以确保模型在转换过程中不会出现不支持的操作 。

  1. 模型验证

验证导出的 ONNX 模型的正确性是确保推理部署顺利进行的重要环节。一种常见的方法是对比 PyTorch 模型和 ONNX 模型的推理结果。代码示例如下:

import onnxruntime

import torch

import numpy as np

def to_numpy(tensor):

return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()

# 加载ONNX模型并创建推理会话

ort_session = onnxruntime.InferenceSession("yolo11s.onnx")

# 准备输入数据

input_tensor = torch.randn(1, 3, 640, 640)

ort_inputs = {ort_session.get_inputs()[0].name: to_numpy(input_tensor)}

# ONNX模型推理

ort_outs = ort_session.run(None, ort_inputs)

# PyTorch模型推理

model = YOLO("yolo11s.pt")

model.eval()

with torch.no_grad():

torch_outs = model(input_tensor)

# 对比推理结果

np.testing.assert_allclose(to_numpy(torch_outs[0]), ort_outs[0], atol=1e-03)

print("The ONNX model has been validated successfully.")

在这段代码中,首先使用onnxruntime.InferenceSession加载 ONNX 模型并创建推理会话 。然后,准备与导出模型时相同的输入数据input_tensor,并将其转换为 ONNX Runtime 所需的格式ort_inputs 。接着,分别使用 ONNX 模型和 PyTorch 模型进行推理,得到推理结果ort_outs和torch_outs 。最后,使用np.testing.assert_allclose函数对比两个模型的推理结果,atol=1e-03表示允许的绝对误差为 0.001,在这个误差范围内,如果两个结果相近,则认为 ONNX 模型验证成功 。如果推理结果差异较大,可能是模型转换过程中出现了问题,需要检查模型导出代码、操作集版本以及输入输出的一致性等 。

(二)推理代码实现

  1. 导入必要库

在 Python 中进行 YOLO11 推理,需要导入多个关键库,代码如下:

import onnxruntime

import cv2

import numpy as np

onnxruntime是 ONNX 模型的推理引擎,用于加载和运行 ONNX 格式的模型 。通过它,可以方便地在不同的硬件平台上进行高效推理,充分发挥 ONNX 模型的跨平台优势 。cv2即 OpenCV 库,是计算机视觉领域中广泛使用的库,提供了丰富的图像处理和计算机视觉功能 。在 YOLO11 推理中,主要用于图像的读取、预处理和后处理,如图像的缩放、裁剪、绘制检测框等操作 。numpy是 Python 的核心数值计算支持库,提供了强大的数组对象和数值计算函数 。在 YOLO11 推理过程中,用于处理和操作图像数据以及模型的输入输出数据,例如将图像数据转换为 numpy 数组,进行数据的归一化、维度变换等操作 。

  1. 图像预处理

YOLO11 图像预处理的步骤主要包括图像缩放、归一化、通道转换等,以确保输入图像符合模型的要求。代码实现如下:

def preprocess(image, target_size=(640, 640)):

height, width = image.shape[:2]

new_height, new_width = target_size

# 计算缩放比例

scale = min(new_width / width, new_height / height)

new_unpad_w, new_unpad_h = int(width * scale), int(height * scale)

# 计算填充大小

dw, dh = (new_width - new_unpad_w) // 2, (new_height - new_unpad_h) // 2

# 缩放图像

resized_image = cv2.resize(image, (new_unpad_w, new_unpad_h), interpolation=cv2.INTER_LINEAR)

# 填充图像

padded_image = cv2.copyMakeBorder(resized_image, dh, dh, dw, dw, cv2.BORDER_CONSTANT, value=(114, 114, 114))

# 归一化图像

normalized_image = padded_image / 255.0

# 通道转换(HWC -> CHW)

processed_image = normalized_image.transpose(2, 0, 1)

# 添加批次维度

processed_image = np.expand_dims(processed_image, axis=0).astype(np.float32)

return processed_image

首先,获取原始图像的高度和宽度,以及目标尺寸 。然后,计算缩放比例,确保图像在缩放过程中不会发生拉伸变形 。根据缩放比例计算出新的未填充尺寸,并计算填充的宽度和高度 。接着,使用cv2.resize函数对图像进行缩放,使用cv2.copyMakeBorder函数对图像进行填充,填充颜色为灰色(114, 114, 114) 。之后,将填充后的图像进行归一化处理,将像素值从 0 - 255 转换为 0.0 - 1.0 。再将图像的通道顺序从 HWC(高度、宽度、通道)转换为 CHW(通道、高度、宽度),这是因为 ONNX 模型通常期望的输入格式是 CHW 。最后,添加批次维度,将图像转换为模型输入所需的形状 。

  1. 模型推理

使用 ONNX - Runtime 进行模型推理的代码如下:

def inference(ort_session, input_image):

ort_inputs = {ort_session.get_inputs()[0].name: input_image}

ort_outs = ort_session.run(None, ort_inputs)

return ort_outs

在这段代码中,ort_session是之前创建的 ONNX Runtime 推理会话 。首先,构建输入数据字典ort_inputs,将预处理后的图像输入数据与模型的输入名称进行关联 。然后,使用ort_session.run方法进行模型推理,该方法接受两个参数,第一个参数为None,表示获取所有输出;第二个参数为输入数据字典ort_inputs 。推理结果ort_outs是一个包含模型输出的列表,后续将对其进行后处理 。

  1. 后处理与结果展示

对推理结果进行后处理,主要包括非极大值抑制(NMS),以去除重叠的检测框,保留最佳的检测结果 。展示检测结果,即在图像上绘制检测框和类别标签 。代码实现如下:

def nms(boxes, scores, iou_threshold):

x1 = boxes[:, 0]

y1 = boxes[:, 1]

x2 = boxes[:, 2]

y2 = boxes[:, 3]

areas = (x2 - x1 + 1) * (y2 - y1 + 1)

order = scores.argsort()[::-1]

keep = []

while order.size > 0:

i = order[0]

keep.append(i)

xx1 = np.maximum(x1[i], x1[order[1:]])

yy1 = np.maximum(y1[i], y1[order[1:]])

xx2 = np.minimum(x2[i], x2[order[1:]])

yy2 = np.minimum(y2[i], y2[order[1:]])

w = np.maximum(0.0, xx2 - xx1 + 1)

h = np.maximum(0.0, yy2 - yy1 + 1)

inter = w * h

ovr = inter / (areas[i] + areas[order[1:]] - inter)

inds = np.where(ovr <= iou_threshold)[0]

order = order[inds + 1]

return keep

def postprocess(image, outputs, conf_threshold=0.5, iou_threshold=0.5):

num_classes = 80 # COCO数据集类别数,可根据实际情况修改

boxes = outputs[0][0, :, :4]

scores = outputs[0][0, :, 4:]

# 过滤低置信度的检测结果

conf_mask = scores.max(axis=1) > conf_threshold

boxes = boxes[conf_mask]

scores = scores[conf_mask]

# 计算类别

classes = scores.argmax(axis=1)

# 非极大值抑制

keep = nms(boxes, scores.max(axis=1), iou_threshold)

boxes = boxes[keep]

scores = scores[keep]

classes = classes[keep]

# 绘制检测框和类别标签

for box, score, cls in zip(boxes, scores, classes):

x1, y1, x2, y2 = box

x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)

label = f"{class_names[cls]}: {score:.2f}"

cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)

cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

return image

在nms函数中,首先提取检测框的坐标和面积,并根据得分对检测框进行排序 。然后,通过循环不断选择得分最高的检测框,并计算其与其他检测框的交并比(IoU) 。如果 IoU 大于设定的阈值,则认为这些检测框是重叠的,将其移除 。最终,返回保留下来的检测框索引 。在postprocess函数中,首先从推理结果中提取检测框、得分和类别信息 。然后,过滤掉置信度低于阈值的检测结果,并计算出每个检测框对应的类别 。接着,使用nms函数进行非极大值抑制,去除重叠的检测框 。最后,在原始图像上绘制检测框和类别标签,使用cv2.rectangle函数绘制矩形框,使用cv2.putText函数添加文本标签 。

(三)案例实践与优化

  1. 实际应用案例

在监控视频中使用 YOLO11 进行物体检测的完整代码实现如下:

import cv2

import onnxruntime

import numpy as np

# 加载ONNX模型

ort_session = onnxruntime.InferenceSession("yolo11s.onnx")

# 打开视频文件

cap = cv2.VideoCapture("test_video.mp4")

while True:

ret, frame = cap.read()

if not ret:

break

# 图像预处理

input_image = preprocess(frame)

# 模型推理

outputs = inference(ort_session, input_image)

# 后处理与结果展示

result_image = postprocess(frame.copy(), outputs)

# 显示结果

cv2.imshow("Object Detection", result_image)

# 按下'q'键退出

if cv2.waitKey(1) & 0xFF == ord('q'):

break

# 释放资源

cap.release()

cv2.destroyAllWindows()

在这段代码中,首先加载 ONNX 格式的 YOLO11 模型,并创建推理会话 。然后,使用cv2.VideoCapture打开视频文件 。在循环中,逐帧读取视频画面,对每一帧进行图像预处理、模型推理、后处理和结果展示 。最后,当按下 'q' 键时,退出循环,释放视频资源并关闭所有窗口 。运行效果展示:在监控视频中,YOLO11 能够实时检测出各种物体,如行人、车辆、自行车等,并在画面上准确地绘制出检测框和类别标签,实现了实时的目标检测功能 。

  1. 性能优化技巧

在 Python 环境下优化 YOLO11 推理性能,可以采用以下方法:

  • 使用多线程:利用 Python 的threading或multiprocessing模块,将图像预处理、模型推理和后处理等任务分配到不同的线程或进程中,实现并行处理,提高整体效率 。例如,使用threading模块创建一个预处理线程和一个推理线程,在预处理线程中对图像进行预处理,在推理线程中进行模型推理,这样可以减少等待时间,提高推理速度 。
  • 优化预处理和后处理流程:对预处理和后处理代码进行优化,减少不必要的计算和内存操作 。在图像缩放时,可以使用更高效的插值算法;在非极大值抑制中,可以采用更快速的计算方法 。使用cv2.INTER_AREA插值算法进行图像缩放,在处理缩小图像时,该算法比cv2.INTER_LINEAR算法更高效 。
  • 模型量化:将模型从浮点数格式转换为低精度格式,如 INT8,以减少模型的内存占用和计算量,提高推理速度 。可以使用 ONNX - Runtime 的量化工具对 YOLO11 模型进行量化处理 。通过量化,模型在保持一定精度的前提下,推理速度可以得到显著提升 。
  • 硬件加速:如果硬件支持,使用 GPU 进行推理,充分发挥 GPU 的并行计算能力 。在安装 ONNX - Runtime 时,选择安装 GPU 版本,并确保系统中安装了相应的 CUDA Toolkit 和 cuDNN 。在推理时,将模型和数据加载到 GPU 上进行计算,从而加快推理速度 。在 NVIDIA GPU 上,使用 ONNX - Runtime 结合 CUDA 加速,可以使 YOLO11 模型的推理速度大幅提升 。

四、C++ 语言下基于 ONNX 的 YOLO11 推理部署

(一)环境配置与依赖安装

  1. 安装 C++ 相关库

在 C++ 环境下进行 YOLO11 推理部署,需要安装一系列关键库。ONNX - Runtime C++ API 是必不可少的,它提供了在 C++ 中加载和运行 ONNX 模型的功能 。以在 Ubuntu 系统上安装为例,可以通过以下步骤进行:

首先,确保系统已经安装了 CMake 和 GCC 编译器。如果没有安装,可以使用以下命令进行安装:

sudo apt - get install cmake build - essential

然后,下载 ONNX - Runtime 的源代码。可以从官方 GitHub 仓库克隆代码:

git clone https://github.com/microsoft/onnxruntime.git

进入克隆的目录,创建一个 build 文件夹并进入该文件夹:

cd onnxruntime

mkdir build

cd build

接着,使用 CMake 进行配置。如果要使用 GPU 加速,并且系统中已经安装了 CUDA 和 cuDNN,可以使用以下命令进行配置:

cmake.. - DUSE_CUDA = ON - DUSE_TENSORRT = ON - DCUDA_TOOLKIT_ROOT_DIR = /usr/local/cuda - DCUDNN_INCLUDE_DIR = /usr/local/cuda/include - DCUDNN_LIBRARY = /usr/local/cuda/lib64/libcudnn.so

这里/usr/local/cuda是 CUDA 的安装路径,根据实际情况进行调整 。如果只使用 CPU,可以省略-DUSE_CUDA = ON和-DUSE_TENSORRT = ON等与 GPU 相关的选项 。配置完成后,使用 make 命令进行编译和安装:

make - j$(nproc)
sudo make install

-j$(nproc)表示使用系统的所有核心进行并行编译,以加快编译速度 。

OpenCV C++ 库也是常用的图像处理库,用于图像的读取、预处理和后处理等操作 。在 Ubuntu 系统上安装 OpenCV C++ 库可以使用以下命令:

sudo apt - get install libopencv - dev

这将安装 OpenCV 的开发库和头文件,方便在 C++ 项目中使用 。安装过程中可能会遇到一些问题,如依赖库冲突等。如果遇到依赖库冲突,可以检查系统中已安装的相关库版本,尝试升级或降级相关库,或者在安装时指定库的版本 。在安装 ONNX - Runtime 时,如果遇到 CUDA 版本不兼容的问题,需要检查 CUDA Toolkit 和 cuDNN 的版本是否与 ONNX - Runtime 的要求匹配,必要时更新 CUDA Toolkit 和 cuDNN 的版本 。

  1. 设置开发环境

以 Visual Studio 为例,创建一个新的 C++ 项目。打开 Visual Studio,选择 “创建新项目”,在模板中选择 “空项目”,然后点击 “下一步” 。在项目配置中,输入项目名称和位置,点击 “创建” 。接下来,配置项目属性。右键点击项目名称,选择 “属性” 。在 “VC++ 目录” -> “包含目录” 中,添加 ONNX - Runtime 和 OpenCV 的头文件路径 。如果 ONNX - Runtime 安装在默认路径,其头文件路径通常为/usr/local/include/onnxruntime;OpenCV 的头文件路径在安装后通常为/usr/include/opencv4(根据实际安装路径调整) 。在 “VC++ 目录” -> “库目录” 中,添加 ONNX - Runtime 和 OpenCV 的库文件路径 。ONNX - Runtime 的库文件路径通常为/usr/local/lib,OpenCV 的库文件路径在安装后通常为/usr/lib/x86_64 - linux - gnu(根据实际安装路径调整) 。在 “链接器” -> “输入” -> “附加依赖项” 中,添加 ONNX - Runtime 和 OpenCV 的库文件名称 。对于 ONNX - Runtime,如果使用 CPU 版本,添加onnxruntime.lib;如果使用 GPU 版本,添加onnxruntime_gpu.lib 。对于 OpenCV,根据需要添加opencv_world480.lib等相关库文件(根据实际安装的 OpenCV 版本调整库文件名) 。

在 CLion 中配置 C++ 项目也类似。首先,创建一个新的 C++ 项目。然后,在项目的 CMakeLists.txt 文件中添加依赖项 。假设 ONNX - Runtime 和 OpenCV 已经安装在系统默认路径,可以添加以下内容:

cmake_minimum_required(VERSION 3.15)

project(Yolo11_ONNX_CPP)

set(CMAKE_CXX_STANDARD 17)

# 添加ONNX - Runtime库

find_package(ONNXRuntime REQUIRED)

include_directories(${ONNXRuntime_INCLUDE_DIRS})

target_link_libraries(${PROJECT_NAME} ${ONNXRuntime_LIBRARIES})

# 添加OpenCV库

find_package(OpenCV REQUIRED)

include_directories(${OpenCV_INCLUDE_DIRS})

target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})

这样就完成了在 CLion 中对 C++ 项目的配置,使其能够使用 ONNX - Runtime 和 OpenCV 进行 YOLO11 推理部署开发 。

(二)模型加载与推理流程

  1. 加载 ONNX 模型

在 C++ 中使用 ONNX - Runtime C++ API 加载 YOLO11 ONNX 模型的代码示例如下:

#include <onnxruntime_cxx_api.h>

#include <iostream>

#include <fstream>

#include <vector>

int main() {

// 创建ORT环境

Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Yolo11Inference");

Ort::SessionOptions session_options;

session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);

// 加载ONNX模型

std::wstring model_path = L"yolo11s.onnx";

Ort::Session session(env, model_path.c_str(), session_options);

// 获取输入输出节点信息

const Ort::AllocatorWithDefaultOptions allocator;

auto input_names = session.GetInputNamesAllocated(allocator);

auto output_names = session.GetOutputNamesAllocated(allocator);

std::cout << "Input names: ";

for (const auto& name : input_names) {

std::cout << name.get() << " ";

}

std::cout << std::endl;

std::cout << "Output names: ";

for (const auto& name : output_names) {

std::cout << name.get() << " ";

}

std::cout << std::endl;

return 0;

}

在这段代码中,首先创建了一个Ort::Env对象,用于管理 ONNX - Runtime 的日志和环境信息 。然后,创建了一个Ort::SessionOptions对象,并设置了图优化级别为ORT_ENABLE_ALL,以启用所有可用的优化 。接着,通过Ort::Session的构造函数加载 ONNX 模型,传入环境对象、模型文件路径和会话选项 。最后,获取模型的输入和输出节点名称,并打印出来 。在加载模型过程中,Ort::Session的构造函数会解析 ONNX 模型文件,检查模型的完整性和兼容性 。如果模型文件不存在、格式错误或与当前 ONNX - Runtime 版本不兼容,将会抛出异常 。因此,在实际应用中,需要对异常进行捕获和处理,以确保程序的稳定性 。

  1. 图像预处理

在 C++ 中对输入图像进行预处理的步骤与 Python 中类似,主要包括图像缩放、归一化、通道转换等 。代码实现如下:

#include <opencv2/opencv.hpp>

#include <vector>

std::vector<float> preprocess(const cv::Mat& image, int target_width = 640, int target_height = 640) {

cv::Mat resized_image;

cv::resize(image, resized_image, cv::Size(target_width, target_height), 0, 0, cv::INTER_LINEAR);

cv::Mat normalized_image;

resized_image.convertTo(normalized_image, CV_32F, 1.0 / 255.0);

cv::Mat transposed_image;

cv::transpose(normalized_image, transposed_image);

cv::Mat swapped_image;

cv::cvtColor(transposed_image, swapped_image, cv::COLOR_BGR2RGB);

std::vector<float> input_data(target_width * target_height * 3);

float* data_ptr = input_data.data();

for (int i = 0; i < target_height; ++i) {

for (int j = 0; j < target_width; ++j) {

cv::Vec3f pixel = swapped_image.at<cv::Vec3f>(i, j);

*data_ptr++ = pixel[0];

*data_ptr++ = pixel[1];

*data_ptr++ = pixel[2];

}

}

return input_data;

}

与 Python 中的预处理方法相比,C++ 中的实现更加底层,使用了 OpenCV 的 C++ 接口进行图像处理操作 。在 Python 中,使用numpy数组进行数据操作更加方便和简洁,而 C++ 中则需要手动处理内存和数据类型转换 。在图像缩放时,C++ 中使用cv::resize函数,Python 中使用cv2.resize函数,参数和功能类似,但语法略有不同 。在归一化操作中,C++ 中使用convertTo函数将图像转换为 32 位浮点数并进行归一化,Python 中则直接进行数组运算 。

  1. 模型推理与结果处理

在 C++ 中进行模型推理,并对推理结果进行后处理的代码逻辑如下:

#include <onnxruntime_cxx_api.h>

#include <opencv2/opencv.hpp>

#include <iostream>

#include <vector>

#include <algorithm>

// 假设COCO数据集类别数为80

const int num_classes = 80;

// 计算IoU

float compute_iou(const std::vector<float>& box1, const std::vector<float>& box2) {

float xmin = std::max(box1[0], box2[0]);

float ymin = std::max(box1[1], box2[1]);

float xmax = std::min(box1[2], box2[2]);

float ymax = std::min(box1[3], box2[3]);

float intersection_area = std::max(0.0f, xmax - xmin) * std::max(0.0f, ymax - ymin);

float box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1]);

float box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1]);

float union_area = box1_area + box2_area - intersection_area;

return intersection_area / union_area;

}

// 非极大值抑制

std::vector<int> nms(std::vector<std::vector<float>> boxes, std::vector<float> scores, float iou_threshold) {

std::vector<int> keep;

std::vector<bool> suppressed(boxes.size(), false);

for (size_t i = 0; i < boxes.size(); ++i) {

if (suppressed[i]) continue;

keep.push_back(i);

for (size_t j = i + 1; j < boxes.size(); ++j) {

if (suppressed[j]) continue;

float iou = compute_iou(boxes[i], boxes[j]);

if (iou > iou_threshold) {

suppressed[j] = true;

}

}

}

return keep;

}

// 后处理

void postprocess(const cv::Mat& image, const std::vector<Ort::Value>& outputs, float conf_threshold = 0.5,

float iou_threshold = 0.5) {

const float* output_data = outputs[0].GetTensorData<float>();

int num_detections = outputs[0].GetTensorTypeAndShapeInfo().GetElementCount() / (4 + 1 + num_classes);

std::vector<std::vector<float>> boxes;

std::vector<float> scores;

std::vector<int> class_ids;

for (int i = 0; i < num_detections; ++i) {

const float* detection = output_data + i * (4 + 1 + num_classes);

float confidence = detection[4];

if (confidence > conf_threshold) {

int class_id = std::distance(detection + 5, std::max_element(detection + 5, detection + 5 + num_classes));

float x1 = detection[0];

float y1 = detection[1];

float x2 = detection[2];

float y2 = detection[3];

boxes.push_back({x1, y1, x2, y2});

scores.push_back(confidence);

class_ids.push_back(class_id);

}

}

std::vector<int> keep = nms(boxes, scores, iou_threshold);

for (int i : keep) {

int x1 = static_cast<int>(boxes[i][0]);

int y1 = static_cast<int>(boxes[i][1]);

int x2 = static_cast<int>(boxes[i][2]);

int y2 = static_cast<int>(boxes[i][3]);

int class_id = class_ids[i];

float score = scores[i];

cv::rectangle(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 255, 0), 2);

std::string label = std::to_string(class_id) + ": " + std::to_string(score);

cv::putText(image, label, cv::Point(x1, y1 - 10), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 2);

}

cv::imshow("Object Detection", image);

cv::waitKey(0);

}

int main() {

// 创建ORT环境

Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Yolo11Inference");

Ort::SessionOptions session_options;

session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);

// 加载ONNX模型

std::wstring model_path = L"yolo11s.onnx";

Ort::Session session(env, model_path.c_str(), session_options);

// 获取输入输出节点信息

const Ort::AllocatorWithDefaultOptions allocator;

auto input_names = session.GetInputNamesAllocated(allocator);

auto output_names = session.GetOutputNamesAllocated(allocator);

// 读取图像并预处理

cv::Mat image = cv::imread("test.jpg");

std::vector<float> input_data = preprocess(image);

// 创建输入张量

Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);

Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_data.data(), input_data.size(),

{1, 3, 640, 640});

// 模型推理

std::vector<Ort::Value> outputs = session.Run(Ort::RunOptions{nullptr}, input_names.data(), &input_tensor, 1,

output_names.data(), output_names.size());

// 后处理

postprocess(image, outputs);

return 0;

}

在这段代码中,首先定义了计算 IoU 和非极大值抑制(NMS)的函数 。在postprocess函数中,从模型输出中解析出检测框、得分和类别信息,过滤掉低置信度的检测结果,使用 NMS 去除重叠的检测框,最后在原始图像上绘制检测框和类别标签 。在main函数中,加载 ONNX 模型,读取并预处理图像,创建输入张量,进行模型推理,最后调用postprocess函数对推理结果进行后处理 。

(三)部署与性能调优

  1. 部署到实际应用

将基于 C++ 的 YOLO11 推理部署到实际应用中,如嵌入式设备时,需要注意硬件资源的限制 。在 NVIDIA Jetson Nano 等嵌入式设备上部署时,首先要确保设备已经安装了相应的驱动和依赖库 。由于 Jetson Nano 的计算资源相对有限,可能需要对模型进行量化处理,将模型从浮点数格式转换为低精度格式,如 INT8,以减少模型的内存占用和计算量 。可以使用 ONNX - Runtime 的量化工具对 YOLO11 模型进行量化,在部署时加载量化后的模型,以提高推理效率 。同时,要优化代码的内存管理,避免内存泄漏和过多的内存分配操作 。在图像预处理和后处理过程中,可以使用内存池技术,预先分配一定大小的内存块,避免频繁地申请和释放内存 。

部署到服务器时,要考虑多线程和并发处理的需求 。可以使用 C++ 的多线程库,如std::thread,将图像预处理、模型推理和后处理等任务分配到不同的线程中,实现并行处理 。在处理大量并发请求时,需要使用线程池技术,如boost::thread_pool,以提高线程的复用性和系统的性能 。还可以结合服务器的硬件资源,如多核 CPU 和高性能 GPU,合理分配任务,充分发挥硬件的性能优势 。在使用 GPU 进行推理时,要确保服务器已经安装了相应的 CUDA Toolkit 和 cuDNN

五、其他架构下的 YOLO11 推理部署

(一)基于 NVIDIA TensorRT 的部署

  1. TensorRT 简介

NVIDIA TensorRT 是一个专门为 NVIDIA GPU 设计的高性能深度学习推理优化器和运行时库。它旨在将深度学习模型转化为高度优化的运行时图形流水线,从而在 NVIDIA GPU 上实现低延迟和高吞吐量的推理服务。

TensorRT 的核心优势之一是其强大的优化能力。它通过自动执行图层融合、量化以及其他优化策略,能够显著提高模型的计算效率,降低内存占用。在处理 YOLO11 模型时,TensorRT 可以将多个连续的卷积层、批归一化层和激活函数层融合为一个单独的计算节点,减少了数据在计算节点之间的移动,从而提高了性能 。这种图层融合技术可以有效减少内存访问次数,提高计算资源的利用率,使得复杂模型在嵌入式、服务器乃至数据中心级别的设备上都能顺畅运行。

动态形状支持也是 TensorRT 的一大亮点。它允许模型处理不同大小的输入,这对于需要处理可变输入的场景(如自然语言处理或图像分割)非常有用。在实际应用中,图像的分辨率可能会有所不同,TensorRT 的动态形状支持使得 YOLO11 模型能够灵活地处理这些不同分辨率的图像,而无需重新训练或调整模型 。无论是高清视频监控中的大尺寸图像,还是移动设备拍摄的小尺寸图像,TensorRT 都能确保 YOLO11 模型高效运行。

量化是 TensorRT 提高模型推理速度的重要手段之一。它通过自动或手动对模型权重和激活进行精度降低,可以减小模型大小并加速运算,同时保持预测准确率 。TensorRT 支持 INT8 和 FP16 量化,利用 NVIDIA GPU 对半精度 (FP16) 和 INT8 数据类型的支持,实现更快的计算速度 。在一些对计算资源有限制的场景中,将 YOLO11 模型量化为 INT8 格式,可以在不损失太多精度的前提下,显著提高推理速度,降低内存占用,使得模型能够在资源受限的设备上运行。

  1. 将 ONNX 模型转换为 TensorRT 引擎

将 YOLO11 ONNX 模型转换为 TensorRT 引擎的步骤如下:

  • ONNX 模型调整:在导出 ONNX 模型前,需确保 YOLO11 模型的某些操作与 TensorRT 兼容。例如,YOLO11 中可能存在一些 TensorRT 不支持的算子,需要进行替换 。YOLO11 中可能使用了一些特殊的上采样操作,在转换前需要将其替换为 TensorRT 支持的 Resize 操作,并且要确保插值模式为 nearest 。如果模型需要支持动态输入(如不同分辨率),需在导出 ONNX 时设置 dynamic_axes 。可以使用以下代码设置:
torch.onnx.export(model, im, "yolo11.onnx", dynamic_axes={'input':{0:'batch', 2:'height', 3:'width'}})
  • 使用 trtexec 命令行工具转换:trtexec 是 TensorRT 提供的一个强大的命令行工具,用于将 ONNX 模型转换为 TensorRT 引擎 。使用时,需指定 ONNX 模型文件路径、输出的 TensorRT 引擎文件路径,以及其他一些参数 。以下是一个示例命令:
trtexec --onnx=yolo11.onnx --saveEngine=yolo11.engine --fp16 --workspace=4096 --minShapes=input:1x3x640x640 --optShapes=input:1x3x640x640 --maxShapes=input:1x3x640x640

--fp16表示启用 FP16 精度加速,可提高推理速度,但可能会对精度有一定影响;--workspace指定分配的显存空间大小,单位为 MB;--minShapes、--optShapes和--maxShapes分别指定动态输入的最小尺寸、最优尺寸和最大尺寸 。

  • Python API 转换:使用 TensorRT 的 Python API 可以更灵活地控制转换过程 。示例代码如下:
import tensorrt as trt

logger = trt.Logger(trt.Logger.WARNING)

builder = trt.Builder(logger)

network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))

parser = trt.OnnxParser(network, logger)

with open("yolo11.onnx", "rb") as f:

parser.parse(f.read())

config = builder.create_builder_config()

config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 << 30) # 1GB

config.set_flag(trt.BuilderFlag.FP16) # 启用FP16

serialized_engine = builder.build_serialized_network(network, config)

with open("yolo11.engine", "wb") as f:

f.write(serialized_engine)

在这段代码中,首先创建了 TensorRT 的日志记录器、构建器、网络和解析器 。然后,打开 ONNX 模型文件并使用解析器将其解析到网络中 。接着,创建构建器配置,设置工作空间大小和启用 FP16 精度 。最后,构建序列化的引擎并将其保存到文件中 。

  1. 使用 TensorRT 进行推理

在 TensorRT 环境下进行 YOLO11 推理的代码实现如下:

import tensorrt as trt

import pycuda.driver as cuda

import pycuda.autoinit

import cv2

import numpy as np

# 加载TensorRT引擎

def load_engine(engine_path):

runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING))

with open(engine_path, "rb") as f:

engine_data = f.read()

engine = runtime.deserialize_cuda_engine(engine_data)

return engine

# 图像预处理

def preprocess(image, target_size=(640, 640)):

height, width = image.shape[:2]

new_height, new_width = target_size

scale = min(new_width / width, new_height / height)

new_unpad_w, new_unpad_h = int(width * scale), int(height * scale)

dw, dh = (new_width - new_unpad_w) // 2, (new_height - new_unpad_h) // 2

resized_image = cv2.resize(image, (new_unpad_w, new_unpad_h), interpolation=cv2.INTER_LINEAR)

padded_image = cv2.copyMakeBorder(resized_image, dh, dh, dw, dw, cv2.BORDER_CONSTANT, value=(114, 114, 114))

normalized_image = padded_image / 255.0

processed_image = normalized_image.transpose(2, 0, 1)

processed_image = np.expand_dims(processed_image, axis=0).astype(np.float32)

return processed_image

# 推理

def infer(engine, input_image):

context = engine.create_execution_context()

# 分配输入输出缓冲区

input_size = trt.volume(engine.get_binding_shape(0)) * engine.max_batch_size

output_size = trt.volume(engine.get_binding_shape(1)) * engine.max_batch_size

input_buffer = cuda.pagelocked_empty(input_size, dtype=np.float32)

output_buffer = cuda.pagelocked_empty(output_size, dtype=np.float32)

input_device_buffer = cuda.mem_alloc(input_buffer.nbytes)

output_device_buffer = cuda.mem_alloc(output_buffer.nbytes)

np.copyto(input_buffer, input_image.ravel())

# 数据传输到GPU

cuda.memcpy_htod_async(input_device_buffer, input_buffer)

bindings = [int(input_device_buffer), int(output_device_buffer)]

# 执行推理

context.execute_async(batch_size=1, bindings=bindings)

# 数据传输回CPU

cuda.memcpy_dtoh_async(output_buffer, output_device_buffer)

# 同步

cuda.Context.synchronize()

return output_buffer.reshape((1, -1))

# 加载引擎

engine = load_engine("yolo11.engine")

# 读取图像并预处理

image = cv2.imread("test.jpg")

input_image = preprocess(image)

# 推理

output = infer(engine, input_image)

# 后处理

# 这里省略后处理代码,可参考前面Python推理部分的后处理代码

对比使用 TensorRT 和直接使用 ONNX - Runtime 的推理性能,通常情况下,TensorRT 在 NVIDIA GPU 上能够实现更高的推理速度和更低的延迟 。在处理高分辨率图像时,TensorRT 通过优化和加速,可以使 YOLO11 模型的推理速度比直接使用 ONNX - Runtime 提高数倍 。具体的性能提升取决于模型的复杂度、输入图像的大小以及 GPU 的性能等因素 。通过实际测试,在使用 RTX 3090 GPU 时,对于分辨率为 1920x1080 的图像,使用 TensorRT 推理 YOLO11 模型的速度比使用 ONNX - Runtime 快约 30%,这使得在实时视频处理等场景中,能够实现更流畅的目标检测效果 。

(二)在移动设备(如 ARM 架构)上的部署

  1. 移动设备环境搭建

在基于 ARM 架构的移动设备(如手机、平板电脑)上进行 YOLO11 推理部署,首先需要搭建交叉编译工具链 。以在树莓派上部署为例,可以通过以下步骤安装交叉编译工具链:

  • 确保树莓派系统已经安装了必要的依赖包,可以使用以下命令进行安装:
sudo apt - get install build - essential git cmake
  • 下载并安装交叉编译工具链,如 arm - linux - gnu - gcc 。可以从官方网站下载对应的工具链压缩包,解压后配置环境变量 。假设下载的工具链解压到/opt/arm - linux - gnu - gcc目录下,可以在~/.bashrc文件中添加以下内容:
export PATH = /opt/arm - linux - gnu - gcc/bin:$PATH

然后执行source ~/.bashrc使环境变量生效 。

依赖库的移植也是重要环节 。例如,OpenCV 库在移动设备上的移植步骤如下:

  • 下载 OpenCV 源代码,可以从 OpenCV 官方 GitHub 仓库克隆代码:
git clone https://github.com/opencv/opencv.git
  • 进入克隆的目录,创建一个 build 文件夹并进入该文件夹:
cd opencv
mkdir build
cd build
  • 使用 CMake 进行配置,指定交叉编译工具链和目标平台 。假设交叉编译工具链的前缀为arm - linux - gnu -,可以使用以下命令进行配置:
cmake .. - DCMAKE_SYSTEM_NAME = Linux - DCMAKE_SYSTEM_PROCESSOR = arm - DCMAKE_C_COMPILER = arm - linux - gnu - gcc - DCMAKE_CXX_COMPILER = arm - linux - gnu - g++ - DOPENCV_GENERATE_PKGCONFIG = ON - DOPENCV_EXTRA_MODULES_PATH = /path/to/opencv_contrib/modules

这里/path/to/opencv_contrib/modules是 OpenCV 扩展模块的路径,如果不需要扩展模块,可以省略该参数 。

  • 配置完成后,使用 make 命令进行编译和安装:
make - j$(nproc)
sudo make install

在移植依赖库过程中,可能会遇到一些问题 。例如,依赖库之间的版本不兼容问题,需要仔细检查依赖库的版本要求,并进行相应的调整 。如果在编译 OpenCV 时出现某些模块不支持 ARM 架构的问题,可能需要查找是否有针对 ARM 架构的替代模块或解决方案 。

  1. 模型优化与适配

为了适应移动设备的硬件限制,需要对 YOLO11 模型进行优化 。模型量化是一种常用的优化技术,通过将模型的权重和激活值从高精度数据类型(如 32 位浮点数)转换为低精度数据类型(如 8 位整数),可以减少模型的内存占用和计算量 。在 Python 中,可以使用 ONNX - Runtime 的量化工具对 YOLO11 模型进行量化 。示例代码如下:

import onnxruntime.quantization as quantization

# 加载原始ONNX模型

model_path = "yolo11s.onnx"

quantized_model_path = "yolo11s_quantized.onnx"

# 量化模型

quantization.quantize_dynamic(model_path, quantized_model_path)

模型剪枝也是一种有效的优化方法,通过移除对模型性能影响较小的连接或神经元,可以减少模型的参数量和计算量 。在 PyTorch 中,可以使用 torchprune 库进行模型剪枝 。首先安装 torchprune 库:

pip install torchprune

然后使用以下代码进行模型剪枝:

import torch

from torchprune import Pruner

# 加载YOLO11模型

model = torch.load("yolo11s.pt")

# 创建剪枝器

pruner = Pruner(model)

# 定义剪枝策略,例如剪去50%的连接

pruner.prune_global(0.5)

# 保存剪枝后的模型

torch.save(model.state_dict(), "yolo11s_pruned.pt")

在移动设备上运行 YOLO11 的代码示例如下:

import cv2

import onnxruntime

import numpy as np

# 加载ONNX模型

ort_session = onnxruntime.InferenceSession("yolo11s_quantized.onnx")

# 读取图像并预处理

image = cv2.imread("test.jpg")

input_image = preprocess(image)

# 模型推理

ort_inputs = {ort_session.get_inputs()[0].name: input_image}

ort_outs = ort_session.run(None, ort_inputs)

# 后处理

# 这里省略后处理代码,可参考前面Python推理部分的后处理代码

实际效果展示:在三星 Galaxy S21 手机上运行量化和剪枝后的 YOLO11 模型,能够在保持一定检测精度的前提下,实现实时的目标检测 。对于常见的场景,如室内场景、户外街道场景等,模型能够快速准确地检测出行人、车辆、家具等物体,平均推理时间在 50ms 左右,满足了移动设备实时性的要求 。

六、总结与展望

(一)多种语言与架构部署 YOLO11 的总结

在本次探索中,我们深入研究了多种语言与架构下基于 ONNX 的 YOLO11 推理部署。在 Python 语言环境中,借助 PyTorch 将 YOLO11 模型导出为 ONNX 格式相对便捷,通过torch.onnx.export函数即可实现,并且利用onnxruntime库进行推理时,代码实现较为直观,易于理解和调试 。在图像预处理阶段,Python 的numpy和cv2库提供了丰富的函数和方法,能够方便地对图像进行缩放、归一化、通道转换等操作,使得输入图像符合模型的要求 。后处理过程中,通过实现非极大值抑制(NMS)等算法,能够有效地过滤掉重叠的检测框,保留准确的检测结果,并使用cv2库在图像上绘制检测框和类别标签,直观地展示检测结果 。Python 语言的简洁性和丰富的库资源,使得 YOLO11 的推理部署在开发效率上具有明显优势,适合快速原型开发和算法验证。

C++ 语言下的 YOLO11 推理部署则展现了不同的特点。在环境配置和依赖安装方面,相较于 Python 更为复杂,需要安装 ONNX - Runtime C++ API 和 OpenCV C++ 库等,并且要在开发环境(如 Visual Studio 或 CLion)中进行正确的配置 。在模型加载与推理流程中,C++ 使用 ONNX - Runtime C++ API 加载 ONNX 模型,代码实现更加底层,需要手动管理内存和处理数据类型转换 。图像预处理和后处理的代码也更加注重性能优化,例如在图像缩放时使用cv::resize函数,在归一化时使用convertTo函数,这些操作在 C++ 中能够更高效地利用硬件资源 。虽然 C++ 的开发难度相对较高,但其在性能和资源利用方面具有优势,适合对性能要求苛刻的实际应用场景,如嵌入式设备和服务器端的实时目标检测。

在其他架构下,基于 NVIDIA TensorRT 的部署通过将 ONNX 模型转换为 TensorRT 引擎,利用 TensorRT 强大的优化能力,实现了在 NVIDIA GPU 上的高性能推理 。通过图层融合、量化等优化策略,显著提高了模型的计算效率,降低了内存占用,使得 YOLO11 在处理高分辨率图像时能够实现更快的推理速度和更低的延迟 。在移动设备(如 ARM 架构)上的部署,需要搭建交叉编译工具链,移植依赖库,并对模型进行优化与适配,如模型量化和剪枝,以适应移动设备的硬件限制 。尽管部署过程面临诸多挑战,但通过合理的优化和调整,仍然能够在移动设备上实现实时的目标检测功能。

(二)未来发展趋势与挑战

展望未来,YOLO11 在目标检测领域有望取得更显著的进展。随着硬件技术的不断发展,如 GPU 性能的持续提升、新型硬件加速器的出现,YOLO11 将能够利用更强大的计算资源,进一步提高推理速度和检测精度 。在自动驾驶领域,随着车辆计算平台性能的增强,YOLO11 可以处理更复杂的路况和更多的目标,为自动驾驶的安全性和可靠性提供更有力的支持 。在人工智能技术方面,模型压缩和量化技术将不断演进,使得 YOLO11 模型能够在保持高精度的同时,进一步减小模型体积,降低计算量,从而更方便地部署在各种资源受限的设备上 。未来的模型可能会采用更先进的量化算法,实现更高程度的压缩,同时保持甚至提升检测性能。

然而,YOLO11 在推理部署过程中也面临着一些挑战。模型的进一步优化仍然是一个重要课题,尽管目前已经取得了一定的成果,但在复杂场景下,如何在不增加计算量的前提下提高检测的准确性和鲁棒性,仍然需要深入研究 。在一些光照条件复杂、目标遮挡严重的场景中,YOLO11 的检测性能可能会受到影响,需要通过改进算法和模型结构来提升其适应性 。跨平台兼容性的提升也是一个挑战,随着新的硬件平台和软件框架的不断涌现,如何确保 YOLO11 能够在不同的环境中高效运行,需要持续关注和解决 。不同的操作系统、硬件架构和深度学习框架之间的差异,可能会导致模型部署出现问题,需要开发通用的解决方案,以降低部署的难度。

鼓励读者继续探索和创新,在 YOLO11 的基础上,尝试结合新的技术和方法,如结合 Transformer 和卷积神经网络的优势,开发更强大的目标检测模型;利用迁移学习和元学习技术,减少模型的训练时间和数据需求 。希望读者能够将 YOLO11 应用到更多的实际场景中,如智能家居、智能农业、文物保护等领域,为推动人工智能技术的发展贡献自己的力量 。相信在广大研究者和开发者的共同努力下,YOLO11 以及目标检测技术将迎来更加辉煌的未来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空云风语

人工智能,深度学习,神经网络

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值