本文受 pyimagesearch 的读者 ezekiel 的启发,他上个星期在邮件中咨询道:
adrian 你好,我仔细地浏览了您之前关于深度学习目标检测 的文章及其跟进的实时深度学习目标检测 。感谢你做的这一切,我在自己的样例项目中使用了你的源代码,但是我有两个问题:1. 我该如何过滤/忽略那些我不感兴趣的类?2. 我如何才能向自己的目标检测器中增加新类别?有这个可能吗?如果你能就这两个问题写一篇文章,我将不胜感激。ezekiel 并不是受此问题困扰的唯一读者。事实上,如果你仔细浏览了我最近关于深度目标检测两篇文章的评论,你会发现最常见的问题可以被表述为:我该如何修改你的源代码来包含我自己的类别?
由于这是一个如此常见的问题,并且是关于神经网络/深度学习目标检测器实际工作的一个误解,所以我决定在今天的博客中重温深度学习目标检测的话题。
具体地,你将在这篇文章中学到以下内容:
图像分类和目标检测的区别深度学习目标检测器的组成:包含不同目标检测架构的区别和基本模型之间的区别如何使用预训练模型进行深度学习目标检测如何从一个深度学习模型中过滤或者忽略一些预测类别向深度神经网络增加类别或从中删除类别时常见的误区和误解
为了更多地了解深度学习目标检测,并解释清楚我们对基于深度学习的目标检测的一些误区和误解,请继续阅读本文。
想要查看本文相关的源码?请查看原文的下载链接(https://pyimagesearch/2018/05/14/a-gentle-guide-to-deep-learning-object-detection/#):
深度学习目标检测的一般指南
今天的博客是对基于深度学习的目标检测的简单介绍。我尽可能对深度学习目标检测器的组成做一个概述,包括使用预训练的目标检测器执行任务的源代码。
你可以使用这份指南来帮助学习深度学习目标检测,但是也要意识到,目标检测是高度细节化的工作,我不可能在一篇文章中包含关于深度学习目标检测的所有细节。
这篇文章将从讨论图像分类和目标检测之间的本质区别开始,其中包括判断一个图像分类网络是否可以用于目标检测,以及在什么情况下可以这样使用等话题。
当我们理解了什么是目标检测时,随后会概述一个深度学习目标检测器的核心模块。它一般包括目标检测架构和基本模型,不熟悉目标检测的读者可能会误解这两个部分。
在这里,我们将使用 opencv 来实现实时深度学习目标检测。我也会展示如何在不修改网络架构或者重新训练的情况下忽略或者过滤一些不感兴趣的目标类别。最后,我们通过讨论如何从深度学习目标检测器中增加或者删除类别来总结本文。
图像分类和目标检测的区别
图 1: 图像分类(左)和目标检测(右)的区别是比较直观和简单的。在图像分类中,整幅图像被分类为单一的标签。而在目标检测中,我们的神经网络还要找出图像中目标的位置(有可能是多个)。
在进行标准的图像分类时,我们将一张给定的图像输入到神经网络,然后得到一个最可能的标签,而且也许会同时得到相关的概率。
这个类别标签用来表征整个图像的内容,或者至少是图像最主要的可见内容。例如,上面的图 1 中,给定输入图像(左),我们的 cnn 给它的标签是「比格犬」。所以我们可以认为图像分类具有以下特点:
一张图像输入一个类别标签输出
无论是通过深度学习还是其他计算机视觉技术的目标检测,都是基于图像分类构建的,只不过需要精确定位每个对象在图像中出现的位置。在进行目标检测的时候,给定一张输入图像,我们期望得到:
一个边界框列表,或者一幅图像中每个对象的(x,y)坐标与每个边界框关联的类别标签与每个边界框和类别标签关联的概率或者置信度得分
图 1(右)展示了一个深度学习目标检测的例子。请注意,人物和狗都被用边界框找出了位置,同时类标签也被预测到了。
所以,目标检测允许我们:
向网络输入一张图像得到多个边界框以及类别标签
深度学习图像分类可以被用于目标检测吗?
图 2:非端到端深度学习的目标检测器使用一个滑动窗口(左)+图像金字塔(右)相结合的方法来分类。
所以现在你理解了图像分类和目标检测的根本区别:
在进行图像分类时,我们输入一张图像,得到一个输出类别然而在进行目标检测时,我们输入一张图像,得到多个边界框以及类别标签的输出
这自然引发这么一个问题:
我们可以拿一个已训练的分类网络,将其用于目标检测吗?
这个答案有些棘手,因为这在技术上是可以的,但是理由并不太明显。解决方案涉及:
1. 应用基于计算机视觉的标准目标检测方法(非深度学习方法),例如滑动窗口和图像金字塔等方法通常被用在 hog+基于线性 svm 的目标检测器。
2. 采用预训练的网络,并将其作为深度学习目标检测架构的基本网络(例如 faster r-cnn, ssd, yolo)。
方法 #1: 传统的目标检测技术路线
第一个方法不是纯端到端的深度学习目标检测器。相反,我们使用:
1. 固定尺寸的滑动窗口,它从左到右,自上而下滑动,来定位不同位置的对象。
2. 图像金字塔,用来检测不同尺度的对象
3. 一个预训练(分类)的 cnn 来分类
在滑动窗和对应图像金字塔每一次停留的时候,我们会提取 roi(感兴趣区域),将其输入到 cnn 中,得到对 rio 的分类。
如果标签 l 的分类概率比某个阈值 t 高,我们就将这个 roi 的边界框标记为该标签(l)。对滑动窗和图像金字塔的每次停留都重复这个过程,我们就得到了目标检测器的输出。最终,我们对边界框应用非极大值抑制(nms),得到最终输出的检测结果:
图 3:应用 nms 会抑制重叠的和置信度不高的边界框。这个方法在一些特定的用例中是有效的,但是它通常比较慢和繁琐,也容易出错。
然而,这个方法也是值得学习的,因为它可以将任意图像分类网络转换为一个目标检测器,而不需要显式地训练一个端到端的深度学习目标检测器。这个方法可以节省大量的时间和精力,且效率的高低具体取决于你的用例。
方法 #2:目标检测架构的基本网络
第二个深度学习目标检测的方法允许我们将一个预训练的分类网络作为深度学习目标检测架构(例如 faster r-cnn、ssd 或者 yolo)的基本网络。
这个方法的好处是:你可以创建一个基于深度学习的复杂端到端目标检测器。
而其不足之处是:它需要一些关于深度学习目标检测器如何工作的知识,我们将在后面的部分中讨论这个问题。
深度学习目标检测器的模块
图 4: vgg16 基本网络是 ssd 深度学习目标检测框架的一个特征抽取模块。
深度学习目标检测器有很多模块,子模块以及更小的子模块,但是我们今天要重点关注的是深度学习入门读者所困惑的两个:
1. 目标检测框架(不包括 faster r-cnn, ssd, yolo)
2. 适合目标检测框架的基本网络
你可能已经比较熟悉基本网络(只是你之前并没听到它被称作基本网络而已)。基本网络就是你常用的分类 cnn 架构,包括:
vggnetresnetmobilenetdensenet
通常这些网络在大数据集上进行预训练来进行分类,例如 imagenet,它们可以学习到很多具有鉴别能力的滤波器。
目标检测框架由很多组成部分和子模块构成。例如,faster r-cnn 框架包括:
候选区域网络(rpn)一组锚点roi 池化模块最终基于区域的卷积神经网络
在使用 ssd(单步检测器,single shot detectors)时,具有以下的组成部分:
多框(multibox)先验(priors)固定先验(fixed priors)
请记住,基本网络只是整个深度学习目标检测框架的众多组件之一,上文图 4 描述了 ssd 框架内部的 vgg-16 网络。通常,我们需要在基本网络上进行「网络手术」。这种修改:
让它变成全卷积的形式,并接受任意输入维度。剪除了基本网络中更深层的卷积和池化层,将它们以一系列新层(ssd)、新模块(faster r-cnn),或者这两者的一些组合代替。
这里的「网络手术」是一种口语化的说法,它的意思是移除基本网络中的一些原始卷积层,将它们用新层替代。网络手术也是讲究策略的,我们移除一些不需要的部分,然后用一组新的部分来替代它们。
然后,当我们开始训练我们的框架进行目标检测时,(1)新层、模块和(2)基本网络的权重都被修改了。
再强调一次,综述关于不同深度学习目标检测框架是如何工作的(包括基本网络所起的作用)并不属于本文的探讨范围。
如果你对深度学习目标检测的完整综述(包括理论和实现)感兴趣,请参考机器之心曾经发过的文章:从 rcnn 到 ssd,这应该是最全的一份目标检测算法盘点 。
我是如何计算一个深度学习目标检测器的准确度的?
在评价目标检测器的性能时我们使用了一个叫做均值平均精度(map)的指标,它是以我们数据集中所有类别的交并比(iou)为基础的。
交并比(iou)
图 5: 在这个交并比的可视化例子中,标注边界框(绿色)可以与预测的边界框(红色)进行对比。iou 与 map 一起被用来评价一个深度学习目标检测器的精度。计算 iou 的简单方程如图 5(右)所示。
你通常会发现 iou 和 map 被用于评价 hog+线性 svm 检测器、haar cascades 以及基于深度学习的方法的性能;但是请记住,实际用于生成预测边界框的算法并不是那么重要。
任何一个以预测边界框作(以及可选择的标签)为输出的算法都可以用 iou 来评价。更一般的地,为了使用 iou 来评价任意一个目标检测器,我们需要:
1. 真实的边界框(也就是测试集中表明我们的目标在图像的哪个位置的人工标签)
2. 模型预测到的边界框
3. 如果你想一起计算召回率和精度,那么还需要真实类别标签和预测类别标签
在图 5(左)中,我展示了真实边界框(绿色)与预测边界框(红色)相比的可视化例子。iou 的计算可以用图 5 右边的方程表示。
仔细检查这个方程你会发现,iou 就是一个比值。在分子项中,我们计算了真实边界框和预测边界框重叠的区域。分母是一个并集,或者更简单地说,是由预测边界框和真实边界框所包括的区域。两者相除就得到了最终弄的得分:交并比。
平均精度均值(map)
图 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(https://github/chuanqi305/mobilenet-ssd)在 coco 数据集上训练得到。更多细节请浏览我之前的文章(https://pyimagesearch/2017/09/11/object-detection-with-deep-learning-and-opencv/),这篇文章介绍了该模型以及相关的背景信息。
让我们回到 ezekiel 在本文开始提出的第一个问题上。
1. 我该如何过滤/忽略那些我不感兴趣的类?
我会在下面的示例代码中回答这个问题,但是首先你需要准备一下系统:
你需要在 python 虚拟环境中安装版本不低于 3.3 的 opencv(如果你在使用 python 虚拟环境的话)。opencv 3.3+ 包含运行以下代码所需的 dnn 模块。确保使用链接中的 opencv 安装教程之一(https://pyima...