精通 TensorFlow 2.x 计算机视觉(三)
七、YOLO 对象检测
在上一章中,我们详细讨论了各种神经网络图像分类和对象检测架构,这些架构利用多个步骤进行对象检测,分类和边界框优化。 在本章中,我们将介绍两种单阶段的快速对象检测方法-仅查看一次(YOLO)和 RetinaNet。 我们将讨论每个模型的架构,然后使用 YOLO v3 在真实的图像和视频中进行推理。 我们将向您展示如何使用 YOLO v3 优化配置参数和训练自己的自定义映像。
本章涵盖的主题如下:
- YOLO 概述
- 用于对象检测的 Darknet 简介
- 使用 Darknet 和 Tiny Darknet 进行实时预测
- 比较 YOLO – YOLO 与 YOLO v2 与 YOLO v3
- 什么时候训练模型?
- 使用 YOLO v3 训练自己的图像集以开发自定义模型
- 特征金字塔和 RetinaNet 概述
YOLO 概述
我们在“第 5 章”,“神经网络架构和模型”中了解到,每个已发布的神经网络架构都通过学习其架构和功能,然后开发一个全新的分类器来改进前一架构,从而改进它的准确率和检测时间。 YOLO 参加了计算机视觉和模式识别会议(CVPR),在 2016 年中,Joseph Redmon,Santosh Divvala,Ross Girshick 和 Ali Farhadi 的论文 《只看一次:统一的实时对象检测》。 YOLO 是一个非常快速的神经网络,可以以每秒 45 帧(基本 YOLO)到每秒 155 帧(快速 YOLO)的惊人速度一次检测多种物体。 相比之下,大多数手机相机以每秒 30 帧的速度捕获视频,而高速相机以每秒 250 帧的速度捕获视频。 YOLO 的每秒帧数等于大约 6 到 22 ms 的检测时间。 将此与人类大脑检测大约 13 毫秒图像所需的时间进行比较-YOLO 以与人类相似的方式立即识别图像。 因此,它为机器提供了即时目标检测功能。
在进一步研究细节之前,我们将首先讨论交并比(IOU)的概念。
IOU 的概念
IOU 是基于预测边界框和地面真实边界框(手工标记)之间的重叠程度的对象检测评估指标。 让我们看一下 IOU 的以下派生:
下图说明了 IOU,显示了一辆大型货车的预测和地面实况边界框:
在这种情况下,IOU 值接近0.9,因为重叠区域非常大。 如果两个边界框不重叠,则 IOU 值为0,如果它们确实重叠 100%,则 IOU 值为1。
YOLO 如何如此快速地检测物体?
YOLO 的检测机制基于单个卷积神经网络(CNN),该预测同时预测对象的多个边界框以及在每个边界框中检测给定对象类别的可能性。 下图说明了这种方法:
前面的照片显示了三个主要步骤,从边界框的开发到使用非最大抑制和最终边界框。 具体步骤如下:
- YOLO 中的 CNN 使用整个图像中的特征来预测每个边界框。 因此,预测是全局的,而不是局部的。
- 整个图像分为
S x S个网格单元,每个网格单元预测B个边界框以及边界框包含对象的概率(P)。 因此,总共有S x S x B个边界框,每个边界框都有相应的概率。 - 每个边界框包含五个预测(
x,y,w,h和c),以下内容适用:
o(x, y)是边界框中心相对于网格单元坐标的坐标。o(w, h)是边框相对于图像尺寸的宽度和高度。o(c)是置信度预测,表示预测框和地面真实框之间的 IOU。
- 网格单元包含对象的概率定义为类乘以 IOU 值的概率。 这意味着,如果网格单元仅部分包含一个对象,则其概率将较低,而 IOU 值将保持较低。 这将对该网格单元的边界框产生两个影响:
- 边界框的形状将小于完全包含对象的网格单元的边界框的大小,因为网格单元只能看到对象的一部分并从中推断出其形状。 如果网格单元仅包含对象的一小部分,则它可能根本无法识别该对象。
- 边界框类置信度将很低,因为部分图像产生的 IOU 值将不符合地面真实性预测。
- 通常,每个网格单元只能包含一个类,但是使用锚框原理,可以将多个类分配给一个网格单元。 锚框是预定义的形状,表示要检测的类的形状。 例如,如果我们检测到三个类别(汽车,摩托车和人),那么我们可能可以通过两个锚框形状来解决-一个代表摩托车和人,另一个代表汽车。 可以通过查看先前图像中最右边的图像来确认。 我们可以通过使用 K 均值聚类等算法分析每个类的形状来确定锚框形状以形成训练 CSV 数据。
让我们以前面的图像为例。 在这里,我们有三类:car,motorcycle和human。 我们假设一个5 x 5的网格具有 2 个锚定框和 8 个维度(5 个边界框参数(x,y,w,h和c)和 3 类(c1,c2和c3))。 因此,输出向量大小为5 x 5 x 2 x 8。
我们为每个锚框重复两次Y = [x, y, w, h, c, c1, c2, c3, x, y, w, h, c, c1, c2, c3]参数。 下图说明了边界框坐标的计算:
图像的大小为448 x 448。此处,出于说明目的,显示了human和car两类的计算方法。 请注意,每个锚定框的大小为448/5 ~ 89。
YOLO v3 神经网络架构
TYOLO v3 由 Joseph Redmon 和 Ali Farhadi 于 2018 年在论文《YOLOv3:增量改进》中引入。 下图显示了 YOLO v3 神经网络架构。 该网络具有 24 个卷积层和 2 个全连接层。 它没有任何 softmax 层。
下图以图形方式说明了 YOLO v3 架构:
YOLO v3 的最重要的功能是它的检测机制,它是在三种不同的规模上完成的-在第 82、94 和 106 层:
- 该网络由第 1 层和第 74 层之间的 23 个卷积和残差块组成,其中输入图像大小从
608降低到19,深度通过交替的3 x 3和1 x 1个过滤器从3增长到1,024。 - 除 5 种情况下,将跨步值
2与3 x 3过滤器一起用于减小尺寸外,跨步通常保持为1。 - 剩余的块之后是交替的
1 x 1和1 x 1过滤器的预卷积块,直到在第 82 层进行第一次检测为止。已经使用了两次短路-一个在第 61 和 85 层之间,另一个在第 36 和 97 层之间 。
YOLO 与 Faster R-CNN 的比较
下表显示了 YOLO 和 Faster R-CNN 之间的相似之处:
| YOLO | R-CNN |
|---|---|
| 预测每个网格单元的边界框。 | 选择性搜索会为每个区域提议(实际上是一个网格单元)生成边界框。 |
| 使用边界框回归。 | 使用边界框回归。 |
下表显示了 YOLO 和 Faster R-CNN 之间的区别:
| YOLO | R-CNN |
|---|---|
| 分类和边界框回归同时发生。 | 选择性搜索会为每个区域提议生成一个边界框-这些是单独的事件。 |
| 每个图像 98 个边界框。 | 每个图像约有 2,000 个区域提议边界框。 |
| 每个网格单元 2 个锚点。 | 每个网格单元 9 个锚点。 |
| 它无法检测到小物体和彼此相邻的物体。 | 检测小物体和彼此相邻的物体。 |
| 快速算法。 | 更快的 R-CNN 比 YOLO 慢。 |
因此,总而言之,如果您需要生产级的准确率并且不太关心速度,请选择 Faster R-CNN。 但是,如果需要快速检测,请选择 YOLO。 像任何神经网络模型一样,您需要有足够的样本(大约 1,000 个)以不同的角度,不同的颜色和形状定向以做出良好的预测。 完成此操作后,根据我的个人经验,YOLO v3 会给出非常合理且快速的预测。
用于对象检测的 Darknet 简介
Darknet 是一个开放的神经网络框架,由 C 编写,并由 YOLO 的第一作者 Joseph Redmon 管理。 有关 Darknet 的详细信息,请访问 pjreddie.com。 在本节中,我们将讨论用于对象检测的 Darknet 和 Tiny Darknet。
使用 Darknet 检测对象
在本节中,我们将从官方 Darknet 站点安装 Darknet,并将其用于对象检测。 请按照以下步骤在您的 PC 上安装 Darknet 并进行推断:
- 在终端中应输入以下五行。 在每个命令行之后点击
Enter。 这些步骤将从 GitHub 克隆 Darknet,这将在您的 PC 中创建 Darknet 目录,并获取 YOLO v3 权重,然后检测图像中的对象:
git clone https://github.com/pjreddie/darknet.git
cd darknet
make
wget https://pjreddie.com/media/files/yolov3.weights
./darknet detect cfg/yolov3.cfg yolov3.weights data/carhumanbike.png
- 执行
git clone命令后,您将在终端中获得以下输出:
Cloning into 'darknet'...
remote: Enumerating objects: 5901, done.
remote: Total 5901 (delta 0), reused 0 (delta 0), pack-reused 5901
Receiving objects: 100% (5901/5901), 6.16 MiB | 8.03 MiB/s, done.
Resolving deltas: 100% (3916/3916), done.
- 输入
wget yolov3权重后,将在终端中获得以下输出:
Resolving pjreddie.com (pjreddie.com)... 128.208.4.108
Connecting to pjreddie.com (pjreddie.com)|128.208.4.108|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 248007048 (237M) [application/octet-stream]
Saving to: 'yolov3.weights'
yolov3.weights 100%[======================================================>] 236.52M 8.16MB/s in 29s
… (8.13 MB/s) - 'yolov3.weights' saved [248007048/248007048]
- 然后,一旦输入
darknet$ ./darknet detect cfg/yolov3.cfg yolov3.weight data/carhumanbike.png,您将在终端中获得以下输出:
layer filters size input output
0 conv 32 3 x 3 / 1 608 x 608 x 3 -> 608 x 608 x 32 0.639 BFLOPs --> image size 608x608
1 conv 64 3 x 3 / 2 608 x 608 x 32 -> 304 x 304 x 64 3.407 BFLOPs
2 conv 32 1 x 1 / 1 304 x 304 x 64 -> 304 x 304 x 32 0.379 BFLOPs
3 conv 64 3 x 3 / 1 304 x 304 x 32 -> 304 x 304 x 64 3.407 BFLOPs
4 res 1 304 x 304 x 64 -> 304 x 304 x 64 --> this implies residual block connecting layer 1 to 4
5 conv 128 3 x 3 / 2 304 x 304 x 64 -> 152 x 152 x 128 3.407 BFLOPs
6 conv 64 1 x 1 / 1 152 x 152 x 128 -> 152 x 152 x 64 0.379 BFLOPs
7 conv 128 3 x 3 / 1 152 x 152 x 64 -> 152 x 152 x 128 3.407 BFLOPs
8 res 5 152 x 152 x 128 -> 152 x 152 x 128 --> this implies residual block connecting layer 5 to 8
...
...
...
83 route 79 --> this implies layer 83 is connected to 79, layer 80-82 are prediction layers
84 conv 256 1 x 1 / 1 19 x 19 x 512 -> 19 x 19 x 256 0.095 BFLOPs
85 upsample 2x 19 x 19 x 256 -> 38 x 38 x 256 --> this implies image size increased by 2X
86 route 85 61 --> this implies shortcut between layer 61 and 85
87 conv 256 1 x 1 / 1 38 x 38 x 768 -> 38 x 38 x 256 0.568 BFLOPs
88 conv 512 3 x 3 / 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BFLOPs
89 conv 256 1 x 1 / 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BFLOPs
90 conv 512 3 x 3 / 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BFLOPs
91 conv 256 1 x 1 / 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BFLOPs
92 conv 512 3 x 3 / 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BFLOPs
93 conv 255 1 x 1 / 1 38 x 38 x 512 -> 38 x 38 x 255 0.377 BFLOPs
94 yolo --> this implies prediction at layer 94
95 route 91 --> this implies layer 95 is connected to 91, layer 92-94 are prediction layers
96 conv 128 1 x 1 / 1 38 x 38 x 256 -> 38 x 38 x 128 0.095 BFLOPs
97 upsample 2x 38 x 38 x 128 -> 76 x 76 x 128 à this implies image size increased by 2X
98 route 97 36\. --> this implies shortcut between layer 36 and 97
99 conv 128 1 x 1 / 1 76 x 76 x 384 -> 76 x 76 x 128 0.568 BFLOPs
100 conv 256 3 x 3 / 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BFLOPs
101 conv 128 1 x 1 / 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BFLOPs
102 conv 256 3 x 3 / 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BFLOPs
103 conv 128 1 x 1 / 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BFLOPs
104 conv 256 3 x 3 / 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BFLOPs
105 conv 255 1 x 1 / 1 76 x 76 x 256 -> 76 x 76 x 255 0.754 BFLOPs
106 yolo --> this implies prediction at layer 106
执行代码后,您将看到完整的模型。 为简便起见,我们仅在前面的代码段中显示了模型的开头。
上面的输出描述了 YOLO v3 的详细神经网络构建块。 花一些时间来了解所有 106 个卷积层及其目的。 上一节提供了所有唯一代码行的说明。 前面的代码导致图像的以下输出:
Loading weights from yolov3.weights...Done!
data/carhumanbike.png: Predicted in 16.140244 seconds.
car: 81%
truck: 63%
motorbike: 77%
car: 58%
person: 100%
person: 100%
person: 99%
person: 94%
预测输出如下所示:
YOLO v3 模型在预测方面做得很好。 即使是很远的汽车也能正确检测到。 前面的汽车分为汽车(在图中看不到标签)和卡车。 所有四个人(两个步行摩托车和两个骑摩托车)被检测到。 在两辆摩托车中,检测到一辆摩托车。 请注意,尽管汽车的颜色是黑色,但模型不会错误地将阴影检测为汽车。
使用 Tiny Darknet 检测对象
Tiny Darknet 是一个小型且快速的网络,可以非常快速地检测到对象。 它的大小为 4 MB,而 Darknet 的大小为 28 MB。 您可以在这个页面中找到其实现的详细信息。
完成上述步骤后,Darknet 应该已经安装在您的 PC 上。 在终端中执行以下命令:
$ cd darknet
darknet$ wget https://pjreddie.com/media/files/tiny.weights
前面的命令会将 Darknet 权重安装在darknet文件夹中。 您还应该在cfg文件夹中包含tiny.cfg。 然后,执行以下命令以检测对象。 在这里,我们将使用与参考 Darknet 模型相同的图像进行检测。 我们只是将权重和cfg文件从 Darknet 更改为 Tiny Darknet:
darknet$ ./darknet detect cfg/tiny.cfg tiny.weights data /carhumanbike.png
与 Darknet 一样,前面的命令将显示 Tiny Darknet 模型的所有 21 层(Darknet 则为 106 层),如下所示:
layer filters size input output
0 conv 16 3 x 3 / 1 224 x 224 x 3 -> 224 x 224 x 16 0.043 BFLOPs
1 max 2 x 2 / 2 224 x 224 x 16 -> 112 x 112 x 16
2 conv 32 3 x 3 / 1 112 x 112 x 16 -> 112 x 112 x 32 0.116 BFLOPs
3 max 2 x 2 / 2 112 x 112 x 32 -> 56 x 56 x 32
4 conv 16 1 x 1 / 1 56 x 56 x 32 -> 56 x 56 x 16 0.003 BFLOPs
5 conv 128 3 x 3 / 1 56 x 56 x 16 -> 56 x 56 x 128 0.116 BFLOPs
6 conv 16 1 x 1 / 1 56 x 56 x 128 -> 56 x 56 x 16 0.013 BFLOPs
7 conv 128 3 x 3 / 1 56 x 56 x 16 -> 56 x 56 x 128 0.116 BFLOPs
8 max 2 x 2 / 2 56 x 56 x 128 -> 28 x 28 x 128
9 conv 32 1 x 1 / 1 28 x 28 x 128 -> 28 x 28 x 32 0.006 BFLOPs
10 conv 256 3 x 3 / 1 28 x 28 x 32 -> 28 x 28 x 256 0.116 BFLOPs
11 conv 32 1 x 1 / 1 28 x 28 x 256 -> 28 x 28 x 32 0.013 BFLOPs
12 conv 256 3 x 3 / 1 28 x 28 x 32 -> 28 x 28 x 256 0.116 BFLOPs
13 max 2 x 2 / 2 28 x 28 x 256 -> 14 x 14 x 256
14 conv 64 1 x 1 / 1 14 x 14 x 256 -> 14 x 14 x 64 0.006 BFLOPs
15 conv 512 3 x 3 / 1 14 x 14 x 64 -> 14 x 14 x 512 0.116 BFLOPs
16 conv 64 1 x 1 / 1 14 x 14 x 512 -> 14 x 14 x 64 0.013 BFLOPs
17 conv 512 3 x 3 / 1 14 x 14 x 64 -> 14 x 14 x 512 0.116 BFLOPs
18 conv 128 1 x 1 / 1 14 x 14 x 512 -> 14 x 14 x 128 0.026 BFLOPs
19 conv 1000 1 x 1 / 1 14 x 14 x 128 -> 14 x 14 x1000 0.050 BFLOPs
20 avg 14 x 14 x1000 -> 1000
21 softmax 1000
Loading weights from tiny.weights...Done!
data/carhumanbike.png: Predicted in 0.125068 seconds.
但是,该模型无法检测图像中的对象。 我将检测更改为分类,如下所示:
darknet$ ./darknet classify cfg/tiny.cfg tiny.weights data/dog.jpg
前面的命令生成的结果类似于 Tiny YOLO 链接中发布的结果(wget https://pjreddie.com/media/files/tiny.weights):
Loading weights from tiny.weights...Done!
data/dog.jpg: Predicted in 0.130953 seconds.
14.51%: malamute
6.09%: Newfoundland
5.59%: dogsled
4.55%: standard schnauzer
4.05%: Eskimo dog
但是,同一图像在通过对象检测时不会返回边界框。
接下来,我们将讨论使用 Darknet 对视频进行实时预测。
使用 Darknet 的实时预测
涉及 Darknet 的预测都可以使用终端中的命令行来完成。 有关更多详细信息,请参阅这里。
到目前为止,我们已经在图像上使用 Darknet 进行了推断。 在以下步骤中,我们将学习如何在视频文件上使用 Darknet 进行推理:
-
通过在终端中键入
cd darknet转到darknet目录(已在前面的步骤中安装)。 -
确保已安装 OpenCV。 即使您已安装 OpenCV,它仍可能会创建一个错误标志。 使用
sudo apt-get install libopencv-dev命令将 OpenCV 安装在darknet目录中。 -
在
darknet目录中,有一个名为Makefile的文件。 打开该文件,设置OpenCV = 1并保存。 -
通过转到这里从终端下载权重。
-
此时,由于更改了
Makefile,因此必须重新编译。 您可以通过在终端中键入make来执行此操作。 -
然后,通过在终端中键入以下命令来下载视频文件:
./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights data/road_video.mp4
- 如前所述,这里将编译具有 106 层的 YOLO 模型,并播放视频。 您会注意到视频播放非常慢。 可以通过以下两个步骤解决此问题。 它们应该一一执行,因为每个步骤都会产生影响。
- 再次打开
Makefile。 将GPU更改为1,保存Makefile,然后重复步骤 4 至 6。此时,我注意到步骤 6 提供了以下 CUDAout of memory错误:
…….
57 conv 512 3 x 3 / 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BFLOPs
58 res 55 38 x 38 x 512 -> 38 x 38 x 512
59 conv 256 1 x 1 / 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BFLOPs
60 CUDA Error: out of memory
darknet: ./src/cuda.c:36: check_error: Assertion `0' failed.
Aborted (core dumped)
通过两种机制解决了该错误:
- 更改图像尺寸。
- 将 NVIDIA CUDA 版本从 9.0 更改为 10.1。 请访问 NVIDIA 网站以更改 NVIDIA 版本。
首先,尝试更改图像尺寸。 如果这不起作用,则检查 CUDA 版本并更新(如果您仍在使用 9.0 版)。
- 在
darknet目录中,在cfg目录下有一个名为yolov3.cfg的文件。 打开该文件,并将宽度和高度从608更改为416或288。 我发现当将该值设置为304时,它仍然会失败。 保存文件并重复步骤 5 和 6。
这是将图像尺寸设置为304时将得到的错误代码:
.....
80 conv 1024 3 x 3 / 1 10 x 10 x 512 -> 10 x 10 x1024 0.944 BFLOPs
81 conv 255 1 x 1 / 1 10 x 10 x1024 -> 10 x 10 x 255 0.052 BFLOPs
82 yolo
83 route 79
84 conv 256 1 x 1 / 1 10 x 10 x 512 -> 10 x 10 x 256 0.026 BFLOPs
85 upsample 2x 10 x 10 x 256 -> 20 x 20 x 25
86 route 85 61
87 Layer before convolutional layer must output image.: File exists
darknet: ./src/utils.c:256: error: Assertion `0' failed.
Aborted (core dumped)
下图显示了带有交通标志标签和汽车检测的视频文件的屏幕截图:
请注意,所有汽车均被正确检测,甚至主交通灯和侧面交通灯也被检测到。
我们之前讨论了默认大小为608的 YOLO v3 层。 以下是相同的输出,其大小更改为416,以便正确显示视频文件:
layer filters size input output
0 conv 32 3 x 3 / 1 416 x 416 x 3 -> 416 x 416 x 32 0.299 BFLOPs
1 conv 64 3 x 3 / 2 416 x 416 x 32 -> 208 x 208 x 64 1.595 BFLOPs
2 conv 32 1 x 1 / 1 208 x 208 x 64 -> 208 x 208 x 32 0.177 BFLOPs
3 conv 64 3 x 3 / 1 208 x 208 x 32 -> 208 x 208 x 64 1.595 BFLOPs
4 res 1 208 x 208 x 64 -> 208 x 208 x 64
5 conv 128 3 x 3 / 2 208 x 208 x 64 -> 104 x 104 x 128 1.595 BFLOPs
6 conv 64 1 x 1 / 1 104 x 104 x 128 -> 104 x 104 x 64 0.177 BFLOPs
7 conv 128 3 x 3 / 1 104 x 104 x 64 -> 104 x 104 x 128 1.595 BFLOPs
8 res 5 104 x 104 x 128 -> 104 x 104 x 128
...
...
...
94 yolo
95 route 91
96 conv 128 1 x 1 / 1 26 x 26 x 256 -> 26 x 26 x 128 0.044 BFLOPs
97 upsample 2x 26 x 26 x 128 -> 52 x 52 x 128
98 route 97 36
99 conv 128 1 x 1 / 1 52 x 52 x 384 -> 52 x 52 x 128 0.266 BFLOP
100 conv 256 3 x 3 / 1 52 x 52 x 128 -> 52 x 52 x 256 1.595 BFLOPs
101 conv 128 1 x 1 / 1 52 x 52 x 256 -> 52 x 52 x 128 0.177 BFLOPs
102 conv 256 3 x 3 / 1 52 x 52 x 128 -> 52 x 52 x 256 1.595 BFLOPs
103 conv 128 1 x 1 / 1 52 x 52 x 256 -> 52 x 52 x 128 0.177 BFLOPs
104 conv 256 3 x 3 / 1 52 x 52 x 128 -> 52 x 52 x 256 1.595 BFLOPs
105 conv 255 1 x 1 / 1 52 x 52 x 256 -> 52 x 52 x 255 0.353 BFLOPs
106 yolo
Loading weights from yolov3.weights...Done!
video file: data/road_video.mp4
执行前面的代码后,您将看到完整的模型。 为简便起见,我们仅在前面的代码段中显示了模型的开头。
下表总结了两种不同图像尺寸的输出:
| 层 | 608 尺寸 | 416 尺寸 |
|---|---|---|
| 82 | 19 x 19 | 13 x 13 |
| 94 | 38 x 38 | 26 x 26 |
| 106 | 76 x 76 | 52 x 52 |
注意,原始图像尺寸和第 82 层输出尺寸之间的比率保持为 32。 到目前为止,我们已经比较了使用 Darknet 和 Tiny Darknet 的推论。 现在,我们将比较不同的 YOLO 模型。
YOLO 与 YOLO v2 与 YOLO v3
下表显示了三种 YOLO 版本的比较:
| YOLO | YOLO v2 | YOLO v3 | |
|---|---|---|---|
| 输入尺寸 | 224 x 224 | 448 x 448 | |
| 构架 | Darknet 在 ImageNet-1,000 上接受了训练。 | Darknet-19,19 个卷积层和 5 个最大池化层。 | Darknet-53,53 个卷积层。 为了进行检测,增加了 53 层,总共有 106 层。 |
| 小尺寸检测 | 它找不到小图像。 | 在检测小图像方面比 YOLO 更好。 | 在小图像检测方面优于 YOLO v2。 |
| 使用锚框。 | 使用残差块。 |
下图比较了 YOLO v2 和 YOLO v3 的架构:
基本卷积层相似,但是 YOLO v3 在三个独立的层上执行检测:82、94 和 106。
您应该从 YOLO v3 中获取的最关键的项目是在三个不同的层级和三个不同的级别进行对象检测:82(最大),94(中级)和 106(最小)。
什么时候训练模型?
在迁移学习中,通过训练大量数据来开发训练模型。 因此,如果您的类属于以下类之一,则无需为这些类训练模型。 为 YOLO v3 训练的 80 个类如下:
Person, bicycle, car, motorbike, airplane, bus, train, truck, boat, traffic light, fire hydrant, stop sign, parking meter, bench, bird, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe, backpack, umbrella, handbag, tie, suitcase, frisbee, skis, snowboard, sports ball, kite, baseball bat, baseball glove, skateboard, surfboard. tennis racket, bottle, wine glass, cup, fork, knife, spoon, bowl, banana, apple, sandwich, orange, broccoli, carrot, hot dog, pizza, donut, cake, chair, sofa, potted plant, bed, dining table, toilet, tv monitor, laptop, mouse, remote, keyboard, cell phone, microwave, oven, toaster, sink, refrigerator, book, clock, vase, scissors, teddy bear, hair drier, toothbrush
因此,如果您要检测食物的类型,YOLO v3 会很好地检测banana,apple,sandwich,orange,broccoli,carrot,hot dog, pizza,donut和cake,但无法检测到hamburger。
同样,在 PASCAL VOC 数据集上训练的 YOLO v3 将能够检测所有 20 个类别,即airplane,bicycle,bird,boat,bottle,bus和car ,cat,chair,cow,dining table,dog,horse,motorbike,person,potted plant,sheep,sofa,train和 tv monitor,但它无法检测到新类别hot dog。
因此,这就是训练您自己的图像集的地方,下一节将对此进行介绍。
使用 YOLO v3 训练自己的图像集来开发自定义模型
在本节中,我们将学习如何使用 YOLO v3 训练您自己的自定义检测器。 训练过程涉及许多不同的步骤。 为了清楚起见,以下流程图中显示了每个步骤的输入和输出。 YOLO 的《YOLOv3:Incremental Improvement》由 Redmon,Joseph,Farhadi 和 Ali 于 2018 年在 arXiv 上发布,其中包含许多训练步骤。这些训练步骤也包含在 VOC 的“训练 YOLO”部分。
下图显示了如何使用 YOLO v3 训练 VOC 数据集。 在我们的案例中,我们将使用我们自己的自定义家具数据,该数据用于在“第 6 章”,“使用迁移学习的视觉搜索”中使用 Keras 对图像进行分类:
在此描述第 1 至 11 部分的详细说明。
准备图像
请按照以下步骤准备图像:
-
研究您要检测的类数-在此示例中,我们将考虑在第 6 章“使用迁移学习的视觉搜索”中讨论的
bed,chair和sofa类。 -
确保每个类的图像数量相同。
-
确保您的类名称中没有空格; 例如,使用
caesar_salad代替caesar salad。 -
每个类至少收集 100 张图像以开始初始训练(因此,请完成步骤 1 至 10 以开发模型),然后随着图像数量的增加而增加数量。 理想情况下,1,000 张图像是训练的不错数目。
-
将所有图像批量调整为
416 x 416— 您可以在 macOS 预览窗格中选择选项,然后选择多个图像,然后批量调整大小,或者可以使用 Ubuntu 中的 ImageMagick 等程序在终端中批量调整大小。 之所以需要执行此步骤,是因为 YOLO v3 希望图像的尺寸为416 x 416,因此会自行调整图像的尺寸,但这可能会导致该图像的边界框出现不同的外观,从而在某些情况下无法检测到。
生成标注文件
此步骤涉及为数据集中每个图像中的每个对象创建边界框坐标。 此边界框坐标通常由四个参数表示:(x, y)用于确定初始位置以及宽度和高度。 边界框可以表示为.xml或.txt形式。 该坐标文件也称为标注文件。 请按照以下步骤完成本节:
- 许多图像标注软件应用都用于标记图像。 在面部关键点检测期间,我们已经在“第 3 章”,“使用 OpenCV 和 CNN 进行面部检测”中介绍了 VGG 图像标注器。 在“第 11 章”,“具有 CPU/GPU 优化功能的边缘设备深度学习”中,我们将介绍用于自动图像标注的 CVAT 工具。 在本章中,我们将介绍一个称为
labelImg的标注工具。 - 从这里下载
pypi的labelImg标注软件。 您可以按照此处的说明为操作系统安装labelImg-如果有任何问题,一种简单的安装方法是在终端中键入pip3 install lableImg。 然后,要运行它,只需在终端中键入labelImg。 - 在
labelImg中,在“打开目录”框中单击图像目录。 选择每个图像并通过单击Create/RectBox创建一个包围框,然后为包围框添加一个类名,例如bed,chair或sofa。 保存标注,然后单击右箭头转到下一张图像。 - 如果图片中图像中有多个类别或同一类别的多个位置,请在每个类别周围绘制矩形。 多个类别的示例是同一图像中的汽车和行人。 同一类别内多个位置的示例是同一图像中不同位置的不同汽车。 因此,如果图像由多把椅子和一张沙发组成,则在每把椅子周围绘制矩形,并在类名称中为每张沙发分别键入
chair,在其周围绘制一个矩形并键入sofa。 如果图像仅由沙发组成,则在沙发周围绘制一个矩形,并输入sofa作为类名。 下图说明了这一点:
此图显示了如何标记属于同一类的多个图像。
将.xml文件转换为.txt文件
YOLO v3 需要将标注文件另存为.txt文件而不是.xml文件。 本节介绍如何转换和排列.txt文件以输入模型。 有许多工具可用于此类转换-我们将在此处提及两个工具:
RectLabel:具有内置的转换器,可将.xml文件转换为.txt文件。- 命令行
xmltotxt工具:您可以在 GitHub 页面上找到此工具。
该过程的输出将是一个包含.jpg,.xml和.txt文件的目录。 每个图像.jpg文件将具有一个对应的.xml和.txt文件。 您可以从目录中删除.xml文件,因为我们将不再需要这些文件。
创建合并的train.txt和test.txt文件
顾名思义,此步骤涉及一个表示所有图像的.txt文件。 为此,我们将运行一个简单的 Python 文件(每个文件用于训练和测试图像)以创建combinedtrain.txt和combinedtest.txt文件。 转到这里获取 Python 文件。
以下屏幕快照显示了 Python 代码的示例输出:
每个文本文件由几行组成-每行包括图像文件的路径,如前所示。
创建类名列表文件
该文件包含所有类的列表。 因此,在我们的例子中,它是一个扩展名为.names的简单文本文件,如下所示:
bed
chair
sofa
创建一个 YOLO .data文件
这些步骤涉及train和valid文件夹的路径。 开始之前,请将合并的train,合并的test和.names文件复制到darknet目录。 以下代码块显示了典型的.data文件(在本示例中为furniture.data)的外观:
classes= 3
train = /home/krishkar/darknet/furniture_train.txt
valid = /home/krishkar/darknet/furniture_test.txt
names = /home/krishkar/darknet/furniture_label.names
backup = backup
这里,我们有三个类(bed,chair和sofa),因此classes的值设置为 3。train,valid和names文件夹显示了合并训练,合并测试和标签.names文件。 将此文件保存在cfg目录中。
调整 YOLO 配置文件
完成这些步骤后,文件排列部分完成,我们现在将致力于优化 YOLO 配置文件中的参数。 为此,请在 Darknet cfg目录下打开YOLO-VOC.cfg并进行以下更改。 生成的代码也可以从这里下载:
请注意,在以下部分中,我们将描述各种行号和要更改的值–这些行号对应于YOLO-VOC.cfg文件。
-
第 6 行-批量大小。 将此设置为
64。 这意味着在每个训练步骤中将使用 64 张图像来更新 CNN 参数。 -
第 7 行-
subdivisions。 这将按批量大小/细分划分批量,然后将其馈送到 GPU 中进行处理。 将对细分的数量重复该过程,直到完成批量大小(64)并开始新的批量为止。 因此,如果subdivisions设置为1,则所有 64 张图像都将发送到 GPU,以在给定批量中同时进行处理。 如果批量大小设置为8,则将 8 张图像发送到 GPU 进行处理,并在开始下一个批量之前重复此过程 8 次。 将该值设置为1可能会导致 GPU 出现故障,但可能会提高检测的准确率。 对于初始运行,将值设置为8。 -
第 11 行-
momentum。 这用于最小化批量之间的较大权重变化,因为在任何时间点都只处理少量图像(在此示例中为 64)。0.9的默认值是 OK。 -
第 12 行-
decay。 通过控制权重值以获取较大的值,可以将过拟合程度降至最低。0.005的默认值是 OK。 -
第 18 行-
learning_rate。 这表明了解当前批量的速度。 下图显示了学习率与批量的关系,下面将对其进行说明。0.001的默认值是一个合理的开始,如果值是不是数字(NaN),则可以减小默认值:
- 第 19 行-
burn_in。这表示学习率上升的初始时间。 将其设置为1000,如果减小max_batches,则将其降低。
请注意,如果您按照前面的步骤设置代码,并且注意到当周期在200至300左右时,您没有学到太多,原因可能是您当时的学习率很低。 您将开始看到学习率逐渐超过1000周期。
-
第 20 行-
max_batches。 最大批量数。 将其设置为 2,000 乘以类数。 对于3类,6000的值是合理的。 请注意,默认值为500200,该值非常高,如果保持不变,则训练将持续数天。 -
第 22 行-
steps。 这是将学习率乘以第 23 行中的标度之后的步骤。将其设置为max_batches的 80% 和 90%。 因此,如果批量大小为6000,则将值设置为4800和5400。 -
第 611、695、779 行。将
classes值从其默认值[20或80)更改为您的类值(在此示例中为3)。 -
605、689、773 行。这些是 YOLO 预测之前的最后卷积层。 将
filters值从其默认值255设置为(5+ # of classes)x3。 因此,对于 3 个类别,过滤器值应为 24。 -
线 610、694、778。这是锚点,是具有高宽比的预设边界框,如此处所示。 锚点的大小由(宽度,高度)表示,它们的值不需要更改,但是了解其上下文很重要。
(10, 13),(16, 30),(32, 23),(30, 61),(62, 45),(59, 119),(116, 90),(156, 198),(373 (326)。 总共有九个锚点,范围从10的高度到373的高度。 这表示从最小到最大的图像检测。 对于此练习,我们不需要更改锚点。 -
609、693、777 行。这些是蒙版。 他们指定我们需要选择哪些锚框进行训练。 如果较低级别的值为
0, 1, 2,并且您在区域94和106的输出中继续观察到NaN,请考虑增加该值。 选择该值的最佳方法是查看训练图像边界框从最小到最大图像的比例,了解它们的下落位置,并选择适当的蒙版来表示。 在我们的测试案例中,最小尺寸的边界框从62, 45, 40开始,因此我们选择5, 6, 7作为最小值。 下表显示了蒙版的默认值和调整后的值:
| 默认值 | 调整值 |
|---|---|
6, 7, 8 | 7, 8, 9 |
3, 4, 5 | 6, 7, 8 |
0, 1, 2 | 6, 7, 8 |
9的最大值表示bed,并且6的最小值表示chair。
请注意,如果图像中的边界框不同,则可以调整mask值以获得所需的结果。 因此,从默认值开始并进行调整以避免出现NaN结果。
为训练启用 GPU
在您的darknet目录中打开Makefile并按如下所示设置参数:
GPU = 1
CUDNN = 1
开始训练
在终端中一一执行以下命令:
-
下载预训练的
darknet53模型权重以加快训练速度。 在终端中运行这个页面中找到的命令。 -
完成预训练权重的下载后,在终端中执行以下命令:
./darknet detector train cfg/furniture.data cfg/yolov3-furniture.cfg darknet53.conv.74 -gpus 0
训练将开始并将继续,直到使用 82、94 和 106 层写入的值创建最大批量为止。 在下面的代码中,我们将显示两个输出-一个用于一切正常进行时的输出,另一个用于当训练未能正确进行时的输出:
Correct training
Region 82 Avg IOU: 0.063095, Class: 0.722422, Obj: 0.048252, No Obj: 0.006528, .5R: 0.000000, .75R: 0.000000, count: 1
Region 94 Avg IOU: 0.368487, Class: 0.326743, Obj: 0.005098, No Obj: 0.003003, .5R: 0.000000, .75R: 0.000000, count: 1
Region 106 Avg IOU: 0.144510, Class: 0.583078, Obj: 0.001186, No Obj: 0.001228, .5R: 0.000000, .75R: 0.000000, count: 1
298: 9.153068, 7.480968 avg, 0.000008 rate, 51.744666 seconds, 298 images
Incorrect training
Region 82 Avg IOU: 0.061959, Class: 0.404846, Obj: 0.520931, No Obj: 0.485723, .5R: 0.000000, .75R: 0.000000, count: 1
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.525058, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.419326, .5R: -nan, .75R: -nan, count: 0
在前面的代码中,IOU描述联合之上的交集,Class表示对象分类-需要接近1的Class值。 Obj是检测到对象的概率,其值应接近1。 NoObj的值应接近0\. 0.5。 R是检测到的阳性样本除以图像中实际样本的比率。
到目前为止,我们已经学习了如何使用 Darknet 来对预训练的 YOLO 模型进行推理,并为自定义图像训练了我们自己的 YOLO 模型。 在下一部分中,我们将概述另一个称为 RetinaNet 的神经网络模型。
特征金字塔网络和 RetinaNet 概述
我们从“第 5 章”,“神经网络架构和模型”了解到,CNN 的每一层本身就是一个特征向量。 与此相关的有两个关键且相互依赖的参数,如下所示:
- 当我们通过各种卷积层将图像的 CNN 提升到全连接层时,我们会确定更多的特征(表面上很强),从简单的边缘到对象的特征再到完整的对象。 但是,这样做时,图像的分辨率会随着特征宽度和高度的减小而深度的增加而降低。
- 不同比例的对象(小到大)受此分辨率和尺寸的影响。 如下图所示,较小的对象在最高层将更难检测,因为其特征将变得非常模糊,以至于 CNN 将无法很好地检测到它:
如前所述,由于小物体的分辨率问题,很难同时检测不同比例的多幅图像。 因此,如上图所示,我们以金字塔形式而不是图像堆叠特征,顶部的尺寸较小,底部的尺寸较大。 这称为特征金字塔。
特征金字塔网络(FPN)由多个特征金字塔组成,这些特征金字塔由每个 CNN 层之间的较高维度和较低分辨率组成。 FPN 中使用此金字塔特征来检测不同比例的物体。 FPN 使用最后一个全连接层特征,该特征将基于其最近的邻居应用 2x 的上采样,然后将其添加到其先前的特征向量中,然后将3 x 3卷积应用于合并的层。 这个过程一直重复到第二个卷积层。 结果是在所有级别上都具有丰富的语义,从而导致不同级别的对象检测。
RetinaNet 由林宗义,Priya Goyal,Ross Girshick,Kakaiming He 在《密集对象检测的焦点损失》中引入, 和皮奥特·多拉尔(PiotrDollár)。 RetinaNet 是一个密集的一级网络,由一个基本的 ResNet 型网络和两个特定于任务的子网组成。 基本网络使用 FPN 为不同的图像比例计算卷积特征映射。 第一个子网执行对象分类,第二个子网执行卷积包围盒回归。
大多数 CNN 对象检测器可分为两类-一级和二级网络。 在诸如 YOLO 和 SSD 的单阶段网络中,单个阶段负责分类和检测。 在诸如 R-CNN 的两阶段网络中,第一阶段生成对象位置,第二阶段评估其分类。 一级网络以其速度而闻名,而二级网络以其准确率而闻名。
已知由于只有几个候选位置实际包含对象,因此一级网络遭受类不平衡的困扰。 该类不平衡使得训练在图像的大部分部分中无效。 RetinaNet 通过引入焦距损失(FL)来解决类别不平衡问题,该焦距微调交叉熵(CE)损失来专注于困难的检测问题。 损失 CE 的微调是通过对损失 CE 应用检测概率(pt)的调制因子(g)来完成的,如下所示:
RetinaNet 通过使用 FL 概念与一级网络的速度相匹配,而与二级网络的精度相匹配。
可以通过以下命令在终端中下载 RetinaNet 的 Keras 版本:
pip install keras-retinanet
在精度和速度方面,YOLO v3 保持平均精度超过 50,并且比 RetinaNet 更快。
总结
在本章中,我们了解了 YOLO 对象检测方法的基本组成部分,并了解了与其他对象检测方法相比,YOLO 如何能够如此快速,准确地检测到对象。 我们了解了 YOLO 的不同演变(原始版本的 YOLO,YOLO v2 和 YOLO v3)及其差异。 我们使用 YOLO 来检测图像和视频文件中的物体,例如交通标志。
我们学习了如何调试 YOLO v3,以便它可以生成正确的输出而不会崩溃。 我们了解了如何使用预训练的 YOLO 进行推断,并了解了使用我们的自定义图像开发新的 YOLO 模型的详细过程,以及如何调整 CNN 参数以生成正确的结果。 本章还向您介绍了 RetinaNet,以及它如何使用特征金字塔的概念来检测不同比例的对象。
在下一章中,我们将学习有关使用语义分割和图像修复的图像内容填充。
八、语义分割与神经样式迁移
深度神经网络的应用不仅限于在图像中找到对象(我们在前面的章节中已经学习过),还可以用于将图像分割成空间区域,从而生成人造图像并从一个图像中迁移样式。 形象给另一个。
在本章中,我们将使用 TensorFlow Colab 执行所有这些任务。 语义分割可预测图像的每个像素是否属于某个类别。 这是用于图像叠加的有用技术。 您将了解 TensorFlow DeepLab,以便可以对图像执行语义分割。 深度卷积生成对抗网络(DCGAN)是强大的工具,可用于生成人造图像,例如人脸和手写数字。 它们也可以用于图像修复。 我们还将讨论如何使用 CNN 将样式从一个图像迁移到另一个图像。
在本章中,我们将介绍以下主题:
- 用于语义分割的 TensorFlow DeepLab 概述
- 使用 DCGAN 生成人工图像
- 使用 OpenCV 修复图像
- 了解神经样式迁移
用于语义分割的 TensorFlow DeepLab 概述
语义分割是在像素级别理解和分类图像内容的任务。 与对象检测不同,在对象检测中,在多个对象类上绘制了一个矩形边界框(类似于我们从 YOLOV3 中学到的知识),语义分割可学习整个图像,并将封闭对象的类分配给图像中的相应像素。 因此,语义分段可以比对象检测更强大。 语义分段的基本架构基于编码器-解码器网络,其中编码器创建一个高维特征向量并在不同级别上对其进行聚合,而解码器在神经网络的不同级别上创建一个语义分段掩码。 编码器使用传统的 CNN,而解码器使用解池,解卷积和上采样。 DeepLab 是 Google 引入的一种特殊类型的语义分段,它使用空洞卷积,空间金字塔池而不是常规的最大池以及编码器-解码器网络。 DeepLabV3+ 是由 Liang-Chieh Chen,Yukun Zhu,George Papandreou,Florian Schro 和 Hartwig Adam 在他们的论文《用于语义图像分割的实用 Atrous 可分离卷积的编码器-解码器》中提出的。
DeepLab 于 2015 年开始使用 V1,并于 2019 年迅速移至 V3+。下表列出了不同 DeepLab 版本的比较:
| DeepLab V1 | DeepLab V2 | DeepLab V3 | DeepLab V3+ | |
|---|---|---|---|---|
| 论文 | 《具有深度卷积网络和全连接 CRF 的语义图像分割》,2015 | 《DeepLab:使用深度卷积网络,空洞卷积和全连接 CRF 进行语义图像分割》,2017 | 《重新思考原子卷积以进行语义图像分割》,2017 | 《具有可分割卷积的语义语义分割的编解码器》,2018 |
| 作者 | 陈良杰,乔治·帕潘德里欧,Iasonas Kokkinos,凯文·墨菲和艾伦·尤里 | 陈良杰,乔治·帕潘德里欧,Iasonas Kokkinos,凯文·墨菲和艾伦·尤里 | 陈良杰,乔治·帕潘德里欧,弗洛里安·施罗夫和哈特维格·Adam | 陈良杰,朱玉坤,乔治·帕潘德里欧,弗洛里安·施罗和哈特维格·Adam |
| 关键概念 | 原子卷积,全连接条件随机场(CRF) | 多孔空间金字塔池(ASPP) | ASPP,图像级特征和批量规范化 | ASPP 和编码器/解码器模块 |
DeepLabV3+ 使用空间金字塔池(SPP)的概念来定义其架构。
空间金字塔池化
我们在“第 5 章”,“神经网络架构和模型”中介绍的大多数 CNN 模型都需要固定的输入图像大小,这限制了输入图像的纵横比和比例。 固定大小约束不是来自卷积运算; 相反,它来自全连接层,该层需要固定的输入大小。 卷积操作从 CNN 的不同层中的图像的边缘,拐角和不同形状生成特征映射。 特征映射在不同的层中是不同的,并且是图像中形状的函数。 它们不会随着输入大小的变化而显着变化。 SPP 代替了最后一个合并层,紧接在全连接层之前,由并行排列的空间容器组成,其空间大小与输入图像的大小成正比,但其总数固定为全连接层数。 空间池化层通过保持过滤器大小固定但更改特征向量的大小来消除输入图像的固定大小约束。
DeepLabV3 的架构基于两种神经网络-空洞卷积和编码器/解码器网络。
空洞卷积
我们在“第 4 章”,“图像深度学习”中介绍了卷积的概念,但是我们没有涉及各种空洞卷积。空洞卷积,也称为膨胀卷积,增加了卷积的视野。 传统的 CNN 使用最大池化和跨步来快速减小层的大小,但这样做也会降低特征映射的空间分辨率。空洞卷积是一种用于解决此问题的方法。 它通过使用 Atrous 值修改跨步来实现此目的,从而有效地更改了过滤器的值字段,如下图所示:
上图显示了速率为 2 的空洞卷积。与深度卷积相比,它跳过了其他每个像元。 实际上,如果应用stride = 2,则3 x 3核就是3 x 3核。与简单的深度方向较小的特征映射相比,空洞卷积增加了视野并捕获了边界信息(与最大池化相比, 缺少对象边界),从而导致图像上下文丰富。 由于其在每个后续层中保持视野相同的特性,空洞卷积用于图像分割。
DeepLabV3 执行几个并行的空洞卷积,所有这些卷积以不同的速率运行,就像我们前面描述的空间池化层概念一样。 上图说明了空洞卷积模块和并行模块彼此相邻堆叠以形成 SPP。 与传统卷积不同,传统卷积与原始图像相比减少了最终特征向量的深度和宽度,而空洞卷积保留了图像大小。 因此,图像的精细细节不会丢失。 在构建具有丰富图像上下文的分割图时,这很有用。
编解码器网络
编码器是获取图像并生成特征向量的神经网络。 解码器执行与编码器相反的操作; 它采用特征向量并从中生成图像。 编码器和解码器一起训练以优化组合损失函数。
编码器-解码器网络可在编码器路径中实现更快的计算,因为不必在编码器路径中扩张特征,并且在解码器路径中恢复了清晰的物体。 编码器-解码器网络包含一个编码器模块,该模块捕获更高的语义信息,例如图像中的形状。 它通过逐渐缩小特征映射来实现。 另一方面,解码器模块保留空间信息和更清晰的图像分割。
编码器和解码器主要在多个比例上使用1 x 1和3 x 3空洞卷积。 让我们更详细地了解它们。
编码器模块
编码器模块的主要功能如下:
- 空洞卷积用于提取特征。
- 输出跨步是输入图像分辨率与最终输出分辨率之比。 其典型值为 16 或 8,这会导致特征提取更加密集。
- 在最后两个块中使用速率为 2 和 4 的空洞卷积。
- ASPP 模块用于在多个尺度上应用卷积运算。
解码器模块
解码器模块的主要功能如下:
1 x 1卷积用于减少来自编码器模块的低级特征映射的通道。3 x 3卷积用于获得更清晰的分割结果。- 4x 上采样。
DeepLab 中的语义分割 - 示例
可在以下由 TensorFlow 管理的 GitHub 页面上找到使用 TensorFlow 训练 DeepLab 的详细代码。
Google Colab 包含基于几个预先训练的模型的内置 DeepLab Python 代码。 可以在这个页面中找到。
Google Colab,Google Cloud TPU 和 TensorFlow
在深入研究示例代码之前,让我们了解 Google 机器学习的一些基本功能,所有这些功能都是免费提供的,以便我们可以开发强大的计算机视觉和机器学习代码:
- Google Colab:您可以从 Google 云端硬盘打开 Google Colab,如以下屏幕截图所示。 如果您是第一次使用它,则必须先单击“新建”,然后单击“更多”,才能将 Google Colab 安装到您的云端硬盘。 Google Colab 可让您无需安装即可打开 Jupyter 笔记本。 它还内置了 TensorFlow,这意味着处理包含给您的所有 TensorFlow 依赖项要容易得多:
上面的屏幕快照显示了 Google Colab 文件夹相对于 Google 云端硬盘的位置。 它使您可以处理.ipynb文件,然后进行存储。
- Google Cloud TPU:这是张量处理单元。 它使您可以更快地运行神经网络代码。 进入 Google Colab 笔记本后,您可以为 Python
.ipynb文件激活 TPU,如以下屏幕截图所示:
如前面的屏幕快照所示,打开 Cloud TPU 将有助于加快处理神经网络的训练和预测阶段的速度。
Google Colab DeepLab 笔记本包含三个示例图像,还为您提供获取 URL 的选项,以便您可以加载自定义图像。 当您获得图像的 URL 时,请确保 URL 末尾带有.jpg。 从互联网上提取的许多图像没有此扩展名,程序会说找不到它。 如果您有自己想要使用的图像,请将其存储在 GitHub 页面上,然后下载 URL。 以下屏幕截图显示了根据mobilenetv2_coco_voctrainaug接收到的输出:
上面的屏幕快照显示了四个不同的图像-停放的自行车,繁忙的纽约街道,城市道路和带家具的房间。 在所有情况下检测都非常好。 应当注意,该模型仅检测以下 20 个类别,以及背景类别:背景,飞机,自行车,鸟 ,船,瓶,公共汽车,车,猫,椅子,牛,餐桌,狗,马,摩托车,人, 盆栽植物,绵羊,沙发,火车,电视。 以下屏幕快照显示了使用不同模型xception_coco_voctrainval运行的相同四个图像:
与 MobileNet 模型相比,对象对异常模型的预测显示出更多的改进。 与 MobileNet 模型相比,对于例外模型,可以清楚地检测出自行车,人和桌子的细分。
MobileNet 是一种有效的神经网络模型,可用于手机和边缘设备。 与常规卷积相反,它使用深度卷积。 要详细了解 MobileNet 和深度卷积,请参阅“第 11 章”,“具有 GPU/CPU 优化功能的边缘深度学习”。
使用 DCGAN 生成人工图像
在“第 5 章”,“神经网络架构和模型”中,我们了解了 DCGAN。 它们由生成器模型和判别器模型组成。 生成器模型采用表示图像特征的随机向量,并通过 CNN 生成人工图像G(z)。 因此,生成器模型返回生成新图像及其类别的绝对概率具有可分割卷积的语义语义分割的编解码器。 判别器(D)网络是二分类器。 它从样本概率,图像分布(p_data)和来自生成器的人造图像中获取真实图像,以生成最终图像P(z)的概率。 从真实图像分布中采样。 因此,判别器模型返回条件概率,即最终图像的类别来自给定分布。
判别器将生成真实图像的概率信息馈送到生成器,生成器使用该信息来改进其预测,以创建人造图像G(z)。 随着训练的进行,生成器会更好地创建可以欺骗判别器的人工图像,并且判别器将发现很难将真实图像与人工图像区分开。 这两个模型相互对立,因此命名为对抗网络。 当判别器不再能够将真实图像与人工图像分离时,模型收敛。
GAN 训练遵循针对几个周期的判别器和生成器训练的替代模式,然后重复进行直到收敛为止。 在每个训练期间,其他组件保持固定,这意味着在训练生成器时,判别器保持固定,而在训练判别器时,生成器保持固定,以最大程度地减少生成器和判别器相互追逐的机会 。
前面的描述应该已经为您提供了 GAN 的高级理解。 但是,为了编码 GAN,我们需要更多地了解模型架构。 让我们开始吧。
生成器
下图显示了 DCGAN 的生成器网络的架构:
从上图中,我们可以看到以下内容:
- 所有具有跨步但没有最大池化的卷积网络都允许该网络在生成器中学习自己的上采样。 注意,最大池化被跨步卷积代替。
- 第一层从判别器获得概率
P(z),通过矩阵乘法连接到下一个卷积层。 这意味着不使用正式的全连接层。 但是,网络可以达到其目的。 - 我们将批量归一化到所有层以重新调整输入,生成器生成器层除外,以提高学习的稳定性。
判别器
下图显示了 DCGAN 判别器网络的架构:
从上图中,我们可以看到以下内容:
- 所有具有跨步但没有最大池化的卷积网络都允许网络在判别器中学习自己的下采样。
- 我们消除了全连接层。 最后的卷积层被展平并直接连接到单个 Sigmoid 输出。
- 我们对除判别器输入层以外的所有层应用批归一化,以提高学习的稳定性。
训练
训练时要考虑的关键特征如下:
- 激活:Tanh
- 随机梯度下降(SGD),最小批量为 128
- 泄漏 ReLU:斜率为 0.2
- 学习率为 0.0002 的 Adam 优化器
- 动量项 0.5:值 0.9 引起振荡
下图显示了训练阶段 DCGAN 的损失项:
当生成器接收到随机输入并且生成器损失定义为其产生假输出的能力时,训练便开始了。 判别器损失定义为其将实际输出与伪输出分离的能力。 梯度用于更新生成器和判别器。 在训练过程中,同时对生成器和判别器进行训练。 训练过程通常只要我们需要同时训练两个模型即可。 请记住,两个模型是相互同步的。
使用 DCGAN 修复图像
图像修补是根据来自相邻点的信息填充图像或视频缺失部分的过程。 图像修补工作流程涉及以下步骤:
-
图像中缺少片段。
-
收集相应缺失部分的像素信息。 这称为层蒙版。
-
向神经网络馈送步骤 1 和 2 中描述的图像,以确定需要填充图像的哪一部分。这意味着,首先对具有缺失部分的图像进行处理,然后对层蒙版进行处理。
-
输入图像经过类似的 DCGAN,如前所述(卷积和反卷积)。 层蒙版允许网络基于其相邻像素数据仅关注缺失部分,并丢弃图像中已经完成的部分。 生成器网络生成伪造的图像,而判别器网络确保最终绘画看起来尽可能真实。
TensorFlow DCGAN – 示例
TensorFlow.org 有一个很好的图像修复示例,您可以在 Google Colab 或您自己的本地计算机上运行。 该示例可以在 Google Colab。
此示例显示了根据 MNIST 数据集训练 GAN,然后生成人工数字的方法。 使用的模型与前面各节中描述的模型相似。
使用 OpenCV 修复图像
OpenCV 提供了两种图像修复方法,如下所示:
cv.INPAINT_TELEA基于论文《一种基于快速行进方法的图像修复技术》,2004 年由 Alexandru Telea 提出。该方法用所有图像的归一化加权和替换待修复邻域中的像素。 附近的已知像素。 那些位于该点附近和边界轮廓上的像素将获得更大的权重。 修复像素后,将使用快速行进方法将像素移动到下一个最近的像素:
import numpy as np
import cv2 as cv
img = cv.imread('/home/.../krishmark.JPG')
mask = cv.imread('/home/.../markonly.JPG',0)
dst = cv.inpaint(img,mask,3,cv.INPAINT_TELEA)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()
cv.INPAINT_NS基于 Bertalmio,Marcelo,Andrea L. Bertozzi 和 Guillermo Sapiro 在 2001 年发表的论文《Navier-Stokes,流体动态以及图像和视频修补》。它连接具有相同强度的点,同时在修复区域的边界匹配梯度向量。 此方法使用流体动态算法:
import numpy as np
import cv2 as cv
img = cv.imread('/home/.../krish_black.JPG')
mask = cv.imread('/home/.../krish_white.JPG',0)
dst = cv.inpaint(img,mask,3,cv.INPAINT_NS)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()
下图显示了最终输出:
如我们所见,这两个预测都部分成功,但并未完全删除该行。
了解神经样式迁移
神经样式迁移是一种技术,您可以通过匹配内容图像和样式图像的特征分布来混合它们,以生成与内容图像相似但在艺术上以样式化的图像样式绘制的最终图像。 可以在 TensorFlow 中以两种不同方式完成样式迁移:
- 在 TensorFlow Hub 中使用预训练的模型。 这是您上传图像和样式的地方,该工具包将生成样式输出。 您可以在这个页面上上传图像。 请注意,TensorFlow Hub 是许多预训练网络的来源。
- 通过训练神经网络来开发自己的模型。 为此,请按照下列步骤操作:
- 选择 VGG19 网络-它具有五个卷积(
Conv2D)网络,每个Conv2D具有四层,然后是全连接层。 - 通过 VGG19 网络加载内容图像。
- 预测前五个卷积。
- 加载没有顶层的 VGG19 模型(类似于我们在“第 6 章”,“使用迁移学习的视觉搜索”中所做的工作),并列出该层的名称。
- VGG19 中的卷积层具有特征提取功能,而全连接层执行分类任务。 如果没有顶层,则网络将仅具有顶层五个卷积层。 从前面的章节中我们知道,初始层传达原始图像的输入像素,而最终层捕捉图像的定义特征和图案。
- 这样,图像的内容就由中间特征映射表示–在这种情况下,这是第五个卷积块。
- 选择 VGG19 网络-它具有五个卷积(
gram 矩阵是向量G = I[i]^T I[j]的内积的矩阵。 在此,I[i]和I[j]是原始图像和样式图像的特征向量。 内积代表向量的协方差,代表相关性。 这可以用样式来表示。
以下代码输入一个 VGG 模型,并从该模型中提取样式和内容层:
vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')
vgg.trainable = False
for layer in vgg.layers:
print(layer.name)
以下结果显示了卷积块及其顺序:
input_2
block1_conv1
block1_conv2
block1_pool
block2_conv1
block2_conv2
block2_pool
block3_conv1
block3_conv2
block3_conv3
block3_conv4
block3_pool
block4_conv1
block4_conv2
block4_conv3
block4_conv4
block4_pool
block5_conv1
block5_conv2
block5_conv3
block5_conv4
block5_pool
我们将使用从前面的输出生成的卷积结果来开发内容层和样式层,如以下代码所示:
#Content layer: content_layers = ['block5_conv2']
#Style layer: style_layers = ['block1_conv1','block2_conv1','block3_conv1', 'block4_conv1', 'block5_conv1']
style_outputs = [vgg.get_layer(name).output for name in style_layers]
content_outputs = [vgg.get_layer(name).output for name in content_layers]
vgg.input = style_image*255
输入的四个维度是批量大小,图像的宽度,图像的高度和图像通道的数量。 255 乘数将图像强度转换为 0 到 255 的比例:
model = tf.keras.Model([vgg.input], outputs)
如前所述,这种样式可以用克矩阵表示。 在 TensorFlow 中,gram 矩阵可以表示为tf.linalg.einsum。
因此,我们可以编写以下代码:
for style_output in style_outputs:
gram_matrix = tf.linalg.einsum(‘bijc,bijd->bcd’, style_outputs,style_outputs)/tf.cast(tf.shape(style_outputs[1]*style_outputs[2]))
损失计算如下:
style_loss = tf.add_n([tf.reduce_mean((style_outputs[name]-style_targets[name])**2) for name in style_outputs.keys()])
style_loss *= style_weight / num_style_layers
content_loss = tf.add_n([tf.reduce_mean((content_outputs[name]-content_targets[name])**2) for name in content_outputs.keys()])
content_loss *= content_weight / num_content_layers
loss = style_loss + content_loss
可以从 TensorFlow 教程中获得最终代码。 代码结构如此处所述。 我在以下图像上运行它,其输出如下:
请注意,图像输出如何从沙漠中的少量石粒过渡到完全充满石头,同时又保持了沙漠的某些结构。 最后的迭代(迭代 1,000)确实显示出一种艺术融合。
总结
在本章中,我们学习了如何使用 TensorFlow 2.0 和 Google Colab 训练神经网络来执行许多复杂的图像处理任务,例如语义分割,图像修复,生成人工图像和神经样式迁移。 我们了解了生成器网络和判别器网络的功能,以及如何可以平衡地同时训练神经网络以创建伪造的输出图像。 我们还学习了如何使用空洞卷积,空间池和编码器/解码器网络来开发语义分割。 最后,我们使用 Google Colab 训练了一个神经网络来执行神经样式迁移。
在下一章中,我们将使用神经网络进行活动识别。
九、使用多任务深度学习的动作识别
动作识别是计算机视觉的关键部分,涉及识别人的手,腿,头和身体的位置,以检测特定的运动并将其分类为众所周知的类别。 困难在于视觉输入(例如,身体凌乱或衣服被覆盖)的变化,类似的动作,不同的类别(如饮用水或使用手持手机的通话)以及获得代表性的训练数据。
本章详细概述了可用于人体姿势估计和动作识别的关键方法。 动作识别将姿势估计方法与基于加速度的活动识别以及基于视频和三维点云的动作识别相结合。 该理论将通过使用 TensorFlow 2.0 的实现解释加以补充。
本章分为四个部分。 前三个讨论了可用于人体姿势估计的三种不同方法,而第四个全部涉及动作识别:
- 人体姿势估计 – OpenPose
- 人体姿势估计 – 栈式沙漏模型
- 人体姿势估计 - PoseNet
- 使用各种方法的动作识别
人体姿势估计 – OpenPose
人体姿势估计是深度神经网络取得巨大成功的另一个领域,并且近年来发展迅速。 在最后几章中,我们了解到深度神经网络结合使用线性(卷积)和非线性(ReLU)运算来预测给定输入图像集的输出。 在姿势估计的情况下,当提供一组输入图像时,深度神经网络会预测关节位置。 图像中的标记数据集由一个边界框组成,该边界框确定图像中的N个人和每人K关节。 随着姿势的改变,关节的方向也会改变,因此通过观察关节的相对位置来表征不同的位置。 在以下各节中,我们将描述可以使用的不同姿势估计方法。
OpenPose 背后的理论
OpenPose 是第一个针对图像或视频中的多个人的开源实时二维姿态估计系统。 它主要由卡内基梅隆大学(CMU)的学生和教职员工开发。 论文的标题是《OpenPose:使用部分亲和力字段进行实时多人 2D 姿势估计》,作者是 Zhe Cao,Gines Hidalgo,Tomas Simon,Shih-En-Wei 和 Yaser Sheikh。 您可以在这个页面上找到该论文。
请注意,OpenPose 首次出现在 CVPR 2017 中,题目为《使用部分亲和力字段进行实时多人 2D 姿势估计》,可在这个页面中找到。 接下来是 2018 年的论文《OpenPose:使用部分亲和力字段的实时多人 2D 姿势估计》,网络得到了进一步改善。
本文的主要发现如下:
- 网络的输入由 VGG-19 模型的前 10 层组成,用于生成一组特征映射
F。 下图显示了 OpenPose 网络的架构:
- OpenPose 网络将特征映射作为输入,并由 CNN 的两个阶段组成:第一阶段使用
T[p]编号预测部分相似性字段(PAF) 迭代次数,而第二阶段则以T[c]迭代次数预测置信度图。 由于以下两个关键指标,2018 年提出的 OpenPose 模型是对 2017 年提出的早期模型的总体改进: - 通过先计算 PAF,再计算置信度图,可将计算时间减少一半。 这不同于两者的同时计算以及用
3 x 3卷积代替3 x 3卷积。 - 改进的 PAF 估计(2018 年论文)通过增加神经网络的深度来改善常规 PAF(2017 年)的置信度图,从而提高了准确率。
- 在下一阶段中,将前一阶段的预测与原始图像特征
F合并在一起,以为图像中的所有人产生二维关键点预测。 在估计的预测,地面真实特征映射和 PAF 之间的每个阶段的末尾都应用了损失函数。 重复此过程几次迭代,从而得到最新的特征映射和 PAF 检测。 - 使用基于特征向量的自下而上的方法,无论图像中有多少人,都可以实现高精度。
- 置信度图是特定特征(身体部位)可以位于任何给定像素中的概率的二维表示。 另一方面,特征映射表示 CNN 给定层中给定过滤器的输出图。
- 网络架构由几个
1 x 1和1 x 1核组成。 每个1 x 1核的输出是连接的。 - OpenPose 是第一个实时多人系统,使用三个独立的 CNN 块检测 135 个关键点:(a)身体和脚部检测,(b)手部检测和(c)人脸检测。
- 在对象检测讨论(“第 5 章”,“神经网络架构和模型”和“第 7 章”,“使用 YOLO 进行对象检测”)中,我们发现,与诸如 SSD 或 YOLO 等单次检测方法相比,诸如 Faster R-CNN 之类的区域提议方法可带来更高的准确率,但速度却更低。 同样,对于人体姿势估计,自下而上的方法与自下而上的方法相比,具有较高的准确率,但速度较低。 自上而下的方法分别为每个由人组成的边界框提供图像。 自下而上的方法提供了由一个人的多个边界框组成的整个图像,从而产生了一个较小分辨率的人的图像。
在 2019 年,OpenPose 的作者以及其他一些人(Gines Hidalgo,Yaadhav Raaj,Haroon Idrees,Donglai Xiang,Hanbyul Joo,Tomas Simon1 和 Yaser Sheikh)提高了 OpenPose 的准确率和检测时间。 HTG0]单网络全身姿势估计。 您可以在这个页面上找到本文。
主要功能如下:
- 不需要重复进行手和脸姿势检测的网络,因此与 OpenPose 相比,它的速度更快。 多任务学习(MTL)用于从四个不同任务中训练单个全身估计模型:身体,面部,手和脚检测。
- 通过连接面部,手和脚的置信度图可以增强关键点置信度图。 使用这种方法,所有关键点都在同一模型架构下定义。
- 网络架构的输入分辨率和卷积层数增加了,以提高整体精度。
- 对于其中只有一个人的图像,检测时间输出比 OpenPose 快 10%。
了解 OpenPose 代码
CMU 使用 OpenPose 模型,而 OpenCV 在其新的深度神经网络(DNN)框架中集成了经过预训练的 OpenPose 模型。 可以从下面的 GitHub 页面下载整个代码块。 该模型使用 TensorFlow 示例,而不是 OpenPose 作者最初使用的 Caffe 模型,可以在这个页面中找到。
可以使用以下命令在终端中执行 OpenCV 的 OpenPose 代码:
python openpose.py --input image.jpg
要开始使用 PC 的网络摄像头,只需在终端中键入以下内容:
python openpose.py
下图显示了针对足球运动员图像的 OpenPose 实现:
该算法容易受到背景图像的影响,如以下棒球运动员的图像所示:
去除背景后,该算法的预测非常好。
让我们来看一下代码的主要功能。 我们将定义关键点,然后构建预测模型:
- 该模型输入 18 个身体部位和姿势对,如下所示:
BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14,"LEye": 15, "REar": 16, "LEar": 17, "Background": 18 }
POSE_PAIRS = [ ["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"],["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"],["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"],["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"],["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"] ]
- 接下来,使用以下代码通过 TensorFlow 实现 OpenPose:
net = cv.dnn.readNetFromTensorflow("graph_opt.pb")
使用tf-pose-estimation在 TensorFlow 中实现 OpenPose。 TensorFlow/model/graph的实际 GitHub 页面可在这个页面中找到。 请注意,可以在这个页面中找到 MobileNetV1 的描述。
- 接下来,使用
cv.dnn.blobFromImage对图像进行预处理(执行减法和缩放):
net.setInput(cv.dnn.blobFromImage(frame, 1.0, (inWidth, inHeight), (127.5, 127.5, 127.5), swapRB=True, crop=False))
- 接下来,我们使用
out = net.forward()预测模型的输出,并获得 MobileNetV1 输出的前 19 个元素:
out = out[:, :19, :, :] .
- 以下代码计算热图,使用 OpenCV 的
minMaxLoc函数查找点值,并在其置信度高于阈值时添加点。 热图是用颜色表示的数据图:
for i in range(len(BODY_PARTS)):
# Slice heatmap of corresponding body's part.
heatMap = out[0, i, :, :]
# Originally, we try to find all the local maximums. To simplify a sample
# we just find a global one. However only a single pose at the same time
# could be detected this way.
_, conf, _, point = cv.minMaxLoc(heatMap)
x = (frameWidth * point[0]) / out.shape[3]
y = (frameHeight * point[1]) / out.shape[2]
# Add a point if it's confidence is higher than threshold.
points.append((int(x), int(y)) if conf > args.thr else None)
- 以下代码块在原始图像中使用
cv.line和cv.ellipse显示关键点:
for pair in POSE_PAIRS:
partFrom = pair[0]
partTo = pair[1]
assert(partFrom in BODY_PARTS)
assert(partTo in BODY_PARTS)
idFrom = BODY_PARTS[partFrom]
idTo = BODY_PARTS[partTo]
if points[idFrom] and points[idTo]:
cv.line(frame, points[idFrom], points[idTo], (0, 255, 0), 3)
cv.ellipse(frame, points[idFrom], (3, 3), 0, 0, 360, (0, 0, 255), cv.FILLED)
cv.ellipse(frame, points[idTo], (3, 3), 0, 0, 360, (0, 0, 255), cv.FILLED)
到目前为止,我们已经使用 OpenPose 通过自下而上的方法来确定多个身体姿势。 在下一节中,我们将使用堆叠的沙漏方法,该方法同时使用了自上而下和自下而上的方法。
人体姿势估计 – 栈式沙漏模型
栈式沙漏模型是由 Alejandro Newell,Kaiyu Yang 和 Jia Deng 于 2016 年在他们的论文“用于人体姿势估计的栈式沙漏网络”中开发的。 可以在这个页面中找到该模型的详细信息。
下图说明了该模型的架构:
该模型的主要功能如下:
- 通过将多个沙漏堆叠在一起,可在所有刻度上重复进行自下而上和自上而下的处理。 该方法导致能够验证整个图像的初始估计和特征。
- 该网络使用多个卷积和一个最大池化层,这导致最终分辨率较低,然后再进行上采样以提高分辨率。
- 在每个最大池化步骤中,平行于主网络添加其他卷积层。
- 输出结果将产生热图,表示每个像素出现关节的概率。
- 该架构充分利用了残差模型。 每个残差都有三层:
1 x 1 Conv2D,可将尺寸从 256 减少到 128 通道3 x 3 Conv2D的 128 个通道1 x 1 Conv2D,可将尺寸从 128 个通道增加到 256 个通道- 该架构从
7 x 7卷积开始,跨步为 2,将输入图像从7 x 7带到64 x 64,从而可以有效地使用 GPU 内存。 - 跨度为 2 的最大
2 x 2合并用于对图像进行下采样。 在执行每个最大池化之前和之后,将残余块添加,然后在上采样到原始大小后再添加回主块。 - 最后的标题块由两个
1 x 1 Conv2D组成。 - 表现最佳的是将八个沙漏模块堆叠在一起。 每个沙漏模块在每个分辨率下都有一个残差模块。
- 该模型大约需要进行 40,000 次迭代,以达到 70% 以上的精度。
- 训练过程需要 FLIC 约 5,000 张图像(用于训练的 4,000 张和用于测试的 1,000 张)和用于 MPII 的 40,000 张带标注的样本(用于训练的 28,000 张和用于测试的 12,000 张)。 FLIC 和 MPII 是两个姿势估计数据库。 FLIC 由从胶片中获取的 5003 张图像(用于训练的 3987 张图像和用于测试的 1,016 张图像)组成,而 MPII 由 40,000 个带标注的样本(用于训练的 28,000 张图像和用于测试的 12,000 张)组成。
- 该网络在 Torch 7 上进行了训练,学习率为
2.5e-4。 在 12 GB 的 NVIDIA Titan X GPU 上进行训练大约需要 3 天。
了解沙漏模型
沙漏模型在 MPII 人类姿态数据集中的所有关节上都达到了最先进的结果,但这是以占用资源密集型网络带宽为代价的。 这是由于每层通道数量众多而导致训练困难。 张峰,朱夏田和 Mao 在题为“快速人体姿态估计”的论文中,将 FastPose 蒸馏(FPD)引入 CVPR 2019。 与沙漏模型相比,FPD 可以实现更快,更经济的模型推断,同时达到相同的模型表现。 主要功能如下:
- 四个沙漏(而不是八个)可以预测 95% 的模型准确率。
- 从 256 个通道下降到 128 个通道只会导致精度降低 1%。
- 首先,在大型沙漏模型(也称为教师姿势模型)上进行训练。 然后,在教师姿势模型的帮助下训练目标学生模型(四个沙漏,128 个通道)。 定义了蒸馏损失函数,以从教师模型中提取知识并将其传递给学生模型。
- 姿势蒸馏损失函数如下:
在这里,K是关节总数,L[pd]是 FPD 的预测关节置信度图, m[ks]是学生模型预测的第k个关节的置信度图,而m[kt]是教师模型预测的第k个关节。
- 总体损失函数如下:
此处, L[fpd]是整体 FPD 损失函数,L[gt]是用于真实情况标注的置信度图,而M是权重函数。
- 使用前面描述的损失函数分别训练教师模型和目标模型。
可在这个页面中找到该模型的 Keras 实现。
请注意,下一部分的目的是详细解释沙漏网络的代码。 这是一个复杂的神经网络模型,其想法是,一旦掌握了这些代码,您就应该能够自己构建一个非常复杂的神经网络模型。 我们不会在前面的 GitHub 页面上详细说明如何运行代码。
在下一部分中,我们将描述模型的架构并进行详细说明。
编码沙漏模型
下图显示了沙漏模型的编码块:
让我们花一点时间来理解前面的图,因为我们将在以下部分中对其进行编码:
- 前模块将图像作为输入和通道数(每层的第三个尺寸,前两个尺寸是宽度和高度)。
- 图像穿过前模块的不同层,最后一个模块连接到左半块。
- 左半块具有四个瓶颈回旋块-
f1,f2,f4和f8,每个块分别具有 1、1/2、1/4 和 1/8 的分辨率。 如果查看架构图,就在堆叠的沙漏感应下,这应该很清楚。 - 每个块的最后一层–
f1,f2,f4和f8–创建一个对应的特征映射,即lf1,lf2,lf4和lf8。 - 特征映射
lf1,lf2,lf4和lf8连接到右半块。 该块的输出为rf1。 - 底部层也从左半块连接到
lf8特征映射。 - 头部快连接到
rf1。 总共有两个头部快。 每个使用1 x 1卷积。
让我们看一下不同的代码块。
argparse包
Python 命令行参数(通过终端输入)允许程序通过parser.add_argument命令获取有关神经网络操作的不同指令。 可以从argparse函数包中导入。
下图显示了 16 种不同的类:
上图使用的代码块如下:
0 - r ankle, 1 - r knee, 2 - r hip, 3 - l hip, 4 - l knee, 5 - l ankle, 6 - pelvis, 7 - thorax, 8 - upper neck, 9 - head top, 10 - r wrist, 11 - r elbow, 12 - r shoulder, 13 - l shoulder, 14 - l elbow, 15 - l wrist
以下代码导入argparse模块,TensorFlow 和 HourglassNet 模型。 它有两种类型的用户可选模型:用于小型网络的 128 个通道和用于大型网络的 256 个通道:
import argparse
import os
import tensorflow as tf
from keras import backend as k
from hourglass import HourglassNet
parser.add_argument("--resume", default=False, type=bool, help="resume training or not")
parser.add_argument("--resume_model", help="start point to retrain")
parser.add_argument("--resume_model_json", help="model json")
parser.add_argument("--init_epoch", type=int, help="epoch to resume")
parser.add_argument("--tiny", default=False, type=bool, help="tiny network for speed, inres=[192x128], channel=128")
args = parser.parse_args()
if args.tiny:
xnet = HourglassNet(num_classes=16, num_stacks=args.num_stack, num_channels=128, inres=(192, 192),outres=(48, 48))
else:
xnet = HourglassNet(num_classes=16, num_stacks=args.num_stack, num_channels=256, inres=(256, 256),outres=(64, 64))
if args.resume:
xnet.resume_train(batch_size=args.batch_size, model_json=args.resume_model_json,model_weights=args.resume_model,init_epoch=args.init_epoch, epochs=args.epochs)
else:
xnet.build_model(mobile=args.mobile, show=True)
xnet.train(epochs=args.epochs, model_path=args.model_path, batch_size=args.batch_size)
训练沙漏网络
沙漏网络已被描述。 在本节中,我们将解释训练网络背后的代码。
如果您想训练自己的沙漏网络,请按照这个页面上的说明进行操作。
训练沙漏网络的代码如下:
def build_model(self, mobile=False, show=False):
if mobile:
self.model = create_hourglass_network(self.num_classes, self.num_stacks, self.num_channels, self.inres, self.outres, bottleneck_mobile)
else:
self.model = create_hourglass_network(self.num_classes, self.num_stacks,self.num_channels, self.inres, self.outres, bottleneck_block)
# show model summary and layer name
if show:
self.model.summary(def train(self, batch_size, model_path, epochs):
train_dataset = MPIIDataGen("../../data/mpii/mpii_annotations.json", "../../data/mpii/images"
inres=self.inres, outres=self.outres, is_train=True)
#here MPIIDataGen is a data generator function (not shown here) - it takes in json file and the images to preapre data for training similar to how we use image data generator in Chapter6.
train_gen = train_dataset.generator(batch_size, self.num_stacks, sigma=1, is_shuffle=True,rot_flag=True, scale_flag=True, flip_flag=True)
csvlogger = CSVLogger(os.path.join(model_path, "csv_train_" + str(datetime.datetime.now().strftime('%H:%M')) + ".csv"))
modelfile = os.path.join(model_path, 'weights_{epoch:02d}_{loss:.2f}.hdf5')
checkpoint = EvalCallBack(model_path, self.inres, self.outres)
xcallbacks = [csvlogger, checkpoint]
self.model.fit_generator(generatepochs=epochs, callbacks=xcallbacks)
前面的代码是如何设置神经网络进行训练的典型示例。 我们在“第 6 章”,“使用迁移学习的视觉搜索”中对此进行了详细介绍。 主要函数如下:
create_hourglass_network是主要模型。train_dataset使用 MPIIDatagen,这是用于输入数据的外部模块。train_gen输入train_dataset并放大图像。- 它包含回调和检查点,因此我们可以在训练过程中了解模型的内部状态。
model.fit_generator开始训练过程。
创建沙漏网络
沙漏模型代码的实际实现将在此处说明。 用于此的代码称为create_hourglass_network。 如前所述,该代码具有以下组件。
前端模块
以下代码描述了前端模块:
def create_front_module(input, num_channels, bottleneck):
_x = Conv2D(64, kernel_size=(7, 7), strides=(2, 2), padding='same', activation='relu', name='front_conv_1x1_x1')(input)
_x = BatchNormalization()(_x)
_x = bottleneck(_x, num_channels // 2, 'front_residual_x1')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(_x)
_x = bottleneck(_x, num_channels // 2, 'front_residual_x2')
_x = bottleneck(_x, num_channels, 'front_residual_x3')
return _x
front_features = create_front_module(input, num_channels, bottleneck)
如前所述,它由一个 Conv2D 块组成,总共有 64 个过滤器,过滤器大小为7 x 7,跨步为 2。该块的输出为(None, 32, 32,6)。 接下来的几行贯穿批量规范化,瓶颈和最大池化层。 让我们定义瓶颈块。
左半块
左半块的代码如下:
def create_left_half_blocks(bottom, bottleneck, hglayer, num_channels):
# create left half blocks for hourglass module
# f1, f2, f4 , f8 : 1, 1/2, 1/4 1/8 resolution
hgname = 'hg' + str(hglayer)
f1 = bottleneck(bottom, num_channels, hgname + '_l1')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(f1)
f2 = bottleneck(_x, num_channels, hgname + '_l2')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(f2)
f4 = bottleneck(_x, num_channels, hgname + '_l4')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(f4)
f8 = bottleneck(_x, num_channels, hgname + '_l8')
return (f1, f2, f4, f8)
前面的代码执行两个特定的操作:
- 它定义分辨率为
1, ½, ¼, and 1/8的过滤器系数(f1, f2, f4, and f8)。 - 对于每个过滤器块,它通过使用 2 的过滤器和 2 的跨度应用最大池化来生成最终输出。
接下来,下面的代码迭代从 0 到 2,以每个过滤器分辨率创建三个过滤器块:
for i in range(2):
head_next_stage, head_to_loss = create_left_half_blocks (front_features, num_classes, num_channels, bottleneck, i)
outputs.append(head_to_loss)
从左到右的连接
如果查看“编码沙漏模型部分开头的图像,您会注意到左右两个块由connect_left_to_right块连接。 用于将左侧块连接到右侧块的代码如下:
def connect_left_to_right(left, right, bottleneck, name, num_channels):
'''
:param left: connect left feature to right feature
:param name: layer name
:return:
'''
_xleft = bottleneck(left, num_channels, name + '_connect')
_xright = UpSampling2D()(right)
add = Add()([_xleft, _xright])
out = bottleneck(add, num_channels, name + '_connect_conv')
return out
注意,每个右块是通过上采样生成的,并被添加到左块以生成最终输出。 在前面的代码中,_xleft显示左侧块,_xright显示右侧块,add函数将两者相加。
右半块
右侧块的代码如下:
def create_right_half_blocks(leftfeatures, bottleneck, hglayer, num_channels):
lf1, lf2, lf4, lf8 = leftfeatures
rf8 = bottom_layer(lf8, bottleneck, hglayer, num_channels)
rf4 = connect_left_to_right(lf4, rf8, bottleneck, 'hg' + str(hglayer) + '_rf4', num_channels)
rf2 = connect_left_to_right(lf2, rf4, bottleneck, 'hg' + str(hglayer) + '_rf2', num_channels)
rf1 = connect_left_to_right(lf1, rf2, bottleneck, 'hg' + str(hglayer) + '_rf1', num_channels)
return rf1
在前面的代码中,lf8, lf4, lf2, and lf1具有左特征。 相应的右图块的特征rf8, rf4, rf2, and rf1是通过将左到右瓶颈图块应用到每个左图特征而生成的。 以下代码通过为每个左侧范围迭代 0 到 2 来应用此逻辑:
for i in range(2):
head_next_stage, head_to_loss = create_right_half_blocks (front_features, num_classes, num_channels, bottleneck, i)
outputs.append(head_to_loss)
头部快
头部快的代码如下:
def create_heads(prelayerfeatures, rf1, num_classes, hgid, num_channels):
# two head, one head to next stage, one head to intermediate features
head = Conv2D(num_channels, kernel_size=(1, 1), activation='relu', padding='same', name=str(hgid) + '_conv_1x1_x1')(rf1)
head = BatchNormalization()(head)
# for head as intermediate supervision, use 'linear' as activation.
head_parts = Conv2D(num_classes, kernel_size=(1, 1), activation='linear', padding='same',name=str(hgid) + '_conv_1x1_parts')(head)
# use linear activation
head = Conv2D(num_channels, kernel_size=(1, 1), activation='linear', padding='same',name=str(hgid) + '_conv_1x1_x2')(head)
head_m = Conv2D(num_channels, kernel_size=(1, 1), activation='linear', padding='same',name=str(hgid) + '_conv_1x1_x3')(head_parts)
head_next_stage = Add()([head, head_m, prelayerfeatures])
return head_next_stage, head_parts
头部有两个主要块,每个块由一个1 x 1 Conv2D过滤器组成。 它使用激活层和填充。 作为复习,请参考“人体姿势估计-沙漏模型”部分下显示的沙漏架构图,以了解以下组件之间的联系:
- 进入下一个阶段
- 前往中间特征
以下逻辑将头部快应用于 0 到 2 的每个范围,分别对应于左块和右块:
for i in range(2):
head_next_stage, head_to_loss = create_head_blocks (front_features, num_classes, num_channels, bottleneck, i)
outputs.append(head_to_loss)
训练沙漏
沙漏网络在 FLIC 人体姿势数据集上进行了训练,该数据集包含 5,000 张图像(用于训练的 4,000 和用于测试的 1,000)和具有 40,000 图片(用于训练的 28,000 和用于测试的 12,000)的 MPII 人体姿势数据集。
请注意,在本书中,我们没有使用 MPII 数据集来训练沙漏模型。 提供了有关 MPII 数据集的信息,以解释如何训练沙漏模型进行人体姿势估计。
在大约 20,000 次训练迭代中,所有关节的平均准确率达到大约 70%,最大准确率大约为 80%。
到目前为止,我们已经讨论了 OpenPose 和姿势估计的栈式沙漏方法。 在下一节中,我们将讨论 PoseNet。
人体姿势估计 - PoseNet
TensorFlow 发布了 PoseNet 模型,该模型用于使用浏览器检测人体姿势。 它可以用于单个姿势和多个姿势。
PoseNet 基于 Google 的两篇论文。 一种使用自上而下的方法,而另一种使用自下而上的方法。
自上而下的方法
第一篇论文的标题为《在野外进行精确的多人姿势估计》,由 George Papandreou,Tyler Zhu,Nori Kanazawa,Alexander Toshev,Jonathan Tompson,Chris Bregler 和 Kevin Murphy 撰写。 您可以在这个页面上找到该论文。
这是一种由上而下的两阶段方法:
- 使用带有 ResNet-101 网络主干的 Faster R-CNN 确定边界框坐标(
x, y, w, h)。 在“第 5 章”,“神经网络架构和模型”中引入了更快的 R-CNN 和 ResNet,但在“第 10 章”,“使用 R-CNN,SSD 和 R-FCN 进行对象检测”中将在实际实现中一起使用它们。这种分类仅针对人类进行。 调整返回的所有边界框,使其具有固定的纵横比,然后将其裁剪为353 x 257。 - 使用 ResNet-101 估计位于每个边界框内的人的 17 个关键点,用
3 x 17输出替换最后一层。 已经使用分类和回归相结合的方法来找到人体的每个位置与 17 个关键点位置中的每个位置之间的偏移向量或距离。 每 17 个关键点计算一次距离小于半径的概率,从而得出 17 个热图。 使用 Sigmoid 激活函数训练具有 17 个热图的 ResNet-101 模型。
自下而上的方法
第二篇论文的标题为《PersonLab:使用自下而上的,基于部分的几何嵌入模型的人员姿势估计和实例细分》,由第一篇论文的许多相同作者撰写; 分别是 George Papandreou,Tyler Zhu,Chen-Chieh Chen,Spyros Gidaris,Jonathan Tompson 和 Kevin Murphy。 您可以在这个页面上找到该论文。
在这种无框的,自下而上的方法中,作者使用卷积神经网络检测单个关键点及其相对位移,以将关键点分组为人的姿势实例。 另外,设计了几何嵌入描述符来确定人员分割。 该模型使用 ResNet-101 和 ResNet-152 架构进行训练。
像自顶向下方法一样,定义了一个 32 像素大小的半径,对应于 17 个关键点中的每一个。 然后,如果图像中的空间位置在关键点位置的半径之内,则以热图概率 1 定义 17 个独立的二元分类任务; 否则,将其设置为 0。与自顶向下方法一样,图像位置和关键点之间的距离也称为短距离偏移向量。 因此,存在 17 个这样的偏移向量。 与自顶向下方法一样,热图和偏移向量使用二维 Hough 得分图分组在一起。
在这种方法中,我们有一个与关键点相关联的人,但是当图像中存在多个人的实例时,这不允许我们对每个人的关键点进行分组。 为了解决这个问题,开发了 32 个独立的中距离二维偏移量字段来连接成对的关键点。 除此之外,还开发了一个由单个1 x 1 Conv2D层组成的简单语义分割模型,该模型可以执行密集逻辑回归并计算每个图像像素至少属于一个人的概率。 语义分段的详细信息在“第 8 章”,《语义分段和神经样式迁移》中进行了描述。
有关预测图像的信息,请参阅以下有关自上而下与自下而上的文章和这个。 这两篇论文都包含大量示例图像。
预测关键点在两种方法之间或多或少是相同的,但是自上而下的方法从绘制边界框开始,而自下而上的方法执行语义分割。
PoseNet 实现
到目前为止,我们已经讨论了 PoseNet 自上而下和自下而上方法背后的理论。 在本节中,我们将使用 PoseNet 识别这些操作。 有关如何实现 PoseNet 模型的详细信息,请参见这里。 查看此链接以了解 PoseNet 的文档。
接下来,我们将进行现场演示。 此实时演示是使用网络摄像头完成的,可以通过在网络浏览器中键入以下链接来启动。
尽管自上而下和自下而上的方法使用 ResNet-101 模型,但 PoseNet 模型使用 MobileNetV1 或 ResNet-50。 下表说明了它们之间的区别:
| MobileNet V1 | ResNet 50 | |
|---|---|---|
| 跨步 | 16 | 32 |
| 输入分辨率 | 宽度:640,高度:480 | 宽度:257,高度:200 |
PoseNet 网站说明了如何调整模型参数。 可以使用以下屏幕快照中显示的参数窗口来调整模型参数:
在前面的屏幕截图中,我们可以通过更改输入图像的分辨率来演示这两种模型,这似乎效果最好。
下图比较了八种不同配置(对于 MobileNetV1 和 ResNet,分辨率为 200 和 500 的上下姿势)的 PoseNet 输出:
上图显示,当人的手举起时,ResNet 平均比 MobileNetV1 更准确。 当手的位置向下时,表现大致相同。 同样,与 500 分辨率相比,200 分辨率可实现更好的关键点预测。 边界框选项可用,但未显示。 下图显示了用于其他配置的 ResNet 的边界框:
请注意边界框的大小和位置在不同方向上如何变化。 关键点存储在向量中。 生成的关键点之间的角度可用于预测动作。 上一个图像由三个不同的动作组成–侧向运动,向上和向下运动。 这些动作的关键角度不会重叠,因此预测将是可靠的。
将人体姿势应用于手势识别
到目前为止,我们已经学习了如何在给定的关键点上进行训练以生成人体姿势。 手势识别的过程类似。 请按照以下步骤执行手势识别以进行手部运动:
-
收集不同手的位置的图像 - 上,下,左和右。
-
调整图像大小。
-
此时,您可以用关键点标记图像。 如果为关键点标记图像,则每个图像都必须用相应的关键点表示。
-
将图像及其相应的标签加载到两个不同的数组中。
-
下一步是执行图像分类,类似于我们在“第 6 章”,“使用迁移学习进行视觉搜索”时所做的工作。
-
CNN 模型最多可以包含三个
Conv2D层,一个最大合并层和一个 ReLU 层。 -
对于关键点估计,而不是分类,我们将使用每个关键帧手势位置之间的距离,并选择具有最小距离的手势。
到目前为止,我们已经学习了如何开发用于训练的二维神经网络。 我们开发的网络也可以用于生产。
使用各种方法的动作识别
加速度计测量加速度的x,y和z分量,如下图所示:
加速度计的这一特性使其可以放置在可穿戴设备中,例如安装在腕带上的手腕上的手机,智能手表,甚至是鞋子中,以测量加速度的 XYZ 分量。 在本节中,我们将学习如何使用神经网络分析加速度计数据以识别人类活动。 我们将使用 TensorFlow 开发机器学习模型。 这是本书中唯一讨论无图像的原始数据以及如何将其传递到神经网络以开发模型并从中进行推断的章节。
人类活动识别涉及基于加速度计数据对不同类型的活动进行分类。 这里的挑战是关联从不同类型的人体运动生成的加速度计数据,并基于不同的人体运动和活动来区分相似的加速度计轨迹。 例如,当左手移动和右手移动安装到人的腰部时,可能会产生类似的加速度计数据。 这减轻了加速度计数据应与姿势估计或视频图像数据结合的事实。 在本节中,我们将讨论可用于人类活动识别的两种不同工具。
基于加速度计识别动作
此方法涉及以下步骤:
- 处理输入的加速度计数据:加速度计数据对其位置敏感。 例如,如果安装在腰部区域,则与安装在手臂中相比,手部运动不会在加速度计中看到很大的变化。 另外,对于不同的位置,需要收集不同的数据,然后进行组合。
- 准备数据以便可以将其输入到 TensorFlow 中:使用
tf.data.Dataset加载数据以开发简单,高效的数据管道。tensor_slices命令从输入中提取一片数据。 - 开发 CNN 模型并对其进行训练:一层或两层密集层,最后具有
flatten和 Softmax 函数。 - 检查测试数据:对照测试数据验证数据。
请参阅以下 GitHub 页面上的代码,以获取遵循这些步骤的代码示例。
在上一个链接中可以找到两个文件:Chapter9_TF_Accelerometer_activity.ipynb和sample.csv。 下载两个文件,并将它们放在同一文件夹下。
sample.csv文件是一个示例 CSV 文件,其中包含用于六个不同动作的加速度计(x, y, z)数据:慢跑(0),步行(1),楼上(2),楼下(3) ,坐(4)和站立(5),每个动作都包含 5,000 个数据点。 在您的实际情况下,这些数据值可能会根据放置位置和所使用的加速度计的类型而有所不同。 最好使用相同的加速度计进行训练数据进行推理,以避免推理错误。
接下来,根据索引文件将数据分为两部分:训练和测试。 在这里,我们将评估两个不同的分割 18 和 28,这意味着在一种情况下,如果索引文件小于 18,则数据属于train文件夹; 否则,它属于test文件夹。 该模型加载了三个密集(全连接)的层,分辨率为 128。 最终的 softmax 层被 Sigmoid 函数代替。 下图显示了在三种不同情况下模型的迭代:
- Softmax,训练与测试的索引为 18
- Sigmoid 函数,在索引 18 处分割,用于训练与测试
- Softmax,训练与测试的索引为 28:
前面的数据表明,每次迭代大约需要 40 秒,最终精度约为 0.97。 下图以图形方式说明了这一点:
前面的曲线表明,在所研究的三个条件下,训练的精度基本相同。 为了进一步分析这一点,让我们看一下下图中所示的置信度图:
混淆矩阵指示测试数据与预测数据的比较程度。 在这里,我们可以看到在索引 18 处进行了训练分割的 Softmax 函数比其他站立和步行案例提供了更好的结果。 不出所料,Softmax 与 Sigmoid 激活函数没有导致任何显着差异。 一旦开发了模型,就可以使用模型的预测函数来预测实际测试情况下的数据。
将基于视频的动作与姿势估计相结合
动作识别可以是二维的,也可以是三维的。 二维动作识别方法使用人体的关节信息,以关键点表示。 这些关键点以称为特征映射的向量表示。 另一方面,三维动作识别方法不仅需要特征映射,还需要全身的骨架数据。 可以使用深度传感器(例如 Microsoft Kinect 或 Intel RealSense)获得此数据。 在 2018 年,Diogo C.Luvizon,David Picard 和 Hedi Tabia 介绍了他们的论文 《使用多任务深度学习的 2D/3D 姿势估计和动作识别》。 可以在这里中找到本文的详细信息。
在本文中,作者在一个多任务框架中将基于高级人体关节的姿势信息与低级视觉特征(来自对象识别和特征识别)集成在一起。 该方法能够进行二维和三维动作识别。 使用体积表示将二维姿态图扩展为三维图。
这些技术的组合有助于使动作识别对类似身体的关节运动(如饮用水和打个电话)的抵抗力更强。
使用 4D 方法的动作识别
4D 动作识别意味着体积表示的三维动作是时间的函数。 可以将其视为对动作进行批量跟踪。 全全友和郝江提出了一种新颖的 4D 方法,名为《Action4D:人群和杂物中的在线动作识别》。可以在这个页面中找到本文的详细信息。
该方法使用 4D 表示跟踪人类,并在混乱和拥挤的环境中识别他们的行为。 本文的概念如下:
- 使用多个 RGBD 图像为每个场景创建三维点云。
- 在拥挤场景中的检测得到了创新的跟踪建议的补充,该提议不使用背景减法,这意味着它在拥挤空间中不太容易出错。
- 跟踪过程通过训练 3 维 CNN(具有 3 维卷积,ReLU 和池化层)来使用候选人提案,以将每个候选量分类为人与非人。
- 使用一系列三维体积识别动作,这些三维体积经过一系列三维卷积和池化层(称为 Action4D)。
总结
在本章中,我们了解并实现了三种不同的姿势估计方法-OpenPose,栈式沙漏和 PostNet。 我们学习了如何使用 OpenCV 和 TensorFlow 预测人类关键点。 然后,我们了解了栈式沙漏方法的详细理论和 TensorFlow 实现。 我们向您展示了如何在浏览器中评估人体姿势,以及如何使用网络摄像头实时估计关键点。 然后将人体姿势估计与动作识别模型链接,以演示如何使用两者来提高准确率。 基于加速的代码展示了如何使用 TensorFlow 2.0 加载数据,训练模型和预测动作。
在下一章中,我们将学习如何实现 R-CNN 并将其与其他 CNN 模型(如 ResNet,Inception 和 SSD)结合使用,以提高对象检测的预测,准确率和速度。
第 1 节:计算机视觉和神经网络概论
在本节中,您将加深对理论的理解,并学习有关卷积神经网络在图像处理中的应用的动手技术。 您将学习关键概念,例如图像过滤,特征映射,边缘检测,卷积运算,激活函数,以及与图像分类和对象检测有关的全连接和 softmax 层的使用。 本章提供了许多使用 TensorFlow,Keras 和 OpenCV 的端到端计算机视觉管道的动手示例。 从这些章节中获得的最重要的学习是发展对不同卷积运算背后的理解和直觉-图像如何通过卷积神经网络的不同层进行转换。
在本节结束之前,您将能够执行以下操作:
- 了解图像过滤器如何转换图像(第 1 章)
- 应用各种类型的图像过滤器进行边缘检测(第 1 章)
- 使用 OpenCV 轮廓检测和定向梯度直方图(HOG)检测简单对象(第 1 章)
- 使用尺度不变特征变换(SIFT),本地二进制模式(LBP)模式匹配以及颜色匹配来查找对象之间的相似性(第 1 章和第 2 章)
- 使用 OpenCV 级联检测器进行面部检测(第 3 章)
- 从 CSV 文件列表将大数据输入到神经网络中,并解析数据以识别列,然后可以将其作为
x和y值馈入神经网络(第 3 章) - 面部关键点和面部表情识别(第 3 章)
- 为面部关键点开发标注文件(第 3 章)
- 使用 Keras 数据生成器方法将大数据从文件输入到神经网络(第 4 章)
- 构建自己的神经网络并优化其参数以提高准确率(第 4 章)
- 编写代码以通过卷积神经网络的不同层来变换图像(第 4 章)
本节包括以下章节:
- “第 1 章”,“计算机视觉和 TensorFlow 基础知识”
- “第 2 章”,“使用本地二进制模式的内容识别”
- “第 3 章”,“使用 OpenCV 和 CNN 进行面部检测”
- “第 4 章”,“图像深度学习”
第 2 节:使用 TensorFlow 的计算机视觉高级概念
在本节中,您将基于在上一节中学到的知识来执行复杂的计算机视觉任务,例如视觉搜索,对象检测和神经样式迁移。 您将巩固对神经网络的理解,并使用 TensorFlow 进行许多动手的编码练习。
在本节结束之前,您将能够执行以下操作:
- 对各种神经网络模型有基本的了解,包括 AlexNet,VGG,ResNet,Inception,基于区域的 CNN(RCNN),生成对抗网络(GAN),强化学习和迁移学习(第 5 章)
- 学习一些著名模型用于图像识别和对象检测的技术(第 5 章)
- 使用 Keras 数据生成器和
tf.data将图像及其类别输入到 TensorFlow 模型中(第 6 章) - 使用迁移学习的家具图像开发 TensorFlow 模型,并使用该模型对家具图像进行视觉搜索(第 6 章)
- 对图像执行边界框标注以生成
.xml文件,并将其转换为.txt文件格式,以输入到 YOLO 对象检测器中(第 7 章) - 了解 YOLO 和 RetinaNet 的功能,并学习如何使用 YOLO 检测物体(第 7 章)
- 训练 YOLO 对象检测器并优化其参数以完成训练(第 7 章)
- 使用 TensorFlow DeepLab 执行语义分割,并编写 TensorFlow 代码以在 Google Colab 中进行神经样式迁移(第 8 章)
- 使用 DCGAN 生成人工图像,并使用 OpenCV 进行图像修复(第 8 章)
本节包括以下章节:
- “第 5 章”,“神经网络架构和模型”
- “第 6 章”,“使用迁移学习的视觉搜索”
- “第 7 章”,“使用 YOLO 的对象检测”
- “第 8 章”,“语义分割和神经样式迁移”
第 3 节:使用 TensorFlow 的计算机视觉的高级实现
在本节中,您将基于从上一节中获得的理解,并开发更新的概念并学习用于动作识别和对象检测的新技术。 在本节中,您将学习不同的 TensorFlow 工具,例如 TensorFlow Hub,TFRecord 和 TensorBoard。 您还将学习如何使用 TensorFlow 开发用于动作识别的机器学习模型。
在本节结束之前,您将能够执行以下操作:
- 理解理论并开发各种动作识别方法(例如 OpenPose,Stacked HourGlass 和 PoseNet)的直觉(第 9 章)
- 分析 OpenPose 和 Stacked HourGlass 代码,以加深对如何构建非常复杂的神经网络以及连接其不同模块的理解。 希望您可以通过这次学习来构建自己的复杂网络(第 9 章)
- 使用 TensorFlow PoseNet 通过网络摄像头进行动作识别(第 9 章)
- 了解各种类型的对象检测器,例如 SSD,R-FCN,更快的 R-CNN 和 MaskR-CNN(第 10 章)。
- 了解如何在 TFRecord 中转换图像和标注文件以输入到 TensorFlow 对象检测 API(第 10 章)
- 了解如何使用自己的图像来使用 TensorFlow 对象检测 API 训练模型并对其进行推理(第 10 章)
- 了解如何使用 TensorFlow 集线器进行对象检测以及如何使用 TensorBoard 可视化训练进度(第 10 章)
- 了解与对象检测相关的 IOU,ROI,RPN 和 ROI 对齐(第 10 章)。
- 了解如何使用 Mask R-CNN 对图像进行分割(第 10 章)
- 了解不同的 OpenCV 以及基于连体网络的对象跟踪方法,并将其用于视频文件(第 10 章)
本节包括以下章节:
- “第 9 章”,“使用多任务深度学习的动作识别”
- “第 10 章”,“使用 R-CNN,SSD 和 R-FCN 进行对象检测”
第 4 节:边缘和云端的 TensorFlow 实现
在本部分中,您将使用到目前为止获得的所有计算机视觉和 CNN 知识,在边缘设备中打包,优化和部署模型,以解决现实生活中的计算机视觉问题。 在本地计算机上训练大型数据集需要花费时间,因此您将学习如何打包数据并上传到云中的容器,然后开始训练。 您还将看到如何克服一些常见的错误以完成训练并成功生成模型。
在本节结束之前,您将能够执行以下操作:
- 了解边缘设备如何使用各种硬件加速和软件优化技术基于神经网络模型以最小的延迟进行推理(第 11 章)
- 了解 MobileNet 模型的理论,因为它的速度通常会部署在边缘设备中(第 11 章)
- 使用 Intel OpenVINO 工具包和 TensorFlow Lite 在 RaspBerry Pi 中部署神经网络模型进行对象检测(第 11 章)
- 通过在 Android Studio 和 Xcode 中使用 TensorFlow Lite 部署模型来在 Android 手机和 iPhone 上执行对象检测(第 11 章)
- 使用 Create ML 训练自定义对象检测器,并使用 Xcode 和 Swift 将其部署在 iPhone 上(第 11 章)
- 对各种云平台的基础结构有一个总体了解- Google 云平台(GCP), Amazon Web Services(AWS) ,以及 Microsoft Azure 云平台(第 12 章)
- 为使用 GCP,AWS 和 Azure 的自定义对象检测开发端到端机器学习平台(第 12 章)
- 了解如何使用 TensorFlow 进行大规模训练和打包(第 12 章)
- 使用 GCP,AWS 和 Azure 执行可视搜索(第 12 章)
本节包括以下章节:
- “第 11 章”,“通过 CPU/GPU 优化在边缘设备上进行深度学习”
- “第 12 章”,“用于计算机视觉的云计算平台”