![3ee9d2d83bc77796238a969492a77d1f.png](https://i-blog.csdnimg.cn/blog_migrate/70bb7cbc0f9ed9578eda8ce9778b1518.jpeg)
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
下图是我在自己数据集上的anchor,可以比较明显地看出,我这个数据集的真实框大多接近方形。
![638d925fd638bbc6454b03ae3cb6389a.png](https://i-blog.csdnimg.cn/blog_migrate/d8abf4d742052bdf8b7fca10ce89eadd.png)
3. YOLO V3的前向传播和损失函数
3.1 YOLO V3的前向传播
借鉴之前的图(下图),我们在上一篇中,也详细地对YOLO V3地的结构进行了说明。
![47b757ddea102ff18df9b42ff13395c3.png](https://i-blog.csdnimg.cn/blog_migrate/06c67ecb6f5e79b9e6c6aa499b7a3751.jpeg)
可以看出来,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的损失函数,如下图所示。该损失函数看似包含了五个部分,实际是三个部分,即
预测框的位置损失+预测框预测的类别损失+预测框置信度损失
![d0eebac162cfe3ab122d7afbe6727450.png](https://i-blog.csdnimg.cn/blog_migrate/9330b360f78ac040664467dcc1388adf.png)
这里我们结合代码,转到model.py中,这里定义了yolov3这个类。我们转到类函数loss_layer ,乍一看觉得很长,没事,我都做好了标注,如下所示。(如果你懒得看,直接跳过代码段看我后面解析)
def
这个函数的输入为某一特征图(即YOLO V3的输出张量),y_true(图片标注的信息),聚类获得的anchor(这里不同特征图对应的anchor应当也是不同的)。
这里我们详细的说一下y_true的结构,这里我们以特征图宽高为 13 x 13的为例:
y_ture 的shape为 [N, 13, 13, 3, 4 + 1 + num_classes + 1]
这里的
- N为batch_size
- 13为特征图宽高
- 3为anchor数量(每个特征图对应固定3个anchor)
- 4为坐标信息(宽、高、框中心横坐标、框中心纵坐标)
- 1为置信度
- num_classes是一个分类标签,所属分类对应位置为1,其他为0
- 最后一个1指的是mix_w,默认值为1
我们开始进行分析
# size in [h, w] format! don't get messed up!
以上这段中比较容易使人疑惑的是函数self.reorg_layer,所以我们进入该函数,如下
def
通过我们的标注,很容易理解上面该函数的意思,即,将特征图上预测的框的位置,大小结合先验框anchor映射到原图大小,这样做的目的是为了后面求与真实框的IOU更加方便。
接着,我们解析损失函数中的两个值,
他们均有下面代码生成,这里的object_mask指的是
###########
我们可以看出,特征图的哪个单元格负责对应真实框的检测,则该单元格中的3个anchor对应的置信度均为1,否则为0,这就是
接着作者做的事,并不是将
接着,作者对预测框和真实框的位置信息做了两个转换:
# shape: [N, 13, 13, 3, 2]
可以看出,作者做这两个变换是非常灵活的,这样做的目的当然也是便于损失函数的计算啦!
上述的true_xy 和pred_xy是将预测框和真实框的中心坐标(原图比例)转换为下图中的
![70ad34bd10cbb37d32dea3c119162cdd.png](https://i-blog.csdnimg.cn/blog_migrate/43837e186514fa3e9b4e97094eee15e6.png)
上述的true_tw_th和pred_tw_th是将预测框和真实框的宽高信息(原图比例)转换为上图中的
接着,代码出现了一行
# box size punishment: 框大小的惩罚,就是坐标损失前的权重,大的框权重设置小一点,小的设置大一点
# box with smaller area has bigger weight. This is taken from the yolo darknet C source code.
# shape: [N, 13, 13, 3, 1]
# 后面一串为真实框面积相对于原图的
box_loss_scale = 2. - (y_true[..., 2:3] / tf.cast(self.img_size[1], tf.float32)) * (y_true[..., 3:4] / tf.cast(self.img_size[0], tf.float32))
这个box_loss_scale的作用,原论文中也提及,对于大的检测框,其位置损失要远大于,小的框的位置损失。所以设置这个的意思很明显,对大的框的位置损失降低敏感度,对小的框的位置损失增加敏感度。
准备了这么多,进入计算损失的主要阶段,这里先按照上面的损失公式计算位置损失,即
![5c578092003ae57e39b91d642bb309d1.png](https://i-blog.csdnimg.cn/blog_migrate/0e2cb37ca6dfdfbf66030eb50a994d7d.jpeg)
代码为:
#下面求框的损失
这和公式是一一对应,非常直观的。
接着,我们计算置信度损失,公式中是
![199471a1a69321e412be85fa4a182068.png](https://i-blog.csdnimg.cn/blog_migrate/8cfa0ca9562b58a0f42ccbb8bf0c2132.png)
代码为:
#下面求置信度损失
代码中区分了positive和negative置信度,这里我感觉好像和上面的
最后一个损失就是分类损失,这是非常常见的!公式中为:
![f7ec21b1e85ab523ef85a342e008ffe1.png](https://i-blog.csdnimg.cn/blog_migrate/4de7c7064c270a49410c95e21c667e87.png)
代码中为:
# shape: [N, 13, 13, 3, 1]
至此,我们的损失函数这块就讲完了,后面根据这个损失函数进行反向传播就能够愉快地训练YOLO V3了!
4. 小结
至此,拖了近半年的YOLO V3解析就完成了,YOLO V4不久前也出来了,如果后面有空,我们可以聊聊EfficientDet和YOLO V4!