Faster R-CNN (object detection) implemented by Keras for custom data from Google’s Open Images Datas
Faster R-CNN (object detection) implemented by Keras for custom data from Google’s Open Images Dataset V4
Introduction
在探索CNN一段时间之后,我决定尝试计算机视觉中的另一个关键领域,物体检测。这个领域有几种流行的方法,包括更快的R-CNN,RetinaNet,YOLOv3,SSD等。我在本文中尝试了更快的R-CNN。在这里,我想总结一下我所学到的知识,如果你对这个主题感兴趣,可能会给你一点启发。
我使用的快速R-CNN的Keras版本的原始代码是由yhenon编写的(资源链接:GitHub。)他使用了PASCAL VOC 2007,2012和MS COCO数据集。对我来说,我刚从Google的Open Images Dataset V4中提取了三个类,“人物”,“汽车”和“手机”。我应用了与他的工作不同的配置来适应我的数据集,并删除了无用的代码。顺便说一句,要在Google Colab上运行(免费GPU计算长达12小时),我将所有代码压缩成三个.ipynb笔记本。对不起凌乱的结构。
首先,我假设您了解CNN的基本知识以及什么是对象检测。这是原始论文的链接,命名为“更快的R-CNN:利用区域提案网络实现实时目标检测”。对于想要在更快的R-CNN上使用Google的Open Images Dataset V4实现自定义数据的用户,您应该继续阅读以下内容。
我阅读了许多文章,解释了与快速R-CNN相关的主题。他们对此有很好的理解和更好的解释。顺便说一句,如果您已经了解有关更快的R-CNN的详细信息并且对代码更加好奇,您可以跳过下面的部分并直接跳转到代码说明部分。这是我的GitHub这个项目的链接。
Recommendation for reading:
Faster R-CNN: Down the rabbit hole of modern object detection
Deep Learning for Object Detection: A Comprehensive Review
Review of Deep Learning Algorithms for Object Detection
Faster R-CNN (Brief explanation)
R-CNN([R. Girshick等,2014](http://islab.ulsan.ac.kr/files/announcement/513/rcnn_pami.pdf))是快速R-CNN的第一步。 它使用搜索选择性([J.R.R.Uijlings and al。(2012)](http://www.huppelen.nl/publications/selectiveSearchDraft.pdf))找出感兴趣的区域并将它们传递给ConvNet。 它试图通过将类似的像素和纹理组合成几个矩形框来找出可能是对象的区域。 R-CNN论文使用了来自搜索选择的2,000个建议区域(矩形框)。 然后,将这2,000个区域传递给预先训练的CNN模型。 最后,将输出(特征映射)传递给SVM进行分类。 计算预测的边界框(bbox)和地面实况bbox之间的回归。
选择性搜索的示例。 资料来源:[J.R.R。 Uijlings and al。(2012)](http://www.huppelen.nl/publications/selectiveSearchDraft.pdf)
快速R-CNN([R. Girshick(2015)])向前迈进了一步。它不是将2,000倍的CNN应用于建议的区域,而是仅将原始图像传递给预先训练的CNN模型一次。基于前一步骤的输出特征映射计算搜索选择性算法。然后,ROI池层用于确保标准和预定义的输出大小。这些有效输出作为输入传递给完全连接的层。最后,使用两个输出向量来用softmax分类器预测观察对象,并使用线性回归量来调整边界框局部化。
更快的R-CNN(简称frcnn)比Fast R-CNN进一步发展。搜索选择性过程由区域提议网络(RPN)取代。顾名思义,RPN是一个提出地区的网络。例如,在从预训练模型(VGG-16)获取输出要素图之后,如果输入图像具有600x800x3尺寸,则输出要素图将为37x50x256尺寸。
First step of frcnn
37x50中的每个点都被视为锚点。 我们需要为每个锚定义特定的比率和大小(三种比例为1:1,1:2,2:1,原始图像中三种尺寸为128²,256²,512²)。
One anchor projected to the original image.
锚点以原始图像为中心。 资料来源:https://tryolabs.com/blog/2018/01/18/faster-r-cnn-down-the-rabbit-hole-of-modern-object-detection/
接下来,RPN连接到具有3x3滤波器,1个填充,512个输出通道的Conv层。 输出连接到两个1x1卷积层进行分类和盒子回归(请注意,此处的分类是确定盒子是否为对象)。
RPN架构的卷积实现,其中k是锚点的数量。 (此处k = 9)来源:https://tryolabs.com/blog/2018/01/18/faster-r-cnn-down-the-rabbit-hole-of-modern-object-detection/
Javier:对于训练,我们采用所有锚点并将它们分为两个不同的类别。那些与地面真相对象重叠的与集合的交叉(IoU)大于0.5被认为是“前景”而那些不与任何地面实况对象重叠或与地面实况对象具有小于0.1 IoU的那些被认为是“背景”。
在这种情况下,每个锚在原始图像中具有3x3 = 9个对应的框,这意味着原始图像中有37x50x9 = 16650个框。我们只选择这些16650盒子中的256个作为迷你批次,其中包含128个前景(pos)和128个背景(neg)。同时,应用非最大限制抑制以确保建议的区域没有重叠。
完成上述步骤后,RPN完成。然后我们进入frcnn的第二阶段。与快速R-CNN类似,ROI池用于这些提议的区域(ROI)。输出为7x7x512。然后,我们用一些完全连接的层压平该层。最后一步是softmax函数,用于分类和线性回归以修复框的位置。
R-CNN architecture. Source: https://tryolabs.com/blog/2018/01/18/faster-r-cnn-down-the-rabbit-hole-of-modern-object-detection/
Code explanation
Part 1: 从Google的Open Images Dataset v4(Bounding Boxes)中提取自定义类的注释
Download and load three .csv files
在官方网站中,您可以通过单击下面图像名为Class Names的底部的红色框来下载class-descriptions-boxable.csv。。 然后转到图8中的Download并下载其他两个文件。
Open Images网站的屏幕截图
在图八网站中,我下载了train-annotation-bbox.csv和train-images-boxable.csv,如下图所示。
图8的截图
下载后,让我们看一下这些文件里面的内容。 train-images-boxable.csv包含可装箱的图像名称及其URL链接。 class-descriptions-boxable.csv包含与其LabelName类对应的类名。 train-annotations-bbox.csv有更多信息。 train-annotations-bbox.csv中的每一行包含一个图像的一个边界框(简称bbox)坐标,它还具有此bbox的LabelName和当前图像的ID(ImageID +’。jpg’= Image_name)。 XMin,YMin是这个bbox和XMax的左上角,YMax是这个bbox的右下角。 请注意,这些坐标值是标准化的,如果需要,应根据实际坐标计算。
First 5 rows of three .csv file
获得整个数据集的子集
包含600个类的Open Images Dataset V4的整个数据集对我来说太大了。因此,我分别为“三个班级”,“人物”,“移动电话”和“汽车”提取了1,000张图像。
下载这3,000张图像后,我将有用的注释信息保存在.txt文件中。每行的格式如下:file_path,x1,y1,x2,y2,class_name(两个值之间没有空格只是逗号)其中file_path是此图像的绝对文件路径,(x1,y1)和(x2,y2)表示原始图像的左上角和右下角的实际坐标,class_name是当前边界框的类名。我使用80%的图像进行训练,使用20%的图像进行测试。预期的训练图像和测试图像的数量应该是3x800 - > 2400和3x200 - > 600.但是,可能存在一些重叠的图像,它们同时出现在两个或三个类别中。例如,图像可能是在街上行走的人,街上有几辆车。因此,用于训练图像的bbox的数量是7236,并且用于测试图像的bbox的数量是1931。
我提取数据的一个样本。 左边是从给定网址下载的原始图像,右边是通过添加来自train-annotations-bbox.csv的边界框来绘制的。
Part 2: Faster R-CNN code
我将解释代码中的一些主要功能。 每个函数的完整注释都写在.jpynb笔记本中。 请注意,我将调整大小的图像保持为300以便更快地进行训练,而不是我在第1部分中解释的600。
重建VGG-16的结构并加载预先训练的模型(nn_base)
VGG-16 structure. Source: https://www.quora.com/What-is-the-VGG-neural-network
Prepare training data and training labels (get_anchor_gt)
输入数据来自annotation.txt文件,该文件包含一堆带有边界框信息的图像。我们需要使用RPN方法来创建提议的bbox。
- 此函数中的参数
all_img_data:list(文件路径,宽度,高度,列表(bbox))
C:配置
img_length_calc_function:根据输入图像大小计算最终图层的特征图(基本模型)大小的函数
模式:‘火车’或’测试’; '火车’模式需要增强 - 返回此函数中的值
x_img:调整大小和缩放后的图像数据(最小尺寸= 300px)
Y:[y_rpn_cls,y_rpn_regr]
img_data_aug:增强图像数据(带增强的原始图像)
debug_img:显示调试图像
num_pos:显示调试的正锚点数
##计算每个图像的rpn(calc_rpn)
如果要素图的形状为18x25 = 450且锚尺寸= 9,则有450x9 = 4050个潜在锚点。每个锚点的初始状态为“否定”。然后,如果IOU> 0.7,我们将锚设置为正。如果IOU> 0.3且<0.7,则它是模糊的并且不包括在目标中。一个问题是RPN有更多的负面而不是正面区域,因此我们关闭了一些负面区域。我们还将正区域和负区域的总数限制为256. y_is_box_valid表示此锚点是否具有对象。 y_rpn_overlap表示此锚点是否与地面实况边界框重叠。
对于’正’锚,y_is_box_valid = 1,y_rpn_overlap = 1。
对于“中性”锚点,y_is_box_valid = 0,y_rpn_overlap = 0。
对于’负’锚,y_is_box_valid = 1,y_rpn_overlap = 0。
- 此函数中的参数
C:配置
img_data:增强图像数据
宽度:原始图像宽度(例如600)
高度:原始图像高度(例如800)
resized_width:根据C.im_size重新调整图像宽度(例如300)
resized_height:根据C.im_size调整图像高度(例如400)
img_length_calc_function:根据输入图像大小计算最终图层的特征图(基本模型)大小的函数 - 返回此函数中的值
y_rpn_cls:list(num_bboxes,y_is_box_valid + y_rpn_overlap)
y_is_box_valid:0或1(0表示该框无效,1表示该框有效)
y_rpn_overlap:0或1(0表示该框不是对象,1表示该框是对象)
y_rpn_regr:list(num_bboxes,4 * y_rpn_overlap + y_rpn_regr)
y_rpn_regr:x1,y1,x2,y2 bunding boxes坐标
y_rpn_cls的形状是(1,18,25,18)。 18x25是特征地图大小。 要素图中的每个点都有9个锚点,每个锚点分别有y_is_box_valid和y_rpn_overlap的2个值。 所以第四个形状18是9x2。
y_rpn_regr的形状是(1,18,25,72)。 18x25是特征地图大小。 特征映射中的每个点都有9个锚点,每个锚点分别有4个tx,ty,tw和th值。 请注意,这4个值有自己的y_is_box_valid和y_rpn_overlap。 因此第四形状72是9x4x2。
从RPN计算感兴趣的区域(rpn_to_roi)
- 此函数中的参数(num_anchors = 9)
rpn_layer:用于rpn分类的输出层
shape(1,feature_map.height,feature_map.width,num_anchors)
如果调整大小的图像是400宽度和300,则可能是(1,18,25,9)
regr_layer:rpn回归的输出层
shape(1,feature_map.height,feature_map.width,num_anchors * 4)
如果调整大小的图像是400宽度和300,则可能是(1,18,25,36)
C:配置
use_regr:在rpn中使用bboxes回归
max_boxes:非最大抑制(NMS)的最大bbox数
overlap_thresh:如果你在NMS中大于这个阈值,请丢弃该框 - 返回此函数中的值
结果:来自非最大抑制的框(形状=(300,4))
box:bboxes的坐标(在要素图上)
对于上述步骤中的4050个锚点,我们需要提取max_boxes(代码中的300个)作为感兴趣区域的框数,并将它们传递给分类器层(frcnn的第二阶段)。在该函数中,我们首先删除超出原始图像的框。然后,我们使用具有0.7阈值的非最大抑制。
绿色框是真实的边界框。 紫色框是由RPN层计算的锚(标签)。
RoIPooling图层和分类器图层(RoiPoolingConv,classifier_layer)
RoIPooling层是通过max pooling将roi处理为特定大小输出的函数。 每个输入roi被分成一些子单元,并且我们将max pooling应用于每个子单元。 子单元的数量应该是输出形状的尺寸。
如果左图中的大红色矩形是RoI并且它应该执行具有2x2输出的RoI池化层,则将其划分为4个子单元并应用最大池化过程。 右图是2x2输出结果。 资料来源:http://wavelab.uwaterloo.ca/wp-content/uploads/2017/04/Lecture_6.pdf
分类器层是整个模型的最后一层,位于RoIPooling层的后面。 它用于预测每个输入锚点的类名称及其边界框的回归。
- 此函数中的参数
base_layers:vgg
input_rois:(1,num_rois,4)
rois列表,有序(x,y,w,h)
num_rois:一次处理的rois数(此处为4) - 返回此函数中的值
list(out_class,out_regr)
out_class:分类器层输出
out_regr:回归层输出
首先,汇集层是扁平的。
然后,接着是两个完全连接的层和0.5个丢失。
最后,有两个输出层。
out_class:softmax**函数,用于对对象的类名进行分类
out_regr:bbox坐标回归的线性**函数
Dataset
同样,我的数据集是从Google的Open Images Dataset V4中提取的。 选择“Car”,“Person”和“Mobile Phone”三个班级。 每个类包含大约1000个图像。 “Car”,“Mobile Phone”和“Person”的边界框数分别为2383,1108和3745。
Parameters
- Resized(im_size)值为300。
- 锚的数量是9。
- 非最大抑制的最大数量为300。
- 模型中要处理的RoI数量为4(我没有尝试过更大的尺寸,这可能会加快计算速度,但需要更多内存)
- Adam用于优化,学习率为1e-5。 如果我们应用原始论文的解决方案,它可能会有所不同。 对于60k小批量,他们使用的学习率为0.001,对于PASCAL VOC数据集中的下一个20k小批量,他们的学习率为0.0001。
- 对于图像增强,我打开horizontal_flips,vertical_flips和90度旋转。
Environment
Google’s Colab with Tesla K80 GPU acceleration for training.
Training time
我选择的每个纪元的长度是1000.请注意,每个批处理只处理一个图像。 我训练的时期总数是114.每个时期在这种环境下花费大约700秒,这意味着训练的总时间大约是22小时。 如果您像我一样使用Colab的GPU,则需要重新连接服务器并在自动断开连接时加载权重以进行继续培训,因为每个会话都有时间限制。
Result
我们将两个损失函数应用于RPN模型和分类器模型。 正如我们之前提到的,RPN模型有两个输出。 一个用于分类它是否是一个对象而另一个是用于边界框的坐标回归。 从下图中,我们可以看到它在前20个时期学得非常快。 然后,分类器层变慢,而回归层仍然继续下降。 其原因可能是我们训练的早期阶段对象的准确性已经很高,但与此同时,边界框坐标的准确性仍然很低,需要更多的学习时间。
RPN模型的分类输出和bboxes回归输出的Epochs vs. Loss值
类似的学习过程在分类器模型中显示。 与bboxes回归的两个图相比,它们显示出类似的趋势,甚至类似的损失值。 我认为这是因为它们预测了非常相似的值,并且它们的层结构略有不同。 与两个用于分类的图相比,我们可以看到预测对象比预测bbox的类名更容易。
分类器模型的分类输出和bboxes回归输出的Epochs vs. Loss值
Epochs vs. Total loss for two models
这个总损失是上面四个损失的总和。 它有下降的趋势。 但是,mAP(平均平均精度)不会随着损失的减少而增加。 当时期数为60时,mAP为0.15.当时期数为87时,mAP为0.19。当时期数为114时,mAP为0.13。我认为这是因为培训图像数量少,导致 过度拟合模型。
Other things we could tune
- 缩短培训流程。 我选择300作为im_size用于重新调整大小的图像而不是原始代码中的600(和原始文件)。 所以我选择较小的anchor_size [64,128,256]而不是[128,256,512]。
- 我选择VGG-16作为我的基础模型,因为它具有更简单的结构。 但是,像ResNet-50这样的模型可能会有更好的结果,因为它在图像分类方面具有更好的性能。
- 模型中有许多阈值。 我使用它们中的大多数作为原始代码。 rpn_max_overlap = 0.7和rpn_min_overla = 0.3是区分每个锚点的“正”,“中性”和“负”的范围。 overlap_thresh = 0.7是非最大抑制的阈值。
Test on images
在笔记本中,我将培训过程和测试过程分为两部分。 在运行测试.ipynb笔记本之前,请重置所有运行时,如下所示。 也许你需要在运行测试笔记本时关闭训练笔记本,因为内存使用几乎没有限制。
Screen shot for ‘Reset all runtimes’
At Last
为了获得乐趣,您可以创建自己的数据集,该数据集未包含在Google的Open Images Dataset V4中并对其进行训练。对于我在本文中使用的封面图片,他们是由中国制作的三位porcoelainous僧侣。我只是根据他们的脸型来命名它们(不确定那个困了)。它们不包含在Open Images Dataset V4中。所以我使用RectLabel来自己注释。我用了大约3个小时的时间将46个图像(包括’Apple Pen’,‘Lipbalm’,‘Scissor’,‘Sleepy Monk’,‘Upset Monk’和’Happy Monk’)拖入6个等级的地面真相盒中。对于anchor_scaling_size,我选择[32,64,128,256]因为Lipbalm通常在图像中很小。找到这些小方形唇膏。我为较强的模型添加了较小的锚尺寸。考虑到Apple Pen长而薄,anchor_ratio可以使用1:3和3:1甚至1:4和4:1,但我还没试过。培训时间不长,表现也不错。我想这是因为相对简单的背景和简单的场景。实际上,我发现更难的部分不是注释这个数据集,而是考虑如何拍摄它们以使数据集更加健壮。
好的,这就是本文的全部内容。谢谢你的收看。这是我这个项目的GitHub链接。继续训练你自己的物体探测器。如果您有任何问题,请留下您的评论。我很高兴收到你的来信:????