RPN网络

2022-09-10 14:32:11 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

RPN

思路:

1、先通过conv层 pooling层 relu层,可以是vgg,得到feature maps。

2、在feature maps上提取对应的图。在第一步基础上,先通过rpn生成region proposals。通过softmax判断anchors(9个框),是foreground还是background,再通过bounding box regression 进行修正anchors,也就是进一步确定proposals。

3、目的是为了得到proposals feature maps,提取框的特征图,这一步是为了下面的分类做准备,也就是先得到框图在分类。这一步先通过上面得到的proposals,和roi pooling,提取proposals feature maps。

4、分类。用proposals feature maps和一开始的proposals判断类别。

1、一个图片从由p*q大小,变为m*n大小(600*1000),进入网络,经过13个conv,13个relu,4个pooling。得到feature maps(40*60*256)。

2、先经过3*3conv,经过1*1conv,分成foreground和background,上面一条线是通过softmax分类anchor,获得foreground和background,下面计算anchor的boundingboxregression偏移情况。然后计算出来proposals。

3、roi pooling,用proposals从最一开始的feature maps,通过全连接和softmax,分类。

步骤:

1、卷积层

包括conv,relu,pooling。以vgg16为例,13个conv参数为kernelsize为3*3,pad为1,4个pooling为2*2,stride为2,一个图形进去网络出来后大小为m/16 ,n/16的feature maps。

2、region proposal networks(RPN)

  • 上面一层目的是为了完成分类anchors(9个框),以获得foreground和background,目的是留着foreground。
  • 下面是对anchors进行bounding box regression,为了得到精准的参数,可以帮助后面得到精准的proposal。
  • 最后proposal是为了整合上面两个步骤得到proposal。

从第一层说,进入3*3前图形是m/16 ,n/16的feature maps,进行3*3卷积应该是为了更好融合每个点周围的信息,每个点都有9个anchors,如下所示:每个点都有256-d,每个点都要对框分fore和back,为2个维度,所以为2k,k为9个框,所以每个点输出为W*H*18。同理回归是需要四个值定框,分别为左上右下,4个维度,所以为4k,为W*H*36。虽然文章里面有些到,但是其实在程序里面没有这样的内容,找不到所谓的框和所谓框的四个维度。

proposals layer 3个输入,一个是分类器结果foreground,一个是anchor回归结果reg,还有一个是im-info,就是把图像进行了缩减后,进行proposal layer。

1、先再生成anchor,对做reg回归。

2、按照输入foreground softmax scores对anchor排序。目的找到最优anchor(2000个)。

3、通过feat_stride和im_info,把anchor映射回原图。

4、nms。

5、按照nms由大到小排序foreground anchors。提取top作为结果输出proposal(256个)。

from:https://blog.csdn.net/N_Sapientia/article/details/81487968


这里介绍一下分类层中的两个reshape操作:

对于cls层,使用1*1的卷积操作输出了18(9个anchor*2类fg/bg)个通道的feature map,大小不变。而对于regression层,也使用1*1的卷积层输出了36(4*9个anchor)个通道的feature map,大小不变。

对于cls层后又接了一个reshape层,其实只是为了便于softmax分类,这就要从caffe的实现形式说起了。在caffe基本数据结构blob中以如下形式保存数据:blob=[batch_size, channel,height,width]

对应至上面的保存bg/fg anchors的矩阵,其在caffe blob中的存储形式为[1, 2*9, H, W]。而在softmax分类时需要进行fg/bg二分类,所以reshape layer会将其变为[1, 2, 9*H, W]大小,即单独“腾空”出来一个维度以便softmax分类,之后再reshape回复原状。

从中其实我们也能发现,对于rpn每个点的18个输出通道,前9个为背景的预测分数,而后9个为前景的预测分数。

对于regression,不需要这样的操作,那么他的36个通道是不是也是如上面18个通道那样呢?即第一个9通道为dx,第二个为dy,第三个为dw,第五个是dh。还是我们比较容易想到的那种,即第一个通道是第一个盒子的回归量(dx1,dy1,dw1,dh1),第二个为(dx2,dy2,dw,2,dh2)…..。待后面查看对应的bbox_targets就知道了

正如图上所示,我们还需要准备一个层rpn-data。

代码语言:javascript复制
layer {
  name: 'rpn-data'
  type: 'Python'
  bottom: 'rpn_cls_score'
  bottom: 'gt_boxes'
  bottom: 'im_info'
  bottom: 'data'
  top: 'rpn_labels'
  top: 'rpn_bbox_targets'
  top: 'rpn_bbox_inside_weights'
  top: 'rpn_bbox_outside_weights'
  python_param {
    module: 'rpn.anchor_target_layer'
    layer: 'AnchorTargetLayer'
    param_str: "'feat_stride': 16"
  }
}

这一层输入四个量:data,gt_boxes,im_info,rpn_cls_score,其中前三个是我们在前面说过的,

data: 1*3*600*1000 gt_boxes: N*5, N为groundtruth box的个数,每一行为(x1, y1, x2, y2, cls) ,而且这里的gt_box是经过缩放的。 im_info: 1*3 (h,w,scale)

rpn_cls_score是cls层输出的18通道,shape可以看成是1*18*H*W.

输出为4个量:

rpn_labels : (1, 1, 9 * height, width)

rpn_bbox_targets(回归目标): (1, 36,height, width)

rpn_bbox_inside_weights(内权重):(1, 36,height, width)

rpn_bbox_outside_weights(外权重):(1, 36,height, width)

通俗地来讲,这一层产生了具体的anchor坐标,并与groundtruth box进行了重叠度计算,输出了kabel与回归目标。

regression可以利用rpn_bbox_pred,rpn_bbox_targets,rpn_bbox_inside_weights,rpn_bbox_outside_weights计算SmoothL1Loss,输出rpn_loss_bbox。

回到我们之前有一个问题rpn_bbox_pred的shape怎么构造的。其实从rpn_bbox_targets的生成过程中可以推断出应该采用后一种,即第一个盒子的回归量(dx1,dy1,dw1,dh1),第二个为(dx2,dy2,dw,2,dh2)…..,这样顺序着来。

from:https://blog.csdn.net/alibabazhouyu/article/details/80875383

RPN结构流程图:

Note:

  1. 只有在train时,cls reg才能得到强监督信息(来源于ground truth)。即ground truth会告诉cls reg结构,哪些才是真的前景,从而引导cls reg结构学得正确区分前后景的能力;在测试阶段,就要靠cls reg自力更生了。
  2. 在train阶段,会输出约2000个proposal,但只会抽取其中256个proposal来训练RPN的cls reg结构;到了测试阶段,则直接输出最高score的300个proposal。此时由于没有了监督信息,所有RPN**并不知道这些proposal是否为前景**,整个过程只是惯性地推送一波无tag的proposal给后面的Fast R-CNN。

1、在 RPN头部 ,通过以下结构生成 anchor(其实就是一堆有编号有坐标的bbox);

2、在 RPN中部分类分支(cls)边框回归分支(bbox reg) 分别对这堆anchor进行计算。Note: two stage型的检测算法在RPN 之后 还会进行 再一次分类任务边框回归任务,以进一步提升检测精度;

3、在 RPN末端,通过对 两个分支的结果进行汇总,来实现对anchor的 初步筛除(先剔除越界的anchor,再根据cls结果通过NMS去重)和 初步偏移(根据bbox reg结果),此时输出的都改头换面叫 Proposal


损失函数_add_losses

faster rcnn包括两个损失:rpn网络的损失 rcnn网络的损失。其中每个损失又包括分类损失和回归损失。分类损失使用的是交叉熵,回归损失使用的是smooth L1 loss。

其中rpn_cross_entropy和rpn_loss_box是RPN网络的两个损失,cls_score和bbox_pred是rcnn网络的两个损失。前两个损失用于判断archor是否是ground truth(二分类);后两个损失的batchsize是256。

将rpn_label(1,?,?,2)中不是-1的index取出来,之后将rpn_cls_score(1,?,?,2)及rpn_label中对应于index的取出,计算sparse_softmax_cross_entropy_with_logits,得到rpn_cross_entropy。

计算rpn_bbox_pred(1,?,?,36)和rpn_bbox_targets(1,?,?,36)的_smooth_l1_loss,得到rpn_loss_box。

代码语言:javascript复制
rpn_bbox_targets= bbox_targets = _compute_targets(anchors, gt_boxes[argmax_overlaps, :])  # 通过archors和archors对应的正样本计算坐标的偏移

计算cls_score(256*21)和label(256个正样本和负样本对应的真实的类别)的sparse_softmax_cross_entropy_with_logits:cross_entropy。

代码语言:javascript复制
labels, rois, roi_scores, bbox_targets, bbox_inside_weights = _sample_rois(all_rois, all_scores, gt_boxes, fg_rois_per_image, rois_per_image, _num_classes) # 选择256个archors

计算bbox_pred(256*84)和bbox_targets(256*84)的_smooth_l1_loss:loss_box。

最终将上面四个loss相加,得到总的loss(还需要加上regularization_loss)。

至此,损失构造完毕。

代码语言:javascript复制
def _add_losses(self, sigma_rpn=3.0):
    with tf.variable_scope('LOSS_'   self._tag) as scope:
        rpn_cls_score = tf.reshape(self._predictions['rpn_cls_score_reshape'], [-1, 2])  # 每个archors是正样本还是负样本
        rpn_label = tf.reshape(self._anchor_targets['rpn_labels'], [-1])  # 特征图中每个位置对应的是正样本、负样本还是不关注(去除了边界在图像外面的archors)
        rpn_select = tf.where(tf.not_equal(rpn_label, -1))    # 不关注的archor到的索引
        rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select), [-1, 2])    # 去除不关注的archor
        rpn_label = tf.reshape(tf.gather(rpn_label, rpn_select), [-1])        # 去除不关注的label
        rpn_cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score, labels=rpn_label))  # rpn二分类的损失

        rpn_bbox_pred = self._predictions['rpn_bbox_pred']  #  每个位置的9个archors回归位置偏移
        rpn_bbox_targets = self._anchor_targets['rpn_bbox_targets']   # 特征图中每个位置和对应的正样本的坐标偏移(很多为0)
        rpn_bbox_inside_weights = self._anchor_targets['rpn_bbox_inside_weights']  # 正样本的权重为1(去除负样本和不关注的样本,均为0)
        rpn_bbox_outside_weights = self._anchor_targets['rpn_bbox_outside_weights']   #   正样本和负样本(不包括不关注的样本)归一化的权重
        rpn_loss_box = self._smooth_l1_loss(rpn_bbox_pred, rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights, sigma=sigma_rpn, dim=[1, 2, 3])

        cls_score = self._predictions["cls_score"]  # 用于rcnn分类的256个archors的特征
        label = tf.reshape(self._proposal_targets["labels"], [-1])   # 正样本和负样本对应的真实的类别
        cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=cls_score, labels=label))   # rcnn分类的损失

        bbox_pred = self._predictions['bbox_pred']   # RCNN, bbox loss
        bbox_targets = self._proposal_targets['bbox_targets']    # 256*(4*21)的矩阵,只有为正样本时,对应类别的坐标才不为0,其他类别的坐标全为0
        bbox_inside_weights = self._proposal_targets['bbox_inside_weights']  # 256*(4*21)的矩阵,正样本时,对应类别四个坐标的权重为1,其他全为0
        bbox_outside_weights = self._proposal_targets['bbox_outside_weights']   # 256*(4*21)的矩阵,正样本时,对应类别四个坐标的权重为1,其他全为0
        loss_box = self._smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights)

        self._losses['cross_entropy'] = cross_entropy
        self._losses['loss_box'] = loss_box
        self._losses['rpn_cross_entropy'] = rpn_cross_entropy
        self._losses['rpn_loss_box'] = rpn_loss_box

        loss = cross_entropy   loss_box   rpn_cross_entropy   rpn_loss_box  # 总共的损失
        regularization_loss = tf.add_n(tf.losses.get_regularization_losses(), 'regu')
        self._losses['total_loss'] = loss   regularization_loss

        self._event_summaries.update(self._losses)

    return loss

caffe prototxt文件:https://github.com/rbgirshick/py-faster-rcnn/tree/master/models/pascal_voc/ZF/faster_rcnn_end2end

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152276.html原文链接:https://javaforall.cn

0 人点赞