anchor_target_layer()该层执行完毕后,我们继续回到网络模型中观看下一层,是一个卷积层,该层的作用是计算rpn分类(二分类)损失和回归的损失。紧接着进入reshape_layer(),输入的参数为rpn_cls_score(1,h,w,18)通道数为d。reshape_layer(2,name = 'rpn_cls_score_reshape')、reshape_layer(len(anchor_scales)*3*2,name = 'rpn_cls_prob_reshape'))。代码先后用了两个reshape_layer。第一个reshape_layer中,d=2, 将输入的rpn_cls_score变成(1,9h,w,2),然后在进行softmax。特别注意的是tensorflow的默认的最后一维是通道,因此softmax也要是默认通道间进行。因此都是通道放在最后一维。输出的是rpn_cls_prob。第二个reshape_layer,d
=18,紧接着将rpn_cls_prob还原成(1,h,w,18)为输出:rpn_cls_prob_reshape
*proposal_layer
输入参数:
rpn_cls_prob_reshape,(1, h, w, 18),层内转置成(1, 18, h, w)-----是将预测的rpn_cls_score进行softmax操作
rpn_bbox_pred,(1, h, w, 36),层内转置成(1, 36, h, w)----为预测出的回归偏移量
im_info: (1,3)
程序运行:
bbox_deltas = rpn_bbox_pred, (1, 36, h,w) 记住我们预测的是偏移值,因此叫做deltas没毛病。
和anchor_target_layer一样,也每个位置产生9个anchor,堆叠成anchors, (K×A, 4), 遍历顺序是先遍历完一个位置的所有anchor,然后宽度遍历,最后高度遍历,这种遍历顺序记作(h,w,a)
bbox_deltas = bbox_deltas.transpose((0, 2, 3, 1)).reshape((-1, 4)),现在形状变成(9×h×w, 4)遍历顺序(h, w, a)
scores = scores.transpose((0, 2, 3, 1)).reshape((-1, 1)),形状变成(9×h×w, 1),遍历顺序(h,w,a)
proposals = bbox_transform_inv(anchors, bbox_deltas),回想anchor_target_layer,他给每个anchor产生的回归目标是到各个gt box的偏移量,bbox_transform函数完成这个计算。那么现在我们模型回归出bbox_deltas,因此只要在anchors基础上做一个bbox_transform_inv的逆运算,就可以计算出模型预测的proposals的框,形状是和anchors形状一样,(9×h×w, 4)=(K×A, 4),左上角右下角顶点坐标值。
进一步对proposals做后续处理,首先是clipped,即每个box的边界缩回到不超过原图边界;然后_filter_boxes, 通过上面的min_size×scale,scale从im_info获得,限制每个框的最小高宽,返回保留的框的序号;proposals和scores都取序号索引的框;这时框的数目少于A×K个。
order是将scores展开,并由大到小排序的标号,如果有pre_nms_topN的限制,就先截取分数最高的pre_nms_topN个框,比如12000个(注意如果少于这个数就是全部),然后proposals和scores都按照这个顺序将框排好。这个时候的框已经没有(h,w,a)的遍历顺序了。
然后再做NMS。NMS的步骤就是对于分数由高到低排序的框,从分数高的开始,看他和后面每一个没有被扔掉的框的IoU是否大于阈值,是的话就将后面的这些框扔掉;
小总结:proposal_layer就是将预测出的rpn_bbox_pred(框的回归偏移量)拿过来,经过一系列操作,生成真正的proposals,形状是5列。注意这里是rpn的proposals,只有是否前景之分,没有对应的物体类别,这一层的用处是还原出真正的proposal信息,在test时用于prediction
*proposal_target_layer
紧接着到了网络的proposal_target_layer。
输入参数:
rpn_rois: 5列,来自于proposal_layer的rpn预测出的bbox,第一列全0,表示batch id,因为只有一张图像。
gt_boxes:五列,最后一列是框的类别
_num_classes: 类别数
程序运行:
all_rois = np.vstack((all_rois, np.hstack((zeros, gt_boxes[:, :-1])))) 将rois和gt_boxes在0维拼合在一起,数据还是五列,第一列全0,后四列是box坐标;
num_images = 1
rois_per_image = cfg.TRAIN.BATCH_SIZE / num_images=128/1=128
fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)=0.25*128=32
labels, rois, bbox_targets, bbox_inside_weights = _sample_rois( all_rois, gt_boxes, fg_rois_per_image, rois_per_image, _num_classes)
具体看_sample_rois函数,传入参数all_rois, gt_boxes, 32, 128, 21。 该函数首先计算all_rois和gt_boxes的overlaps,得出每个roi bbox最大IoU的gt_box对应的类别标号,用labels表示。然后用前景阈值0.5筛选出前景框标号,记为fg_inds。
fg_inds = np.where(max_overlaps >= cfg.TRAIN.FG_THRESH)[0]
fg_rois_per_this_image = int(min(fg_rois_per_image, fg_inds.size))
if fg_inds.size > 0:
fg_inds = npr.choice(fg_inds, size=fg_rois_per_this_image, replace=False)
这段代码,当fg_inds的个数比fg_rois_per_image大时,就只筛选32个出来;否则,全部保留; 同样,bg框也是筛选,最后筛选出来前景+背景128个;对输入的框,进行筛选,得到最终的128个。
相应的,labels设置,将背景框label置为0;接下来就和anchor_target_layer类似了,传入rois,gt_boxes给_compute_targets来计算要回归的偏移量,唯一不同的就是需要将偏移量Normalize,即减均值和除以标准差,返回的bbox_target_data有五列,第一列是label,后面四列是回归的目标;然后继续调用_get_bbox_regression_labels,主要目的是将bbox_target_data扩充成输入到网络的形式,即表示回归目标的4个元素值扩充成84维,只有class label对应的那4个位置填上目标值,其他位置为0。返回的bbox_targets和bbox_inside_weights都是84列,后者对应label的4个位置全1,其余全0.
最后,这一层返回如下:
rois:128×5,第一列是全0,后面是框的左上角右下角坐标;
labels: 128×1,每个框的物体类别;
bbox_targets: 128×84,每个框回归的偏差值,经过了normalize
bbox_inside_weights, bbox_outside_weights: 128×84对应类别位置为1.
小总结:这一层就是将proposal_layer提供的roi进行筛选,然后加上物体类别标签和bbox的回归目标,并计算权重weights。注意上面的anchor_target_layer加上的标签和回归目标用于rpn训练,这里的用于目标检测训练。
*最后一部分是模型训练中的损失部分:
loss的构成:loss主要分成4个部分。
-
rpn分类损失
从anchor_target_layer返回数据读取第一块rpn_label,排成一列,共有K×A个,读取rpn_cls_score_reshape数据,reshape成(K×A, 2)矩阵,然后根据label取出不为-1的行,共256行,然后输入给tf.nn.sparse_softmax_cross_entropy_with_logits计算分类损失。
rpn回归损失
读取rpn_bbox_pred,然后从anchor_target_layer返回数据读取第二到第四块数据,分别是回归的目标值,计算modified L1 loss的inside和outside weight,都reshape成(1, h, w, A*4),从而计算出L1 loss。
目标检测分类损失
读取最后的cls_score,(128, 21),从proposal_target_layer返回数据读取第二块labels,排成一列,然后计算tf.nn.sparse_softmax_cross_entropy_with_logits分类损失。
目标检测回归损失
读取最后的bbox_pred,(128, 84),从proposal_target_layer返回数据读取第三到第五块数据,同样计算L1 loss。
总算写完了,以上就是我对faster rcnn的理解,如有错误或不对的地方,请帮忙指出来,谢谢!
参考资料:
http://www.cnblogs.com/Kenneth-Wong/