yolov3相关模块的解析与实现(二)
三、上采样函数
作用:用于将特征图扩展到想要的尺寸大小,和其他特征叠加到一起使用。
上采样的方法为近邻差值法
上采样函数的实现
# 定义上采样函数
def _upsample(inputs, out_shape):
'''
:param inputs: 输入 类型tensor 形状 [batch, height_in, width_in, channels].
:param out_shape: 调整后的tensor的形状 类型tensor out_shape = [batch, new_height, new_width,channels]
:return: 返回一个 调整大小后的tensor
'''
# 由于上采样的填充方式不同,tf.image.resize_bilinear会对结果影响很大
# 以近邻插值法来修改输入tensor(图片)的大小
inputs = tf.image.resize_nearest_neighbor(inputs, (out_shape[1], out_shape[2]))
# 返回一个和形参inputs一样的新的tensor。实际是为了将tensor利用tf.identity()来转化为op。因为只有op才会受操作域约束
inputs = tf.identity(inputs, name='upsampled')
return inputs
四、Yolo前向传播函数
1、yolo前向传播的网络结构
2、yolo前向传播的网络结构的实现
# 定义yolo函数
def yolo_v3(inputs, num_classes, is_training=False, data_format='NHWC', reuse=False):
'''
:param inputs: 输入 类型tensor 形状 [batch, height_in, width_in, channels].
:param num_classes: 分类个数 类型int
:param is_training:是否是训练状态 类型bool
:param data_format: 数据格式
:param reuse: 变量是否重用
:return:
'''
# 判断data_format 是否等于'NHWC' 是则继续执行 否则报错
assert data_format == 'NHWC'
img_size = inputs.get_shape().as_list()[1:3] # 获得输入图片大小
inputs = inputs / 255 # 输入图片归一化
# 用字典定义批量归一化参数
batch_norm_params = {
'decay': _BATCH_NORM_DECAY,
'epsilon': _BATCH_NORM_EPSILON,
'scale': True,
'is_training': is_training,
'fused': None,
}
# 定义yolo网络.
# 定义超参数管理作用域 在此作用域下 指定的op操作函数的形参默认赋值
# 即:在arg_scope中定义一个或多个操作的许多默认参数,这些参数将会在这些操作中传递下去。
with slim.arg_scope([slim.conv2d, slim.batch_norm], data_format=data_format, reuse=reuse):
with slim.arg_scope([slim.conv2d], normalizer_fn=slim.batch_norm, normalizer_params=batch_norm_params,
biases_initializer=None, activation_fn=lambda x: tf.nn.leaky_relu(x, alpha=_LEAKY_RELU)):
# 定义名称为darknet-53的变量作用域,darknet53函数返回三个尺度的特征图
with tf.variable_scope('darknet-53'):
route_1, route_2, inputs = darknet53(inputs)
# 定义名称为yolo-v3的变量作用域
with tf.variable_scope('yolo-v3'):
# ===============================================================================尺寸为13*13的特征图的处理
# 13*13的特征图经过_yolo_block块处理,得到两个特征图 一个inputs 一个route
route, inputs = _yolo_block(inputs, 512) # (-1, 13, 13, 1024)
# 使用候选框参数来辅助识别
# inputs再通过yolo检测快,获得基于13*13的特征图的预测值
detect_1 = _detection_layer(inputs, num_classes, _ANCHORS[6:9], img_size, data_format)
# 将基于13*13的特征图的预测值转化成一个op
detect_1 = tf.identity(detect_1, name='detect_1')
# 再将route进行卷积操作,目的是修改通道数,然后让后将route变成和尺度26*26的特征图一样大小的特征图并和darknet53输出的尺度为26*26的特征图组合到了一起
inputs = slim.conv2d(route, 256, 1, stride=1, padding='SAME') # 正常卷积
upsample_size = route_2.get_shape().as_list()
inputs = _upsample(inputs, upsample_size)
inputs = tf.concat([inputs, route_2], axis=3)
# ================================================================================尺寸为26*26的特征图的处理
# 将组合的特征图输入_yolo_block块处理,得到两个特征图 一个inputs 一个route
route, inputs = _yolo_block(inputs, 256) # (-1, 26, 26, 512)
# inputs再通过yolo检测快,获得基于26*26的特征图的预测值
detect_2 = _detection_layer(inputs, num_classes, _ANCHORS[3:6], img_size, data_format)
# 将基于darknet53输出的尺度为26*26的特征图和darknet53输出的_yolo_block块处理53*53的特征图经过下采样组合的特征图得到的预测值转化成一个op
detect_2 = tf.identity(detect_2, name='detect_2')
# 再将route进行卷积操作,目的是修改通道数,然后将route变成和尺度13*13的特征图一样大小的特征图并和darknet53输出的尺度为13*13的特征图组合到了一起
inputs = slim.conv2d(route, 128, 1, stride=1, padding='SAME') # 正常卷积
upsample_size = route_1.get_shape().as_list()
inputs = _upsample(inputs, upsample_size)
inputs = tf.concat([inputs, route_1], axis=3)
# ================================================================================尺寸为52*52的特征图的处理
# inputs再通过yolo检测快,获得基于52*52的特征图的预测值
_, inputs = _yolo_block(inputs, 128) # (-1, 52, 52, 256)
detect_3 = _detection_layer(inputs, num_classes, _ANCHORS[0:3], img_size, data_format)
detect_3 = tf.identity(detect_3, name='detect_3')
# 将三个尺度的特征图的预测值组合到一起
detections = tf.concat([detect_1, detect_2, detect_3], axis=1)
detections = tf.identity(detections, name='detections')
return detections # 返回了3个尺度。每个尺度里又包含3个结果(-1, 10647( 507 +2028 + 8112), 5+c)