最近一直在研究CV领域的一些算法,由于本人编程水平较弱加之faster rcnn算法比较复杂,虽然原理在很早就大致看懂,但在看源码的时候仍然吃了不少苦头,近些时候,终于了解了代码的大致含义并借鉴大神们的代码自己用pytorch实现了一下。在此,想跟大家详细地解释一下代码的含义, 帮助读者更好的理解这一算法。本人代码如下:
需要先了解一下faster rcnn的读者可以点击以下链接,这是本人看到的比较全面细致的faster rcnn教程。
model
这一部分对应model_easy.py,我们知道, Faster RCNN 总共实际上就是四个模型,CNN模型, RPN(Region proposal layer), ROI Pooling layer, Classification layer(可以称为Faster RCNN层)。CNN层使用预训练的vgg16提取特征,得到feature map,然后进入RPN层, RPN层s首先经过一个kernel_size为3的层,之后再分为两部分,分别输出29大小和49大小的向量(对应于9 anchor 2 classifier以及 9 anchor 4 coordinate),通过训练修正proposal的位置以及数量(通过rpn文件里面的proposal_layer.py),修正好的proposal进入ROI Pooling层统一划成7*7大小,最后进入FasterRCNN层输出最后的bbox_pred(bbox_delta)以及scores。
CNN层比较简单,在这里不再赘述。我们看一看RPN层的源码。
RPN
1 | # the simple model of RPN |
注意forward的返回值,一个是rpn_bbox_pred,这个是我们我提到的教程里面的预测的[dx, dy, dh, dw],也是之后的bbox_delta。rpn_cls_prob也仅仅只是logits经过了一层softmax函数而已。模型大致架构比较简单,下面来看看与RPN层相关的函数。
1 | def proposal(self, rpn_bbox_pred, rpn_cls_prob, im_info, test, args): |
这段代码的作用是什么?用教程里面的话就是:
- 生成anchors,利用[dx(A),dy(A),dw(A),dh(A)]对所有的anchors做bbox regression回归(这里的anchors生成和训练时完全一致)
- 按照输入的foreground softmax scores由大到小排序anchors,提取前pre_nms_topN(e.g. 6000)个anchors,即提取修正位置后的foreground anchors。
- 利用im_info将fg anchors从MxN尺度映射回PxQ原图,判断fg anchors是否大范围超过边界,剔除严重超出边界fg anchors。
- 进行nms(nonmaximum suppression,非极大值抑制)
- 再次按照nms后的foreground softmax scores由大到小排序fg anchors,提取前post_nms_topN(e.g. 300)结果作为proposal输出。
- 之后输出proposal=[x1, y1, x2, y2],注意,由于在第三步中将anchors映射回原图判断是否超出边界,所以这里输出的proposal是对应MxN输入图像尺度的
简单来说,就是对产生的所有anchor进行修正以及筛选。此函数输入的主要参数是之前RPN网络输出的rpn_bbox_pred以及rpn_cls_prob。这里有几行代码需要解释一下:
1 | bbox_deltas = self._get_bbox_deltas(rpn_bbox_pred).data.cpu().numpy() |
_get_bbox_deltas仅仅是把rpn_bbox_pred转换为了(H/16 W/16 9, 4), bbox_transform_inv则是根据bbox_deltas以及anchors生成了对应的pred_anchor(注意之前的rpn_bbox_pred仅仅是生成的[dx, dy, dh, dw],不是最终确定的proposal),_get_pos_score也仅仅是将rpn_cls_score转化为(H/16 W/16 9, 1)
ROI Pooling
1 | class ROIpooling(nn.Module): |
ROI Pooling显得比较简单,但这并不是因为其算法本身简单,实际上,此层需要实现的算法是对任意输入的proposal,都要加工成7*7大小,这个工作量似乎不小,实际上,之前大神的代码里,ROI Pooling的实现代码很长,但正因为如此,pytorch的contributor将其封装,现在仅仅只需要调用adapmax2d就可以了。
FasterRCNN
这个代码比较简单,没什么好讲的。
1 | class FasterRcnn(nn.Module): |
Loss
loss部分主要分为两个部分,rpn_loss以及fasterRCNN_loss
1 | def rpn_loss(rpn_cls_prob, rpn_logits, rpn_bbox_pred, rpn_labels, rpn_bbox_targets, rpn_bbox_inside_weights): |
首先注意一点,rpn_bbox_targets, rpn_bbox_inside_weights这两个参数是从anchor_target此函数得来的。
rpn_loss的主要操作流程如下:
筛选出label值不是-1的proposal(-1表示don’t care area)
计算出是前景的proposal和是背景的proposal的数目。
classification使用CrossEntropyLoss(注意CrossEntropyLoss已经包含了log softmaxLoss,所以只需要使用logits作为参数)
regression使用smooth_l1_loss
综合两个函数
frcnn_loss与之相近,就不再赘述