目标检测2——借Detectron2源码理解Anchors与RPN


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


1.Regionn Proposal Network背景

RPN,Region Proposal Network是中科大与微软亚洲研究院联合培养博士,原Momenta研发总监任少卿与何凯明,Ross Girshick共同发表的论文Faster R-CNN中提出的一个网络结构,用于目标检测,RPN论文最早发表于20150604号,是在Fast RCNN上的改进,与其一起提出的Translation-Invariant Anchors极大的提高的检测的性呢和准确度。Faster R-CNN使用RPNanchors替代RCNNFast RCNN中的selective search方法,Faster R-CNN将检测问题分成了特征提取backcbone的训练和RPN候选框生成网络的训练,因此是Two Stage检测框架。RPN用于生成Proposal Boxes, 以用来输入ROI PoolingROI Align,做进一步的类别判断和检测框定位。

RPNFaster R-CNN中提出的,先来看下Faster R-CNN的整体结构:
在这里插入图片描述

上图中,可以看到,对于输入的图像,先经过Conv Layers进行特征提取得到feature map,再将feature map一支用于输入RPN结合anchors来生成Proposal Boxes,另一支feature mapRPN生成的Proposal boxes一起输入ROI Pooling,经过全连接层后做检测物体类别的回归和检测框的精细化定位。从上图中可以知道,RPN网络的作用就是输入feature map,输出Proposal Boxes,在进行检测网络整体训练之前,需基于现有的Model先训练RPN网络,使其能够用来生成Proposal Boxes,然后再训练Model,循环3次。

2.Regionn Proposal Network的结构

在这里插入图片描述

如图,这是目前R-CNN衍生出来的检测算法都会使用的RPN Head的网络结构,用来生成Proposal Boxes,并判断其中是否包含物体,输出每个Proposal Boxes的置信度。结合上图,介绍一下rpn网络的结构,RPN网络的输入是backbone提取得到的feature map(NCHW),网络结构中先有一个3x3的卷积,进一步融合特征,然后将卷积结果分别输入到两个分支上。每个分支都包含一个1x1的卷积,只改变输入特征图的通道大小,不改变feature map的宽高。其中一支负责预测anchors偏移量,输出通道数变为num_anchors*box_dims,关于anchors的介绍见下一部分。另一支负责预测每个proposal boxes的置信度,其输出通道数为num_anchors。因总的proposal boxes数里过多,得到置信度proposal boxes对应的位置后,可据此对proposal boxes进行过滤,正是图中proposals层做的事情,其介绍见图2,这里是以一个feature map进行说明的,对于FPN结构的网络,对不同层级的特征分别进行处理即可。代码实现可以参考detectron2

class StandardRPNHead(nn.Module):
     def forward(self, features: List[torch.Tensor]):
          """
          Args:
               features (list[Tensor]): list of feature maps

          Returns:
               list[Tensor]: A list of L elements.
                    Element i is a tensor of shape (N, A, Hi, Wi) representing
                    the predicted objectness logits for all anchors. A is the number of cell anchors.
               list[Tensor]: A list of L elements. Element i is a tensor of shape
                    (N, A*box_dim, Hi, Wi) representing the predicted "deltas" used to transform anchors
                    to proposals.
          """
          pred_objectness_logits = []
          pred_anchor_deltas = []
          for x in features:
               t = self.conv(x)
               pred_objectness_logits.append(self.objectness_logits(t))
               pred_anchor_deltas.append(self.anchor_deltas(t))
          return pred_objectness_logits, pred_anchor_deltas

3.Anchors

‵AnchorsFaster RCNN论文中提出的用来更好的回归bounding boxes`的算法。

_C.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64, 128, 256, 512]]
# Anchor aspect ratios. For each area given in `SIZES`, anchors with different aspect
# ratios are generated by an anchor generator.
# Format: list[list[float]]. ASPECT_RATIOS[i] specifies the list of aspect ratios (H/W)
# to use for IN_FEATURES[i]; len(ASPECT_RATIOS) == len(IN_FEATURES) must be true,
# or len(ASPECT_RATIOS) == 1 is true and aspect ratio list ASPECT_RATIOS[0] is used
# for all IN_FEATURES.
_C.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.5, 1.0, 2.0]]

如上代码中,分别是5种size3种宽高比的Anchors配置,Anchors的大小是在检测输入图像的尺度上的,通过变换可知对于每个点共有15种不同宽高比和大小的anchors,

在这里插入图片描述

Anchors作用在feature map上的每个cell中心点的,再根据图像信息和特征提取网络的stride,找到原图上Anchors的对应位置。其应用可以参考faster rcnn论文的一个图,

在这里插入图片描述

使用Anchors时,bounding boxes回归的原理是anchors的中心 x , y x,y x,y和宽高 w , h w,h w,h通过平移和缩放可以得到对应的bounding box。给定 a n c h o r A ( A x , A y , A w , A h ) anchor A(A_x, A_y, A_w, A_h) anchorA(Ax,Ay,Aw,Ah) g t G ( G x , G y , G w , G h ) gt G(G_x, G_y,G_w, G_h) gtG(Gx,Gy,Gw,Gh)可以寻找一种变换F使得 F ( A x , A y , A w , A h ) = G ′ ( G x ′ , G y ′ , G w ′ , G h ′ ) F(A_x, A_y, A_w, A_h)=G'(G_x', G_y', G_w', G_h') F(Ax,Ay,Aw,Ah)=G(Gx,Gy,Gw,Gh),而 G ≈ G ′ G\approx G' GG,变换F可以表示为:

  • 先平移
    G x ′ = A w ∗ d x ( A ) + A x G y ′ = A h ∗ d x ( A ) + A y \begin{matrix} G_x'=A_w*d_x(A)+A_x \\ G_y'=A_h*d_x(A)+A_y \end{matrix} Gx=Awdx(A)+AxGy=Ahdx(A)+Ay

  • 再缩放

G w ′ = A w e x p ( d w ( A ) ) G h ′ = A h e x p ( d h ( A ) ) G_w'=A_w exp(d_w(A)) \\ G_h'=A_h exp(d_h(A)) Gw=Awexp(dw(A))Gh=Ahexp(dh(A))

其中 d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) d_x(A),d_y(A),d_w(A),d_h(A) dx(A),dy(A),dw(A),dh(A)四个变换,当anchorgt box相差很小时,可看成线性变换即Y=WX

在这里插入图片描述

上图种蓝色的网格表示feature map特征图,在其中间一个cell上生成的一个Anchor如图中黄色框所示,其中心 A x , A y A_x,A_y Ax,Aycell的中心对应的原图上的坐标,与当前这个对应IoU最大的ground truth box如图中红色的框,其中心坐标为 G x , G y G_x,G_y Gx,Gy,可以知道一般情况下Anchor只是大概定位了检测框的位置,还需对其进行少量的平移才能实现准确定位。同样Anchor也只是大概确定了检测框的宽高,还需在宽高方向上进行适量的缩放才能得到准确的检测框。

faster rcnn中的偏移量预测是tx,没有范围限制,容易导致产生超出边界的预测框,在2016年12月25号Joseph Redmon发表的Yolov2中对其进行了修改,改成了在预测相对于featue map cell左上点的偏移量,并做sigmoid,使得偏移量始终在 0 − 1 0-1 01之间。

对于检测网络训练时,传入AGround Truth Boxes之间的变换量 t x , t y , t ω , t h t_x, t_y, t_\omega,t_h tx,ty,tω,th,借此使用L1损失函数回归Y=WX函数即可完成RPN的训练。

4.Regionn Proposal Network的训练

训练RPN网络时,需先将feaure map经过RPN前向推理得到shape=(N, Hi*Wi*A)pred_objectness_logits scoresshape=(N, Hi*Wi*A, B)pred_anchor_deltas。然后将shape = [N, H*W*A]anchorsshape=[N, M, B]ground truth boxes对应起来,再计算含有物体的positive target anchorspredicted anchors之间的定位损失及positive and negative target anchors和对应predicted anchors之间的分类损失。

训练RPN网络时,比较多的工作花费在了anchor assignment,即实现anchorsground truth box之间的匹配。faster R-CNN中主要使用的anchorground truth box之间的IoU来实现。对于ManchorsNround truth boxes,两两之间分别计算IoU,可以得到MxNIoU_Match矩阵,取每个anchorNgt boxes IoU最大的box作为与anchor匹配的Ground Truth Box,如此找到了每个anchor对应的Ground Truth Box。再根据两者之间的IoU,判断其是背景bg还是前景fg即是否有物体,判断方式通常是设者IoU_Threshold_LowIoU_Threshold_HighIoU大于IoU_Threshold_High的是positive,小于IoU_Threshold_Low的是negative,介于两者之间的忽略。通过这样定义可以知道一个ground truth box可以对应多个anchor初步判断出positive/negative anchors之后,还需经过超参数每个图像中训练最多使用的anchor box数量其中positive anchors fractionanchors再次处理,限制参与训练的anchor box不多于超参数最大数量,见detectron2 rpn.py中的label_and_sample_anchors函数

def label_and_sample_anchors(
     self, anchors: List[Boxes], gt_instances: List[Instances]
) -> Tuple[List[torch.Tensor], List[torch.Tensor]]:
     anchors = Boxes.cat(anchors)

     gt_boxes = [x.gt_boxes for x in gt_instances]
     image_sizes = [x.image_size for x in gt_instances]
     del gt_instances

     gt_labels = []
     matched_gt_boxes = []
     for image_size_i, gt_boxes_i in zip(image_sizes, gt_boxes):
          """
          image_size_i: (h, w) for the i-th image
          gt_boxes_i: ground-truth boxes for i-th image
          """

          match_quality_matrix = retry_if_cuda_oom(pairwise_iou)(gt_boxes_i, anchors)
          matched_idxs, gt_labels_i = retry_if_cuda_oom(self.anchor_matcher)(match_quality_matrix)
          # M个anchors与N个ground truth boxes匹配,得到M个anchors分别对应的ground truth box,找到每个anchor的标签
          gt_labels_i = gt_labels_i.to(device=gt_boxes_i.device)
          del match_quality_matrix

          if self.anchor_boundary_thresh >= 0:
               # Discard anchors that go out of the boundaries of the image
               # NOTE: This is legacy functionality that is turned off by default in Detectron2
               anchors_inside_image = anchors.inside_box(image_size_i, self.anchor_boundary_thresh)
               gt_labels_i[~anchors_inside_image] = -1

          # A vector of labels (-1, 0, 1) for each anchor
          # 根据超参数,限制每张图像中参与训练的anchors上限和positive anchor的比例
          gt_labels_i = self._subsample_labels(gt_labels_i)

          if len(gt_boxes_i) == 0:
               # These values won't be used anyway since the anchor is labeled as background
               matched_gt_boxes_i = torch.zeros_like(anchors.tensor)
          else:
               # TODO wasted indexing computation for ignored boxes
               matched_gt_boxes_i = gt_boxes_i[matched_idxs].tensor

          gt_labels.append(gt_labels_i)  # N,AHW
          matched_gt_boxes.append(matched_gt_boxes_i)
     return gt_labels, matched_gt_boxes

损失函数

  • 位置回归使用的是smooth L1 loss,通过positive mask实现只取target positive anchor和对应的predicted anchors计算regressive loss

s m o o t h L 1 ( x ) = { 0.5 x 2 i f ∣ x ∣ < 1 ∣ x ∣ − 0.5 o t h e r w i s e smooth_{L_1(x)} = \left\{\begin{matrix} 0.5x^2 & if \left | x \right | \lt 1\\ \left | x \right | - 0.5 & otherwise \end{matrix}\right. smoothL1(x)={0.5x2x0.5ifx<1otherwise

  • 分类损失使用的是binary cross entropy loss,只取positive/negative target loss计算。
    L i = y t r u e l o g y p r e d + ( 1 − y t r u e ) l o g ( 1 − y p r e d ) L_i = y_{true}log y_{pred} + (1-y_{true})log(1-y_{pred}) Li=ytruelogypred+(1ytrue)log(1ypred)
def losses(
     self,
     anchors: List[Boxes],
     pred_objectness_logits: List[torch.Tensor],
     gt_labels: List[torch.Tensor],
     pred_anchor_deltas: List[torch.Tensor],
     gt_boxes: List[torch.Tensor],
) -> Dict[str, torch.Tensor]:

     num_images = len(gt_labels)
     gt_labels = torch.stack(gt_labels)  # (N, sum(Hi*Wi*Ai))

     # Log the number of positive/negative anchors per-image that's used in training
     pos_mask = gt_labels == 1
     num_pos_anchors = pos_mask.sum().item()
     num_neg_anchors = (gt_labels == 0).sum().item()
     storage = get_event_storage()
     storage.put_scalar("rpn/num_pos_anchors", num_pos_anchors / num_images)
     storage.put_scalar("rpn/num_neg_anchors", num_neg_anchors / num_images)

     localization_loss = _dense_box_regression_loss(
          anchors,
          self.box2box_transform,
          pred_anchor_deltas,
          gt_boxes,
          pos_mask,
          box_reg_loss_type=self.box_reg_loss_type,
          smooth_l1_beta=self.smooth_l1_beta,
     )

     valid_mask = gt_labels >= 0
     objectness_loss = F.binary_cross_entropy_with_logits(
          cat(pred_objectness_logits, dim=1)[valid_mask],
          gt_labels[valid_mask].to(torch.float32),
          reduction="sum",
     )
     normalizer = self.batch_size_per_image * num_images
     losses = {
          "loss_rpn_cls": objectness_loss / normalizer,
          # The original Faster R-CNN paper uses a slightly different normalizer
          # for loc loss. But it doesn't matter in practice
          "loss_rpn_loc": localization_loss / normalizer,
     }
     losses = {k: v * self.loss_weight.get(k, 1.0) for k, v in losses.items()}
     return losses

Fast R-CNN论文中,bounding boxes回归使用的就是smooth L1 loss了,与 L 1 , L 2 L_1,L_2 L1,L2相比,smooth L1的导数在x较小时(0-1)时更敏感,因此可以有更好的收敛效果。

在这里插入图片描述

图片来自于
在这里插入图片描述


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值