项目源码

一、Faster-RCNN简介

『cs231n』Faster_RCNN

『计算机视觉』Faster-RCNN学习_其一:目标检测及RCNN谱系

一篇讲的非常明白的文章:一文读懂Faster RCNN

(1)输入测试图像;

(2)将整张图片输入CNN,进行特征提取;

(3)用RPN生成建议窗口(proposals),每张图片保留约300个建议窗口;

(4)把建议窗口映射到CNN的最后一层卷积feature map上;

(5)通过RoI pooling层使每个RoI生成固定尺寸的feature map;

(6)利用Softmax Loss(探测分类概率) 和Smooth L1 Loss(探测边框回归)对分类概率和边框回归(Bounding box regression)联合训练.

相比FAST-RCNN,主要两处不同

(1)使用RPN(Region Proposal Network)代替原来的Selective Search方法产生建议窗口;

(2)产生建议窗口的CNN和目标检测的CNN共享

改进

快速产生建议框:FASTER-RCNN创造性地采用卷积网络自行产生建议框,并且和目标检测网络共享卷积网络,使得建议框数目从原有的约2000个减少为300个,且建议框的质量也有本质的提高.

RPN简介

放到整体网络中如下,

对于共享的Feature Map,RPN使用3*3的滑窗,每个滑动窗口位置生成9个候选窗口(不同尺度、不同宽高),对应36个坐标、18个分类。

训练过程中,

1)丢弃跨越边界的anchor;

2)与样本重叠区域大于0.7的anchor标记为前景,重叠区域小于0.3的标定为背景;

总结一下:

  • 在feature map上滑动窗口
  • 建一个神经网络用于物体分类+框位置的回归
  • 滑动窗口的位置提供了物体的大体位置信息
  • 框的回归提供了框更精确的位置

这里的分类只需要区分候选框内特征为前景或者背景,这里的边框回归也是为了之后获得更精确的目标位置。

损失函数

故整个网络包含四个损失函数;
  • RPN calssification(anchor good.bad),判断anchor前景背景类别
  • RPN regression(anchor->propoasal),计算anchor和gt box的偏差,利用mask仅计算前景anchor
  • Fast R-CNN classification(over classes),判断proposal分类,包含类别数C+背景
  • Fast R-CNN regression(proposal ->box),计算proposal和gt box的偏差,利用mask仅计算类别的proposal

注意到前两个损失函数目标是修正anchor,后两个损失函数目标是修正proposal,实际上产生了众多anchors后,会进行筛选(非极大值抑制并按照前景得分排序等等),选出特定比例的前景背景anchors作为proposal进行后面的运算。

二、代码解读

Faster-RCNN网络设计很是复杂,个人觉得但看论文或者其他材料并不能很好的理解这套流程,本篇我们从实际的代码出发,看看到底是这套网络系统到底是如何设计的。

anchor和proposal

region proposal(或者简称proposal,或者简称ROI),可以说RPN网络的目的就是为了得到proposal,这些proposal是对ground truth更好的刻画(和anchor相比,坐标更贴近ground truth,毕竟anchor的坐标都是批量地按照scale和aspect ratio复制的)。如果你还记得在系列二中关于网络结构的介绍,那么你就应该了解到RPN网络的回归支路输出的值(offset)作为smooth l1损失函数的输入之一时,其含义就是使得proposal和anchor之间的offset(RPN网络的回归支路输出)尽可能与ground truth和anchor之间的offset(RPN网络的回归支路的回归目标)接近。

调包部分

涉及了mx基础框架和一个作者自己创建的operate,

import mxnet as mx from symnet import proposal_target

特征提取器一

如开篇的图,这里是最上面一行的网络,即提取Feature Map的部分,使用了4个pooling层,意味着原始图像:Feature Map = 16:1,

def get_vgg_feature(data):
# group 1
conv1_1 = mx.symbol.Convolution(
data=data, kernel=(3, 3), pad=(1, 1), num_filter=64, workspace=2048, name="conv1_1")
relu1_1 = mx.symbol.Activation(data=conv1_1, act_type="relu", name="relu1_1")
conv1_2 = mx.symbol.Convolution(
data=relu1_1, kernel=(3, 3), pad=(1, 1), num_filter=64, workspace=2048, name="conv1_2")
relu1_2 = mx.symbol.Activation(data=conv1_2, act_type="relu", name="relu1_2")
pool1 = mx.symbol.Pooling(
data=relu1_2, pool_type="max", kernel=(2, 2), stride=(2, 2), name="pool1")
# group 2
conv2_1 = mx.symbol.Convolution(
data=pool1, kernel=(3, 3), pad=(1, 1), num_filter=128, workspace=2048, name="conv2_1")
relu2_1 = mx.symbol.Activation(data=conv2_1, act_type="relu", name="relu2_1")
conv2_2 = mx.symbol.Convolution(
data=relu2_1, kernel=(3, 3), pad=(1, 1), num_filter=128, workspace=2048, name="conv2_2")
relu2_2 = mx.symbol.Activation(data=conv2_2, act_type="relu", name="relu2_2")
pool2 = mx.symbol.Pooling(
data=relu2_2, pool_type="max", kernel=(2, 2), stride=(2, 2), name="pool2")
# group 3
conv3_1 = mx.symbol.Convolution(
data=pool2, kernel=(3, 3), pad=(1, 1), num_filter=256, workspace=2048, name="conv3_1")
relu3_1 = mx.symbol.Activation(data=conv3_1, act_type="relu", name="relu3_1")
conv3_2 = mx.symbol.Convolution(
data=relu3_1, kernel=(3, 3), pad=(1, 1), num_filter=256, workspace=2048, name="conv3_2")
relu3_2 = mx.symbol.Activation(data=conv3_2, act_type="relu", name="relu3_2")
conv3_3 = mx.symbol.Convolution(
data=relu3_2, kernel=(3, 3), pad=(1, 1), num_filter=256, workspace=2048, name="conv3_3")
relu3_3 = mx.symbol.Activation(data=conv3_3, act_type="relu", name="relu3_3")
pool3 = mx.symbol.Pooling(
data=relu3_3, pool_type="max", kernel=(2, 2), stride=(2, 2), name="pool3")
# group 4
conv4_1 = mx.symbol.Convolution(
data=pool3, kernel=(3, 3), pad=(1, 1), num_filter=512, workspace=2048, name="conv4_1")
relu4_1 = mx.symbol.Activation(data=conv4_1, act_type="relu", name="relu4_1")
conv4_2 = mx.symbol.Convolution(
data=relu4_1, kernel=(3, 3), pad=(1, 1), num_filter=512, workspace=2048, name="conv4_2")
relu4_2 = mx.symbol.Activation(data=conv4_2, act_type="relu", name="relu4_2")
conv4_3 = mx.symbol.Convolution(
data=relu4_2, kernel=(3, 3), pad=(1, 1), num_filter=512, workspace=2048, name="conv4_3")
relu4_3 = mx.symbol.Activation(data=conv4_3, act_type="relu", name="relu4_3")
pool4 = mx.symbol.Pooling(
data=relu4_3, pool_type="max", kernel=(2, 2), stride=(2, 2), name="pool4")
# group 5
conv5_1 = mx.symbol.Convolution(
data=pool4, kernel=(3, 3), pad=(1, 1), num_filter=512, workspace=2048, name="conv5_1")
relu5_1 = mx.symbol.Activation(data=conv5_1, act_type="relu", name="relu5_1")
conv5_2 = mx.symbol.Convolution(
data=relu5_1, kernel=(3, 3), pad=(1, 1), num_filter=512, workspace=2048, name="conv5_2")
relu5_2 = mx.symbol.Activation(data=conv5_2, act_type="relu", name="relu5_2")
conv5_3 = mx.symbol.Convolution(
data=relu5_2, kernel=(3, 3), pad=(1, 1), num_filter=512, workspace=2048, name="conv5_3")
relu5_3 = mx.symbol.Activation(data=conv5_3, act_type="relu", name="relu5_3") return relu5_3

特征提取器二

下面是最后的一小部分网络,我们将筛选出来的proposal作用于Feature Map,得到候选区域,然后使用ROIPooling得到大小一致的候选区域,这些区域送入本部分网络,得到用于分类、回归的特征,

def get_vgg_top_feature(data):
# group 6
flatten = mx.symbol.Flatten(data=data, name="flatten")
fc6 = mx.symbol.FullyConnected(data=flatten, num_hidden=4096, name="fc6")
relu6 = mx.symbol.Activation(data=fc6, act_type="relu", name="relu6")
drop6 = mx.symbol.Dropout(data=relu6, p=0.5, name="drop6")
# group 7
fc7 = mx.symbol.FullyConnected(data=drop6, num_hidden=4096, name="fc7")
relu7 = mx.symbol.Activation(data=fc7, act_type="relu", name="relu7")
drop7 = mx.symbol.Dropout(data=relu7, p=0.5, name="drop7")
return drop7

主体函数

函数接口

def get_vgg_train(anchor_scales, anchor_ratios, rpn_feature_stride,
rpn_pre_topk, rpn_post_topk, rpn_nms_thresh, rpn_min_size, rpn_batch_rois,
num_classes, rcnn_feature_stride, rcnn_pooled_size, rcnn_batch_size,
rcnn_batch_rois, rcnn_fg_fraction, rcnn_fg_overlap, rcnn_bbox_stds):

首先定义了一些用于接收数据&标签信息的占位符变量,

    num_anchors = len(anchor_scales) * len(anchor_ratios)

    data = mx.symbol.Variable(name="data")                     # 图片信息:[batch, channel, height, width]
im_info = mx.symbol.Variable(name="im_info")
gt_boxes = mx.symbol.Variable(name="gt_boxes") # 真实框信息:[obj数量, 5]
# anchor标签:-1、0、1 (无效、背景、目标)
rpn_label = mx.symbol.Variable(name='label') # [batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
# 根据初始anchor和ground truth计算出来的offset
rpn_bbox_target = mx.symbol.Variable(name='bbox_target') # [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
# mask,如果anchor标签是1,则mask对应值为1,anchor标签是0或-1,则mask对应值为0
rpn_bbox_weight = mx.symbol.Variable(name='bbox_weight') # [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]

获取卷积特征

   # shared convolutional layers
conv_feat = get_vgg_feature(data)

使用3×3的滑窗,进行卷积,

  # RPN layers
rpn_conv = mx.symbol.Convolution(
data=conv_feat, kernel=(3, 3), pad=(1, 1), num_filter=512, name="rpn_conv_3x3")
rpn_relu = mx.symbol.Activation(data=rpn_conv, act_type="relu", name="rpn_relu")

每个滑动窗口位置生成9个候选窗口(不同尺度、不同宽高),对应36个坐标、18个分类,(即根据每张图片的feat map大小,生成对应数量的中心点,以及候选框体)

RPN calssification(anchor good.bad)

下面是计算全部候选窗口对应2分类的部分,生成损失函数的第一部分:全部候选窗含/不含obj的二分类损失

    # rpn classification: obj / not_obj
# [batch, 2*每个位置anchors数目, 中心点行数, 中心点列数]
rpn_cls_score = mx.symbol.Convolution(
data=rpn_relu, kernel=(1, 1), pad=(0, 0), num_filter=2 * num_anchors, name="rpn_cls_score") # [batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
rpn_cls_score_reshape = mx.symbol.Reshape(
data=rpn_cls_score, shape=(0, 2, -1, 0), name="rpn_cls_score_reshape")
# 获取交叉熵回传梯度,对于出现了-1的图片梯度直接全部置为0
# 在Faster RCNN算法中,anchor一共有3种标签:-1、0、1,分别表示无效、背景、目标
rpn_cls_prob = mx.symbol.SoftmaxOutput(data=rpn_cls_score_reshape, label=rpn_label, multi_output=True,
normalization='valid', use_ignore=True, ignore_label=-1, name="rpn_cls_prob") # 切换为含/不含obj两通道,对每一个anchor是否有obj进行softmax处理
# [batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
rpn_cls_act = mx.symbol.softmax(
data=rpn_cls_score_reshape, axis=1, name="rpn_cls_act")
# [batch, 2*每个位置anchors数目, 中心点行数, 中心点列数]
# Reshpe方法不同于reshape方法,详情见此方法文档
rpn_cls_act_reshape = mx.symbol.Reshape(
data=rpn_cls_act, shape=(0, 2 * num_anchors, -1, 0), name='rpn_cls_act_reshape')

RPN regression(anchor->propoasal)

下面计算全部候选窗口对应4个坐标值的部分,生成损失函数第二部分:全部候选框坐标回归

    # rpn bbox regression
# [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
rpn_bbox_pred = mx.symbol.Convolution(
data=rpn_relu, kernel=(1, 1), pad=(0, 0), num_filter=4 * num_anchors, name="rpn_bbox_pred")
# rpn_bbox_weight: anchor的mask,如果某个anchor的标签是1,则mask对应值为1,anchor的标签是0或-1,则mask对应值为0
# rpn_bbox_target: 根据初始anchor和ground truth计算出来的offset
rpn_bbox_loss_ = rpn_bbox_weight * mx.symbol.smooth_l1(name='rpn_bbox_loss_',
scalar=3.0,
data=(rpn_bbox_pred - rpn_bbox_target))
# 获取回归梯度
# grad_scale: 表示回归损失占RPN网络总损失比,该参数相差几个量级对结果影响也不大
rpn_bbox_loss = mx.sym.MakeLoss(name='rpn_bbox_loss', data=rpn_bbox_loss_, grad_scale=1.0 / rpn_batch_rois)

候选框生成

我们会获取数量繁多的候选框(即上面得到的候选框含有obj的得分候选框坐标修正值),首先会根据框体含有obj的概率进行一次初筛,然后经由非极大值抑制算法筛选2000个候选框进入下一步的操作;在第二步中,我们会获取最终的128个候选框、它们各自对应的标签、它们坐标回归的目标、以及标定它们正负的坐标掩码,用于精确的训练候选框,生成目标边框。这个一步的操作使用了作者新建的操作节点,具体实现见源码,这里只简单介绍该操作的输入输出以及作用。

另注,函数返回的候选框5个属性与后面的ROIPooling一脉相承,[候选框对应batch中图片的索引,候选框坐标×4],后面使用这个值可以直接进行ROIPooling。

    # rpn proposal
# 获取指定数目的候选框proposal
# [2000个候选框, 5个属性]
# 5个属性: batch中图片index,4个坐标
rois = mx.symbol.contrib.MultiProposal(
cls_prob=rpn_cls_act_reshape, # cls_prob: [batch, 2*每个位置anchors数目, 中心点行数, 中心点列数]
bbox_pred=rpn_bbox_pred, # bbox_pred: [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
im_info=im_info, # mx.symbol.Variable(name="im_info")
feature_stride=rpn_feature_stride, # 16
scales=anchor_scales, ratios=anchor_ratios, # (0.5, 1, 2)
rpn_pre_nms_top_n=rpn_pre_topk, # 12000,对RPN网络输出的anchor进行NMS操作之前的proposal最大数量
# 输入proposal的数量大于12000时,对这些proposal的预测为foreground的概率(也叫score,
# 换句话说就是预测标签为1的概率)从高到低排序,然后选择前面12000个
rpn_post_nms_top_n=rpn_post_topk, # 2000,经过NMS过滤之后得到的proposal数量
threshold=rpn_nms_thresh, # 0.7,是NMS算法阈值
rpn_min_size=rpn_min_size, # 16
name = 'rois'
) # rcnn roi proposal target
# 根据上一步获取的proposal(即rois)和真实框(gt_boxes)进行筛选
# 获取128个proposal,对应的128个label,对应的128个真实坐标,128个坐标掩码(构建损失函数使用)
group = mx.symbol.Custom(rois=rois, # 2000*5的roi信息
gt_boxes=gt_boxes, # n*5的ground truth信息,n表示object数量
op_type='proposal_target',
num_classes=num_classes, # num_classes是实际要分类的类别数加上背景类
batch_images=rcnn_batch_size, # 1
batch_rois=rcnn_batch_rois, # 128
fg_fraction=rcnn_fg_fraction, # 0.25,正样本所占的比例
fg_overlap=rcnn_fg_overlap, # 0.5
box_stds=rcnn_bbox_stds # (0.1, 0.1, 0.2, 0.2)
) rois = group[0] # rois (128, 5)
label = group[1] # roi对应的标签(128)
bbox_target = group[2] # 坐标回归的目标,维度是(128,84), 其中84来自(20+1)*4
bbox_weight = group[3] # 坐标回归时候的权重,维度是(128,84),对于foreground都是1,对于backgroud都是0

在RPN网络得到proposal后还会经过一系列的过滤操作才会得到送入检测网络的proposal,节点proposal_target会将2000个proposal过滤成128个,且为这128个proposal分配标签、回归目标、定义正负样本的1:3比例等,这部分算是RPN网络和检测网络(Fast RCNN)的衔接,值得注意的是该节点不接受后面层数传递而来的梯度,且向前传递梯度强制为0,已即网络训练由其割裂为两个部分(尽管两部分仍然相连)。

ROIPooling层

将任意大小的输入层下采样为同样大小的输出层,方便后面的计算。这里我们使用候选框(128个)寻找各自对应的图片,并将各自对应的区域裁剪出来,然后pooling为同样的大小,

本层的两条虚线输入中上面一条表示batch图像特征,下面一条表示候选框输入

    # rcnn roi pool
# [128个候选框, 512个通道, 7行, 7列]
roi_pool = mx.symbol.ROIPooling(
name='roi_pool',
data=conv_feat,
rois=rois,
pooled_size=rcnn_pooled_size, # (7, 7),输出大小
spatial_scale=1.0 / rcnn_feature_stride # 16, feat/raw_img
)

将对应的候选区域(注意此时抠图完成,已经不是候选框了)经由vgg的分类头部分(实际上就是对候选区域再进一步抽象提取特征)进行处理,

    # rcnn top feature
# [128, 4096],对每个roi提取最终的vgg特征
top_feat = get_vgg_top_feature(roi_pool)

最后的两个损失函数生成过程,对128个候选区域的抽象特征进行分类(此时不是含/不含obj,而是直接进行21分类),并对每一个抽象特征回归出4个坐标值

两个损失函数标签均来自上面自定义节点的输出:

label = group[1]                   # roi对应的标签(128)

bbox_target = group[2]       # 坐标回归的目标,维度是(128,84), 其中84来自(20+1)*4

bbox_weight = group[3]      # 坐标回归时候的权重,维度是(128,84),对于foreground都是1,对于backgroud都是0

    # rcnn classification
# 对每个roi进行分类,并构建损失函数
cls_score = mx.symbol.FullyConnected(name='cls_score', data=top_feat, num_hidden=num_classes)
cls_prob = mx.symbol.SoftmaxOutput(name='cls_prob', data=cls_score, label=label, normalization='batch') # rcnn bbox regression
# 对每个roi进行回归,并构建损失函数
bbox_pred = mx.symbol.FullyConnected(name='bbox_pred', data=top_feat, num_hidden=num_classes * 4)
bbox_loss_ = bbox_weight * mx.symbol.smooth_l1(name='bbox_loss_', scalar=1.0, data=(bbox_pred - bbox_target))
bbox_loss = mx.sym.MakeLoss(name='bbox_loss', data=bbox_loss_, grad_scale=1.0 / rcnn_batch_rois)

附、主体函数全览

def get_vgg_train(anchor_scales, anchor_ratios, rpn_feature_stride,
rpn_pre_topk, rpn_post_topk, rpn_nms_thresh, rpn_min_size, rpn_batch_rois,
num_classes, rcnn_feature_stride, rcnn_pooled_size, rcnn_batch_size,
rcnn_batch_rois, rcnn_fg_fraction, rcnn_fg_overlap, rcnn_bbox_stds):
num_anchors = len(anchor_scales) * len(anchor_ratios) data = mx.symbol.Variable(name="data") # 图片信息:[batch, channel, height, width]
im_info = mx.symbol.Variable(name="im_info")
gt_boxes = mx.symbol.Variable(name="gt_boxes") # 真实框信息:[obj数量, 5]
# anchor标签:-1、0、1 (无效、背景、目标)
rpn_label = mx.symbol.Variable(name='label') # [batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
# 根据初始anchor和ground truth计算出来的offset
rpn_bbox_target = mx.symbol.Variable(name='bbox_target') # [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
# mask,如果anchor标签是1,则mask对应值为1,anchor标签是0或-1,则mask对应值为0
rpn_bbox_weight = mx.symbol.Variable(name='bbox_weight') # [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数] # shared convolutional layers
conv_feat = get_vgg_feature(data) # RPN layers
rpn_conv = mx.symbol.Convolution(
data=conv_feat, kernel=(3, 3), pad=(1, 1), num_filter=512, name="rpn_conv_3x3")
rpn_relu = mx.symbol.Activation(data=rpn_conv, act_type="relu", name="rpn_relu") # rpn classification: obj / not_obj
# [batch, 2*每个位置anchors数目, 中心点行数, 中心点列数]
rpn_cls_score = mx.symbol.Convolution(
data=rpn_relu, kernel=(1, 1), pad=(0, 0), num_filter=2 * num_anchors, name="rpn_cls_score") # [batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
rpn_cls_score_reshape = mx.symbol.Reshape(
data=rpn_cls_score, shape=(0, 2, -1, 0), name="rpn_cls_score_reshape")
# 获取交叉熵回传梯度,对于出现了-1的图片梯度直接全部置为0
# 在Faster RCNN算法中,anchor一共有3种标签:-1、0、1,分别表示无效、背景、目标
rpn_cls_prob = mx.symbol.SoftmaxOutput(data=rpn_cls_score_reshape, label=rpn_label, multi_output=True,
normalization='valid', use_ignore=True, ignore_label=-1, name="rpn_cls_prob") # 切换为含/不含obj两通道,对每一个anchor是否有obj进行softmax处理
# [batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
rpn_cls_act = mx.symbol.softmax(
data=rpn_cls_score_reshape, axis=1, name="rpn_cls_act")
# [batch, 2*每个位置anchors数目, 中心点行数, 中心点列数]
# Reshpe方法不同于reshape方法,详情见此方法文档
rpn_cls_act_reshape = mx.symbol.Reshape(
data=rpn_cls_act, shape=(0, 2 * num_anchors, -1, 0), name='rpn_cls_act_reshape') # rpn bbox regression
# [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
rpn_bbox_pred = mx.symbol.Convolution(
data=rpn_relu, kernel=(1, 1), pad=(0, 0), num_filter=4 * num_anchors, name="rpn_bbox_pred")
# rpn_bbox_weight: anchor的mask,如果某个anchor的标签是1,则mask对应值为1,anchor的标签是0或-1,则mask对应值为0
# rpn_bbox_target: 根据初始anchor和ground truth计算出来的offset
rpn_bbox_loss_ = rpn_bbox_weight * mx.symbol.smooth_l1(name='rpn_bbox_loss_',
scalar=3.0,
data=(rpn_bbox_pred - rpn_bbox_target))
# 获取回归梯度
# grad_scale: 表示回归损失占RPN网络总损失比,该参数相差几个量级对结果影响也不大
rpn_bbox_loss = mx.sym.MakeLoss(name='rpn_bbox_loss', data=rpn_bbox_loss_, grad_scale=1.0 / rpn_batch_rois) # rpn proposal
# 获取指定数目的候选框proposal
# [2000个候选框, 5个属性]
# 5个属性: batch中图片index,4个坐标
rois = mx.symbol.contrib.MultiProposal(
cls_prob=rpn_cls_act_reshape, # cls_prob: [batch, 2*每个位置anchors数目, 中心点行数, 中心点列数]
bbox_pred=rpn_bbox_pred, # bbox_pred: [batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
im_info=im_info, # mx.symbol.Variable(name="im_info")
feature_stride=rpn_feature_stride, # 16
scales=anchor_scales, ratios=anchor_ratios, # (0.5, 1, 2)
rpn_pre_nms_top_n=rpn_pre_topk, # 12000,对RPN网络输出的anchor进行NMS操作之前的proposal最大数量
# 输入proposal的数量大于12000时,对这些proposal的预测为foreground的概率(也叫score,
# 换句话说就是预测标签为1的概率)从高到低排序,然后选择前面12000个
rpn_post_nms_top_n=rpn_post_topk, # 2000,经过NMS过滤之后得到的proposal数量
threshold=rpn_nms_thresh, # 0.7,是NMS算法阈值
rpn_min_size=rpn_min_size, # 16
name = 'rois'
) # rcnn roi proposal target
# 根据上一步获取的proposal(即rois)和真实框(gt_boxes)进行筛选
# 获取128个proposal,对应的128个label,对应的128个真实坐标,128个坐标掩码(构建损失函数使用)
group = mx.symbol.Custom(rois=rois, # 2000*5的roi信息
gt_boxes=gt_boxes, # n*5的ground truth信息,n表示object数量
op_type='proposal_target',
num_classes=num_classes, # num_classes是实际要分类的类别数加上背景类
batch_images=rcnn_batch_size, # 1
batch_rois=rcnn_batch_rois, # 128
fg_fraction=rcnn_fg_fraction, # 0.25,正样本所占的比例
fg_overlap=rcnn_fg_overlap, # 0.5
box_stds=rcnn_bbox_stds # (0.1, 0.1, 0.2, 0.2)
) rois = group[0] # rois (128, 5)
label = group[1] # roi对应的标签(128)
bbox_target = group[2] # 坐标回归的目标,维度是(128,84), 其中84来自(20+1)*4
bbox_weight = group[3] # 坐标回归时候的权重,维度是(128,84),对于foreground都是1,对于backgroud都是0 # rcnn roi pool
# [128个候选框, 512个通道, 7行, 7列]
roi_pool = mx.symbol.ROIPooling(
name='roi_pool',
data=conv_feat,
rois=rois,
pooled_size=rcnn_pooled_size, # (7, 7),输出大小
spatial_scale=1.0 / rcnn_feature_stride # 16, feat/raw_img
) # rcnn top feature
# [128, 4096],对每个roi提取最终的vgg特征
top_feat = get_vgg_top_feature(roi_pool) # rcnn classification
# 对每个roi进行分类,并构建损失函数
cls_score = mx.symbol.FullyConnected(name='cls_score', data=top_feat, num_hidden=num_classes)
cls_prob = mx.symbol.SoftmaxOutput(name='cls_prob', data=cls_score, label=label, normalization='batch') # rcnn bbox regression
# 对每个roi进行回归,并构建损失函数
bbox_pred = mx.symbol.FullyConnected(name='bbox_pred', data=top_feat, num_hidden=num_classes * 4)
bbox_loss_ = bbox_weight * mx.symbol.smooth_l1(name='bbox_loss_', scalar=1.0, data=(bbox_pred - bbox_target))
bbox_loss = mx.sym.MakeLoss(name='bbox_loss', data=bbox_loss_, grad_scale=1.0 / rcnn_batch_rois) # reshape output
# [1, 128], [1, 128, 21], [1, 128, 84]
label = mx.symbol.Reshape(data=label, shape=(rcnn_batch_size, -1), name='label_reshape')
cls_prob = mx.symbol.Reshape(data=cls_prob, shape=(rcnn_batch_size, -1, num_classes), name='cls_prob_reshape')
bbox_loss = mx.symbol.Reshape(data=bbox_loss, shape=(rcnn_batch_size, -1, 4 * num_classes), name='bbox_loss_reshape')
print(cls_score.infer_shape(**{'data': (1, 3, 1000, 600), 'gt_boxes': (5, 5)})[1]) # group output
# mx.symbol.BlockGrad会阻止经由节点label回传的梯度
group = mx.symbol.Group([rpn_cls_prob, # anchors分类损失:[batch, 2, 中心点行数*每个位置anchors数目, 中心点列数]
rpn_bbox_loss, # anchors回归损失:[batch, 4*每个位置anchors数目, 中心点行数, 中心点列数]
cls_prob, # 候选框分类损失:[1, 128, 21]
bbox_loss, # 候选框回归损失:[1, 128, 84]
mx.symbol.BlockGrad(label) # 候选框标签:[1, 128]
])
return group if __name__=='__main__':
net = get_vgg_train(anchor_scales=(8, 16, 32),
anchor_ratios=(0.5, 1, 2),
rpn_feature_stride=16,
rpn_pre_topk=12000,
rpn_post_topk=2000,
rpn_nms_thresh=0.7,
rpn_min_size=16,
rpn_batch_rois=256,
num_classes=21,
rcnn_feature_stride=16, # 4个pooling层
rcnn_pooled_size=(7, 7),
rcnn_batch_size=1,
rcnn_batch_rois=128,
rcnn_fg_fraction=0.25,
rcnn_fg_overlap=0.5,
rcnn_bbox_stds=(0.1, 0.1, 0.2, 0.2))

『计算机视觉』经典RCNN_其二:Faster-RCNN的更多相关文章

  1. 『计算机视觉』经典RCNN_其一:从RCNN到Faster-RCNN

    RCNN介绍 目标检测-RCNN系列 一文读懂Faster RCNN 一.目标检测 1.两个任务 目标检测可以拆分成两个任务:识别和定位 图像识别(classification)输入:图片输出:物体的 ...

  2. 『计算机视觉』Mask-RCNN

    一.Mask-RCNN流程 Mask R-CNN是一个实例分割(Instance segmentation)算法,通过增加不同的分支,可以完成目标分类.目标检测.语义分割.实例分割.人体姿势识别等多种 ...

  3. 『计算机视觉』FPN:feature pyramid networks for object detection

    对用卷积神经网络进行目标检测方法的一种改进,通过提取多尺度的特征信息进行融合,进而提高目标检测的精度,特别是在小物体检测上的精度.FPN是ResNet或DenseNet等通用特征提取网络的附加组件,可 ...

  4. 『计算机视觉』Mask-RCNN_从服装关键点检测看KeyPoints分支

    下图Github地址:Mask_RCNN       Mask_RCNN_KeyPoints『计算机视觉』Mask-RCNN_论文学习『计算机视觉』Mask-RCNN_项目文档翻译『计算机视觉』Mas ...

  5. 『计算机视觉』Mask-RCNN_训练网络其二:train网络结构&损失函数

    Github地址:Mask_RCNN 『计算机视觉』Mask-RCNN_论文学习 『计算机视觉』Mask-RCNN_项目文档翻译 『计算机视觉』Mask-RCNN_推断网络其一:总览 『计算机视觉』M ...

  6. 『计算机视觉』Mask-RCNN_训练网络其一:数据集与Dataset类

    Github地址:Mask_RCNN 『计算机视觉』Mask-RCNN_论文学习 『计算机视觉』Mask-RCNN_项目文档翻译 『计算机视觉』Mask-RCNN_推断网络其一:总览 『计算机视觉』M ...

  7. 『计算机视觉』Mask-RCNN_锚框生成

    Github地址:Mask_RCNN 『计算机视觉』Mask-RCNN_论文学习 『计算机视觉』Mask-RCNN_项目文档翻译 『计算机视觉』Mask-RCNN_推断网络其一:总览 『计算机视觉』M ...

  8. 『计算机视觉』Mask-RCNN_推断网络其六:Mask生成

    一.Mask生成概览 上一节的末尾,我们已经获取了待检测图片的分类回归信息,我们将回归信息(即待检测目标的边框信息)单独提取出来,结合金字塔特征mrcnn_feature_maps,进行Mask生成工 ...

  9. 『计算机视觉』Mask-RCNN_推断网络其四:FPN和ROIAlign的耦合

    一.模块概述 上节的最后,我们进行了如下操作获取了有限的proposal, # [IMAGES_PER_GPU, num_rois, (y1, x1, y2, x2)] # IMAGES_PER_GP ...

随机推荐

  1. Spring与MyBatis面试

    Spring: https://www.cnblogs.com/wang-meng/p/5701982.html https://www.cnblogs.com/liangyihui/p/591777 ...

  2. 记录一下 ajax的基础传送

    传数据 var json = $("#form").serializeObject(); $.ajax({ url: "/getUser", type: &qu ...

  3. CentOS7设置定时任务 每隔30分钟执行一次命令

    ref   https://blog.csdn.net/xiangxianghehe/article/details/78149094 一.安装 crontabs服务并设置开机自启: yum inst ...

  4. (转)Understanding, generalisation, and transfer learning in deep neural networks

    Understanding, generalisation, and transfer learning in deep neural networks FEBRUARY 27, 2017   Thi ...

  5. Python 安装与环境变量配置

    一.软件下载 Python安装包下载地址:https://www.python.org/ 二.安装过程(略) 三.环境变量配置: 方法一:使用cmd命令添加path环境变量 在cmd下输入: path ...

  6. Java基础 【类之间的关系】

    在Java与其他面向对象设计语言中,类之间常见的关系有6种 分别是:  依赖.关联.聚合.组合.继承.实现,他们的耦合度依次增强. 其中,关联.聚合.组合关系仅仅是在语义上有所区别,所谓语义就是指上下 ...

  7. C# 缓存操作类

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...

  8. unity 截图 压缩 处理

    /****************************************************** unity屏幕截图,并转换成Base64码* 作者: lyb* 日期:2017年7月25 ...

  9. 数据库无法打开到SQL Server连接

    今天打开数据库,发现连接不上,弹出错误提示: 打开SQLServer Configuration Manager,发现SQL Server状态已经停止,双击启动弹出错误提示: 打开SQL Server ...

  10. 踩坑记录:spring boot的POST请求数据注入不了的问题

    概述: 今天在使用spring boot框架的时候,踩了一个坑,是关于control层request body依赖注入的问题的,内容如下: 进过: 由于目前公司采用的系统架构,要求把springboo ...