结构简述
主要用了darknet-53,采用全卷积模式的结构,同时通过级联不同尺度的特征,可以使得网络对不同尺度的目标具有更好的适应性。
网络结构图
yolo-v3训练过程中,随机选择输入尺度,必须是32的倍数,比如[320,352,384,416,448,480,512,544,576,608]
. 下面的结构图,以416x416x3
为例,output_stride=[8,16,32],对应的输出分别为52x52x255,26x26x255,13x13x255
。由于,8倍尺度降低的感受野更小,所以该尺度适用于检测小目标,相应的,26x26检测中等大小的目标,而13x13适合检测大目标。
图中,coco数据集的检测类别为80类,所以 255 = 3 *(4+1+80)
,其中每个 grid cell 预测3
个框,4+1
分别是边界框的中心和宽高,以及置信度(x,y,w,h)
。
代码分析
import core.common as common
import tensorflow as tf
def darknet53(input_data, trainable):
""" backbone network: darknet53 """
with tf.variable_scope('darknet'):
input_data = common.convolutional(input_data, filters_shape=(3, 3, 3, 32), trainable=trainable, name='conv0')
input_data = common.convolutional(input_data,
filters_shape=(3, 3, 32, 64),
trainable=trainable,
name='conv1',
downsample=True)
# 定义残差模块:0
for i in range(1):
input_data = common.residual_block(input_data, 64, 32, 64, trainable=trainable, name='residual%d' %(i+0))
input_data = common.convolutional(input_data, filters_shape=(3, 3, 64, 128),
trainable=trainable, name='conv4', downsample=True)
# 定义残差模块:1,2
for i in range(2):
input_data = common.residual_block(input_data, 128, 64, 128, trainable=trainable, name='residual%d' %(i+1))
input_data = common.convolutional(input_data, filters_shape=(3, 3, 128, 256),
trainable=trainable, name='conv9', downsample=True)
# 定义残差模块:3-10
for i in range(8):
input_data = common.residual_block(input_data, 256, 128, 256, trainable=trainable, name='residual%d' %(i+3))
route_1 = input_data
input_data = common.convolutional(input_data, filters_shape=(3, 3, 256, 512),
trainable=trainable, name='conv26', downsample=True)
# 定义残差模块:11-18
for i in range(8):
input_data = common.residual_block(input_data, 512, 256, 512, trainable=trainable, name='residual%d' %(i+11))
route_2 = input_data
input_data = common.convolutional(input_data, filters_shape=(3, 3, 512, 1024),
trainable=trainable, name='conv43', downsample=True)
# 定义残差模块:19-22
for i in range(4):
input_data = common.residual_block(input_data, 1024, 512, 1024, trainable=trainable, name='residual%d' %(i+19))
return route_1, route_2, input_data
DarkNet-53
darknet-53
是yolo-v3特征提取的主干网络,权重是在ImageNet
训练得到,下载地址darknet53.conv.74。Darknet-53 的主体框架如下图所示,它主要由 Convolutional 和 Residual 结构所组成。需要特别注意的是,最后三层 Avgpool、Connected 和 softmax layer 是用于在 Imagenet 数据集上作分类训练用的。当我们用 Darknet-53 层对图片提取特征时,是不会用到这三层的。
Darknet-53 有多牛逼?看看下面这张图,作者进行了比较,Darknet-53 在精度上可以与最先进的分类器媲美,同时它的浮点运算更少,计算速度也最快。和 ReseNet-101 相比,Darknet-53 网络的速度是前者的1.5倍;虽然 ReseNet-152 和它性能相似,但是用时却是它的2倍以上。
YOLO-V3部分
def __build_network(self, input_data):
# input_data:32倍输出
# route_1:8倍输出
# route_2:16倍输出
route_1, route_2, input_data = backbone.darknet53(input_data, self.trainable)
# DBL*5
input_data = common.convolutional(input_data, (1, 1, 1024, 512), self.trainable, 'conv52')
input_data = common.convolutional(input_data, (3, 3, 512, 1024), self.trainable, 'conv53')
input_data = common.convolutional(input_data, (1, 1, 1024, 512), self.trainable, 'conv54')
input_data = common.convolutional(input_data, (3, 3, 512, 1024), self.trainable, 'conv55')
input_data = common.convolutional(input_data, (1, 1, 1024, 512), self.trainable, 'conv56')
# 网络输出1:13x13x255,针对大尺寸目标
conv_lobj_branch = common.convolutional(input_data, (3, 3, 512, 1024), self.trainable, name='conv_lobj_branch')
conv_lbbox = common.convolutional(conv_lobj_branch, (1, 1, 1024, 3 * (self.num_class + 5)),
trainable=self.trainable, name='conv_lbbox', activate=False, bn=False)
# 上采样与route_2级联,采用最近邻差值,不需要学习额外的参数
input_data = common.convolutional(input_data, (1, 1, 512, 256), self.trainable, 'conv57')
input_data = common.upsample(input_data, name='upsample0', method=self.upsample_method)
with tf.variable_scope('route_1'):
input_data = tf.concat([input_data, route_2], axis=-1)
# DBL*5
input_data = common.convolutional(input_data, (1, 1, 768, 256), self.trainable, 'conv58')
input_data = common.convolutional(input_data, (3, 3, 256, 512), self.trainable, 'conv59')
input_data = common.convolutional(input_data, (1, 1, 512, 256), self.trainable, 'conv60')
input_data = common.convolutional(input_data, (3, 3, 256, 512), self.trainable, 'conv61')
input_data = common.convolutional(input_data, (1, 1, 512, 256), self.trainable, 'conv62')
# 网络输出2:26x26x255,针对中等尺寸目标
conv_mobj_branch = common.convolutional(input_data, (3, 3, 256, 512), self.trainable, name='conv_mobj_branch')
conv_mbbox = common.convolutional(conv_mobj_branch, (1, 1, 512, 3 * (self.num_class + 5)),
trainable=self.trainable, name='conv_mbbox', activate=False, bn=False)
# 上采样与route_1级联
input_data = common.convolutional(input_data, (1, 1, 256, 128), self.trainable, 'conv63')
input_data = common.upsample(input_data, name='upsample1', method=self.upsample_method)
with tf.variable_scope('route_2'):
input_data = tf.concat([input_data, route_1], axis=-1)
input_data = common.convolutional(input_data, (1, 1, 384, 128), self.trainable, 'conv64')
input_data = common.convolutional(input_data, (3, 3, 128, 256), self.trainable, 'conv65')
input_data = common.convolutional(input_data, (1, 1, 256, 128), self.trainable, 'conv66')
input_data = common.convolutional(input_data, (3, 3, 128, 256), self.trainable, 'conv67')
input_data = common.convolutional(input_data, (1, 1, 256, 128), self.trainable, 'conv68')
# 网络输出3:52x52x255,针对小尺寸目标
conv_sobj_branch = common.convolutional(input_data, (3, 3, 128, 256), self.trainable, name='conv_sobj_branch')
conv_sbbox = common.convolutional(conv_sobj_branch,
(1, 1, 256, 3 * (self.num_class + 5)),
trainable=self.trainable,
name='conv_sbbox', activate=False, bn=False)
return conv_lbbox, conv_mbbox, conv_sbbox
多尺度检测
YOLOv3 对输入图片进行了粗、中和细网格划分,以便分别实现对大、中和小物体的预测。假如输入图片的尺寸为 416X416, 那么得到粗、中和细网格尺寸分别为 13X13、26X26 和 52X52。这样一算,那就是在长宽尺寸上分别缩放了 32、16 和 8 倍。
参考资源:
https://yunyang1994.github.io/posts/YOLOv3/#more