Faster Rcnn算法复现
复现算法Faster Rcnn
Faster Rcnn算法原文链接: https://arxiv.org/abs/1506.01497
Faster Rnn 实现流程
Faster Rcnn是双阶段目标检测家族中的一员,由Rcnn -> Spp-net -> Fast Rcnn 再到Faster Rcnn,Faster Rcnn中首次使用深度学习的方法进行关键区域的提取,真正实现了end to end的目标检测,Faster Rcnn是双阶段目标检测系列最关键的节点,其后出现的Mask Rcnn与Cascade Rcnn都是基于Faster Rcnn而来,本次实现一个简要版的Faster Rcnn以增强自己对其的理解。
在之前参加天池比赛时,使用了Faster Rcnn和FPN,并做出了一定的改进也取得了不错的成绩,但当时是在mmdetection框架的基础上进行改进,难免无法顾及一些细节,通过这次从头开始实现Faster Rcnn和FPN,对细节方面有了更好的掌握,相信在实现了Faster Rcnn后,双步和和单步的目标检测算法我都可以进行简要版的复现,下图是Faster Rcnn的结构图。
Faster Rcnn的实现分为五个阶段:
-
第一阶段,根据输入的图像和标注的框信息(后续称为ground-truth)计算anchor的真实标签和位移坐标,该阶段生成的anchor的真实标签和位移坐标将用于与RPN网络预测的anchor的标签和位移坐标计算RPN网络的损失以更新RPN网络的权重。
假设输入图像大小为(800, 800),采用vgg16作为特征提取网络,下采样16倍,得到的特征图大小为(50,
50),对特征图上的每个点,映射回原图产生anchor,假设设置anchor_scale为(8, 16,
32),anchor_ratio为(0.5,1,2),那么每个位置将产生9个anchor,其中anchor_scale为anchor的大小,anchor_ratio为anchor的长宽比,需要注意的是,这里设置的anchor_scale是相对于特征图的,当映射回原图时需要乘以下采样倍数。对每个位置产生9个anchor,一共需要产生 50 ∗ 50 ∗ 9 50*50*9 50∗50∗9即22500个anchor,对这些anchor进行anchor的定位和采样,即将anchor分配给与其具有最大iou的ground_trouth(会从中采样256个,别的忽略即label为-1,正负样本比例为1:1,根据iou判断正负样本),转换公式如式1、2、3、4。
d x = ( g t x − a n c h o r x ) / a n c h o r w (1) dx=(gt_x-anchor_x)/anchor_w \tag{1} dx=(gtx−anchorx)/anchorw(1)
d y = ( g t y − a n c h o r y ) / a n c h o r h (2) dy=(gt_y-anchor_y)/anchor_h\tag{2} dy=(gty−anchory)/anchorh(2)
d w = l o g ( g t w / a n c h o r w ) (3) dw=log(gt_w/anchor_w)\tag{3} dw=log(gtw/anchorw)(3)
d h = l o g ( g t h / a n c h o r h ) (4) dh=log(gt_h/anchor_h)\tag{4} dh=log(gth/anchorh)(4)
其中,dx、dy、dw、dh为anchor相对于ground_truth的位移坐标,gt_x、gt_y、gt_w、gt_h为ground_truth的中心坐标和宽高,anchor_x、anchor_y、anchor_w、anchor_h为anchor的中心坐标和宽高,同时根据anchor与ground_truth的iou来生成其真实标签(0或1),RPN网络只有前景和背景两种。该阶段的目的是对所有anchor生成其真实的位移坐标和标签,即gt_anchor_locations和gt_anchor_labels,用于联合RPN网络预测的pred_anchor_locations和pred_anchor_labels计算损失函数。 -
第二阶段,用RPN网络预测所有anchor的位移坐标和标签,即pred_anchor_locations和pred_anchor_labels。
下图显示了RPN网络细节 如图3所示,为RPN网络的实现细节,在实际实现时,第一阶段产生的特征图大小为 50 ∗ 50 50*50 50∗50, 通道数为512,RPN网络由一个 3 ∗ 3 3*3 3∗3的卷积核和两个 1 ∗ 1 1*1 1∗1的卷积分支构成, 3 ∗ 3 3*3 3∗3的卷积核加入了padding=1,即不改变原特征图的尺寸大小,两个 1 ∗ 1 1*1 1∗1的卷积分支分别预测每个位置9个anchor的类别和位移坐标,因此此处输入为提取到的(50,50,512)的特征图,其中512为通道数,而输出为(50, 50,18)的类别预测和(50,50,36)的位移坐标预测。第二阶段产生的pred_anchor_labels和pred_anchor_locations将用于与第一阶段计算的gt_anchor_labels和gt_anchor_locations一起计算RPN阶段的损失loss。
-
第三阶段,对第二阶段预测的anchor处理,根据第二阶段预测的pred_anchor_locations中的dx、dy、dw、dh结合初始anchor信息反向计算出RPN阶段预测的ground_truth的左上坐标和右下坐标(x1,y1,x2,y2),根据score对其进行排序,取前12000个进行nms,在nms后的剩余框中取前2000个,注意此时的pred_anchor_locations中存储的是反向推算出的预测框在原图上的位置,对剩下的这2000个框根据ground_truth进行采样和定位,计算出这2000个框相对于ground_truth的真实labels和位移坐标locations,根据iou进行采样和定位,与groud_truth的iou大于0.5的分为正样本,此时需要记录其对应的ground_truth的label,该部分标签为类别数,而不是前景背景(0,1),定位公式同第一阶段,然后对定位后的框进行采样,该阶段采样128个,其中正样本比例为0.25,该阶段最后产生的是根据RPN网络预测的pred_anchor_locations、pred_anchor_labels与ground_truth计算出的128个gt_roi_labels和gt_roi_locations。
-
第四阶段,第二阶段通过RPN网络产生了pred_anchor_labels和pred_anchor_locations,第三阶段从其中采样出了128个sample_rois,对这128个sample_rois计算出了其相对于ground_truth的真实标签和位移坐标即gt_roi_labels和gt_roi_locations,第四阶段将第三阶段采样出的sample_rois先送入roi pooling层获得 7 ∗ 7 ∗ 512 7*7*512 7∗7∗512固定大小的特征图,然后将其拉平产生一个(1, 25088)的特征向量,然后送入两层全连接层得到(1,4096)的特征向量,最后通过两个全连接层分支,分别预测其类别(num_class+1)和位移坐标((num_class+1)*4),即pred_roi_labels和pred_roi_locations。
-
第五阶段,根据前四个阶段计算的结果计算损失,其中RPN阶段的损失通过gt_anchor_labels、gt_anchor_locations、pred_anchor_labels、pred_anchor_locations计算,ROI阶段的损失通过gt_roi_labels、gt_roi_locations、pred_roi_labels、pred_roi_locations计算,分类损失使用交叉熵损失函数计算,回归损失通过smooth L1损失函数计算,分别计算出rpn_cls_loss、rpn_loc_loss、roi_cls_loss、roi_loc_loss,计算损失时要注意,分类损失是对所有框进行计算,而回归损失只对样本标签有意义的框计算,因此在计算总损失时要在回归损失前乘以10或者使分类损失除以10,即
rpn_loss = rpn_cls_loss/10 + rpn_loc_loss,
roi_loss = roi_cls_loss/10+roi_loc_loss,
total_loss = rpn_loss+roi_loss。
最后根据损失更新权重。 交叉熵损失函数如式5所示,smooth L1损失如式6所示。
L = − ∑ c = 1 M y c log ( p c ) (5) L=-\sum_{c=1}^{M} y_{c} \log \left(p_{c}\right)\tag{5} L=−c=1∑Myclog(pc)(5)
L = { 0.5 x 2 , ∣ x ∣ < 1 ∣ x ∣ − 0.5 , ∣ x ∣ ≥ 1 (6) L=\left\{\begin{array}{cc}{0.5 \mathrm{x}^{2},} & {|x|<1} \\ {|x|-0.5,} & {|x| \geq 1}\end{array}\right.\tag{6} L={ 0.5x2,∣x∣−0.5,∣x∣<1∣x∣≥1(6)
代码
辅助模块util.py
1. import numpy as np
2.
3. def iou(valid_anchors, gt_box):
4. # 传入两个box,左上坐标和右下坐标,大小为 n*4
5. # 返回ious,((len(valid_anchors)*len(gt_box)))
6. # 每个valid_anchor与每个gt_box都有iou,ious维度:(len(valid_anchors)*len(gt_box))
7. valid_anchors_num = valid_anchors.shape[0]
8. gt_box_num = gt_box.shape[0]
9. ious = np.empty((valid_anchors_num, gt_box_num))
10. ious.fill(0)
11. for i, anchor in enumerate(valid_anchors):
12. xa1, ya1, xa2, ya2 = anchor
13. area1