基于TF2的YOLOv3算法详解

YOLOv3算法的详细解释在上篇文章中已经详细介绍过,目前YOLO代码的TensorFlow版本大部分还是TensorFlow1的版本。但是在2019年10月,发布了TensorFlow2的版本,TensorFlow2的版本比TensorFlow1的版本更加易用、灵活和强大。所以我结合自己Tensorflow2的学习,结合代码部分来详细解释一下YOLOv3中的网络结构与损失函数。
我们需要将自己的数据集或者VOC、COCO的数据集放在dataset的文件夹下面。如果是VOC或者COCO的数据集,我们可以直接运行write_voc_to_txt脚本,将数据集的XML文件进行解析,解析完的txt文件会存储在data_process文件下,名称为data.txt。如果是自己的数据集,我们需要添加一个write_custom_to_txt脚本,与write_voc_to_txt脚本基本一致,然后需要在data_process文件夹下添加一个parse_custom的脚本,只需要将导入configuration中的关于custom的相关信息,然后将脚本中的变量改成custom的相关信息。最后,运行train_for_scratch脚本即可进行训练。
首先,我们需要说一下configuration脚本文件。里面的参数都很容易明白,如果是自定义的数据集,需要在custom_dataset_classes字典中添加样本中物体的名称及编号。我们可以通过修改configuration中的变量来加速网络的收敛,提高网络的性能。
网络结构:
接下来看一下网络结构,网络结构是在yolo_v3的脚本文件中。DarknetConv2D类是指一个Convolutional,由一个卷积层、BN层和一个Leaky_relu激活函数层组成。ResidualBlock类是指一个11的卷积和一个33的卷积短接形成Resnet网络结构。make_residual_block函数是指生成一个filters=2*filters、stride=2的Convolutional层以进行下采样(YOLOv3中不在使用池化层,因此使用卷积层进行下采样)和num_blocks个Residual Block结构。Darknet53类是生成主网络结构,如图1所示,首先经过一个filters=32的Convolutional,然后是经过一个filters=64、stride=2的Convolutional和一个filters=32的Residual Block,然后是经过一个filters=128、stride=2的Convolutional和经过两个filters=64的Residual Block,然后是经过一个filters=256、stride=2的Convolutional和经过八个filters=128的Residual Block,然后是经过一个filters=512、stride=2的Convolutional和经过八个filters=256的Residual Block,最后经过一个filters=1024、stride=2的Convolutional和经过四个filters=512的Residual Block。并且需要在filters=256的Residual Block保存一个输出值output_1,在filters=512的Residual Block保存一个输出值output_2,在filters=1024的Residual Block保存一个输出值output_3。
图1 Darknet53网络结构
YOLOTail类是指如图2所示除Darknet53结构之外金字塔结构,主要是指五个Convolutional和直接连接的一个Convolutional。YOLOV3类是指由最后一个卷积层输出的output_3经过五个Convolutional,之后分为两部分,一部分经过一个3×3的Convolutional和一个1×1卷积层输出13×13×255(COCO数据集);另一部分经过一个Convolutional和上采样将13×13变换到26×26以便于output_2进行concat连接,然后该部分在经过五个Convolutional,之后分为两部分,一部分经过一个3×3的Convolutional和一个1×1卷积层输出26×26×255;另一部分经过一个Convolutional和上采样将13×13变换到52×52以便于output_1进行concat连接,然后该部分在经过五个Convolutional,然后经过一个3×3的Convolutional和一个1×1卷积层输出52×52×255。
图2 YOLOv3网络结构
损失函数:
YOLOv3的损失函数如下所示。

YOLOv3的置信度损失和类别损失都是使用的二值交叉熵损失,位置损失还是使用的平方和损失。
__generate_grid_shape函数主要是生成[[13. 13.], [26. 26.], [52. 52.]]列表,将图片划分成三个尺寸的网格单元。
__get_scale_size函数是生成图片像素/对应三个anchor的值。
__binary_crossentroy_keep_dim是使用二值交叉熵函数计算损失。
__calculate_loss是计算损失的主函数。先生成划分的网格单元,true_object_mask表示是否有物体,1表示有物体,0表示没有物体;true_classs_probs表示每个类别的可能性。因为真实图片的坐标是[xmin, ymin, xmax, ymax],但是神经网络的输出是[center_x, center_y, w, h ],所以我们需要将真实图片的坐标转换成神经网络输出坐标的形式。true_xy_offset表示物体中心相对于该网格单元左上角的坐标值,true_wh_offset表示真实框的宽度与高度。box_loss_scale适用于加大对小box的损失,YOLOv3中宽度与高度不在使用平方根,因此引入了该参数来调整box大小对损失函数的影响。然后计算真实框与预测框的IOU,选出最大的IOU。然后将小于阈值的IOU写入ignore_mask,作为负样本,大于IOU但不是最佳的样本为忽略样本。
网格单元中没有object时,只需要计算置信度损失;网格单元中有object时,需要计算分类损失以及两个bbox的置信度损失;与真实框IOU最大的bbox负责计算位置损失。因此,损失函数的计算需要加上true_object_mask以便进行计算网格单元中有object的损失。在该程序中,xy_loss的计算是使用的二值交叉熵,与我所给的公式有所出入,作者在论文中也没有给出损失函数的计算,可以尝试使用平方和损失和二值交叉熵损失,观察那个损失函数相对更有利于网络首先。wh_loss的计算是使用的平方和损失;confidence_loss和class_loss都是采用的二值交叉熵损失。但是confidence_loss这里好像缺少了负样本的置信度损失,需要自己加上。
如果想要看loss的实时曲线,我们需要在train_from_scratch脚本文件中加上tensorboard。
current_time= datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
log_dir = ‘logs/’ + current_time
summary_writer = tf.summary.create_file_writer(log_dir)

if step % 100 == 0:
		with summary_writer.as_default():
       tf.summary.scalar('train_loss',float(loss.metric.result()), 

step=len(loss.metric.result()))
这里需要说明一下,如果样本中的小物体很多,将原有的特征图尺寸13×13,26×26,52×52扩展为13×13,26×26,52×52,104×104,可以增加对小物体识别的准确率。anchor的值可以根据k-means聚类获得。

源码获取关注微信公众号:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值