自定义目标检测:探索YOLO流程并在自定义数据上进行训练

深度学习在过去的十年里取得了巨大的进展,尽管早期模型难以理解和应用,但现代框架和工具使得每个具备一些代码理解能力的人都能训练自己的神经网络来处理计算机视觉任务。

在这篇文章中,我将详细演示如何加载和增强数据以及边界框,训练目标检测算法,并最终查看我们在测试图像中能够多精确地检测对象。虽然随着时间推移可用的工具包变得更易于使用,但仍然存在一些可能遇到的陷阱。

计算机视觉(CV)简介

计算机视觉既是一个非常流行的领域,更是一个广泛的研究和应用领域。深度学习在过去的十年中取得的进步,特别是极大地加速了我们对深度学习及其广泛潜力的理解。

为什么我们现在看到这些进步?正如Keras库的创始人Francois Chollet所描述的,我们目睹了CPU计算能力在1990年至2010年间增长了大约5000倍。对GPU的投资甚至推动了研究的进一步发展。总的来说,我们看到与CV相关的三个基本任务:

  1. 图像分类 这可能是我们能想到的最直观的任务。给定一张图像,我们希望算法要么为图像分配单个类别标签(例如“猫”),要么我们更倾向于多类别,如在一张图像中同时有“猫”、“狗”和“人”。

  2. 图像分割 这个任务在我们的手机上可能最为熟知。每当我们在手机上选择“人像”模式时,我们可以看到手机将主要物体从背景中分割出来。如果我们在公司电话中使用虚拟背景,也是在进行背景分割任务。

  3. 目标检测 这就是我们们所关心的!我们想在图像中找到特定的对象并在它们周围绘制矩形。每个绘制的矩形都与一个类别相关联。

    自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第1张图片

计算机视觉的三个学科:分类、分割和(目标)检测

更具体地说,下面的图像显示了目标检测和分割(紫色区域)任务。如果我们要对图像进行分类,我们显然会瞄准“猫”或甚至在多标签场景中瞄准“猫”和“太阳镜”。为了让我们对这些模型的工作效果有一点了解,下面的预测是通过一个更大的预训练模型进行的。

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第2张图片自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第3张图片

检测任务(边界框)以及右侧检测和分割结合的图像 图片由Unsplash上的Raoul Droog提供。

这篇文章的目标是什么?我们试图做什么?

为了演示如何处理自定义数据的检测,我们将探讨一系列步骤来处理数据、训练模型,并查看预测的准确性有多高。为了实现这一目标,我们将使用包含不同车牌的数据集,以便我们的最终模型能够从图像中提取车牌,同时将图像的其余部分丢弃为背景。

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第4张图片通过我们的最终模型进行的车牌检测

为了使这个项目可复用,我将在GitHub仓库中提供说明、数据以及代码。为了提供一些处理的流程,我们将依次研究以下主题:

  1. 大图像的问题以及如何处理它们

  2. 如何充分利用我们的数据-适当的数据准备

  3. 预训练模型的威力

  4. 关于YOLO算法的一些注释

  5. 训练自定义模型

  6. 结论

1. 大图像的问题以及如何处理它们

CV的技术挑战是多样的,尤其是在处理大图像或者更棘手的,大图像数据集时。使用图像数据,通常是一个HxWxC(高度、宽度、通道)形状的3D张量,可能很容易变成资源密集型的任务,因为一个单独的图像通常表示为如下的张量:

image = cv2.imread('cars.jpg')
image.shape
>_ (4100, 3300, 3)

如果将这视为一个单独的向量,我们将迅速处理一个解开形状为40,590,000的图像向量:

torch.prod( image )
>_ 40590000

此时,我们已经对处理这样的图像尺寸的可行性或合理性产生了怀疑。因此,让我们考虑处理潜在大数据集问题的可能方式 什么解决方案可能有助于将我们的数据集缩减为更易管理的大小?

  1. 调整图像大小:有各种简单的方法来减小图像的大小。像CV2这样的库或者PyTorch和TensorFlow这样的深度学习框架允许我们将图像转换为更易处理的格式。

  2. 与调整大小密切相关的是,我们还可以使用压缩来减小图像大小。然而,请记住,像JPEG这样的格式是所谓的“有损”压缩,这可能会导致在压缩图像中丢失信息。

  3. 移除颜色通道:正如我们在上面的快速计算中看到的,不仅高度和宽度在计算图像大小时起着重要作用,颜色通道也对大小有很大影响。一张黑白图像只有一个颜色通道,因此黑白图像的大小只有原来的三分之一。上面的图像将变成4100 x 3300 x 1。

  4. 中心裁剪图像:通常,我们只对图像的某个区域感兴趣。如果是这样,我们可以在图像预处理的最开始进行简单的裁剪。

  5. 在这个上下文中不太常见,但非常有趣且可行的选择是降维。这允许我们通过另一种“有损”方法对图像进行下采样。

2. 适当的数据准备

好吧,到目前为止,我们对CV有相当多的了解,我们也知道稍后将使用什么模型,现在是时候看一下我们将要使用的数据。正如我提到的,我们将下载一个在Kaggle上免费提供的带标签的数据集。为了满足我们的数据需求,我“分叉”了Larxel关于车牌的数据集,并创建了一个适用于训练YOLO模型的衍生数据集。

数据增强

数据增强是利用手头现有的图像,并以一种方式对它们进行转换,以增加我们的训练图像数量的概念 同时保持过拟合的风险较低。例如,如果我们将手头的图像旋转90°,然后在图像的某个位置进行随机裁剪,最终将其缩放到所需的目标图像大小,得到的图像将对我们的算法来说显得相当“新”。我们在训练数据上执行像上述那样的随机变换 同时确保这些变换后的图像不会泄漏到验证或测试数据集中(这是至关重要的⚠)。

transform = A.Compose([
    # List the augmentations you want to apply here
    A.HorizontalFlip(p=0.5),
    A.RandomRotate90(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
    A.ShiftScaleRotate(shift_limit=0.2, scale_limit=0.2, rotate_limit=45, p=0.5),
    A.Resize(640, 640),
    ToTensorV2(),],
    bbox_params=A.BboxParams(format='yolo', label_fields=['category_ids'])) # THIS IS ESSENTIAL

正如我之前提到的,不同模型所需的格式可能会非常不同。在非常强大的Albumentations库下,我们可以直接使用BboxParams来设置format='YOLO'。如果我们有一个与COCO边界框格式相似的数据集,并且已经在考虑如何将其输入到后续的YOLO模型中,我们可能需要将COCO转换为YOLO坐标,可以按照以下方式完成:

x_center = (2*x1 + w)/(2*image_w)
y_center = (2*y1 + h)/(2*image_h)
w, h = w/image_w, h/image_h
return [x_center , y_center, w, h]

COCO格式的特征是:[x_min, y_min, width, height],可能是[98, 345, 322, 117]。相比之下,YOLO格式的特征是[x, y, width, height],因此看起来像[.2, .35, .12, .07]。如我们所见,坐标是标准化的,并显示相对表示。我强烈建议将增强和训练循环分开。因此,将上述的增强逻辑集成起来将允许创建一个更丰富的数据集:

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第5张图片

对图像数据进行训练-测试分割。确保没有增强数据流入“train”以外的数据集。

如所示,确保增强不会流入其他数据子集是非常重要的。

3. 预训练模型的威力

什么是预训练模型?预训练模型简单来说意味着底层模型,即我们希望用于手头任务的模型,已经在大型数据集上进行了训练。这种预训练导致了模型权重的调整 或者换句话说  它已经通过查看给定的训练数据集学到了很多东西。

更具体地说,想象一下有两个预训练模型:一个已经在包含60种不同动物物种的100万张图像上进行了训练,另一个已经在不同花卉的75万张图像上进行了训练。如果我们的任务是对玩具动物的图像进行分类,我们可能明智地选择已经知道如何处理动物图像而不是花卉图像的预训练模型。在这个基础上,我们可以利用这种“预知”来微调模型,使其能够理解如何对类似于已知动物的玩具进行分类。

随着时间的推移,预训练模型变得越来越受欢迎,因为它们允许研究人员和行业从他人学到的知识中受益,而无需从零开始。

YOLO模型是在像COCO(上下文中的常见对象)数据集之类的数据集上进行预训练的,并且它们有着非常不同的模型大小。我们可以根据1)任务的复杂性,2)用于预测设备的计算能力和3)用于训练的设备的能力,在nano到large之间选择模型。硬件越好,计算机越能够处理更大的训练模型。通常情况下,硬件越好,我们训练的模型就可以越大。

请注意,“更好的硬件”通常指的是GPU的能力。下面的示例使用了纯预训练模型(到目前为止还没有进行FINE-TUNING!)在相同的图像上。图像的顺序是指使用以下模型:

  1. 纯图像

  2. YOLOv8 Nano模型生成的预测(汽车)

  3. YOLOv8 Medium模型生成的预测(汽车)

  4. YOLOv8 X-Large模型生成的预测(汽车)

我们可以清楚地看到更大的模型给我们带来了多大的改进。然而,这又是以稍微较慢的推理(预测)速度为代价,而且训练(资源密集型)更为困难(这正好描述了我们将在下面看到的微调的情况)。

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第6张图片自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第7张图片

左:原始图像 | 右:Nano模型的预测

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第8张图片自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第9张图片

左:Medium模型的预测 | 右:XL模型的预测

我们已经走了很远,已经研究了计算机视觉的基础知识以及预训练模型的能力 现在是时候将这些模型塑造成我们的需要:检测并非必然属于COCO类别的自定义对象(例如“Cars”、“Cat”、“Person”),而更像是“Rubber Duck”、“Green Tea Leaf”或“License Plate”这样的东西。如果我们对YOLO的工作原理更感兴趣,请跟随下一节,否则,直接跳到关于自定义数据训练的部分。

4. 关于YOLO基本思想的一些注释

YOLO在许多方面都具有革命性的意义。我认为最令人印象深刻的一点是,YOLO设法摆脱了非常复杂的基于区域的卷积神经网络(R-CNN),这些网络通常很难优化,甚至更糟糕的是,训练非常缓慢。正如作者所美丽地描述的那样,这种模型设置将检测问题框架为单一的回归问题,直接从图像像素开始。

YOLO与以前的检测方法的主要区别之一是,该模型不使用滑动窗口或基于区域建议的技术,而方便地一次性使用整个图像(利用CNN层的技术)进行预测。构建YOLO模型的步骤可以概述如下:

  1. 每个图像被划分为一个S x S的网格,每个单元格负责在该单元格中检测一个对象(实际上只有一个)。

  2. 每个单元格最终会预测一个概率分数[0,1],表示模型对于正在查看的提议对象有多自信,这个置信度预测(p)。分配的置信度与模型识别的最佳边界框有关 这个“最佳”框是通过具有更高交并比(IOU)的一个框来定义的。

  3. 在每个单元格中,我们可能会看到B个边界框(B是一个超参数),每个边界框都有x,y坐标,高度,宽度以及刚才描述的置信度。在描述YOLO的第一篇论文中,我们有两个边界框,旨在覆盖高且宽的对象。

刚才描述的这个结构,使我们面临一个回归问题,旨在预测一个张量,如下所示:

471c553870552cb1db6b9a1c20011e21.jpeg

对应于:Grid * [Boxes * (p, x, y, h, w) + c ]

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第10张图片

1) 网格,2/3) x、y、h、w+C的预测 / 有条件的类概率 和 4) 最终的预测 与YOLO论文(arXiv:1506.02640)中共享的图像相同。

正如任何涉及深度学习的文献都是强制的一样,我将卷积神经网络(CNN)的建议架构从原始论文中复制/粘贴如下。

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第11张图片

模型架构

现在,人们可能会想知道模型如何处理每个网格中的重叠边界框,因为在进行预测时,这种情况可能经常发生。在训练时,我们只想看到每个网格单元格的一个预测。为了实现这一点,我们利用具有最高置信度分数的预测,并将其选择为我们要继续使用的预测。随着时间的推移,这为边界框预测器之间提供了连接,意味着每个预测器在预测对象的典型属性(如大小和形状)方面变得越来越好。

正如我们可能听说的那样,神经网络经常面临过拟合的风险,由于它们的参数规模(尤其是深度网络)以及对大量数据的隐式需求。YOLO使用了辍学以及图像增强来克服这个过拟合问题。

虽然YOLO的损失函数一开始看起来令人生畏,但实际上它是一系列类似于回归的最小二乘和的很好的串联(至少在YOLO算法的第一篇论文中是这样。在后来的论文中,损失包括熵损失)。由于多个单元格可能预测相同的对象,导致几个重叠的框,模型使用一种称为非极大值抑制的技术,该技术基本上将几个重叠的区域压缩为具有最高置信度分数的区域。

5. 训练自定义模型

到达我们的最终目标,即微调,我们将使用较大的YOLO模型来训练我们从Kaggle获得的自定义数据集,经过增强,并现在用于提供给我们的模型。

model = YOLO('yolov8l.pt')
results = model.train(
data='plates.yaml',
imgsz=640,
epochs=10,
batch=8,
optimizer='AdamW',
lr0 = 1e-2,
device
= [0,1,2,3]
)

为了理解训练方法,我将简要阐述参数。从数据开始,我们需要传递一个YAML文件,指示自定义数据的路径和类别。以下是YAML可能看起来的示例:

train: train/images
val: valid/images
test: test/images
nc: 1
names: ['LicensePlate']

虽然epochs、batch、optimizer和lr0(学习率)是深度学习中的常见超参数,但device参数对于定义要使用哪些GPU非常有用。我们微调的模型越大,这个参数就越重要。在训练较大的模型时,我们希望并行化正在进行的工作。因此,我们选择设备来分配工作。

YOLO的Ultralytics实现(版本8)非常慷慨,因为所有训练运行都会自动输出各种统计数据供人们查看。就个人而言,我喜欢F1-Confidence曲线,因为它很好地描述了我们模型在给定置信度下的F1分数。

这有什么用呢?在非常特定的检测任务中,我们可能认为置信度低(例如仅为0.35)就足够“有信心”,因为在我们的情况下,这已经几乎达到了峰值准确度。

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第12张图片

车牌数据集的F1曲线

除了F1曲线之外,查看模型在验证数据上的预测(左图)是一个好的实践,以了解预测在真实图像中的表现如何。此外,混淆矩阵是观察我们模型的预测能力的一种可靠方式。由于F1分数建立在混淆矩阵的结果基础上,所以看到我们对于预测“plate” 真实“plate”情况(0.74)的好结果并不令人惊讶。在下面的图像矩阵的右上方,我们可以看到白色汽车上的一个遗漏的预测。

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第13张图片自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第14张图片

其他指标

在成功微调模型之后,我们可以使用它对新图像进行预测。正如前面提到的,我们不需要调整图像的大小,因为这是由模型执行的 当使用Ultralytics的实现时。

results = model.predict(frame)
boxes = None
for result in results:
boxes = result.boxes.xyxy # 请注意我们想要获取的表示方式!

边界框是我们模型的最终产品,自然地,我们希望可视化结果以查看预测的准确性。为此,我们提取了适当格式的坐标(如上所示,xyxy)以在图像上绘制矩形。

# assuming we:
# imported all libraries
# read in the image (img) properly
for box in bboxes:
# for regular boxes
x1 = box[0].cpu().numpy().astype(int)
y1 = box[1].cpu().numpy().astype(int)
x2 = box[2].cpu().numpy().astype(int)
y2 = box[3].cpu().numpy().astype(int)            
# CV2 Convention is BGR NOT RGB 
color = (0,255,0)
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y1+10)), color, -1) # 只是一个

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第15张图片

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第16张图片

我们“精调”后的大模型做出的预测

6. 结论

目标检测在各种行业和项目中起着至关重要的作用。随着You Only Look Once的出现,训练和推断都变得更快,并且在最近的发展中,准确性也显著提高。YOLO在目标检测任务中显著提高了速度和效率。通过各种开源实现,广泛而迅速地使人们能够广泛使用这项技术,同时保持了构建和维护检测模型的简单性。

·  END  ·

HAPPY LIFE

自定义目标检测:探索YOLO流程并在自定义数据上进行训练_第17张图片

本文仅供学习交流使用,如有侵权请联系作者删除

你可能感兴趣的:(目标检测,YOLO,人工智能,计算机视觉)