Relation Networks for Object Detection源码解读(网络结构细节)

本文深入探讨了Relation Networks for Object Detection的源码,重点关注网络结构的改进,特别是如何在Faster RCNN的基础上加入relation module。通过解析关键方法如get_symbol、extract_position_matrix、extract_position_embedding和attention_module_multi_head,揭示了ROI Pooling之后的全连接层如何引入object relation module,并介绍了坐标变换、坐标embedding和关系特征的计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

论文:Relation Networks for Object Detection
论文链接:https://arxiv.org/abs/1711.11575
代码链接:https://github.com/msracver/Relation-Networks-for-Object-Detection

这篇文章的细节可以通过阅读源码来加深理解,这篇博客就来介绍这篇文章的部分源码。

因为这篇文章主要是在网络结构上做改动,所以这篇博客以resnet_v1_101_rcnn_attention_1024_pairwise_position_multi_head_16_learn_nms.py为例介绍网络结构,这个例子在Faster RCNN基础上引入relation module,包括全连接层和NMS阶段。

链接:resnet_v1_101_rcnn_attention_1024_pairwise_position_multi_head_16_learn_nms.py

接下来介绍的代码顺序和链接中的不大一样,这里按照代码运行时的顺序介绍,目的是方便跟着数据流进行阅读,主要涉及几个方法:全局网络构造:get_symbol;ROI信息的坐标变换:extract_position_matrix;ROI信息的坐标embedding:extract_position_embedding;object relation module的计算:attention_module_multi_head。

这里将网络结构封装成一个类,初始化函数中主要是resnet_v1_101网络的设置。

class resnet_v1_101_rcnn_attention_1024_pairwise_position_multi_head_16_learn_nms(resnet_v1_101_rcnn_learn_nms_base):
    def __init__(self):
        """
        Use __init__ to define parameter network needs
        """
        self.eps = 1e-5
        self.use_global_stats = True
        self.workspace = 512
        self.units = (3, 4, 23, 3)  # use for 101
        self.filter_list = [256, 512, 1024, 2048]

get_symbol方法是获取网络结构时候调用的方法,也是这个脚本中的主干,接下来都会围绕该方法进行介绍。在该方法中包含了整体网络结构的构造,非常重要。提取说明下,在ROI Pooling层之前的结构基本上和Faster RCNN类似,重点在于ROI Pooling层后面的两个全连接层。

def get_symbol(self, cfg, is_train=True):

        # config alias for convenient
# 首先是一个参数设定。以COCO数据集为例(这篇文章的实验都是在COCO数据集上做的),
# num_classes是81。cfg.CLASS_AGNOSTIC参数表示回归时是否不区分类别,默认是True,
# 也就是不区分类别,因此num_reg_classes默认是2。需要注意的是原生的Faster RCNN在
# 回归时是区分类别的。num_anchors默认是12,这个数值比原生的Faster RCNN要大。
        num_classes = cfg.dataset.NUM_CLASSES
        num_reg_classes = (2 if cfg.CLASS_AGNOSTIC else num_classes)
        num_anchors = cfg.network.NUM_ANCHORS

        # input init
# 输入数据和信息的初始化,需要和数据读取的变量同名。
        if is_train:
            data = mx.sym.Variable(name="data")
            im_info = mx.sym.Variable(name="im_info")
            gt_boxes = mx.sym.Variable(name="gt_boxes")
            rpn_label = mx.sym.Variable(name='label')
            rpn_bbox_target = mx.sym.Variable(name='bbox_target')
            rpn_bbox_weight = mx.sym.Variable(name='bbox_weight')
        else:
            data = mx.sym.Variable(name="data")
            im_info = mx.sym.Variable(name="im_info")

        # shared convolutional layers
# conv_feat,包含resnet_v1_101从开始到conv4结束,这一部分是RPN网络的输入。
# relu1,包含resnet_v1_101从开始到conv5结束,用来做object的坐标回归和分类。这都是常规的做法。
        conv_feat = self.get_resnet_v1_conv4(data)
        # res5
        relu1 = self.get_resnet_v1_conv5(conv_feat)

# 这部分是基于conv_feat,调用get_rpn方法得到rpn网络的输出。
        rpn_cls_score, rpn_bbox_pred = self.get_rpn(conv_feat, num_anchors)

        if is_train:
            # prepare rpn data
            rpn_cls_score_reshape = mx.sym.Reshape(
                data=rpn_cls_score, shape=(0, 2, -1, 0), name="rpn_cls_score_reshape")

            # classification
# 这部分是对bbox的二分类损失函数。
            rpn_cls_prob = mx.sym.SoftmaxOutput(data=rpn_cls_score_reshape, label=rpn_label, multi_output=True, normalization='valid', use_ignore=True, ignore_label=-1, name="rpn_cls_prob")

            # bounding box regression
# 这部分是对bbox的坐标回归损失函数。
            rpn_bbox_loss_ = rpn_bbox_weight * mx.sym.smooth_l1(name='rpn_bbox_loss_', scalar=3.0, data=(rpn_bbox_pred - rpn_bbox_target))

            rpn_bbox_loss = mx.sym.MakeLoss(name='rpn_bbox_loss', data=rpn_bbox_loss_, grad_scale=1.0 / cfg.TRAIN.RPN_BATCH_SIZE)

            # ROI proposal
# 这部分是对bbox做过滤得到proposal。注意几个参数:1、cfg.TRAIN.RPN_PRE_NMS_TOP_N表示
# 进行NMS操作之前的roi数量,默认是6000。2、cfg.TRAIN.RPN_POST_NMS_TOP_N表示
# NMS操作之后的roi操作,默认是300(FPN网络中默认用1000)。这两个参数设置和Faster RCNN不同。
# 3、cfg.network.ANCHOR_SCALES默认是4,8,16,32。
# 4、cfg.network.ANCHOR_RATIOS默认是0.5,1,2。
            rpn_cls_act = mx.sym.SoftmaxActivation(
                data=rpn_cls_score_reshape, mode="channel", name="rpn_cls_act")
            rpn_cls_act_reshape = mx.sym.Reshape(
                data=rpn_cls_act, shape=(0, 2 * num_anchors, -1, 0), name='rpn_cls_act_reshape')
            if cfg.TRAIN.CXX_PROPOSAL:
                rois = mx.contrib.sym.Proposal(
                    cls_prob=rpn_cls_act_reshape, bbox_pred=rpn_bbox_pred, im_info=im_info, name='rois', feature_stride=cfg.network.RPN_FEAT_STRIDE, scales=tuple(cfg.network.ANCHOR_SCALES), ratios=tuple(cfg.network.ANCHOR_RATIOS),                    rpn_pre_nms_top_n=cfg.TRAIN.RPN_PRE_NMS_TOP_N, rpn_post_nms_top_n=cfg.TRAIN.RPN_POST_NMS_TOP_N,                    threshold=cfg.TRAIN.RPN_NMS_THRESH, rpn_min_size=cfg.TRAIN.RPN_MIN_SIZE)
            else:
                rois = mx.sym.Custom(
                    cls_prob=rpn_cls_act_reshape, bbox_pred=rpn_bbox_pred, im_info=im_info, name='rois', op_type='proposal', feat_stride=cfg.network.RPN_FEAT_STRIDE,
scales=tuple(cfg.network.ANCHOR_SCALES), ratios=tuple(cfg.network.ANCHOR_RATIOS),
                    rpn_pre_nms_top_n=cfg.TRAIN.RPN_PRE_NMS_TOP_N, rpn_post_nms_top_n=cfg.TRAIN.RPN_POST_NMS_TOP_N,                    threshold=cfg.TRAIN.RPN_NMS_THRESH, rpn_min_size=cfg.TRAIN.RPN_MIN_SIZE)

# ROI proposal target
# 这部分一方面对前面的propsoal做过滤得到batch_rois个proposal(或者叫roi),最后
# 得到的rois数量由cfg.TRAIN.BATCH_ROIS决定,这里因为cfg.TRAIN.BATCH_ROIS默认
# 是-1(和Faster RCNN中默认的128不同),因此最后得到的roi数量等于输入roi的数量加
# 上ground truth的数量,默认情况下输入roi数量是300,因此最后得到的roi数量是300+x,
# x表示object的数量。另一方面计算Fast RCNN的分类和回归支路的训练目标。
            gt_boxes_reshape = mx.sym.Reshape(data=gt_boxes, shape=(-1, 5), name='gt_boxes_reshape')
            rois, label, bbox_target, bbox_weight = mx.sym.Custom(rois=rois, gt_boxes=gt_boxes_reshape, op_type='proposal_target', num_classes=num_reg_classes, batch_images=cfg.TRAIN.BATCH_IMAGES, batch_rois=cfg.TRAIN.BATCH_ROIS, cfg=cPickle.dumps(cfg),
fg_fraction=cfg.TRAIN.FG_FRACTION)
        else:
# 这部分是测试时候的网络结构设置,总体而言就是去掉了RPN网络的损失函数和Fast RCNN的
# 分类和检测目标生成等。
            # ROI Proposal
            rpn_cls_score_reshape = mx.sym.Reshape(
                data=rpn_cls_score, shape=(0, 2, -1, 0), name="rpn_cls_score_reshape")
            rpn_cls_prob = mx.sym.SoftmaxActivation(
                data=rpn_cls_score_reshape, mode="channel", name="rpn_cls_prob")
            rpn_cls_prob_reshape = mx.sym.Reshape(
                data=rpn_cls_prob, shape=(0, 2 * num_anchors, -1, 0), name='rpn_cls_prob_reshape')
            if cfg.TEST.CXX_PROPOSAL:
                rois = mx.contrib.sym.Proposal(
                    cls_prob=rpn_cls_prob_reshape, bbox_pred=rpn_bbox_pred, im_info=im_info, name='rois', feature_stride=cfg.network.RPN_FEAT_STRIDE, scales=tuple(cfg.network.ANCHOR_SCALES), ratios=tuple(cfg.network.ANCHOR_RATIOS),
rpn_pre_nms_top_n=cfg.TEST.RPN_PRE_NMS_TOP_N, rpn_post_nms_top_n=cfg.TEST.RPN_POST_NMS_TOP_N,
threshold=cfg.TEST.RPN_NMS_THRESH, rpn_min_size=cfg.TEST.RPN_MIN_SIZE)
            else:
                rois = mx.sym.Custom(
                    cls_prob=rpn_cls_prob_reshape, bbox_pred=rpn_bbox_pred, im_info=im_info, name='rois', op_type='proposal', feat_stride=cfg.network.RPN_FEAT_STRIDE,
scales=tuple(cfg.network.ANCHOR_SCALES), ratios=tuple(cfg.network.ANCHOR_RATIOS),
rpn_pre_nms_top_n=cfg.TEST.RPN_PRE_NMS_TOP_N, rpn_post_nms_top_n=cfg.TEST.RPN_POST_NMS_TOP_N,
threshold=cfg.TEST.RPN_NMS_THRESH, rpn_min_size=cfg.TEST.RPN_MIN_SIZE)

# nongt_dim在训练中采用cfg.TRAIN.RPN_POST_NMS_TOP_N,默认是300,这个值在后续会经常用到。
        nongt_dim = cfg.TRAIN.RPN_POST_NMS_TOP_N if is_train else cfg.TEST.RPN_POST_NMS_TOP_N

# 接下来这个卷积层是接在conv5后面的。
        conv_new_1 = mx.sym.Convolution(data=relu1, kernel=(1, 1), num_filter=
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值