理论介绍:有关Faster RCNN理论介绍的文章,可以自行搜索,这里就不多说理论部分了。
复现过程:代码配置过程没有记录,具体怎么把源码跑起来需要自己搜索一下。
faster rcnn源码确实挺复杂的,虽然一步步解析了,但是觉得还是没有领会其中的精髓,只能算是略知皮毛。在这里将代码解析的过程给大家分享一下,希望对大家有帮助。先是解析了代码的整体结构,然后对各个子结构进行了分析。代码中的注释,有的是原来就有的注释,有的是参考网上别人的,有的是自己理解的,里面或多或少会有些错误,如果发现,欢迎指正!
本文解析的源码地址:https://github.com/lijianaiml/tf-faster-rcnn-windows
上面链接里的文章,自己手动抄了一份,加深理解。
rcnn包括两个损失:rpn网络的损失+rcnn网络的损失,其中每个损失又包括分类损失和回归损失,分类损失使用的是交叉熵,回归损失使用的是smooth L1 loss。
程序通过**_add_losses**增加对应的损失函数。其中:
rpn_cross_entropy和rpn_loss_box是RPN网络的两个损失,这两个损失用于判断archor是否是ground truth(二分类);
cls_score和bbox_pred是rcnn网络的两个损失。这两个损失的batchsize是256。
最终将上面四个loss相加,得到总的loss(还需要加上regularization_loss),至此,损失构造完毕。
'''
faster rcnn包括两个损失:rpn网络的损失+rcnn网络的损失。其中每个损失又包括分类损失和回归损失。
分类损失使用的是交叉熵,回归损失使用的是smooth L1 loss。
程序通过**_add_losses**增加对应的损失函数。其中
rpn_cross_entropy和rpn_loss_box是RPN网络的两个损失,这两个损失用于判断archor是否是ground truth(二分类);
cls_score和bbox_pred是rcnn网络的两个损失。这两个损失的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。
计算cls_score(256*21)和label(256)的sparse_softmax_cross_entropy_with_logits:cross_entropy。
计算bbox_pred(256*84)和bbox_targets(256*84)的_smooth_l1_loss:loss_box。
最终将上面四个loss相加,得到总的loss(还需要加上regularization_loss)。
至此,损失构造完毕。
'''
def _add_losses(self, sigma_rpn=3.0):
with tf.variable_scope('LOSS_' + self._tag) as scope:
# (1)RPN, class loss 分类损失
# 每个anchors是正样本还是负样本
rpn_cls_score = tf.reshape(self._predictions['rpn_cls_score_reshape'], [-1, 2])
# 特征图中每个位置对应的是正样本、负样本还是不关注(去除了边界框在图像外面的anchors)
rpn_label = tf.reshape(self._anchor_targets['rpn_labels'], [-1])
rpn_select = tf.where(tf.not_equal(rpn_label, -1)) #不关注的anchors的索引
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select), [-1, 2]) #去除不关注的anchors
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二分类的损失
# (2)RPN, bbox loss 回归损失
rpn_bbox_pred = self._predictions['rpn_bbox_pred'] #每个位置的9个anchors回归位置偏移
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])
# (3)RCNN, class loss 分类损失
cls_score = self._predictions["cls_score"] #用于rcnn分类的256个anchors的特征
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))
# (4)RCNN, bbox loss 回归损失
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']
bbox_outside_weights = self._proposal_targets['bbox_outside_weights']
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') #获取总正则化loss.
self._losses['total_loss'] = loss + regularization_loss
self._event_summaries.update(self._losses)
return loss
'''
程序中先计算pred和target的差box_diff,而后得到正样本的差in_box_diff
(通过乘以权重bbox_inside_weights将负样本设置为0)及绝对值abs_in_box_diff,之后计算上式(3)
中的符号smoothL1_sign,并得到的smooth L1 loss:in_loss_box,乘以bbox_outside_weights权重,
并得到最终的loss:loss_box。
'''
def _smooth_l1_loss(self, bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights, sigma=1.0, dim=[1]):
sigma_2 = sigma ** 2
box_diff = bbox_pred - bbox_targets # 预测的和真实的相减 x
in_box_diff = bbox_inside_weights * box_diff # 乘以正样本的权重1(rpn:去除负样本和不关注的样本,rcnn:去除负样本)
abs_in_box_diff = tf.abs(in_box_diff) # 绝对值 |x|
smoothL1_sign = tf.stop_gradient(tf.to_float(tf.less(abs_in_box_diff, 1. / sigma_2))) # 小于阈值的截断的标志位
in_loss_box = tf.pow(in_box_diff, 2) * (sigma_2 / 2.) * smoothL1_sign \
+ (abs_in_box_diff - (0.5 / sigma_2)) * (1. - smoothL1_sign) # smooth l1 loss
out_loss_box = bbox_outside_weights * in_loss_box # rpn:除以有效样本总数(不考虑不关注的样本),进行归一化;rcnn:正样本四个坐标权重为1,负样本为0
loss_box = tf.reduce_mean(tf.reduce_sum(
out_loss_box,
axis=dim
))
return loss_box
终于更新完了,写完后自己对FasterRCNN也有了更深的理解,送人玫瑰,手有余香。感谢你一直以来的关注!一起努力一起加油!