你可能已经比较熟悉基本网络(只是你之前并没听到它被称作基本网络而已)。基本网络就是你常用的分类 CNN 架构此内容不能显示在一个框架中,包括:
通常这些网络在大数据集上进行预训练来进行分类,例如 ImageNet,它们可以学习到很多具有鉴别能力的滤波器。
目标检测框架由很多组成部分和子模块构成。例如,Faster R-CNN 框架包括:
在使用 SSD(单步检测器,single shot detectors)时,具有以下的组成部分:
请记住,基本网络只是整个深度学习目标检测框架的众多组件之一,上文图 4 描述了 SSD 框架内部的 VGG-16 网络。通常,我们需要在基本网络上进行「网络手术」。这种修改:
这里的「网络手术」是一种口语化的说法,它的意思是移除基本网络中的一些原始卷积层,将它们用新层替代。网络手术也是讲究策略的,我们移除一些不需要的部分,然后用一组新的部分来替代它们。
然后,当我们开始训练我们的框架进行目标检测时,(1)新层、模块和(2)基本网络的权重都被修改了。
再强调一次,综述关于不同深度学习目标检测框架是如何工作的(包括基本网络所起的作用)并不属于本文的探讨范围。
如果你对深度学习目标检测的完整综述(包括理论和实现)感兴趣,请参考机器之心曾经发过的文章:。
我是如何计算一个深度学习目标检测器的准确度的?
在评价目标检测器的性能时我们使用了一个叫做均值平均精度(mAP)的指标,它是以我们数据集中所有类别的交并比(IoU)为基础的。
图 5: 在这个交并比的可视化例子中,标注边界框(绿色)可以与预测的边界框(红色)进行对比。IoU 与 mAP 一起被用来评价一个深度学习目标检测器的精度。计算 IoU 的简单方程如图 5(右)所示。
你通常会发现 IoU 和 mAP 被用于评价 HOG+线性 SVM 检测器、Haar cascades 以及基于深度学习的方法的性能;但是请记住,实际用于生成预测边界框的算法并不是那么重要。
任何一个以预测边界框作(以及可选择的标签)为输出的算法都可以用 IoU 来评价。更一般的地,为了使用 IoU 来评价任意一个目标检测器,我们需要:
1. 真实的边界框(也就是测试集中表明我们的目标在图像的哪个位置的人工标签)
2. 模型预测到的边界框
3. 如果你想一起计算召回率和精度,那么还需要真实类别标签和预测类别标签
在图 5(左)中,我展示了真实边界框(绿色)与预测边界框(红色)相比的可视化例子。IoU 的计算可以用图 5 右边的方程表示。
仔细检查这个方程你会发现,IoU 就是一个比值。在分子项中,我们计算了真实边界框和预测边界框重叠的区域。分母是一个并集,或者更简单地说,是由预测边界框和真实边界框所包括的区域。两者相除就得到了最终弄的得分:交并比。
图 6:为了计算目标检测器的 mAP@0.5,我们执行了以下计算。对于所有被标记为「正检测」(positive detection)、具备至少 0.5 的交并比(IoU)的对象,我们对所有 N 个类别计算 IoU (>0.5) 均值,然后对 N 个均值再求平均。这个指标就是 mAP@0.5。
为了在我们的数据集中评估目标检测器,我们需要同时基于以下两者的 IoU 来计算 mAP:
1. 基于每个类别(也就是说每个类别的平均 IoU);
2. 数据集中所有类别(也就是说所有类别平均 IoU 的均值,所以这个术语就是平均精度均值)。
为了计算每个类别的平均精度,我们在所有的数据点上计算某个类别的 IoU。一旦我们计算出了一个类别在每个数据点的 IoU,我们对它们求一次平均(第一次平均)。
为了计算 mAP,我们对所有的 N 个类别计算平均 IoU,然后对这 N 个平均值取平均值(均值的平均)。
通常我们使用 mAP@0.5,表示测试集中要被标记为「正检测」的目标必须具备的条件,真值不小于 0.5 的 IoU(并可以被正确地标记)。这里的 0.5 是可以调整的,但是在所有的目标检测数据集和挑战中,0.5 是一个相对标准的数值。
再次强调,这只是一个关于目标检测评价指标的快速指南,所以我将整个过程简化了一些。
使用 OpenCV 进行基于深度学习的目标检测
我们已经在本文以及之前的博客中讨论了深度学习和目标检测。出于完整性考虑,我们将在本文中概述实际的代码。
我们的例子包含以 MobileNet 作为基础模型的单次检测器(SSD)。该模型由 GitHub 用户 chuanqi305()在 COCO 数据集上训练得到。更多细节请浏览我之前的文章(),这篇文章介绍了该模型以及相关的背景信息。
让我们回到 Ezekiel 在本文开始提出的第一个问题上。
1. 我该如何过滤/忽略那些我不感兴趣的类?
我会在下面的示例代码中回答这个问题,但是首先你需要准备一下系统:
系统准备好之后,创建一个新文件,命名为 filter_object_detection.py。下面让我们开始:
在第 2 到 8 行中,我们导入了所需的包和模块,尤其是 imultils 和 OpenCV。我们会使用我的 VideoStream 类处理从摄像头获取的帧。
我们已经具备了所需的工具,接着我们来解析命令行参数:
我们的脚本在运行时需要两个命令行参数:
你还可以有选择性地指定–confidence,这是过滤弱检测的阈值。
我们的模型可以预测 21 个对象类别:
CLASSES 列表包含该网络训练时的所有类别标签(也就是 COCO 中的标签)。
对 CLASSES 列表的一个常见误解是你可以:
1. 向列表增加一个新的类别标签;
2. 或者从列表移除一个类别标签。
……以及以为网络可以自动「了解」你想要完成的任务。
不是这样的。
你不能简单地修改文本标签列表,让网络自动修改自己,在非训练所用数据上学习、增加或者移除模式。这并不是神经网络的运行方式。
也就是说,有一个快速的技巧,你可以使用它来过滤或者忽略你不感兴趣的预测。
解决方案就是:
1. 定义一个 IGNORE 标签集合(即网络是在这个类别标签列表上进行训练的,但你现在想忽略这些标签)。
2. 对一个输入图像/视频帧进行预测。
3. 忽略类别标签存在于 IGNORE 集合中的所有预测结果。
在 Python 中实现时,IGNORE 集合是这样的:
这里我们忽略所有具有类别标签「person」的预测对象(用于过滤的 if 语句会在后续内容中介绍)。
你可以很容易地增加额外的元素(CLASS 列表中的类别标签)来忽略该集合。
接下来,我们将生成随机的类别/框颜色,加载模型,然后启动视频流:
第 27 行中名为 COLORS 的随机数组为 21 个类别中的每一个随机生成颜色。这些颜色会在后边用于显示。
第 31 行中使用 cv2.dnn.readNetFromCaffe 函数加载我们的 Caffe 模型,我们所需的两个命令行参数作为参数被传递。
然后我们将 VideoStream 对象实例化为 vs,并开始 fps 计数(36-38 行)。2 秒钟的 sleep 让我们的摄像头有足够的预热时间。
现在我们已经准备好在来自摄像头的视频帧中进行循环,并将它们发送到我们的 CNN 目标检测器中:
在第 44 行,我们抓取 1 帧,然后重新调整它的大小并保留用于显示的长宽比(第 45 行)。
我们从中提取高度和宽度,稍后会用到(第 48 行)。
第 48 行和 49 行从这一帧中生成 blob。要了解更多 blob,以及如何使用 cv2.dnn.blobFromImage 函数构建 blob,请在以前的博文中查看所有细节()。
下一步,我们将 blob 发送到神经网络中来检测目标(54-55 行)。
循环检测:
从第 58 行开始检测循环。
对于每一次检测,我们都提取 confidence(#61 行),然后将它与置信度阈值进行比较(#65 行)。当 confidence 超过最小值(默认的 0.5 可以通过命令行参数进行修改),我们可以认为这次检测是一次积极有效的检测,可以继续进行处理。
首先,我们从 detections 中提取类别标签索引(#68)。
然后,回到 Ezekiel 的第一个问题,我们可以忽略 INGNORE 集合中的类别(#72—73)。如果这个类别是要被忽略的,我们只需返回到顶部的检测循环(不会显示这个类别的标签或边界框)。这符合我们的「quick hack」解决方案。
否则,我们检测到的目标就在白名单中,我们需要在该帧中显示对应的类别标签和矩形框:
在这段代码中,我们提取出了边界框的坐标(#77-78),然后画出这帧的标签和矩形框(#81-87)。
每个类别的标签和矩形框都是同样的颜色,也就是说,同一类别的对象都会具有相同的颜色(即视频中所有的「boats」都具有相同颜色的标签和边界框)。
最后,仍然在这个 while 循环中,我们将结果显示在屏幕上:
我们显示出这一帧,并且捕捉按键(#90-91)。
如果 q 键被按下,则我们通过跳出循环来结束程序(#94-95)。
否则,我们会继续更新 fps 计数(#98),并且继续抓取并分析视频帧。
在后面几行中,当循环中断后,我们会显示时间+fps(帧每秒)指标然后清空。
运行你的深度学习目标检测器
为了运行今天的脚本,你需要滚动到下面的「下载」部分来抓取文件。
当你提取到文件之后,打开一个终端,切换到已下载代码+模型的路径。并在这里执行以下命令:
图 6: 使用同一个模型的实时深度学习目标检测演示,在右边的视频中我在程序中忽略了某些目标类别。
在上边的动图中,你在左边可以看到「person」类别被检测到了。这是由于 IGNORE 是空的。在右边的动图中,你可以看到我没有被检测到,这是由于我把「person」增加到了 IGNORE 集合了。
尽管我们的深度学习目标检测器仍然从技术上检测到了「person」类别,但是我们的后期处理代码将它过滤出来了。
排除故障的第一步是检查你是否连接了摄像头。如果这个是正常的,也许你会在你的终端中看到以下错误信息:
如果你看到这个信息,那说明你没有向程序传递「命令行参数」。如果他们不熟悉 Python、argparse 以及命令行参数的话()。
这是 PyImageSearch 读者最常遇见的问题。查看一下这个链接,看看你是否存在这个问题。带有注释的完整视频在这里:
如何向深度学习目标检测器添加或者删除类别?
图7:深度学习目标检测的微调过程和迁移学习。
正如我在这份指南中前期所提到的此内容不能显示在一个框架中,你不能在 CLASS 列表中简单地增加或者删除类别,基础的网络并没有改变。
你所能做的,最好就是修改一个能够列出所有类别标签的文本文件。
此外,如果你想显式地在神经网络中增加或者删除类别,你需要做的工作有:
1. 从零开始训练
2. 进行微调
从零开始训练通常会比较耗时间,是一个代价昂贵的操作,所以我们尽可能避免,但是在一些情况下是无法避免的。另一个选择就是进行微调。微调是一种迁移学习的形式,它是这样的过程:
1. 删除负责分类/打标签的全连接层
2. 并用全新的、随机初始化的全连接层替代
我们也可以选择性地修改网络中的其它层(包括在训练过程中冻结一些层的权重,以及解冻它们)。
准确来说,如何训练你自己的定制的深度学习目标检测器(包括微调和从零开始训练)是比较高级的话题,并不属于本文的讨论范围,但是阅读下面的部分可以有助于你开始这项工作。
我可以在哪里学到更多关于深度学习目标检测的内容?
图 8: 汽车前后视角的实时深度学习目标检测
正如我们在这篇博文中讨论过的,目标检测并不是像图像分类一样简单,其细节和复杂度超出了本文的范围(我已经啰嗦了好多遍了)。
本教程肯定不是我在深度学习目标检测方方面的最后一篇文章(毫无疑问,我会在未来写更多关于深度学习目标检测的文章),但是如果你对学习以下内容感兴趣:
1. 为目标检测准备你的数据集。
2. 在你的数据集上精调并训练你自己的定制化目标检测器,包括 Faster R-CNN 和 SSD。
3. 了解我的最好的实践做法、技术和过程,并使用它们来训练自己的深度学习目标检测器。
… 然后,你可能会想看一下我的新书()。在《Deep Learning for Computer Vision with Python》一书中,我会一步一步地指导你构建自己的深度学习目标检测器。
总结
这篇博客简单介绍了深度学习目标检测所涉及的一些难点。我们以概述图像分类和目标检测之间的本质区别作为开始,包括如何将一个图像分类的神经网络用于目标检测。
然后我们概述了深度学习目标检测器的核心组成:
1. 检测框架
2. 基本模型
基本模型通常是一个预训练的(分类)网络,为了学习到一系列具有辨识能力的滤波器,一般是在大型图像数据集(例如 ImageNet)上进行训练的。
我们也可以从零开始训练基本网络,但是,对于目标检测器而言,为了达到较合理的准确率。这通常需要更长的训练时间。
在绝大多数情况下,你应该以一个预训练的基本模型作为开始,而不是尝试着从零开始训练。
当我们对深度学习目标检测器有了充分的理解之后,我们就可以在 OpenCV 中实现能够实时运行的目标检测器。
我还概述了如何过滤或者忽略那些不感兴趣的类别标签。
最后,我们了解到:实际地向深度学习目标检测器增加一个类别标签,或者从深度学习目标检测器中删除一个类别标签并不是像从硬编码的标签列表张增加或者删除标签一样简单。
神经网络本身并不在乎你是否修改了类别标签,相反,你需要:
1. 通过删除全连接目标预测层并进行调整来修改网络结构
2. 或者从零开始训练目标检测网络框架
对于更多的深度学习目标检测项目,你需要从一个基于目标检测任务(例如 COCO)的预训练深度学习目标检测器开始。然后基于预训练模型进行微调,以得到你自己的检测器。
训练一个端到端的定制深度学习目标检测器并不属于本文的范畴,所以,如果你对探索如何训练自己的目标检测器感兴趣,请参考我的书籍《deep learning for computer vision with python》。
在这本书中,我描述了一些深度学习目标检测的例子,包括为以下任务训练你自己的深度学习目标检测器:
1. 检测交通标志,例如 Stop 标志,人行横道标志等等。
2. 汽车的前后视角
原文链接:
5 月 25 日!杭州云栖小镇将成为离世界最近、离年青人最近、离未来最近的地方,点击阅读原文,立即加入这场年青人的盛会,共同创造不一样的 2050。
添加机器之心小助手:syncedai3,备注“2050”,获取5折购票码。
限时特惠:本站每日持续更新海量展厅资源,一年会员只需29.9元,全站资源免费下载
站长微信:zhanting688