赞
踩
学习目标检测不久,花一个多月的空闲时间啃完了maskRCNN论文和tensorflow+keras开源代码,感觉进入了一个新的境界,在此做一个比较详细的流程梳理和总结,同时也希望对其他学习目标检测和语义分割的同学有点帮助。
本文主要针对代码结构进行梳理,对于原理还不熟悉的同学可以参考这几篇博文:
格灵深瞳DeepGlint:干货 | 目标检测入门,看这篇就够了(已更完)
白裳:一文读懂Faster RCNN
stone:令人拍案称奇的Mask RCNN
另外由于博主比较懒,借用了以下系列博文的部分框图,侵删:
BINGO Hong:MASK_RCNN代码详解(1)-Basebone部分
开始前必须膜拜一下华人学者的骄傲——何恺明(K.He)大神。MaskRCNN虽然模型比较复杂,不太适合应用于实时性要求较高的场景,但其检测精度从2017至今仍未被超越,更值得一提的是,模型的BackBone(Resnet101)以及前身FasterRCNN都出自他手,更要命的是,比我大不了几岁%¥#&^*
现在进入正题。本文共分三部分,分别是:
第一部分 数据输入
第二部分 模型介绍
第三部分 Train模式和Inference模式流程总结
第一部分 数据输入
data_generator无限循环读取数据,每次读取一个batch,包括三个真值:input_gt_class_ids, input_gt_boxes, input_gt_masks,两个输入数据:input_image, input_image_meta , 两个rpn标签:input_rpn_match, input_rpn_bbox,每batch_size次打包输入模型。
一、generate_pyramid_anchors 在BackBone每一层特征图上生成anchors。
对于2-6每层特征图的每个像素点,分别生成大小为RPN_ANCHOR_SCALES (32, 64, 128, 256, 512),尺寸比例为RPN_ANCHOR_RATIOS [0.5, 1, 2]三种大小的anchors,总数为3*(2562+1282+642+322+16^2)=261888个。
二、load_image_gt生成所需格式的数据和真值
(包括image,image_meta, gt_class_ids, gt_boxes, gt_masks)
meta = np.array(
[image_id] + # size=1
list(original_image_shape) + # size=3
list(image_shape) + # size=3
list(window) + # size=4 (y1, x1, y2, x2)
[scale] + # size=1
list(active_class_ids) # size=num_classes)
三、build_rpn_targets 生成rpn层的训练标签,与head网络分开训练
输入:image.shape, anchors, gt_class_ids, gt_boxes, config
输出:rpn_match, rpn_bbox
rpn_bbox[ix] = [
(gt_center_y - a_center_y) / a_h,
(gt_center_x - a_center_x) / a_w,
np.log(gt_h / a_h),
np.log(gt_w / a_w),
第二部分 模型部分
一、BackBone网络
作为底层的特征提取网络
1.深度残差网络ResNet101:
保证在堆叠网络的过程中,网络至少不会因为继续堆叠而产生退化
2. 特征金字塔FPN:
同时利用低层特征图的空间信息和高层特征图的语义信息
二、RPN网络
build_rpn_model生成RPN网络,得到所有anchors的分类、回归信息
输入FPN每层特征图,分别计算,再将结果拼接在一起
以P2特征图为例:(batch256256*256)
三、ProposalLayer层
根据RPN网络输出的分类、回归信息,经过NMS得到最终的ROIs
输入: [rpn_class, rpn_bbox, anchors]
输出:rpn_rois
四、DetectionTargetLayer层(train模式特有)
生成head网络的训练标签
输入:target_rois, input_gt_class_ids, gt_boxes, input_gt_masks
输出:rois, target_class_ids, target_bbox, target_mask
五、Head网络
对筛选出来的ROIs进行分类、回归、mask分割操作
1.fpn_classifier_graph分类和回归分支
首先经过PyramidROIAlign,采用双线性插值将每个ROI精确地转换为相同的大小,再输入分类回归网络,得到每个ROI的分类和bbox回归结果
输入:rois, mrcnn_feature_maps, input_image_meta
输出:mrcnn_class_logits, mrcnn_class, mrcnn_bbox
其中train模式输入的是DetectionTargetLayer层筛选的用于训练的roi, 数量是TRAIN_ROIS_PER_IMAGE(200个)
Inference模式输入的是ProposalLayer层输出的全部的用来预测的ROI,数量是POST_NMS_ROIS_INFERENCE(1000个)
1)PyramidROIAlign层
输入:[rois, image_meta] + feature_maps
输出:pooled
a.根据roi的w和h与图片大小的比例算出应该对应特征图哪个层:roi_level = log2_graph(tf.sqrt(h * w) / (224.0 / tf.sqrt(image_area))), 并把level缩放到2-5之间
b.找到每一个level的box:level_boxes和在所有box中的索引:box_to_level(第几个batch,第几个),和box在batch中的索引box_indices
c.将level_boxes和box_indices做梯度阻断(和RPN不通)
d.在这个level的特征图中根据level_boxes, box_indices抠图,并采用双线性插值
pooled.append(tf.image.crop_and_resize(
feature_maps[i], level_boxes, box_indices, self.pool_shape,
method="bilinear"))
e.循环每个level,将抠的图合在一起,得到pooled
f.把pooled按batch排序(方法是先把box_to_level按batch排序,得到序号,把pooled重排),然后转成[batch, num, 7, 7, 256]大小,7为pool_size
2)FPN_Classifier网络
对于ROIAlign层输出的[batch, num, 7, 7, 256]大小的数据,用KL.TimeDistributed实现在第一维(num)将每个batch的每个ROI拆分,,并行执行各项操作
a. mrcnn_class_conv1: 1024 * 7 * 7,归一化,relu
b. mrcnn_class_conv2: 1024 * 1 * 1,归一化,relu
c. 去掉多余的维度,从(batch, 200, 1, 1, 1024)降到(batch, 200, 1024)
d. 分两个分支,一个经过num_classes维全连接,softmax,得到mrcnn_class_logits和mrcnn_class
e. 一个经过num_classes * 4维全连接,reshape,得到mrcnn_bbox_fc和mrcnn_bbox
2.fpn_mask_graph mask分割分支
每个ROI同样先经过PyramidROIAlign,然后输入MASK分割网络,得到mask结果
输入:rois, mrcnn_feature_maps, input_image_meta,
输出:mrcnn_mask
其中train模式输入的是DetectionTargetLayer层筛选的用于训练的roi, 数量是TRAIN_ROIS_PER_IMAGE(200个)
Inference模式输入的是DetectionLayer层输出的NMS后的检测结果,数量是实际数量,最多DETECTION_MAX_INSTANCES(100个)
1) PyramidROIAlign层
和分类和回归分支不同的只有pool_size,此处为14
2) FPN_mask网络
a. mrcnn_mask_conv1,2,3,4: 256 * 3 * 3,归一化,relu
b. mrcnn_mask_deconv: 256 * 2 * 2, 步长2,relu, 2倍上采样
c. num_classes个1*1卷积得到mrcnn_mask
六、LOSS部分
RPN损失
1. rpn_class_loss
输入:input_rpn_match(真值), rpn_class_logits(检测值)
将input_rpn_match中-1,1的标签变成0,1,并去掉原来标签为0的(非正非负),和相应的检测值rpn_class_logits求平均交叉熵:
loss = K.sparse_categorical_crossentropy(target=anchor_class,output=rpn_class_logits,from_logits=True)
2. rpn_bbox_loss
输入:input_rpn_bbox (真值), input_rpn_match, rpn_bbox(检测值)
只找input_rpn_match为1的anchors,并且需要将真值的每个batch裁出和input_rpn_match为1的anchors一样的数量(取前count个),然后算平均smooth_l1_loss:
loss = smooth_l1_loss(target_bbox, rpn_bbox)
MRCNN损失
3. mrcnn_class_loss
输入:target_class_ids, mrcnn_class_logits, active_class_ids
求mrcnn_class_logits和target_class_ids的稀疏交叉熵,并找到检测值对应logit最大的类,去除不需要的类。
loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=target_class_ids, logits=pred_class_logits)
4. mrcnn_bbox_loss
输入:target_bbox, target_class_ids, mrcnn_bbox
target_bbox(每个roi只有一个)找到对应target_class_ids大于0的,mrcnn_bbox(每个class_id预测了一个)找到target_class_ids>0且与真值class_id一致的那个bbox,算平均smooth_l1_loss:
loss = K.switch(tf.size(target_bbox) > 0, smooth_l1_loss(y_true=target_bbox, y_pred=pred_bbox), tf.constant(0.0))
5. mrcnn_mask_loss
输入:target_mask, target_class_ids, mrcnn_mask
真值为0,1,检测值为0-1,与bbox类似,选择对应真值target_class_ids>0 且class_id与真值一致的mask计算二值化交叉熵:
loss = K.switch(tf.size(y_true) > 0, K.binary_crossentropy(target=y_true, output=y_pred), tf.constant(0.0))
七、DetectionLayer层(inference模式特有)
将分类和回归结果应用于ROI,并执行NMS和置信度过滤,得到最终的检测结果
输入:rpn_rois, mrcnn_class, mrcnn_bbox, input_image_meta
输出:detections(包含refined_rois, class_ids, class_scores)
a. 得到每个ROI预测概率最大的类别class_ids
b. 找到每个ROI预测的class_id对应的class_scores, deltas
c. 将delta应用于ROI,得到refined_rois
d. 按照image_meta中window的大小裁剪到window内部。
e. 筛选出class_id大于0且scores > DETECTION_MIN_CONFIDENCE(0.9)的ROI
f. 对于每个class_id,做NMS,阈值为DETECTION_NMS_THRESHOLD(0.3),保留个数是DETECTION_MAX_INSTANCES(100),不够的补-1
g. 对NMS后的ROI去掉值为-1的,找到对应的class_scores,然后排序,取class_scores最大的DETECTION_MAX_INSTANCES个refined_rois,不够的padding 0,与class_ids和class_scores合并为detections输出。
第三部分 Train模式和Inference模式流程总结
一、Train模式流程总结
.通过数据生成器不断读取数据和真值,转换为需要的格式
二、Inference模式流程总结
关于MaskRCNN的介绍告一段落,大家如果有疑问可以留言,尽量回答,共同进步。同时由于作者比较跨界,所以其他任何问题也欢迎留言~~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。