YOLO模型是目标检测领域的经典模型,目前已经发展到V3版本。经过一周的学习,已经基本掌握了对YOLO V3前向传播的理解。下面分享一下YOLO V3的前向传播流程。
注:以下代码均来自于《深度学习之TensorFlow工程化项目实战》一书。这里只是记录一下自己的学习笔记。
1.YOLO V3模型的结构
YOLO V3模型属于监督式训练模型。该模型所使用的样本需要包含两部分的标注信息。
- 物体的位置坐标(矩形框)
- 物体所属的类别
将样本中的图片作为输入,将图片上的物体类别及位置坐标作为标签,对模型进行训练。最终得到的模型将会具有计算物体坐标及识别物体类别的能力。
在YOLO V3模型中,主要通过两部分结构来完成物体位置坐标计算和分类预测。
- 特征提取部分:用于提取特征。
- 检测部分:用于对提取的特征进行处理,预测图像的边框坐标(bounding box)和标签(label)。
YOLO V3模型的更多信息可以参考一下链接中的论文
https://pjreddie.com/media/files/papers/YOLOv3.pdf
1.1 特征提取部分(Darknet-53模型)
在YOLO V3中用Draknet-53模型来提取特征。如下图所示,该模型一共有78层,包括52个卷积层和1个平均池化层。
- 在实际使用中,没有用最后的平均池化层。
- Darknet-53模型由多个块组成,并且所有Darknet块都具有一样的结构:由两个卷积层(卷积核大小分别1*1和3*3)与一个残差连接组成。
- 每两个darknet块之间都有一个单独的卷积层。它们都是下采样卷积,是将原有的输入补0,再通过步长为2、卷积核为3的VALID卷积方式来实现。
- Darknet-53模型并没有只返回最后的特征结果,而是将倒数三个darkent块的结果返回。这三个返回值有不同的尺度(13,26,52),是为了给YOLO V3检测模块提供更丰富的视野特征。
(1)代码实现:Darknet-53模型的darknet块
如上图所示,Darknet-53模型由多个darknet块组成,且所有darknet块都由两个卷积层和一个残差链接组成。(53指的是有52个卷积层+1个全局平均池化层,不包括残差)
import numpy as np
import tensorflow as tf
slim = tf.contrib.slim
#定义darknet块:一个短链接加一个同尺度卷积再加一个下采样卷积
def _darknet53_block(inputs, filters):
shortcut = inputs
inputs = slim.conv2d(inputs, filters, 1, stride=1, padding='SAME')#正常卷积
inputs = slim.conv2d(inputs, filters * 2, 3, stride=1, padding='SAME')#正常卷积
inputs = inputs + shortcut
return inputs
(2)代码实现:Darknet-53模型中darknet块之间的下采样卷积
如上图所示,Darknet-53模型中每个darknet块之间都会有一个卷积层。它们都是下采样卷积,是将原有的输入补0,再通过步长为2,卷积核大小为3*3的VALID卷积来实现。
def _conv2d_fixed_padding(inputs, filters, kernel_size, strides=1):
assert strides>1
inputs = _fixed_padding(inputs, kernel_size)#外围填充0,好支持valid卷积
inputs = slim.conv2d(inputs, filters, kernel_size, stride=strides, padding= 'VALID')
return inputs
#对指定输入填充0
def _fixed_padding(inputs, kernel_size, *args, mode='CONSTANT', **kwargs):
pad_total = kernel_size - 1
pad_beg = pad_total // 2
pad_end = pad_total - pad_beg
#inputs 【b,h,w,c】 pad b,c不变。h和w上下左右,填充0.kernel = 3 ,则上下左右各加一趟0
padded_inputs = tf.pad(inputs, [[0, 0], [pad_beg, pad_end],
[pad_beg, pad_end], [0, 0]], mode=mode)
return padded_inputs
(3)搭建Darknet-53模型,并返回3种尺度特征
按照上图所示搭建一个完整的Darknet-53模型,并返回3种尺度特征。
#定义Darknet-53 模型.返回3个不同尺度的特征
def darknet53(inputs):
inputs = slim.conv2d(inputs, 32, 3, stride=1, padding='SAME')#正常卷积
inputs = _conv2d_fixed_padding(inputs, 64, 3, strides=2)#需要填充,并使用了'VALID' (-1, 208, 208, 64)
inputs = _darknet53_block(inputs, 32)#darknet块
inputs = _conv2d_fixed_padding(inputs, 128, 3, strides=2)
for i in range(2):
inputs = _darknet53_block(inputs, 64)
inputs = _conv2d_fixed_padding(inputs, 256, 3, strides=2)
for i in range(8):
inputs = _darknet53_block(inputs, 128)
route_1 = inputs #特征1 (-1, 52, 52, 128) #Darknet-53模型的第36层
inputs = _conv2d_fixed_padding(inputs, 512, 3, strides=2)
for i in range(8):
inputs = _darknet53_block(inputs, 256)
route_2 = inputs#特征2 (-1, 26, 26, 256) #Darknet-53模型的第61层
inputs = _conv2d_fixed_padding(inputs, 1024, 3, strides=2)
for i in range(4):
inputs = _darknet53_block(inputs, 512)#特征3 (-1, 13, 13, 512) #Darknet-53模型的第74层
return route_1, route_2, inputs#在原有的darknet53,还会跟一个全局池化。这里没有使用。
1.2 检测部分(YOLO V3模型)
在YOLO V3模型中使用了候选框技术,该技术有助于辅助YOLO检测模块对目标尺寸的检测计算,以提升YOLO检测块的准确率。
1.2.1候选框尺寸
候选框的尺寸来自于YOLO V3训练模型时使用的COCO数据集。在训练模型前,对COCO数据集里面的样本数据先进行聚类分析,得到的候选框尺寸基本上可以说是COCO数据集中最常见的尺寸。在训练或验证模型时,可以将这些尺寸数据提前设定好,然后用到模型中,这样做可以提高模型的准确率。这部分代码如下
_BATCH_NORM_DECAY = 0.9
_BATCH_NORM_EPSILON = 1e-05
_LEAKY_RELU = 0.1
#定义候选框,来自coco数据集
_ANCHORS = [(10, 13), (16, 30), (33, 23), (30, 61), (62, 45), (59, 119), (116, 90), (156, 198), (373, 326)]
1.2.2检测部分
在YOLO V3模型中,检测部分的模型是由一个YOLO检测块加一个检测层组成的。YOLO检测块负责进一步提取特征;检测层负责将最终的特征转化为bbox attrs单元。
YOLO V3模型的检测部分所完成的步骤如下。
(1)将Darknet-53模型提取到的特征输入检测块中进行处理。
(2)在检测块处理完之后,使用检测层生成具有bbox attrs单元的检测结果。
(3)根据bbox attrs单元检测到的结果在原有的图片上进行标注,完成检测任务。
bbox attrs单元的维度为“5+c”。其中:
- 5代表边框坐标为5维,包括框的中心坐标(x,y),长宽(w,h),目标得分(置信度)。
- c代表具体分类的个数。
其中,目标得分的计算公式如下
c o n f i d e n c e = P ( o b j e c t ) × I O U confidence=P(object)×IOU confidence=P(object)×IOU
若bounding box中包含物体,则P(object)=1,否则P(object)=0
(1)YOLO V3的检测块代码如下
#yolo检测块
def _yolo_block(inputs, filters):
inputs &