PyTorch深度学习实战(22)——从零开始实现YOLO目标检测

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测

    • 0. 前言
    • 1. YOLO 架构
      • 1.1 R-CNN 目标检测模型的局限性
      • 1.2 YOLO 目标检测模型原理
    • 2. 实现 YOLO 目标检测
      • 2.1 编译 DarkNet
      • 2.2 设置数据集格式
      • 2.3 配置网络架构
      • 2.4 模型训练和测试
    • 小结
    • 系列链接

0. 前言

YOLO (You Only Look Once) 是一种实时目标检测算法,它以其高效性和准确性而闻名。相比于传统的目标检测方法,YOLO 的主要特点是在单个前向传递中同时完成目标检测和分类,因此称为 You Only Look Once。由于整个检测过程只需要一次前向传递,因此非常高效,可以实现实时目标检测。此外,YOLO 通过全局感受野捕捉了整个图像中的上下文信息,对小尺寸物体的检测效果较好。在本节中,将介绍 YOLO 的工作原理,然后在自定义数据集上训练 YOLO 目标检测模型。

1. YOLO 架构

YOLO 目标检测模型与 Faster R-CNN 模型相比,YOLO 无需使用区域提议算法 (Region Proposal Network, RPN),其在单个神经网络中预测边界框和类别概率,因此推理效率更高。

1.1 R-CNN 目标检测模型的局限性

首先,我们了解基于 R-CNN 的目标检测算法的局限性。在 Faster R-CNN 中,使用锚框在图像上滑动并识别可能包含对象的区域,然后执行边界框校正。然而,在全连接层中,仅将检测到的区域的 RoI 池化输出作为输入传递,对于不完全包含对象的区域(即对象超出区域提议的边界框),网络只能猜测对象的边界框,因为网络仅仅看到了区域提议,并没有看到完整的图像。
YOLO 能够克服这一问题,它能够在预测目标对象边界框时看到整个图像。此外,由于 Faster R-CNN 有两个网络 (RPN 和预测对象类别及其边界框的分类-回归网络),因此,Faster R-CNN 的推理速度依然很慢。

1.2 YOLO 目标检测模型原理

接下来,我们将介绍 YOLO 如何克服 Faster R-CNN 的局限性,可以在一次前向传递过程中查看整个图像并进行预测。

(1) 为给定图像创建标签信息训练模型。

考虑以下带有真实边界框(红色边界框)的带标签图像:

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第1张图片

将图像划分为 N x N 个网格单元,假设 N=3

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第2张图片
识别包含至少一个真实边界框中心的网格单元,在示例中,为 3 x 3 网格图像的单元格 a1b3。真实边界框的中心点所在的单元负责预测对象的边界框,接下来,创建与每个单元格对应的标签信息。

每个单元格对应的输出标签信息格式如下:

y= pc
bx
by
bw
bh
c1
c2
c3

其中,pc (objectness score) 是单元格包含对象的概率。接下来,我们介绍如何计算 bxbybwbh
首先,我们将网格单元(考虑 a1 网格单元)视为独立图像,并将其尺寸归一化为 01 之间,如下所示:

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第3张图片
bxby 是真实边界框的中点相对于图像(网格单元)的位置,在示例中,bx = 0.8,因为真实边界框的中点距离原点(网格单元左上角) 0.8 个单位,同理,by = 0.25,而 bw 是边界框的宽度与网格单元格宽度的比值;bh 是边界框高度与网格单元格高度的比值:

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第4张图片
接下来,预测与网格单元对应的类别。如果有三个类别 (c1 – 气球、c2 – 玩具、c3 – 花篮),c1,c2,c3分别表示该单元格包含这三个类别的概率。这里我们不需要背景类别,因为 pc 对应于网格单元格是否包含对象。
我们已经了解如何表示每个单元格的输出,接下来,介绍如何构建以上 3 x 3 网格单元格的输出。
首先,考虑网格单元 a3 的输出:

y= 0
?
?
?
?
?
?
?

单元格 a3 的输出如上图所示。由于网格单元不包含对象,因此第一个输出 pc0,其余值则无关紧要,因为该单元不包含任何目标对象的真实边界框中心。
接下来,考虑对应于网格单元 a1 的输出:

y= 1
0.75
0.25
0.5
0.4
1
0
0

网格单元包含具有 bxbybwbh 值的对象,类别是气球,因此 c11,而 c2c30
对于每个单元格,均有 8 个输出。因此,对于 3 x 3 的单元格网格,将得到形状为 3 x 3 x 8 的输出。

(2) 定义模型,其中输入为图像,输出与上一步中定义的真实值相符,输出尺寸为 3 x 3 x 8

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第5张图片
(3) 通过使用锚框定义真实标签值。
在以上示例中,我们一直假设在一个网格单元格中只有一个对象。而在现实中,可能存在同一个网格单元内有多个对象的情况。如果仍按照一个对象构建训练数据,将导致创建的真实标签值并不正确。如下图所示,花篮和气球的真实边界框的中点落在同一个单元格(单元格 a1 中):

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第6张图片
为了避免这种情况,一种方法是使用具有更多行和列的网格,例如,19 x 19 网格。但是,增加网格单元数量仍然可能无济于事。为了解决这一问题,需要再次使用锚框,假设我们有两个锚框,一个高大于宽,另一个宽大于高。通常,锚框的中心设定为网格单元格的中心。当存在两个锚框时,每个单元格的输出表示为两个锚框预期输出的连接:

y= pc
bx
by
bw
bh
c1
c2
c3
pc
bx
by
bw
bh
c1
c2
c3

此时,因为使用了两个锚框,模型输出尺寸为 3 x 3 x 16YOLO 模型预测输出的形状为 N x N x (num_classes + 1) x (num_anchor_boxes ),其中 N x N 是网格单元数,num_classes 是数据集中的类别数,num_anchor_boxes 是每个单元格的锚框数。

(3) 定义损失函数训练模型。

在计算与模型相关的损失时,需要确保在对象置信度得分小于某个阈值(表示单元格不包含目标对象)时不计算回归损失和分类损失。如果单元格包含一个对象,我们需要确保目标对象分类尽可能准确。
如果单元格包含对象,则边界框偏移量应尽可能接近真实边界框。但是,由于宽度和高度的偏移量比中心的偏移量高得多(因为中心的偏移量范围在 01 之间,而宽度和高度的偏移量并无此限制),因此通过获取平方根值来给予宽度和高度偏移较低的权重。
计算定位和分类的损失:
L l o c = λ c o o r d ∑ i = 0 S 2 ∑ j = 0 B 1 i j o b j [ ( x i − x ^ i ) 2 + ( y i − y ^ i ) 2 + ( w i − w ^ i ) 2 + ( h i − h ^ i ) 2 ] L c l s = ∑ i = 0 S 2 ∑ j = 0 B ( 1 i j o b j + λ ( 1 − 1 i j o b j ) ) ( C i j − C ^ i j ) 2 + ∑ i = 0 S 2 ∑ c ∈ C 1 i j o b j ( p i ( c ) − p ^ i ( c ) ) 2 L = L l o c + L c l s L_{loc}=\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}1_{ij}^{obj}[(x_i-\hat x_i)^2+(y_i-\hat y_i)^2+(\sqrt {w_i}-\sqrt{\hat w_i})^2+(\sqrt {h_i}-\sqrt{\hat h_i})^2] \\ L_{cls}=\sum_{i=0}^{S^2}\sum_{j=0}^{B}(1_{ij}^{obj}+\lambda(1-1_{ij}^{obj}))(C_{ij}-\hat C_{ij})^2+\sum_{i=0}^{S^2}\sum_{c∈C}1_{ij}^{obj}(p_i(c)-\hat p_i(c))^2 \\ L=L_{loc}+L_{cls} Lloc=λcoordi=0S2j=0B1ijobj[(xix^i)2+(yiy^i)2+(wi w^i )2+(hi h^i )2]Lcls=i=0S2j=0B(1ijobj+λ(11ijobj))(CijC^ij)2+i=0S2cC1ijobj(pi(c)p^i(c))2L=Lloc+Lcls

其中, λ c o o r d \lambda_{coord} λcoord 是与回归损失相关的权重, 1 i j o b j 1_{ij}^{obj} 1ijobj 表示单元格是否包含对象, p ^ i ( c ) \hat p_i(c) p^i(c) 对应于预测的类别概率, C i j C_{ij} Cij 表示对象置信度得分,总损失是分类和回归损失值的之和。

2. 实现 YOLO 目标检测

为了实现 YOLO 目标检测模型,我们使用官方的 YOLO-v4 实现来识别图像中公共汽车和卡车的位置,继续使用与 R-CNN 一节中相同的数据集,并根据我们的需要对其进行修改。

2.1 编译 DarkNet

首先,从 GitHub 获取 DarkNet 存储库并进行编译,官方 YOLOv4 模型基于 C 语言编写的,为了在 PyTorch 中使用需要进行编译。

(1) 下载 DarkNet 存储库后,重新配置 Makefile 文件,在shell中执行以下命令:

darknet-master$ sed -i 's/OPENCV=0/OPENCV=1/' Makefile
darknet-master$ sed -i 's/GPU=0/GPU=1/' Makefile
darknet-master$ sed -i 's/CUDNN=0/CUDNN=1/' Makefile
darknet-master$ sed -i 's/CUDNN_HALF=0/CUDNN_HALF=1/' Makefile

Makefile 是编译 DarkNet 所需的配置文件,使用以下标志编译 DarkNetOPENCVGPUCUDNNCUDNN_HALF,使用这些优化标志能够使模型训练速度更快。sed 函数表示流编辑器,它是一个强大的 Linux 命令,可以直接从命令提示符修改文本文件中的信息,语法为 sed 's///' path/to/text/file,例如,使用 sed -i 's/OPENCV=0/OPENCV=1/' Makefile 能够将 OPENCV=0 替换为 OPENCV=1

(2) 编译 darknet 源码:

darknet-master$ make

(3) 下载并解压数据集,使用与 R-CNN 一节中相同的数据集。

(4) 下载预训练权重 yolov4.weights 进行样本预测。

(5) 运行以下命令测试是否安装成功:

darknet-master$ ./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/test.jpeg

以上代码使用由神经网络配置 cfg/yolov4.cfg 和预训练权重 yolov4.weights 构建的神经网络对 data/test.jpeg 进行预测。此外,从 cfg/coco.data 文件中获取类别,文件中包含了预训练权重的训练对象类别,示例图像 (data/test.jpeg) 预测结果如下:

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第7张图片

我们已经安装了 darknet,在下一节中,我们将学习如何使用自定义数据集准备训练数据以满足 YOLOv4 输入、输出需求。

2.2 设置数据集格式

YOLO 使用固定格式进行训练,利用所需格式存储图像和标签后,就可以使用自定义数据集训练 YOLO 模型。接下来,我们介绍 YOLO 训练所需的文件和文件夹结构。

(1) 使用 vim 命令在 data 文件夹中创建 obj.names

darknet-master$ vim data/obj.names

data/obj.names 文件中添加以下内容:

bus
truck

obj.names
以上代码可以创建名为 obj.names 的文件,并将 “bus” 和 “truck” 写入其中,每个类别占一行。

(2) 创建文本文件 data/obj.data

darknet-master$ vim data/obj.data

data/obj.data 文件中添加以下内容,描述数据集中的参数以及包含训练和测试图像路径的文本文件位置,以及包含对象名称的文件的位置以及要保存训练模型的文件夹:

classes = 2
train = data/train.txt
valid = data/val.txt
names = data/obj.names
backup = backup/

data/obj.data
上述文本文件的扩展名并非 .txtYOLO 使用硬编码的名称和文件夹来识别数据的位置。

(3) 将所有图像和标签数据文本文件移动到 data/obj 文件夹中:

darknet-master$ mkdir -p data/obj
darknet-master$ cp -r open-images-bus-trucks/images/* data/obj/
darknet-master$ cp -r open-images-bus-trucks/yolo_labels/all/{train,val}.txt data/
darknet-master$ cp -r open-images-bus-trucks/yolo_labels/all/labels/*.txt data/obj/

所有训练和验证图像都位于 data/obj 文件夹中,且将所需文本文件移动到同一个文件夹中。每个包含图像标签数据的文件都与图像共享相同的名称,例如,该文件夹可能包含 1001.jpg1001.txt,文本文件包含该图像中目标对象的类别标签和边界框,如果 data/train.txt 包含 1001.jpg,则它为训练图像;如果它出现在 data/val.txt 中,表示它是验证图像。
文本文件本身包含如下信息:clsxcycwh,其中 cls(xc, yc) 处宽度为 w、高度为 h 的边界框中对象的类别索引,每行都存储图像中的一个目标对象。
例如,如果宽度为 800、高度为 600 的图像包含一个卡车和一个公交车,中心点分别位于 (500,300)(100,400),且它们的宽度和高度分别为 (200,100)(300,50),那么文本文件内容如下所示:

1 0.62 0.50 0.25 0.12
0 0.12 0.67 0.38 0.08

2.3 配置网络架构

YOLO 有多种大小不同的架构配置,适用于规模大小不同的训练数据集。架构配置可以使用不同的主干网络,使用 .cfg 文件定义网络架构配置,预定义的配置文件位于存储库的 cfgs 文件夹中,每个文本文件都包含一个不同的网络架构以及网络所用的超参数,例如批大小和学习率。本节中,使用最小的可用架构 (cfg/yolov4-tiny-custom.cfg) 并根据自定义数据集配置网络架构:

# 创建现有配置的副本
darknet-master$ cp cfg/yolov4-tiny-custom.cfg cfg/yolov4-tiny-bus-trucks.cfg
# 将最大批大小修改为4000
darknet-master$ sed -i 's/max_batches = 500200/max_batches=4000/' cfg/yolov4-tiny-bus-trucks.cfg
# 每个批数据的子批数据大小
darknet-master$ sed -i 's/subdivisions=1/subdivisions=16/' cfg/yolov4-tiny-bus-trucks.cfg
# 学习率衰减
darknet-master$ sed -i 's/steps=400000,450000/steps=3200,3600/' cfg/yolov4-tiny-bus-trucks.cfg 
# 类别数修改为2
darknet-master$ sed -i 's/classes=80/classes=2/g' cfg/yolov4-tiny-bus-trucks.cfg 
# 在分类和回归头中,修改输出卷积滤波器的数量255->21、57->33
darknet-master$ sed -i 's/filters=255/filters=21/g' cfg/yolov4-tiny-bus-trucks.cfg 
darknet-master$ sed -i 's/filters=57/filters=33/g' cfg/yolov4-tiny-bus-trucks.cfg

重新调整 yolov4-tiny 架构,使其可以自定义数据集上进行训练,接下来,就可以加载预训练的权重并微调模型参数。

2.4 模型训练和测试

GitHub 中下载预训练模型权重并存储在 build/darknet/x64 中:

darknet-master$ cp yolov4-tiny.conv.29 build/darknet/x64/

训练模型:

darknet-master$ ./darknet detector train data/obj.data cfg/yolov4-tiny-bus-trucks.cfg yolov4-tiny.conv.29 -dont_show -mapLastAt

在以上代码中,使用 -dont_show 标志跳过显示中间预测图像,-mapLastAt 将定期打印验证数据的平均平均精度,权重会定期存储在备份文件夹中,并且可以在训练后用于预测。
对新图像进行预测:

darknet-master$ ./darknet detector test data/obj.data cfg/yolov4-tiny-bus-trucks.cfg backup/yolov4-tiny-bus-trucks_4000.weights open-images-bus-trucks/images/0d39f132cdf7c027.jpg

PyTorch深度学习实战(22)——从零开始实现YOLO目标检测_第8张图片

小结

YOLO 在目标检测任务中具有显著的优势,由于整个检测过程只需要一次前向传递,因此非常高效,并且可以实现实时目标检测。此外,YOLO 通过全局感受野捕捉了整个图像中的上下文信息,对小尺寸物体的检测效果较好。在本节中,使用 darknet 实现了 YOLO 目标检测模型,并了解了如何根据需要对模型进行修改。

系列链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(6)——神经网络性能优化技术
PyTorch深度学习实战(7)——批大小对神经网络训练的影响
PyTorch深度学习实战(8)——批归一化
PyTorch深度学习实战(9)——学习率优化
PyTorch深度学习实战(10)——过拟合及其解决方法
PyTorch深度学习实战(11)——卷积神经网络
PyTorch深度学习实战(12)——数据增强
PyTorch深度学习实战(13)——可视化神经网络中间层输出
PyTorch深度学习实战(14)——类激活图
PyTorch深度学习实战(15)——迁移学习
PyTorch深度学习实战(16)——面部关键点检测
PyTorch深度学习实战(17)——多任务学习
PyTorch深度学习实战(18)——目标检测基础
PyTorch深度学习实战(19)——从零开始实现R-CNN目标检测
PyTorch深度学习实战(20)——从零开始实现Fast R-CNN目标检测
PyTorch深度学习实战(21)——从零开始实现Faster R-CNN目标检测

你可能感兴趣的:(PyTorch深度学习,深度学习,pytorch,YOLO)