查看原文
其他

深度学习目标检测指南:如何过滤不感兴趣的分类及添加新分类?

Adrian Rosebrock 极市平台 2019-03-28

↑ 点击蓝字关注极市平台 识别先机 创造未来


来源:AI科技大本营

编译:庞佳、Leo

原文地址:

https://www.pyimagesearch.com/2018/05/14/a-gentle-guide-to-deep-learning-object-detection/


编者按:本文编译自 Adrian Rosebrock 发表在 PyImageSearch 上的一篇博文。该博文缘起于一位网友向原作者请教的两个关于目标检测的问题:


  • 如何过滤或忽略我不感兴趣的类?

  • 如何在目标检测模型中添加新的类?这是否可行?


Adrian Rosebrock 认为这两个问题是学习目标检测的同学经常问到的问题,于是创作了本篇文章统一回答。


摘录

具体来说,在这篇文章中你会了解到:

  • 图像分类和目标检测的区别;

  • 深度学习目标检测模型的构成,包括目标检测框架和基本模型框架的不同;

  • 如何将训练好的深度网络模型用于目标检测;

  • 如何过滤和忽略深度学习模型所检测的类别;

  • 在深度神经网络中,添加或删除检测类别时常见误区。


想要了解更多的关于深度学习目标检测方面的知识,或者想要解开关于深度学习目标检测的相关疑惑,请继续阅读。


深度学习目标检测指南

今天的博客旨在简单介绍基于深度学习的目标检测。我已经尽量提供关于深度学习目标检测模型构成的内容,包括提供使用预先训练的目标检测模型实现深度学习的 OpenCV + Python 的源代码(地址:https://www.getdrip.com/forms/564384284/submissions)。


使用这个指南能够帮助你初步了解深度学习目标检测,但同时你也会意识到,涉及目标检测的很多技术细节,我无法在这篇博客中讲得面面俱到。


也就是说,我们将通过讨论图像分类和目标检测的本质区别来引出今天的博客内容,包括图像分类训练好的模型能否用于目标检测(以及在什么情况下)。


我们一旦理解了什么是目标检测后,我们将会回顾深度学习目标检测模型的核心部分,包括目标检测框架和基础模型,这是初次接触目标检测的读者感到疑惑的两个关键部分。


在这基础上,我们将会使用 OpenCV 运行实时深度学习目标检测模型。在不改动网络结构和重新训练模型的前提下,我将会演示如何能够忽略和过滤你不感兴趣的目标类别。


最后,我们将讨论在深度学习目标检测中如何添加或删减类别,我们将以此结束今天的博客,包括我推荐的资源来帮助你入门。让我们开始深入了解深度学习目标检测吧!


图像分类和目标检测的区别


图1:分类(左边)和目标检测(右边)的直观区别。对于图像分类,是将整张图片进行分类,并且是单一标签。对于目标检测的情况,我们的神经网络会对图片中的(潜在的多个)目标进行定位


当进行标准图像分类时,指定一个输入图像,我们将它输入到我们的神经网络中,我们会获得一个类标签,或者是相应被分类标签的概率。这个类标签旨在描述整张图像的内容,或至少是图像中最主要的可视内容。


举例子来说,如图1中指定的输入图像(左边),我们的卷积神经网络把图像标记为“比格犬”。因此,我们可以将图像分类视为:


  • 一张图片输入;

  • 一个类标签输出。


目标检测,无论是通过深度学习还是其他计算机视觉技术实现,目标检测均基于图像分类,同时试图精准定位图像中每个目标的位置。在执行目标检测时,给定一个输入图像,我们希望能够获得:


  • 边框列表,或者图像中每个目标的 (x, y) 坐标;

  • 每个边框所对应的类标签;

  • 每个边框和类标签相应的概率和置信度分数。


图 1(右边)给出了一个运用深度学习进行目标检测的例子。注意,用边界框对人和狗进行定位,并给出预测类标签。因此,目标检测让我们能够:


  • 向网络输入一张图像;

  • 获得多个边框和类标签作为输出。


可以将深度学习图像分类用于目标检测吗?


图 2:使用滑动窗口的非端到端深度学习目标检测模型(左边)+ 结合分类的图像金字塔(右边)方法


好的,所以此时你理解了图像分类和目标检测最重要的区别:


  • 当实行图像分类时,我们向网络中输入一张图像,并获得一个类标签作为输出;

  • 但是,当实行目标检测时,我们输入一张图像,将获得多个边界框和类标签输出。


引出了这个问题:

对于已经训练好的用于分类的网络,我们是否能将它用于目标检测?


答案有点复杂,因为技术上“可以”,但是原因并不那么浅显。解决方案涉及:


  1. 运用传统基于计算机视觉的目标检测方法(即非深度学习方法),比如滑动窗口和图像金字塔,这类方法通常用于基于 HOG 特征和线性支持向量机的目标检测器中;

  2. 获取预先训练好的模型,并将它作为深度学习目标检测框架的基础网络。(比如 Faster R-CNN, SSD, YOLO )。


方法 1:传统目标检测方法

第一种方法不是纯粹的端到端的深度学习目标检测模型。我们采用:


  1. 固定大小的滑动窗口,这个窗口自左到右,自上到下滑动去定位不同位置的目标;

  2. 图像金字塔,用于检测不同尺度的目标;

  3. 通过预先训练好的卷积神经网络(分类器)进行分类。


在滑动窗口和图像金字塔的每次停顿中,我们找出感兴趣的区域,传输到卷积神经网络中,并且输出这个区域的分类。


如果标签L的分类概率比某个阈值T高,我们将标记这个感兴趣区域的边框为标签 L。每次滑动窗口和图像金字塔停顿都将重复这个过程,我们将会获得输出的目标检测结果。最后,我们对所有的边框采用非极大值抑制,生成我们最终输出的检测结果:


图 3:应用非极大值抑制将抑制重叠,减少边框置信度


这个方法可以用于某些特定用例中,但是,一般而言,这种方法很慢,冗长乏味,并且容易出错。


然而,因为这种方法可以将任意图像分类网络转换成目标检测模型,如何运用这个方法还是值得好好研究的,从而避免直接训练端到端的深度学习目标检测模型。根据你的用例,这种方法能为你节省大量的时间和精力。


如果你对这种目标检测的方法很感兴趣,还想了解更多将滑动窗口、图像金字塔和图像分类方法用于目标检测内容,请请参阅我的书,Deep Learning for Computer Vision with Python(https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/).


方法 2:目标检测框架的基础网络

深度学习目标检测中的第二种方法,这种方法将事先训练好的分类网络视为深度学习目标检测框架中的基础网络(比如 Faster R-CNN, SSD, or YOLO )。


这样做的好处是你可以创建一个基于深度学习的完整的端到端的目标检测模型。缺点就是这种方法要求对深度学习目标检测工作原理有一定的了解,下一节将对此加以讨论。


深度学习目标检测的组成元素


图 4:VGG16 基础网络是 SSD 深度学习目标检测框架中的一部分


深度学习目标检测模型有很多组件、子组件和二级子组件,但是,今天我们主要关注两点,深度学习目标检测新手经常混淆的两点:


  1. 目标检测框架(比如 Faster R-CNN, SSD, YOLO );

  2. 符合目标检测框架的基础网络。


你可能已经了解基础网络,基础网络是我们常见的(分类器)卷积神经网络结构,包括:


  • VGGNet

  • ResNet

  • MobileNet

  • DenseNet


一般来说,为了学习得到丰富的判别滤波集合,这些用于图像分类的网络预先在大型图像数据集(如 ImageNet )中已经训练完成。目标检测框架由很多组件和子组件构成。举例子来说,Faster R-CNN 的框架包括:


  • 候选区域生成网络 ( RPN );

  • 候选窗口集合;

  • 感兴趣区域 ROI pooling 层;

  • 最终基于区域的卷积神经网络。


当使用 Single Shot Detectors (SSDs) 时,SSD 会包括如下的组件和子组件:


  • MultiBox,边框回归技术;

  • Priors,预先计算的固定大小的边框(像 Faster-R-CNN terminology 候选窗口);

  • Fixed priors,每个特征图单元都与不同维度和尺寸默认边框的集合相关联。


记住了,基础网络只是其中一个符合总体深度学习目标检测框架的组件,在这一节顶部的图3中,它描述了 SSD 框架中的作为基础网络的 VGG16。通常情况下,在基础网络上进行修改,这个修改包括:


  • 将基础网络编排成全卷积形式(即能够接受任意维度的输入);

  • 移除基础网络结构中更深的 CONV 和 POOL 层,将它们替换成一系列新的层( SSD )、新的模型( Faster R-CNN )或是两者的结合。


术语“网络手术”是一种口语化的表达,用来说明我们删减了一些基础网络中的原始层,并插入一些新的层来取代它们。


你可能看过低预算的恐怖电影,电影中的凶手也许携带斧子或大刀,攻击他们的受害者,毫不手软地攻击他们。网络手术比典型的 B 级恐怖电影中的杀手更加精确和严格。


网络手术很有战略意义,我们删除了网络中我们不需要的部分,将它替换成一组新的组件。


然后,当我们去训练框架用于目标检测时,以下两项的权重均已修改:


(1)新的层和模块;

(2)基本网络。


重复一下,针对各种深度学习目标检测框架工作原理的完整的回顾(包括基础网络所扮演的角色)已经超出了本博客的范围。如果想深入了解深度学习目标检测的内容,包括原理和实 现,请参考我的书籍,Deep Learning for Computer Vision with Python。


如何评估深度学习目标检测的精度?

当评估目标检测模型的性能时,我们使用的评价指标是平均精度均值(mAP ),mAP是基于我们数据集中所有类别的交并比( IoU )计算得到的。


交并比( IoU )


图 5:在这个交并比 IoU 的直观例子中,将真实值的边框(绿色)与预测的边框(红色)进行对比。IoU 与平均精度均值 mAP 一起被用于深度学习目标检测的精度评估。右边为用于计算 IoU 的等式


也许你会发现 IoU 和 mAP 通常用于评价 HOG 特征 +线性 SVM 检测模型,Haar特征级联分类器和基于深度学习模型的性能; 然而,记住,实际上用于生成预测边框的算法并不重要。


任何用来提供预测边框(以及供参考的类标签)作为输出的算法,这些算法均能是用 IoU 进行评估。更正式地说,为了使用 IoU 来评价任意一种目标检测模型,我们需要:


1. 真实值的边框(也就是,在测试集中,通过我们手动标记的,目标对象所处位置的边框);

2. 来自我们模型的预测边框;

3. 如果你想要计算召回率和精确率,你还需要真实值的类标签和预测值的类标签。


在图 4(左边)中,我给出了一个直观的例子,真实值的边框(绿色)与预测边框(红色)对比。通过图 4(右边)的等式来计算IoU。审视这个等式,你会发现 IoU 是一个简单的比值。


在分子部分,我们计算的是预测边框与真实值边框之间的重叠面积。分母是并集区域面积,或者更简单地说,分母是被预测边框和实际边框两者包含的面积。交集区域除以并集区域将得到最终的分数,交并比得分。


平均精度均值( mAP )

为了在我们的数据集中评估目标检测模型的性能,我们需要计算基于 IoU 的mAP:


  1. 基于每个类(也就是每个类的平均精度);

  2. 基于数据集中的所有类别(也就是所有类别的平均精度值的平均值,术语为平均精度均值)


为了计算每个类的平均精度,对指定类中所有数据点计算它的 IoU。一旦我们得到了这个类别中用全部数据计算的 IoU,我们就可以计算该类的平均精度(初次均值)。为了计算 mAP,我们要计算所有N个类别中的平均 IoU,然后就可到了 N 个平均精度的均值(平均精度的均值)。


通常情况下,我们使用 mAP@0.5,mAP@0.5 的意思是在测试集中,为了使目标能够标记为“正检测样本”,这个目标与真实值的 IoU 值至少必须达到 0.5(并且被正确标记类别)。这个 0.5 值是可以调整的,但是在大多数的目标检测数据集和挑战中,0.5 是标准值。


基于OpenCV的深度学习目标检测

在以前的博客中,我们已经讨论了深度学习目标检测,完整起见,让我们先来回顾一下实际运用中的源代码。


我们的例子中包括 SSD 检测器和 MobileNet 基础网络模型。GitHub 用户 chuanqi305 在 COCO 数据集上训练了这个模型。


让我们先来回顾 Ezekiel 的第一个问题,在本文开头就提到的问题:

如何过滤或忽略不感兴趣的类?


这是个很好的问题,我将用以下样例脚本来回答。但是,首先,需要准备以下系统:


  • 你需要至少在你的 Python 虚拟环境中安装 OpenCV 3.3 版本(假设你使用的是 Python 虚拟环境)。运行以下的代码需要 OpenCV 3.3 或 3.3 以上的版本中的 DNN 模块。请选择下页中其中一种 OpenCV 的安装教程,同时特别注意你所下载和安装的 OpenCV 的版本。

  • 同时,你还应该安装我的 imutils 包。想在你的 Python 虚拟环境中安装或更新 imutils 包,可以简单地使用

pip: pip install --upgrade imutils


准备好了之后,继续创建命名为 filter_object_detection.py 的新文件,然后开始:



在 2~8 行中,我们导入了必须的附加包和模块,特别是 imutils 和 OpenCV 。我将会用 VideoStream 类来处理从摄像头捕获的帧图像。


我们配备了必须的工具,然后继续解析命令行参数:



在运行时,我们的脚本需要两个命令行参数:


  • --prototxt:Caffe原型文件的路径,这个明确了模型定义;

  • --model:我们的CNN模型的权重文件路径。

.

或者,你可以指定--confidence,过滤弱检测器的阈值。我们的模型能够预测 21 个目标类别:



CLASSES 列表中包括了网络训练的所有类别( COCO 数据集中的标签)


关于 CLASSES 列表常见的困惑是:

1. 在列表中添加新的类别;

2. 或者,从列表中删除类别。


并能自动的让网络“知道”你正在努力完成什么任务。事实并非如此。


你不能通过对文本标签简单的修改,从而使网络通过自动修正后再去学习、添加和删除未经过训练的数据模式。神经网络不是这样工作的。这里有一个快速的窍门,你可以用来过滤和忽略你不感兴趣的预测标签。


解决方案是:

1. 定义 IGNORE 标签的集合(用于训练网络的标签列表,你想要过滤和忽略的列表);

2. 对输入的图像和视频帧图片进行预测;

3. 忽略任何包含在 IGNORE 集合中类标签的预测。


在 Python 中运行,IGNORE 集合如下:



在这里,我们将会忽略所有标签为“人”的预测目标(用于过滤的if语句稍后讲解)。在集合中,添加附加的元素(CLASSES 列表中的类标签)是很容易的。接下来,我们将生成随机标签和边框颜色,加载我们的模型,并开始 VideoStream:



在第 27 行,生成的 COLORS 的随机数组,被用于对应 21 个 CLASSES。我们将会用这些颜色进行后续的展示。在 31 行,我们使用 cv2.dnn.readNetFromCaffe 函数和我们所需的两个命令行参数作为参数传递加载了的 Caffe 模型。


然后,我们将 VideoStream 目标实例化为 vs,并开始我们的 fps 计数(第 36~38 行)。2 秒的休眠让我们的摄像机有足够的时间准备。此时,我们准备好了接收来自摄像机的循环输入帧图像,并将这些图像输入到 CNN 目标检测模型中:



在第 44 行,我们读取图像并调整图片大小,同时保留显示的纵横比(第 45 行)。在这里,由于后期需要,我们提取了高度和宽度值。第 48 和 49 行,从帧图像中生成了 blob。接下来,我们将 blob 输入到神经 net 中,用于目标检测。(第 54 和 55 行)让我们来循环遍历检测模型:



在 58 行,我们将开始检测器的循环。在每次检测中,我们提取了 confidence( 61 行),将它与我们的置信度阈值对比(第 65 行)。如果我们的 confidence 大于最小值(默认值是 0.2,能够通过命令行参数修改)这个检测结果将会被视为正检测结果,有效的检测并继续进一步的处理。


首先,我们提取从检测模型中提取了类标签的索引(第 68 行)。然后,回顾 Ezekiel 的第一个问题,我们可以忽略在 IGNORE 集合中的列表,在 72 和 73 行。如果这是属于被忽略的类别,我们将简单的继续回到检测模型的初始循环阶段(我们并不展示这个类别的标签或边框)。这实现了“快速破解”解决方案。否则,我们我们在白名单中检测到目标时,我们需要在帧图片中显示这个目标的类标签和矩形框:



在这个代码模块中,我们提取边框坐标(第 77 和 78 行),然后,在帧图片上绘制了类标签和矩形框(第 81~87 行)。同一个类中标签的颜色和矩形框相同,相同类别中的目标将使用相同的颜色(也就是,视频中的“船”,都将使用相同颜色标签和边框)最后,仍然在 while 循环中,我们将在屏幕上展示我们努力工作的成果:



在第 90 和 91 行中,我们显示了帧图片,并捕获按键输入。如果按下“q”键,我们停止并推出循环(第 94 和 95 行)否则,我们继续更新 fps 计数器( 98 行),并继续提取和处理帧图片。在剩下的代码行中,当循环停止时,我们将显示时间和每秒帧数量度,并清除。


运行你的深度学习目标检测模型

运行脚本,打开终端并进入到代码和模型目录,从那里运行接下来的命令:



图6:使用相同的模型进行实时深度学习目标检测演示,在右边的视频中,我编程忽略了特定的目标类别。


在上面的 GIF 中,从左侧你可以看到“人”类别被检测,这是由于我的 IGNORE 集合是空的。在右侧,你会发现我没有被检测到,这是因为将 “person” 类添加到 IGNORE 集合。虽然我们的深度学习目标检测器从技术上仍然检测“人”的类别,但我们后期处理代码能够将这个类别过滤掉。


在运行深度学习目标检测模型时你遇到了错误?


排除错误的第一步是检查你是否连接了摄像头。如果不是这个问题,也许你会在终端中看到以下错误信息:



如果你看到这个信息,那么是你没有将“命令行参数”传递到程序中。如果 PyImageSearch 读者对 Python、argparse 和命令行参数不熟悉,这将是他们普遍会遇到的问题。如果你遇到问题,请查看链接(https://www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/)。


这里是带评论的完整版视频:https://v.qq.com/txp/iframe/player.html?vid=n06592ppayg&width=500&height=375&auto=0


我如何在深度学习目标检测模型中添加和移除类?


图 7:深度学习目标检测模型的微调和迁移学习


正如我在本篇指南中提到的,你不能简单的修改 CLASSES 列表来进行类标签的添加和删除,底层网络本身并没有发生变化。你所做的,充其量只是修改一个类标签的文本文件。


反之,如果你想从神经网络中添加或删除类,你需要:

1. 重新训练;

2. 进行微调。


重新训练往往是耗时、成本高的操作,所以,我们尽可能的避免重新训练,但在某些情况下,从头开始训练是无法避免的。另一种方式是对网络进行微调。


微调是迁移学习的一种形式,微调可以通过以下的过程来完成:

1. 将用于分类和标记的全连接层移除;

2. 将其替换成全新的、随机初始化的全连接层。


我们也可以修改网络中的其他层(包括冻结某些层的权重,在训练过程中再解冻它们)。具体如何训练你自定义的深度学习目标检测模型(包括微调和重新训练),本文不涉及这样的高级主题,但是,可以参考以下部分来帮助你入门。


总结

在今天的博客中,我大致介绍了涉及深度学习目标检测的复杂问题。我们首先回顾了图像分类和目标检测的本质区别,包括我们如何将图像分类训练的网络用于目标检测。


然后,我们回顾了深度学习目标检测的核心部分:

  1. 框架

  2. 基础模型


基础模型通常是预先训练好的网络(分类器),通常是在大型图像数据集中完成训练的,比如 ImageNet ,为的是让网络去学习鲁棒性的判别过滤器集合。我们也可以重新训练基础网络,不过这通常需要训练很长的时间,目标检测模型才能达到合理的精度。


在大多数情况下,你应该从预先训练好的基础模型入手,而不是重新训练。一旦我们深入了解深度学习目标检测模型之后,我们就可以在 OpenCV 中在运行实时目标检测模型。我还演示了怎样做才能过滤或忽略你不感兴趣的类标签。


最后我们了解到,从深度学习目标检测模型中添加或删减类并不像从硬编码中的类标签列表中添加或删减类标签那么容易。神经网络本身并不关心你是否修改了类标签列表,相反,你将需要:


  1. 修改网络结构本身,移除全连接的类预测层,并进行微调;

  2. 或者重新训练目标检测框架。


对于大多数深度学习目标检测项目,你将从预先已在目标检测任务(如 COCO )中训练完成的深度学习目标检测模型开始,然后,通过对模型进行微调获取你自己的检测模型。



*推荐文章*

德国伊尔梅瑙工业大学提出新型「Complex-YOLO」,可实现点云上的「实时3D目标检测」

详解计算机视觉五大技术:图像分类、对象检测、目标跟踪、语义分割和实例分割


PS.5月24日(本周四)晚20:00-21:00,哈尔滨工业大学(深圳)博士生姚远,将为我们讲解迁移学习之异构域适应问题,详情点击:极市分享|姚远 迁移学习之异构域适应简介

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存