Faster R-CNN源码解析一(pytorch)

Faster R-CNN流程图

经典的two-stage目标检测任务中,第一阶段是粗检测和前景背景分类,第二阶段是精修和具体类别分类。Faster R-CNN的第一阶段体现在利用RPN网络生成region proposals,第二阶段体现在最后利用全连接层完成类别分类和位置精修。

Faster R-CNN主要流程包括以下四步:

1、Conv Layers:利用VGG-16网络中的conv+relu+pooling层来提取图片的feature maps,该feature maps被共享于后续RPN层和全连接层。

2、Region Proposal Networks:本论文的创新点是通过RPN取代selective search生成region proposals。该层通过softmax判断anchors属于positive或者negative,再利用bounding box regression修正anchor获得精确的proposals。

3、ROI Pooling:该层通过综合Conv layers提取的feature maps和RPN网络生成的region proposals来提取proposal feature maps,作为后续全连接层的输入。

4、 Classification+Regression。利用proposal feature maps进行具体类别分类,同时再次利用bounding box regression获得检测框最终的精确位置。

下面开始从源码的角度慢慢品味以上4个步骤具体都是怎么实现的,当然源码拿到手,肯定是要从train.py文件开始,Let’s go。

第一步:加载数据集

1、dataset.py和voc_dataset.py

如果看过一两篇关于目标检测的论文以及源码,相信对于VOC数据集应该不陌生,所以这里关于数据的处理,就不花篇幅赘述了,提一点与本论文相关的处理,首先Faster R-CNN是支持输入任意大小的图片,比如上图中输入的P*Q,进入网络之前首先对图片进行了一定比例的缩放,具体缩放方式参照以下源码(dataset.py文件)

第二步:创建网络

1、Conv Layers

  • 这部分代码主要位于model文件夹下,首先看到的是通过decom_vgg16方法返回的其中一个extractor对象,该对象完成的就是Faster R-CNN流程中的第一步,生成Conv Layers用来提取图片的特征。首先了解一下VGG-16网络结构:VGG-16网络实现下采样都是在maxpool层(kernel_size=2, stride=2)实现的,其他conv层(kernel_size=3, padding=1, stride=1)并没有对特征图大小做改变,输入输出特征图大小是一样的,Faster R-CNN提取网络特征借用的是下图中用红框标示出来的部分(13个conv层+13个ReLU层+4个max Pooling层),从这里也可以看出来,经过,原图片M*N的大小经过VGG-16网络提取特征之后,最终网络输出的特征图大小会变为M/16 * N/16,利用VGG-16网络进行特征提取,最终输出的feature maps的shape为(Batch_size, 512, M/16, N/16)。
VGG网络结构
  • 具体代码如下:

2、Region Proposal Networks(RPN)

  • 在了解RPN之间,需要先了解一个概念叫Anchors,Faster R-CNN首先在16*8=128、16*16=256、16*32=512三个不同scale上分别生成长宽比为[0.5,1, 2]三个大小的anchor,所以共生成了9个base_anchors。
base_anchors示意图
  • 生成base_anchors源码:
  • 生成的anchors坐标如下所示:
  • 然后通过height(0~M/16)*16,width=N/16建立shift偏移量数组,再和base_anchors基准坐标数组累加,得到原图上所有的Anchors坐标值,是一个[(M/16)*(N/16)*9,4]的数组。
  • 在原图上生成Anchors源码:
在原图上生成Anchors
  • 在了解完Anchors生成规则之后,现在来具体看看RPN网络,如下图所示,其实RPN网络的结构也是比较简单的,首先是使用一个Conv 层(kernel_size=3, padding=1, stride=1)+ReLU层,利用VGG-16网络进行特征提取,最终输出的feature maps再经过该3*3的Conv + ReLu之后,输出的shape还是跟之前一样(Batch_size, 512, M/16, N/16),之后就分为2条线进行分别处理;
RPN网络结构

  • 首先是上面classification的这条线,先做了一个1*1Conv操作,之后输出的shape则变为(Batch_size, 18, M/16, N/16),此时这里有一个Reshape操作,是将shape由(Batch_size, 18, M/16, N/16)==>(Batch_size, M/16, N/16,18)==>(Batch_size, M/16, N/16,9,2),然后在dim=4维度上通过softmax分类预先在原图片上生成的所有anchors,哪些属于positive,哪些属于negative,也就是对所有的anchors进行前景背景二分类,从分类结果中取出positive anchors对应的rpn_fg_scores,然后再进行一个Reshape操作,将rpn_fg_scores的shape由(Batch_size, M/16, N/16,9)==>(Batch_size, M/16*N/16*9),然后作为Proposal层的输入;

  • 接着是下面bbox regression的这条线,也是先做了一个1*1Conv操作,之后输出的shape则变为(Batch_size, 36, M/16, N/16),之后再进行permute操作,将shape变为(Batch_size, M/16, N/16,36)==>(Batch_size, M/16*N/16*9, 4),然后作为Proposal层的输入;
  • RPN网络结构定义源码
RPN网络结构定义
  • RPN网络生成rpn_scores和bbox regression的源码
RPN网络生成rpn_scores和bbox regression
  • Proposal层的作用是根据RPN生成的positive anchors的scores得分和对应的bounding box regression偏移量对anchors进行位置调整,得到region proposals,具体看源码ProposalCreator类的实现,在该类中首先第一个重要的方法为loc2bbox(anchor, loc),其中anchors的坐标形式为(ymin, xmin, ymax, xmax),loc的坐标形式(dy, dx, dh, dw),两个的shape都为(M/16*N/16*9, 4);
  • 将所有的anchors坐标形式改为(anchor_ctr_y, anchor_ctr_x, anchor_height, anchor_width)
  • 根据RPN网络生成的bbox regression的偏移量将anchors坐标做如下调整
  • ctr_y = anchor_ctr_y + dy * anchor_height
  • ctr_x = anchor_ctr_x + dx * anchor_width
  • h = np.exp(dh) * anchor_height
  • w = np.exp(dw) * anchor_width
  • 将调整后的坐标再转化为(ymin, xmin, ymax, xmax)的形式返回
loc2bbox方法
  • 对超出图片边界的region proposals坐标进行调整以及去掉太小的,具体代码如下
  • 对RPN网络生成的rpn_fg_scores进行降序排序,挑选出前n_pre_nms个,然后在这n_pre_nms中利用极大值抑制nms算法,nms_thresh=0.7,得到最终能保留下来的2k个左右的即为region proposals。
  • 关于nms算法,说说自己的理解
  • 首先对scores进行降序排序,获取排序后的下标列表idx,找到当前最大score对应的idx所对应的anchor,求出剩下的其他anchors与该anchor的交并比(IOU),将IOU大于nms_thresh的对应下标从列表idx删除;
  • 再进行下一轮跟上面一样的步骤,直到idx列表长度为1为止,最后返回经过nms处理后剩下的下标列表;
  • 下面的源码来自于SSD中的源码,本次代码中使用的是torchvision.ops中实现的nms方法;

终于,RPN网络的使命完成了,生成了约2K个左右的region proposals。下一篇接着解读Faster R-CNN剩下的部分。加油⛽️

源码地址:https://github.com/chenyuntc/simple-faster-rcnn-pytorch