Faster R-CNN源码解析二(Pytorch)

Faster R-CNN流程图

上一篇写了Faster R-CNN中feature extraction与region proposal network两部分,在trainer.py文件中,可以看到调用完self.faster_rcnn.rpn方法之后,出现了这个ProposalTargetCreator类,先来具体看看这个类的实现。

  • 首先是ProposalTargetCreator类的定义
  • 该类中第一个重要的方法是bbox_iou(),相信看过目标检测相关论文的,对这个方法都不会陌生,该方法计算RPN网络生成的2K个左右的region proposals与Ground Truth之间的IOU,具体实现如下:
  • 根据计算出的iou,为每一个region proposal分配GT box,并获得对于每一个region proposal来说最大的iou值,然后与pos_iou_thresh=0.5做比较,大于0.5则被记为正样本(positive),同时将位于neg_iou_thresh_lo=0.1与neg_iou_thresh_hi=0.5之间的记为负样本(negative),从这些正负样本中挑选出128个。具体代码实现如下:
  • 该类中最后一个重要方法来咯,那就是bbox2loc,该方法的作用是计算出GT box与roi之间的偏移量,传入的两个参数分别是smaple_roi,这就是从2000个region proposals中挑选出的包含正负样本共128个roi,第二个参数bbox[gt_assignment[keep_index]],代表着这挑选出的128个roi对应的GT box的坐标,具体源码如下:
  • bbox2loc() 方法具体实现源码如下:
  • 偏移量的计算公式:
  • dy = (gt_box_ctr_y – roi_ctr_y) / roi_height
  • dx = (gt_box_ctr_x – roi_ctr_x) / roi_width
  • dh = np.log(gt_box_height/roi_height)
  • dw = np.log(gt_box_width/roi_width)

ok,接着就该说到ROI Pooling以及Classification+Regression的部分了。Let’s go!

3、ROI Pooling

  • 从上图可以看出,ROI Pooling有两个输入,其中一个是通过VGG-16网络提取的feature maps,另一个输入是经过ProposalTargetCreator类从2K个region proposals中挑选出的包含正负样本共128个rois,由于rois是对应原图M*N尺寸的,所以首先需要将其映射回(M/16)*(N/16)大小的feature maps尺寸;再将每个porposal对应的feature map区域平分为roi_size*roi_size的网格,对网格的每一份都进行max pooling处理,这样本来大小不一的proposal feature maps经过ROI Pooling输出结果都是roi_size*roi_size固定大小,实现了固定长度输出。

4、Classification+Regression

  • Classification部分利用已经获得的proposal feature maps,通过全连接层与softmax计算每个proposal具体属于哪个类别,输出cls_prob概率向量;同时再次利用bbox regression获得位置偏移量。。。。。。。。。,对proposal进行更加精确的位置修正。
  • 这两部分的代码实现:

第三步:Loss计算,完成end-to-end训练

网络结构部分已经分析完了,接着还有重要的Loss计算部分,这部分代码主要位于trainer.py文件中,Faster R-CNN的loss分为两部分RPN Loss + ROI Loss,这两部分Loss又分别包含以下两部分类别loss+回归loss,先看看RPN Loss是怎么实现的吧。

1、计算RPN Loss

  • 首先出现一个AnchorTargetCreator类,来具体看看这个类,首先是类的初始化方法;
  • 接着是_get_inside_index()方法,剔除掉超出图片边界的anchors;
  • 然后在保留下来的在图片范围内的anchors,生成相对应的标签;
  • 出现了_calc_ious()方法,该方法对应这论文中的这一部分:
  • 这里提出了两种策略来确定anchor为positive label,第一种:该anchor/anchors与一个GT box有最高的IOU,则被标记为属于正样本,注意此时对于一个GT box来说,可能会有一个或者多个anchor与之匹配;第二种:一个anchor与其中任何一个GT box的IOU大于0.7,则被标记为属于正样本;这里作者采用的是第一种方法,给出的理由是在一些罕见的情况下,第二种方法可能会导致没有找到正样本,回顾完论文,回到该方法,首先计算出所有的anchors与GT box的IOU,然后对于每一个anchor而言,获得与之交并比最大的GT box的下标argmax_ious,以及该最大的交并比的值max_ious,最后一个返回值是对每一个GT box而言,求出与之有最大交并比的anchors所对应的下标gt_argmax_ious。
  • 根据_calc_ious()方法返回的max_ious,当max_ious<0.3,则被标记为negative,当max_ious>=0.7则被标记为positive,其余的iou位于0.3与0.7之间的anchors即不属于positive与不属于negative,并不参与计算Loss,negative+positive的个数为256。
  • 剩下两个方法,一个是bbox2loc()方法,这个方法前面已经见过了,这里利用这个方法计算出所有在图片范围内的anchors与GT box之间的偏移量,还有一个方法_unmap()方法,该方法的作用是将label以及loc扩展到(M/16)*(N/16)*9的空间中去,将label的shape变为((M/16)*(N/16)*9,),将loc的shape变为((M/16)*(N/16)*9, 4)。
  • 最后返回label(所有(M/16) * (N/16) * 9个anchors所对应的label标签),loc(所有anchors与GT box的偏移量)。
  • 经过AnchorTargetCreator类得到了RPN网络的真实标签结果,首先计算rpn_loc_loss,这里采用的是smooth_L1_Loss,具体实现在_fast_rcnn_loc_loss()方法中,需要说明的是,这里计算rpn_loc_loss只针对正样本计算,因为只有正样本才有与只对应的GT box。对于_smooth_l1_loss()方法中用红框标记出来的部分还不是很理解,不应该是0.5/ sigma2嘛????
  • 计算完回归loss,接着计算分类loss,采用的是交叉熵损失函数;

2、计算ROI Loss

  • 首先就算回归loss,同样与RPN Loss中的回归Loss一样采样smooth_L1_Loss;
  • 计算回归loss,采用的交叉熵损失函数;

整个Loss计算也就完成了,还是再稍微总结一下整个过程:

  • 首先也是最简单的部分,就是利用VGG16提取图片特征feature maps,网络最终输出shape为(Batch_size, 512, M/16, N/16);
  • 接着就到了RPN网络部分,首先要先了解的是在该阶段,先在原图尺寸(M*N)上生成了M/16*N/16*9个anchors,之后进行的用1*1的卷积进行分类和回归结果与每个anchor人为对应起来,先对图片的feature maps进行3*3卷积加ReLU,进一步集中特征信息,此时特征图的大小没有变,之后分为两条线:
  • ①第一条是分类,这里做了前景背景分类,(Batch_size, 512, M/16, N/16)==>经过1*1的卷积=>(Batch_size, 18, M/16, N/16)==>经过permute==>(Batch_size, M/16, N/16, 18)==>经过reshape==>(Batch_size, M/16, N/16, 9, 2)==>进行softmax分类,然后得到前景分类scores,将其(Batch_size, M/16, N/16, 9, 1)==>经过reshape==>(Batch_size, M/16*N/16*9)传入到proposal层;
  • ②第二条做bbox regression,(Batch_size, 512, M/16, N/16)==>经过1*1的卷积=>(Batch_size, 36, M/16, N/16)==>经过permute==>(Batch_size, M/16, N/16, 36)==>经过reshape==>(Batch_size, M/16*N/16*9, 4)传入到proposal层;
  • ③proposal层的作用就是最终生成2K左右个roi,首先利用上述RPN网络生成的位置回归偏移量对anchors进行坐标调整生成roi,剔除掉太小的roi,以及对超出图片边界的roi进行调整,之后对网络生成的前景score从大到小排序,保留前12000个roi,又经过nms极大值抑制算法,最终保留下来2K个左右的region proposals。
  • 该源码在RPN网络之后,定义了一个ProposalTargetCreator()对象,作用是从2K个region proposals挑选出128个包含正负样本的roi,具体做法是根据roi与GT box计算出的iou,为每一个roi分配GT box,与哪一个GT box的iou最大,则负责预测该GT box,其中 >=0.5则是正样本,在[0.1, 0.5)之间则是负样本,并返回128个roi与该负责预测的GT box之间的坐标偏移量,以及该128个roi对应的label标签。
  • 将128个roi以及feature maps作为ROI Pooling的输入,feature maps的shape为(Batch_size, 512, M/16, N/16),roi的shape为(128, 4),经过ROI Pooling层之后,输出的shape为(128, 512, 7, 7),reshape为(128, 512*7*7)=(128, 25088),再送入到VGG-16的分类层,得到的输出为(128, 4096),最后经过本网络的分类层(4096, 21),得到的roi_scores的shape为(128, 21),经过本网络的回归层(4096, 21 *4),得到的roi_cls_locs的shape为(128, 21*4)。

整个Faster R-CNN的网络结构以及loss计算已经分析完了,剩下的就是使用梯度下降法最小化loss,并迭代更新参数,以及最后对模型和优化器的保存,这部分代码位于train.py和trainer.py文件中,具体可以看看。Faster R-CNN就结束了,加油⛽️。