0%

Mask R-CNN 网络结构详解

Mask R-CNN 作为经典的two-stage目标检测模型被广泛使用,相关的讲解论文已经有很多,本文将着重在结合实际代码模型结构以及 layer 方面的讲解,这样会对模型有更加清晰的认识。本文以 Detectron2 mask_rcnn_R_50_FPN_1x.yaml 默认 config 生成的 Mask R-CNN 进行讲解。

1. Mask R-CNN 整体结构

从上图可看出 Mask R-CNN 主要由以下4大块构成:

  1. convolutional bakcbone: 用于提取 image feature,生成 feature map,例如 FPN
  2. RPN: 从 feature map 中生成 objects proposals
  3. box head:预测 box 的分类和回归
  4. mask head:预测 pixel mask

2. Convolutional Backbone

特征提取网络可使用任何能提取 image feature 的网络,本文以 FPN 为例。FPN 全称 Feature Pyramid Network,即特征金字塔网络,其结构如下图所示。

Detectron2 生成的以 ResNet50 为 backbone 的 FPN 具体参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
FPN(
(fpn_lateral2): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1))
(fpn_output2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(fpn_lateral3): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1))
(fpn_output3): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(fpn_lateral4): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
(fpn_output4): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(fpn_lateral5): Conv2d(2048, 256, kernel_size=(1, 1), stride=(1, 1))
(fpn_output5): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(top_block): LastLevelMaxPool()
(bottom_up): ResNet50 # for simplification
)

带有lateral 关键字的 layer 是上图中的1x1 conv,带有output 是上图中的predict部分,output layer 后接RPN,所以可以看到每个output的输出 channel number 与 RPN 的输入 channel number 相同,此处都为 256。

3. RPN

RPN 全称 Region Proposal Network,其作用过程如下图所示。

RPN的详细结构和配置如下所示。

1
2
3
4
5
6
7
8
9
10
RPN(
(anchor_generator): DefaultAnchorGenerator(
(cell_anchors): BufferList()
)
(rpn_head): StandardRPNHead(
(conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(objectness_logits): Conv2d(256, 3, kernel_size=(1, 1), stride=(1, 1))
(anchor_deltas): Conv2d(256, 12, kernel_size=(1, 1), stride=(1, 1))
)
)

detectron2StandardRPNHead类的定义 可看出具体参数的数字对应的意义:

1
2
3
4
5
6
# 3x3 conv for the hidden representation
self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1)
# 1x1 conv for predicting objectness logits
self.objectness_logits = nn.Conv2d(in_channels, num_anchors, kernel_size=1, stride=1)
# 1x1 conv for predicting box2box transform deltas
self.anchor_deltas = nn.Conv2d(in_channels, num_anchors * box_dim, kernel_size=1, stride=1)
  • (objectness_logits): Conv2d(256, 3, kernel_size=(1, 1), stride=(1, 1)): 3 代表anchor数目
  • (anchor_deltas): Conv2d(256, 12, kernel_size=(1, 1), stride=(1, 1)): 12 = 3*4,3代表anchor数目,4代表box的 x, y, w, h 相对于 anchor 的偏移量

4. Box Branch

Box branch 为对 box 的分类和回归任务,其具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(box_pooler): ROIPooler(
(level_poolers): ModuleList(
(0): ROIAlign(output_size=(7, 7), spatial_scale=0.25, sampling_ratio=0, aligned=True)
(1): ROIAlign(output_size=(7, 7), spatial_scale=0.125, sampling_ratio=0, aligned=True)
(2): ROIAlign(output_size=(7, 7), spatial_scale=0.0625, sampling_ratio=0, aligned=True)
(3): ROIAlign(output_size=(7, 7), spatial_scale=0.03125, sampling_ratio=0, aligned=True)
)
)
(box_head): FastRCNNConvFCHead(
(fc1): Linear(in_features=12544, out_features=1024, bias=True)
(fc2): Linear(in_features=1024, out_features=1024, bias=True)
)
(box_predictor): FastRCNNOutputLayers(
(cls_score): Linear(in_features=1024, out_features=81, bias=True)
(bbox_pred): Linear(in_features=1024, out_features=320, bias=True)
)

4.1 ROIAlign

Faster R-CNN 采用的是 ROI Pooling 从 feature map 中提取特征,但是由于 ROI Pooling 本身存在精度问题,导致特征没法对齐,所以 Mask R-CNN 中 RPN 之后的取特征操作采用了 ROI Align。关于 ROI Pooling 和 ROI Align 的具体区别请细读Mask R-CNN原文。

  • output_size: 输出的feature map 的尺寸
  • spatial_scale : 代表相对与输入原图的比例(0.25 -> 1/4, 0.125 -> 1/8, 0.0625 -> 1/16, 0.03125 -> 1/32)

4.2 Box Head

  • (fc1): Linear(in_features=12544, out_features=1024, bias=True): 12544 = 256*7*7,256代表 ROIAlign 之后的 feature map 通道数目,7*7 为 ROIAlign 的输出feature map 尺寸经过 reshape 操作成一维的大小。
  • (fc2): Linear(in_features=1024, out_features=1024, bias=True): 略

4.3 Box Predictor

  • (cls_score): Linear(in_features=1024, out_features=81, bias=True): 81代表80类前景目标 + 1背景
  • (bbox_pred): Linear(in_features=1024, out_features=320, bias=True): 320 = 80*4,80代表类别,4代表回归量

5. Mask Predictor

Faster R-CNN 模型结构到上一节 box branch 就结束了,也就是没有本节的 mask branch。mask branch 与 box branch 最大的区别在于 box branch 包含很多额外的layer,如reshapeNMS等。Mask branch 可看做在有限区域(ROIAlign)内的语义分割任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(mask_pooler): ROIPooler(
(level_poolers): ModuleList(
(0): ROIAlign(output_size=(14, 14), spatial_scale=0.25, sampling_ratio=0, aligned=True)
(1): ROIAlign(output_size=(14, 14), spatial_scale=0.125, sampling_ratio=0, aligned=True)
(2): ROIAlign(output_size=(14, 14), spatial_scale=0.0625, sampling_ratio=0, aligned=True)
(3): ROIAlign(output_size=(14, 14), spatial_scale=0.03125, sampling_ratio=0, aligned=True)
)
)
(mask_head): MaskRCNNConvUpsampleHead(
(mask_fcn1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(mask_fcn2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(mask_fcn3): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(mask_fcn4): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(deconv): ConvTranspose2d(256, 256, kernel_size=(2, 2), stride=(2, 2))
(predictor): Conv2d(256, 80, kernel_size=(1, 1), stride=(1, 1))
)

ROIAlign 同 Box Predictor 内的 ROIAlign。

Detectron2 中的 mask head 使用的是MaskRCNNConvUpsampleHead,可以看到在predictor之前加了一层ConvTranspose2d,增大了输入最后 predictor 的 feature map, 使得局部定位更加精确 。

Conclusion

Mask R-CNN 模型包含的4块结构拆分,对于每一块的分别进行分析之后,可增加对网路的认识。结合Detectron2的条理清晰的代码,能对每一层有更加清晰的认识。提取feature的网络可替换成任意其他网络,RPN 作用是利用 feature map 结合 anchor 找到最佳的 proposal,剩下的 box branch 和 mask branch 可根据任务进行调整,如增加网路的深度等。

Reference

  1. Mask R-CNN
  2. Detectron2
  3. Feature Pyramid Networks for Object Detection
  4. Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
  5. 对anchor机制的探索