论文 https://arxiv.org/pdf/1506.02640.pdf
代码 https://github.com/hizhangp/yolo_tensorflow
基本原理
将输入图片分成个网格,如果物体的中心位于某个网格中,这个网格就负责检测该物体。
每个网格预测B个bounding boxes和confidence, ,即confidence描述的是该box含有object的置信度以及该box位置的准确度。
每个bounding box包含5个预测值,,即预测框的中心坐标、宽高以及置信度(网络实际输出中心坐标和宽高并不是实际结果,需要进行转换,后面会细讲)。
每个网格还预测C个类别条件概率,即某个网格有物体的情况下该物体属于类别的概率。不管一个网格预测几个box,都只预测一组类别概率。
论文在PASCAL VOC集上训练,输入为448448,取S=7,B=2,C=20。
网络结构
网络由24个卷积层和两个全连接层组成,网络最终输出为7730的tensor,其中30=20+25即20个类别、2个bounding box、每个box预测 5个值。
Loss函数
其中表示网格负责的预测(即的中心点位于网格内),表示网格的第的box负责对的预测
首先看最后一行的分类loss
# class_loss
class_delta = response * (predict_classes - classes) # (45,7,7,20) 乘以response是因为存在对象的网格才计入误差
class_loss = tf.reduce_mean(tf.reduce_sum(tf.square(class_delta), axis=[1, 2, 3]),
name='class_loss') * self.class_scale # (45,7,7,20)->(45,)->()
其中reponse就是,shape为(batch_size, 7, 7, 1),即的网格中负责预测某个物体的网格值为1,其余为0。predict_classes和classes的shape都为(batch_size, 7, 7, 20),分别是网络预测的分类输出和ground truth的分类标签。class_scale是分类loss的权重,论文里是1,这个代码里是2。
第三行是含的box的置信度loss
# object_loss
object_delta = object_mask * (predict_scales - iou_predict_truth)
object_loss = tf.reduce_mean(tf.reduce_sum(tf.square(object_delta), axis=[1, 2, 3]),
name='object_loss') * self.object_scale
其中object_mask就是,iou_predict_truth是预测box和gt box的iou
iou_predict_truth = self.calc_iou(predict_boxes_tran, boxes) # (45,7,7,2)
object_mask = tf.reduce_max(iou_predict_truth, axis=3, keep_dims=True) # (45,7,7,1)
object_mask = tf.cast((iou_predict_truth >= object_mask), tf.float32) * response # (45,7,7,2)
前面说过response是,即含的网格。这里object_mask是含的网格中具体负责预测的那个box,即找到含的网格里和ground truth的iou最大的那个box。前面说过置信度的表达式如下,既描述某个网格内有没有同时又描述具体的box输出位置准不准,某个网格内有物体则,因此这里置信度的target就是iou_predict_truth。object_sclae是权重值为1。
第四行是不含的box的置信度loss
noobject_mask = tf.ones_like(object_mask, dtype=tf.float32) - object_mask
noobject_delta = noobject_mask * predict_scales # noobject_mask * (predict_scales - 0)
noobject_loss = tf.reduce_mean(tf.reduce_sum(tf.square(noobject_delta), axis=[1, 2, 3]),
name='noobject_loss') * self.noobject_scale
这里不含的target置信度为0
第一行和第二行分别是预测框中心点坐标和宽高的loss
首先需要注意的是网络输出的2个box的位置信息的具体含义,其中是将448448的输入划分成77的网格后box的中心距离所在网格左上角的横坐标和纵坐标,划分成77网格后每个网格大小实际应该是6464,但是这里将每个网格归一化成11大小,则的取值范围为。则是box的宽高相对于448448的输入归一化后并开方的结果。
因此在计算中心和宽高loss时网络的输出和target要保持一致,下面代码中boxes是归一化后的gt box的中心点坐标和宽高,通过如下代码转化成了网络输出box的格式,其中cell_size=7,offset和offset_tran分别是的网格中每个网格的横坐标和纵坐标索引,如上图中标XY的网格是从左到右第3个,从上到下第2个。
boxes_tran = tf.stack([boxes[..., 0] * self.cell_size - offset,
boxes[..., 1] * self.cell_size - offset_tran,
tf.sqrt(boxes[..., 2]),
tf.sqrt(boxes[..., 3])], axis=-1)
中心点和宽高的loss如下
# coord_loss
coord_mask = tf.expand_dims(object_mask, 4) # (45,7,7,2,1)
boxes_delta = coord_mask * (predict_boxes - boxes_tran)
coord_loss = tf.reduce_mean(tf.reduce_sum(tf.square(boxes_delta), axis=[1, 2, 3, 4]),
name='coord_loss') * self.coord_scale
参考
https://zhuanlan.zhihu.com/p/89143061
https://luckmoonlight.github.io/2018/11/28/yoloV1yolov2yoloV3/