1. 前言
距离上次YOLO V3深度解析(上) ,隔了很久了,其实自己忙着自己的论文+专利+学习任务,在写文章这块也是有点懈怠了,但是事儿不能做一半就结束了(也有小伙伴催更了),所以接着对YOLO V3进行解析,代码是基于Tensorflow的。
上一节讲到了YOLO V3模型的搭建,模型生成了三个特征图feature_map_1, feature_map_2, feature_map_3。接下来我们要结合模型主要讲解下:
- YOLO V3的先验框anchor
- YOLO V3的损失函数
2. YOLO V3的Anchor
YOLO V3继承了YOLO V2中的锚(anchor,我们理解为基础框/先验框,后面需要以这些框为基础进行bounding box微调),但是又不太一样。
在YOLO V2中,设置了5个宽高比例的anchor(通过聚类获得),每个cell(特征图的一个单元格)负责的anchor的数量为5,而在YOLO V3中,共设置9个宽高不同的anchor(同样是通过聚类获取得到),每个cell的anchor的数量为9/3=3个,因为YOLO V3有3个feature_map,不同feature_map的size和感受野是不一样的,较小size的feature_map具有较大的感受野,所以负责检测较大的物体,同理,较大size的feature_map感受野较小,负责检测较小的物体。
所以在训练前,我们需要根据自己的数据集通过聚类生成适合自己数据集训练的基础框anchor,这样可以加速自己的训练进程。就比如说你要做人脸识别和行人识别的话,anchor其实差异还是蛮大的,人脸识别一般的anchor长宽比为1,而行人anchor长宽比为0.5居多。
这里我们需要根据你自己数据集上的真实框进行聚类,Tensorflow程序中我们打开get_kmeans.py,更改annotation_path为你自己的防止图片标注信息的文件,然后点击训练即可。
target_size = [416, 416]
annotation_path = r"datamy_datatrain.txt"
anno_result = parse_anno(annotation_path, target_size=target_size)
anchors, ave_iou = get_kmeans(anno_result, 9)
下图是我在自己数据集上的anchor,可以比较明显地看出,我这个数据集的真实框大多接近方形。
3. YOLO V3的前向传播和损失函数
3.1 YOLO V3的前向传播
借鉴之前的图(下图),我们在上一篇中,也详细地对YOLO V3地的结构进行了说明。
可以看出来,YOLO V3模型接受416x416x3的图片,返回三个不同size的特征图y1(13x13x255),y2(26x26x255),y3(52x52x255),这里的255是在COCO数据集下的结果,因为COCO数据集有80类物体,每个特征图的cell负责三个先验框anchor,则有
255 = 3 x [80(类别估计概率分布)+1(置信度)+4(预测框)]
在训练初期的时候,由于网络参数的初始化,y1、y2、y2和真实的框、类别、置信度信息差别较大,所以会产生较大的损失,后面根据损失进行反向传播优化参数就好了,所以接下来我们对YOLO V3的损失函数进行相关的讲解。
3.2 YOLO V3的损失函数
相信大家在看其他博客或者知乎文章的时候,经常会看到很长的一个关于YOLO V3的损失函数,如下图所示。该损失函数看似包含了五个部分,实际是三个部分,即
预测框的位置损失+预测框预测的类别损失+预测框置信度损失
这里我们结合代码,转到model.py中,这里定义了yolov3这个类。我们转到类函数loss_layer ,乍一看觉得很长,没事,我都做好了标注,如下所示。(如果你懒得看,直接跳过代码段看我后面解析)
def loss_layer(self, feature_map_i, y_true, anchors):
'''
calc loss function from a certain scale
input:
feature_map_i: feature maps of a certain scale. shape: [N, 13, 13, 3*(5 + num_class)] etc.
y_true: y_ture from a certain scale. shape: [N, 13, 13, 3, 4(宽、高、x,y)+ 1 + num_class + 1(默认权重)] etc. 最后维度是权重
anchors: shape [3, 2]
'''
# size in [h, w] format! don't get messed up!
grid_size = tf.shape(feature_map_i)[1:3] #输出feature map的宽、高
# the downscale ratio in height and weight
ratio = tf.cast(self.img_size / grid_size, tf.float32) #返回的该特征图相对于原图的scale
# N: batch_size
N = tf.cast(tf.shape(feature_map_i)[0], tf.float32)
# 将特征图上预测的框的位置,大小通过anchor先验框映射到原图大小,这里的x_y_offset, pred_boxes都是原图尺寸的
x_y_offset, pred_boxes, pred_conf_logits, pred_prob_