部分内容参考:
1、https://blog.csdn.net/liuxiaoheng1992/article/details/81843363
2、mmdetection源码(下面简称为mmdet)
论文解读
模型结构
论文中的图
一个更详细的图
Faster-RCNN有两部分组成:RPN和Fast-RCNN。两者共享同一个backbone。具体来说,Faster-RCNN由以下几部分组成:
1、backbone(VGG,ResNet等)
2、neck(FPN,原版的faster没有,FPN出来之后后人才加上的)
3、rpn_head(RPNHead)
4、bbox_roi_extractor(SingleRoIExtractor > RoIAlign,RoIPool)
5、bbox_head(SharedFCBBoxHead)
上面是参考mmdet的config来分类的。backbonhe和neck无需多言,很常见的操作。下面讲以下剩下的三个部分。
rpn_head
上图是论文中的图,conv feature map是backbone出来的,或者是neck出来的特征。在mmdet代码中,滑动窗用一个3x3的conv来实现,独立的分类和回归层分别用两个1x1的conv来实现。代码如下:
@HEADS.register_module
class RPNHead(AnchorHead):
def __init__(self, in_channels, **kwargs):
super(RPNHead, self).__init__(2, in_channels, **kwargs)
def _init_layers(self):
self.rpn_conv = nn.Conv2d(
self.in_channels, self.feat_channels, 3, padding=1) # in:256, out:256
self.rpn_cls = nn.Conv2d(self.feat_channels,
self.num_anchors * self.cls_out_channels, 1)
# self.cls_out_channels = 2 前景和背景
self.rpn_reg = nn.Conv2d(self.feat_channels, self.num_anchors * 4, 1)
def init_weights(self):
normal_init(self.rpn_conv, std=0.01)
normal_init(self.rpn_cls, std=0.01)
normal_init(self.rpn_reg, std=0.01)
# 对单个feature map做操作,forward在AnchorHead中定义,将forward_single应用到feats上
def forward_single(self, x):
x = self.rpn_conv(x)
x = F.relu(x, inplace=True)
rpn_cls_score = self.rpn_cls(x)
rpn_bbox_pred = self.rpn_reg(x)
return rpn_cls_score, rpn_bbox_pred
rpn_head输出proposal的得分+框的位置信息。
bbox_roi_extractor
从名字就可以知道,bbox_roi_extractor是对bbox_roi进行特征提取的组件,faster-rcnn论文中用的是roi_pooling,mask-rcnn中用的是roi_align,R-FCN中的position-sensitive RoI pooling layer等。
训练
论文中的训练方式
论文中,给出了三种训练方式
1、交替训练
先训RPN,然后将RPN得到的proposal拿去训Fast-RCNN,接着Fast-RCNN微调后的网络去初始化RPN,然后训RPN,这个过程交替进行。(这里有一个疑问,是在每一个batch进行交替,还是训好RPN,再训Fast-RCNN这样进行,如果是每一个batch进行交替,那么可以保证proposal在batch内是不变的,如果是另一种,那么需要保证训好RPN之后,要把RPN冻住,才能保证proposal的稳定)
2、近似联合训练
RPN和Fast-RCNN当成一个整体,前向过程中,RPN生成的proposal当成是预先生成好去训练Fast-RCNN,反向传播过程,对于共享的卷积层,计算梯度的是RPN loss和Fast RCNN的losss相加,其他独立的部分就是各自的Loss。"approximate"体现在反向传播阶段RPN产生的置信误差能够获得梯度以更新参数,但是region proposals的坐标预测则直接把梯度舍弃了,也就是说box误差不参与loss的计算(和交替训练相比,这种方法的好处在于缩短训练时间)。
3、非近似联合训练
和近似联合训练相比,非近似联合训练,把RPN生成的proposal的loss也考虑进行梯度回归,通过“RoI warping” 层进行梯度反传???
四步训练法
第一步:imagenet初始化,训RPN,得到proposal
第二步:imagenet初始化,用RPN的proposal训Fast-RCNN
第三步:Fast-RCNN初始化共有卷积,训RPN(fix共有卷积)
第四步:fix共有卷积和RPN,训Fast-RCNN
mmdetection中的训练
RPN生成proposals->RCNN样本匹配(FPN)
# 超参
# 1. nms_pre: 进行nms之前的数目, 2. nms_post: nms之后的输出个数, 3. nms_thr: nms阈值
# 4. max_num: 单张图片最终输出proposals最多的个数, 5. nms_across_levels: 是否在跨levels进行nms操作
mlvl_proposals = []
for stride in strides:
# 1. 在多个strides下对模型预测值进行解码,获取预测框
# 2. 做一些过滤的操作
do clip
remove small
nms # nms_pre和nms_post通常等于2000
# 得到每个stride的proposals
# 3. mlvl_proposals.append(proposals)
# 将各个strides的proposals给concat起来
if nms_across_levels:
proposals, _ = nms(proposals, cfg.nms_thr)
proposals = proposals[:cfg.max_num, :]
else:
scores = proposals[:, 4]
num = min(cfg.max_num, proposals.shape[0])
_, topk_inds = scores.topk(num)
proposals = proposals[topk_inds, :]
return proposals