在《Faster-Rcnn代码之网络架构》中,我们采用DetectionTeamUCAS中的Faster-RCNN Tensorflow实现,对Faster-Rcnn的网络架构的代码进行了理解,在这个文章中,我们将对Faster-RCNN的损失函数的定义有关的代码做一些笔记。笔记中难免会出现一些理解的偏差,如有错误,还请各位看官不吝指出,共同学习,共同提高。
先放上faster-rcnn中RPN 网络的loss的公式:
这里的两个slice 来源于:https://www.slideshare.net/xavigiro/faster-rcnn-towards-realtime-object-detection-with-region-proposal-networks
其中loss函数中的t参量以及在边框回归中使用的smooth_l1 函数定义为下图:
之前的Fast-RCNN网络引入了multi-task loss,在网络采用了全连接网络作为分类和边框回归,因此只有上图中第二个slice的Loss函数,在Faster-RCNN网络中,引入了RPN网络,在训练RPN网络时候,则引入了RPN网络的Loss函数,如上图中第一个slice。
有了如上的概念后,根据我们的网络,我们需要获取的参量为:
RPN网络:
1.anchor的类别预测 pi
2.ground truth的类别标签 pi*
3. 256个位置对应的 256×(scale×ratios) 个anchor对应的编码后的 t 预测矩阵,
4.每一个anchor对应的最大重叠率的ground truth bbox的 t target 矩阵
Fast-RCNN网络:
1.真实的类别标签 u
2.预测的类概率 p
3.真实的ground truth 对应的 t 矩阵
4.预测的bbox 对应的 t 矩阵
有了这些基本的定义,我们回到代码中:
loss_dict = self.build_loss(
rpn_box_pred=rpn_box_pred,
rpn_bbox_targets=rpn_bbox_targets,
rpn_cls_score=rpn_cls_score,
rpn_labels=rpn_labels,
bbox_pred=bbox_pred,
bbox_targets=bbox_targets,
cls_score=cls_score,
labels=labels)
final_bbox, final_scores, final_category = self.postprocess_fastrcnn(
rois=rois,
box_ppred=bbox_pred,
scores=cls_prob,
img_shape=img_shape)
在代码中,定义了一个loss_dict,用来保存RPN网络和Faster-RCNN的loss,其接收的参量如代码所示。回到build_loss 函数,其定义如下:
with tf.variable_scope('build_loss') as sc:
with tf.variable_scope('rpn_loss'):
利用smooth_l1_loss_rpn 函数计算边框回归误差
rpn_bbox_loss = losses.smooth_l1_loss_rpn(bbox_pred=rpn_box_pred,
bbox_targets=rpn_bbox_targets,
label=rpn_labels,
sigma=cfgs.RPN_SIGMA)、
# rpn_bbox_loss = tfapi_loss.smooth_l1_loss_rpn(bbox_pred=rpn_box_pred,
# bbox_targets=rpn_bbox_targets,
# label=rpn_labels,
# sigma=cfgs.RPN_SIGMA)
# rpn_cls_loss:
# rpn_cls_score = tf.reshape(rpn_cls_score, [-1, 2])
# rpn_labels = tf.reshape(rpn_labels, [-1])
# ensure rpn_labels shape is [-1]
只选出那些 label 为 0 与 1 的框label 所对应的 rpn_cls_score
rpn_select = tf.reshape(tf.where(tf.not_equal(rpn_labels, -1)), [-1])
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select), [-1, 2])
选出那些label所对应的 ground_truth 对应的标签 函数接收的时候为所有的anchor对应的,针对label的赋值,来小批量的处理
rpn_labels = tf.reshape(tf.gather(rpn_labels, rpn_select), [-1])
采用交叉熵衡量分类误差,这里只有 有目标与没目标之间两类
rpn_cls_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score,
labels=rpn_labels))
对分类以及回归误差分别进行加权后return
rpn_cls_loss = rpn_cls_loss * cfgs.RPN_CLASSIFICATION_LOSS_WEIGHT
rpn_bbox_loss = rpn_bbox_loss * cfgs.RPN_LOCATION_LOSS_WEIGHT
在计算smooth_l1 误差时候函数计算为:
def _smooth_l1_loss_base(bbox_pred, bbox_targets, sigma=1.0):
'''
:param bbox_pred: [-1, 4] in RPN. [-1, cls_num+1, 4] in Fast-rcnn
:param bbox_targets: shape is same as bbox_pred
:param sigma:
:return:
'''
sigma_2 = sigma**2
box_diff = bbox_pred - bbox_targets
abs_box_diff = tf.abs(box_diff)
smoothL1_sign = tf.stop_gradient(
tf.to_float(tf.less(abs_box_diff, 1. / sigma_2)))
这里得到的是根据smooth_l1 公式得到的 , 这里 x 是 bbox的差值,如果真实值与预测
值之间的差值小于1/sigma_2 则 采用第一种计算方式,如果大于 1/sigma_2 则采用第二种
计算方式
loss_box = tf.pow(box_diff, 2) * (sigma_2 / 2.0) * smoothL1_sign \
+ (abs_box_diff - (0.5 / sigma_2)) * (1.0 - smoothL1_sign)
return loss_box
#这个代码实现的作者,并没有直接采用0.5 而是再引入了 一个sigma 因子来约束
def smooth_l1_loss_rpn(bbox_pred, bbox_targets, label, sigma=1.0):
'''
:param bbox_pred: [-1, 4] all encode rois every feature map location with 9 anchors
#this is the RPN direct output
:param bbox_targets: [-1, 4] all anchors t matrix computed by gt_boxes
:param label: [-1] define all anchors pos or negative
:param sigma:
:return:
'''
value = _smooth_l1_loss_base(bbox_pred, bbox_targets, sigma=sigma)
value = tf.reduce_sum(value, axis=1) # to sum in axis 1
这一块对 相应的 tx+ty+tw+th 求和,获得每一个anchor对应损失值
rpn_select = tf.where(tf.greater(label, 0))
# rpn_select = tf.stop_gradient(rpn_select) # to avoid
在所有的value中,只选择那些为正例的样本,因为为
负例的样本 label 为 0 因此在公式中不参与贡献,但是最后均值的时候确实 正负一起考虑的。
selected_value = tf.gather(value, rpn_select)
non_ignored_mask = tf.stop_gradient(
1.0 - tf.to_float(tf.equal(label, -1))) # positve is 1.0 others is 0.0
bbox_loss = tf.reduce_sum(selected_value) / tf.maximum(1.0, tf.reduce_sum(non_ignored_mask))
这里讲选出来的正样本所有的bbox的loss求和后除以这个batch中 label为0 与
label为1 的样本的总数,起到了公式中除以N(pos+neg)的作用
return bbox_loss
在计算RPN的分类误差的时候,定义如下:
rpn_select = tf.reshape(tf.where(tf.not_equal(rpn_labels, -1)), [-1])
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select), [-1, 2])
rpn_labels = tf.reshape(tf.gather(rpn_labels, rpn_select), [-1])
rpn_cls_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score,
labels=rpn_labels))
首先从所有的rpn_label 中选出仅仅作为minibatch参与计算loss的label,获得相关的rpn网络对于minibatch中anchor的二分类值,最后计算标签与预测值之间的交叉熵,得到分类的误差,得到这两类误差后,就可以对其进行加权相加得到RPN网络的总误差值。在计算Fast-RCNN的边框回归误差以及分类误差的时候,由于RPN与Fast-RCNN采用的loss相同,只是在具体的类别上不一致,因此采用了与RPN网络同样的误差函数:
with tf.variable_scope('FastRCNN_loss'):
if not cfgs.FAST_RCNN_MINIBATCH_SIZE == -1:
bbox_loss = losses.smooth_l1_loss_rcnn(
bbox_pred=bbox_pred,
bbox_targets=bbox_targets,
label=labels,
num_classes=cfgs.CLASS_NUM + 1,
sigma=cfgs.FASTRCNN_SIGMA)
这里的输入为 bbox_pred ,是特征图上对应的roi经过Pooling,全连接层得到bbox_pred,bbox_targets 是 roi对应的具有最大重叠率的ground truth 框的映射因子t矩阵,labels 则是每一个target对应的类别。具体自己算Fast-RCNN的损失函数如下:
def smooth_l1_loss_rcnn(bbox_pred, bbox_targets, label, num_classes, sigma=1.0):
'''
:param bbox_pred: [-1, (cfgs.CLS_NUM +1) * 4]
:param bbox_targets:[-1, (cfgs.CLS_NUM +1) * 4]
:param label:[-1]
:param num_classes:
:param sigma:
:return:
'''
outside_mask = tf.stop_gradient(tf.to_float(tf.greater(label, 0)))
选出那些需要计算损失的roi所在的标签
bbox_pred = tf.reshape(bbox_pred, [-1, num_classes, 4])
每一个roi,对所有的类别预测位置
bbox_targets = tf.reshape(bbox_targets, [-1, num_classes, 4])
value = _smooth_l1_loss_base(bbox_pred,
bbox_targets,
sigma=sigma)
value = tf.reduce_sum(value, 2)
value = tf.reshape(value, [-1, num_classes])
得出roi 在每个类上的预测误差
inside_mask = tf.one_hot(tf.reshape(label, [-1, 1]),
depth=num_classes, axis=1)
得到相关的roi的类别标签的one-hot编码
inside_mask = tf.stop_gradient(
tf.to_float(tf.reshape(inside_mask, [-1, num_classes])))
normalizer = tf.to_float(tf.shape(bbox_pred)[0])
bbox_loss = tf.reduce_sum(
tf.reduce_sum(value * inside_mask, 1)*outside_mask) / normalizer
参与计算的roi(数量为roi_per_img,定义在cfg文件中) 的平均回归误差
return bbox_loss
在计算Fast-RCNN的分类误差时,采用所有类的交叉熵,得到回归误差与分类误差加权。
cls_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=cls_score,
labels=labels)) # beacause already sample before
cls_loss = cls_loss * cfgs.FAST_RCNN_CLASSIFICATION_LOSS_WEIGHT
bbox_loss = bbox_loss * cfgs.FAST_RCNN_LOCATION_LOSS_WEIGHT
根据论文的公式,最后将RPN网络的(分类,回归)误差与Fast-RCNN的(分类,回归)误差相加后作为总的误差进行训练即可。
这一章主要介绍了经过RPN网络以及Fast-RCNN网络后得到的anchors与rois怎么建立起和ground truth 类别与坐标之间的关系,误差的建立,下一章则介绍训练的方式。
因为代码量较大,先开始整体理解了一下,后面分了几次对于关键函数做了理解,写作上免不了有疏漏,错误,理解上的问题等,如有问题,还请及时纠正,谢谢!