Skip to main content

使用 YOLOv8 训练和部署自定义分类模型

介绍​

在本指南中,我们将解释如何使用 YOLOv8 训练和部署自定义分类模型

概述​

我们将创建一个虚拟环境,在其中安装 YOLOv8,从 roboflow 下载分类模型,训练并部署它。

图像分类​

图像分类是计算机视觉最简单的任务,涉及将图像分类到预定义类别之一。 我们得到的输出是单个类别标签和置信度分数。

当我们不需要知道对象在图像中的位置,只需要知道图像属于哪个类别时,图像分类非常有用。

材料要求​

硬件设置​

对于本教程,我们需要一个 Nvidia Jetson Orin NX 16GB。

软件设置​

  • 在 reComputer 中安装 JetPack 6.0
  • 一个 Roboflow 账户来下载数据集

准备 reComputer​

Seeed Studio 的 reComputer J4012 是一个 Jetson Orin NX 16GB。 它是一台强大的机器,但 Tegra Linux 带有很多东西,并且启动到图形模式。让我们改变这一点。

note

我将使用 VScode 和启用 X 转发的 SSH 终端远程运行示例和编程。 X 转发是 SSH 的一个选项,可以在我们连接的这一端运行一些图形应用程序,而不是在远程计算机上。

如果您要使用显示器、键盘和鼠标连接到您的 reComputer,请跳过下一步。

更改启动模式​

这很好,但我们不需要图形界面,在空闲模式下,它消耗大约 1.5GB 的内存。

我们将让它启动到命令行界面。

sudo systemctl set-default multi-user

目前,我们的 reComputer 在启动时会启动到 CLI。 如果您愿意,可以现在重启,或者我们可以用一个命令进入 CLI。

sudo systemctl isolate multi-user

我们现在已经从使用 1.5GB 内存降低到 700MB。在使用机器学习时,每一个内存字节都很重要。

更改功耗模式​

默认情况下,我们的 reComputer 应该运行在级别 2 - 15W。 在训练甚至推理机器学习模型时,如果我们能以全功率运行,效果应该会更好。

让我们学习如何更改它。

在文件 /etc/nvpmodel.conf 中,我们有可用的功耗模式。

< POWER_MODEL ID=0 NAME=MAXN >
< POWER_MODEL ID=1 NAME=10W >
< POWER_MODEL ID=2 NAME=15W >
< POWER_MODEL ID=3 NAME=25W >

然后我们可以使用 sudo nvpmodel -m <power model number> 来更改电源模式。并且,根据这个论坛帖子,这些设置在重启后仍会保持。 要查看我们当前处于什么电源级别,

sudo nvpmodel -q

让我们为模型训练选择最大功率模式

sudo nvpmodel -m 0

重启后,我们可以确认正在以全功率运行

训练模型​

对于模型训练,我们将使用 YOLOv8。以下是安装它并支持 CUDA 所需的步骤。 我们还需要一个 roboflow 账户。

模型​

我将创建一个用于分类鸟类的模型。 这是我将放置在花园中的智能鸟类喂食器项目的一部分,我想知道在那里觅食的鸟类是什么。

因为这是一个分类任务,我们不需要知道鸟类在照片中的位置。

您可以使用您选择的其他数据集,只要它是分类数据集或模型

我已经获得了 12 类我知道生活在我所在地区并且在我附近很常见的鸟类,并在 Roboflow 中创建了一个分类数据集。

我将尝试识别的鸟类类别有:

  • 家燕
  • 欧金冠戴菊
  • 欧夜莺
  • 苍头燕雀
  • 欧岩燕
  • 欧洲金翅雀
  • 欧洲绿金翅雀
  • 欧丝雀
  • 家麻雀
  • 西班牙麻雀
  • 白腰雨燕
  • 白鹡鸰

选择您的数据集并从 roboflow 下载。 选择数据集后,选择"Download Dataset"。- 您需要一个账户。

接下来,在格式中选择 Folder Structure,然后选择 show download code。

接下来,如果您要使用 Jupyter Notebook,请选择 Jupyter,如果您计划在终端中执行此操作,请选择 Terminal。

我选择了 Jupyter,在 Jupyter notebook 中使用。复制代码。

创建环境​

我们将创建一个虚拟环境,安装 PyTorch 并安装 YOLOv8。 根据 YOLOv8 文档提示,最好先安装 PyTorch,然后再安装 ultralytics。

另外,我正在安装 jupyterlab 包以与 VSCode 一起使用。本教程附带了 notebook。

让我们先安装一些依赖项。

注意: 因为我们要使用 YOLOv8,我们需要执行一些通常不需要的步骤。

只需按照 NVIDIA 深度学习文档 安装 Torch 就足以拥有支持 CUDA 的 Torch。

如果我们使用 PIP 正常安装 PyTorch,它将不支持 CUDA。

依赖项​

sudo apt install libopenblas-dev cuda-toolkit libcudnn8 tensorrt python3-libnvinfer nvidia-l4t-dla-compiler

创建 Python 虚拟环境

python -m venv birdClassificationModel

如果您遇到错误,是因为没有安装 python3-venv 包。让我们安装它并重复上述命令。

sudo apt install python3-venv

Activate the virtual environment

source birdClassificationModel/bin/activate

您可以确认它是活动的,因为它的名称放在您的提示之前。

YOLOv8​

在此之前,为了遵循文档提示,让我们首先安装 PyTorch。

我正在使用 JetPack 6.0,它附带 NVIDIA Jetson Linux 36.3 和 CUDA 12.2。 让我们首先升级 PIP

pip install -U pip

安装 Torch 以便能够与 YOLOv8 一起使用,我们需要按照 NVIDIA 论坛中的步骤进行操作。

这将在虚拟环境激活的状态下完成,以便将其安装在虚拟环境中。 从 NVIDIA 下载 Torch 版本 2.3

wget https://nvidia.box.com/shared/static/mp164asf3sceb570wvjsrezk1p4ftj8t.whl -O torch-2.3.0-cp310-cp310-linux_aarch64.whl
sudo apt-get install python3-pip libopenblas-base libopenmpi-dev libomp-dev
pip3 install 'Cython<3'
pip install numpy torch-2.3.0-cp310-cp310-linux_aarch64.whl

接下来,让我们编译 torchvision。如果我们从 wheels 安装它,它将不会有 CUDA 支持。

分支版本是针对已安装的 Torch 版本。您可以在论坛页面中查看更多详细信息。

记住,您需要激活虚拟环境,这样所有内容都会安装在其中。

sudo apt-get install libjpeg-dev zlib1g-dev libpython3-dev libopenblas-dev libavcodec-dev libavformat-dev libswscale-dev
git clone --branch v0.18.0 https://github.com/pytorch/vision torchvision
cd torchvision/
export BUILD_VERSION=0.18.0
python setup.py install

一段时间后,它将被编译和安装。

安装完成后,让我们看看 Cuda 是否可用。

从命令行

python -c "import torch;print (torch.cuda.is_available())"

这应该返回 True

安装 YOLOv8​

现在我们已经安装了支持 CUDA 的 PyTorch,当我们安装 YOLOv8 时,它将使用已安装的版本,而不是尝试安装一个新的包(尽管是相同版本)但不支持 CUDA。

pip install ultralytics

让我们安装 roboflow 和 jupyterlab

pip install roboflow jupyterlab

现在,让我们下载数据集。 如果你正在使用笔记本,只需替换那里的代码。

rf = Roboflow(api_key="<your_api_key>")
project = rf.workspace("bruno-santos-omqsq").project("bird-classification-19z7c")
version = project.version(1)
dataset = version.download("folder")

下载模型后,我们现在有了一组三个目录(test、train、valid),每个目录都包含来自每个类别的一定数量的图像。每个类别的每张图像都在其自己的目录中。 因为这是用于图像分类的,我们不需要标记图像。 YOLOv8 将知道类别,不仅从我们稍后创建的配置文件中,还从目录中。

训练​

通常数据集包含图像和标签(或注释)以及对象坐标。由于这是一个分类任务,我们不需要任何这些。只需要图像位于以类别名称命名的每个目录中。

准备配置文件​

我们仍然需要一个配置文件让 YOLOv8 识别类别。 这个文件应该放在数据集目录内,扩展名为 .yaml。名称并不重要。

cd <dataset_directory>
vi birdClassificationModel.yaml

在文件中插入以下文本

train: train/
valid: valid/
test: test/

# number of classes
nc: 12

# class names

names: ["Barn Swallow","Common Firecrest","Common Nightingale","Eurasian Chaffinch","Eurasian Crag Martin","European Goldfinch","European Greenfinch","European Serin","House Sparrow","Spanish Sparrow","Western House Martin","white Wagtail"]

对于分类,我们将使用 Ultralytics 已经提供的预训练模型 之一。

这些模型已经在 ImageNet 上进行了训练,并针对分类任务进行了微调。 我们将使用它并在我们的数据上进行训练。

这就是所谓的迁移学习。

我将使用模型 YOLOv8l-cls。可能其他模型也能很好地工作,但因为我们不需要实时性,这是速度和准确性之间的权衡。

然后让我们使用 YOLOv8 CLI 接口来训练模型

yolo task=classify mode=train model=yolov8l-cls.pt data=Bird-Classification-1 epochs=100
  • task=classify : 我们将对图像进行分类
  • mode=train : 我们正在训练模型
  • model=yolov8l-cls.pt : 我们使用预训练的分类模型
  • data=Bird-Classification-1 : 我们数据集所在的目录
  • epochs=100 : 我们训练模型的时长。

现在它正在运行,这里是使用 jtop (tegra-stats) 的一些统计信息

几个小时后,训练完成了。

现在,让我们看看模型的表现如何。让我们测试一下。

yolo task=classify mode=predict model='./runs/classify/train6/weights/best.pt' source=Bird-Classification-1/test/**/*.jpg

这将使 yolo 进入测试目录并尝试预测每个

结果都是正确的。让我们尝试两张它从未见过的图像。

yolo task=classify mode=predict model='./runs/classify/train6/weights/best.pt' source=house_sparrow.jpg
yolo task=classify mode=predict model='./runs/classify/train6/weights/best.pt' source=white_wagtail.jpg

我要说这些结果非常棒

导出模型​

我们可以直接使用这个模型进行推理,只需要打开并使用它即可。 为了获得更快的推理时间,我们可以将其导出为 TensorRT,因为我们使用的是 NVIDIA Jetson Orin NX,或者甚至可以导出为 ONNX 格式等。

并不是说我们在这个项目中需要更快的推理时间——我不会在实时视频中使用它——但能够利用我们所在平台的优势是很好的。

不幸的是,由于虚拟环境的原因,我无法将其导出为 TensorRT。出于某种原因,我无法在 Python 中导入 tensorrt,但在虚拟环境之外,我使用 tensorrt 库没有任何问题。

ONNX​

我们可以像这样将模型导出为 ONNX 格式

yolo export model='./runs/classify/train6/weights/best.pt' format=onnx imgsz=640

我们得到了一个可以用来运行推理的 best.onnx 文件。

要使用 ONNX 运行推理,我们需要安装 onnxruntime_gpu wheel。

要在 JetPack 6.0 上安装 onnxruntime-gpu,我们需要从 Jetson Zoo 下载它。

我们将下载 onnxruntime_gpu 1.18.0

为我们的 Python 版本(Python-3.10)下载 pip wheel

wget https://nvidia.box.com/shared/static/48dtuob7meiw6ebgfsfqakc9vse62sg4.whl -O onnxruntime_gpu-1.18.0-cp310-cp310-linux_aarch64.whl

and then, install it

pip install onnxruntime_gpu-1.18.0-cp310-cp310-linux_aarch64.whl

推理​

照片​

我使用以下代码运行 best.pt 模型的推理并查看结果

# running inference
from ultralytics import YOLO
# load the model
bird_model = YOLO("./runs/classify/train6/weights/best.pt")
#run inference
results = bird_model("house_sparrow.jpg")[0]
# get class names
class_names = results.names
# get top class with more probability
top1 = results.probs.top1
# print the class name with the highest probability
print (f" The detected bird is: {class_names[top1]}")

上面的代码所做的是加载模型,在图像上运行推理并将结果保存到 results 变量中。

因为 results 是 ultralytics.engine.results.Results 对象,类型为包含一个项目的列表,该项目是 Results 的一个实例。results 变量中的 [0] 用于保存推理结果,这将允许我们获取我们想要的结果。

results = bird_model("house_sparrow.jpg")[0]

接下来,我们使用结果来获取类名。不是说我们不知道它们,而是这样做可以让这段代码在其他模型中也能正常工作。

class_names = results.names

其中一个结果是一个 top1 变量,它保存着概率最高的 TOP 1 类别。这个 TOP1 是由 probs 列表给出的。

top1 = results.probs.top1

接下来,我们打印出概率最高的类别,这应该是鸟类的种类。

print (f" The detected bird is: {class_names[top1]}")
The detected bird is: House Sparrow

摄像头​

现在,让我们使用摄像头来运行推理。

Jetson 可以使用 USB 摄像头或 RPI 摄像头。我将连接一个 USB 摄像头。

以下代码将检查是否可以显示摄像头画面。

#Lets test if we can use a USB camera
import cv2
cap = cv2.VideoCapture(0)
while True:
ret, img = cap.read()
cv2.imshow('Camera', img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows

这是我在桌面计算机上的操作。只需使用 ssh -X username@jetson_ip,X11 窗口就会转发到你的桌面。这之所以有效是因为我也在使用 Linux。我认为 WSL 也可以工作。

现在,让我们尝试在视频流上运行推理,显示概率较高的类别

代码如下

# again, save this code in a file a run it from the Jetson

import cv2
from ultralytics import YOLO
import time
#define confidence level
#only equal or above this level we say it's a class of bird
confidence = 0.95
# time when processed last frame
prev_frame = 0
# time processed current frame
cur_time = 0
# load the model
bird_model = YOLO("./runs/classify/train6/weights/best.pt")
# cv2 font
font = cv2.FONT_HERSHEY_SIMPLEX
# open camera
cap = cv2.VideoCapture(0)
while True:
ret, img = cap.read()
# to display fps
cur_frame = time.time()
fps = 1 / (cur_frame - prev_frame)
prev_frame = cur_frame
fps = int(fps)
fps = str(fps)
cv2.putText (img, fps, (550,50), font, 1, (124,10,120), 2, cv2.LINE_AA)

# inference current frame
results = bird_model(img, verbose=False)[0]
# get class names
class_names = results.names
# get top class with more probability
top1 = results.probs.top1
top1conf = results.probs.top1conf.tolist()
# we will only show the class name if the confidence is higher than defined level
# print the class name with the highest probability
if (top1conf >= confidence):
bird_class = class_names[top1]
print (f" The detected bird is: {class_names[top1]}")
# color is in BGR
confid = round(top1conf,2)
img = cv2.putText(img, bird_class, (50,50), font, 0.9, (0, 0, 255), 2, cv2.LINE_AA)
img = cv2.putText(img, "Conf: " + str(confid), (50,80), font, 0.6, (255, 0, 255), 1, cv2.LINE_AA)
cv2.imshow('Camera', img)
else:
img = cv2.imshow('Camera', img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows

这里有一个视频展示了在视频流上进行推理的过程

✨ 贡献者项目​

  • 此项目由 Seeed Studio 贡献者项目支持。
  • 感谢 Bruno 的努力,您的工作将被展示。

技术支持与产品讨论​

感谢您选择我们的产品!我们在这里为您提供不同的支持,以确保您使用我们产品的体验尽可能顺畅。我们提供多种沟通渠道,以满足不同的偏好和需求。

Loading Comments...