https://github.com/GuodongQi/yolo3_tensorflow
import tensorflow as tf
#darknet53的卷积等封装
def convolutional(input_data, filters_shape, trainable, name, downsample=False, activate=True, bn=True):
with tf.variable_scope(name):
if downsample:
pad_h, pad_w = (filters_shape[0] - 2) // 2 + 1, (filters_shape[1] - 2) // 2 + 1
paddings = tf.constant([[0, 0], [pad_h, pad_h], [pad_w, pad_w], [0, 0]])
input_data = tf.pad(input_data, paddings, 'CONSTANT')
strides = (1, 2, 2, 1)
padding = 'VALID'
else:
strides = (1, 1, 1, 1)
padding = "SAME"
#讨论initializer对卷积的影响 0513
weight = tf.get_variable(name='weight', dtype=tf.float16, trainable=True,shape=filters_shape,
# initializer=tf.random_normal_initializer(stddev=0.01)) #run ok
initializer=tf.truncated_normal_initializer(stddev=0.01)) #run ok
# initializer=tf.contrib.layers.xavier_initializer()) #not ok
# initializer=tf.glorot_normal_initializer()) #not ok
# initializer=tf.keras.initializers.he_normal()) #not ok
# initializer=tf.random_uniform_initializer())#not ok
conv = tf.nn.conv2d(input=input_data, filter=weight, strides=strides, padding=padding)
if bn:
conv = tf.layers.batch_normalization(conv, beta_initializer=tf.zeros_initializer(),
gamma_initializer=tf.ones_initializer(),
moving_mean_initializer=tf.zeros_initializer(),
moving_variance_initializer=tf.ones_initializer(), training=trainable)
else:
bias = tf.get_variable(name='bias', shape=filters_shape[-1], trainable=True,
dtype=tf.float16, initializer=tf.constant_initializer(0.0))
conv = tf.nn.bias_add(conv, bias)
if activate == True: conv = tf.nn.leaky_relu(conv, alpha=0.1)
return conv
def residual_block(input_data, input_channel, filter_num1, filter_num2, trainable, name):
short_cut = input_data
with tf.variable_scope(name):
input_data = convolutional(input_data, filters_shape=(1, 1, input_channel, filter_num1),
trainable=trainable, name='conv1')
input_data = convolutional(input_data, filters_shape=(3, 3, filter_num1, filter_num2),
trainable=trainable, name='conv2')
residual_output = input_data + short_cut
return residual_output
def route(name, previous_output, current_output):
with tf.variable_scope(name):
output = tf.concat([current_output, previous_output], axis=-1)
return output
def upsample(input_data, name, method="deconv"):
assert method in ["resize", "deconv"]
if method == "resize":
with tf.variable_scope(name):
input_shape = tf.shape(input_data)
output = tf.image.resize_nearest_neighbor(input_data, (input_shape[1] * 2, input_shape[2] * 2))
if method == "deconv":
# replace resize_nearest_neighbor with conv2d_transpose To support TensorRT optimization
numm_filter = input_data.shape.as_list()[-1]
output = tf.layers.conv2d_transpose(input_data, numm_filter, kernel_size=2, padding='same',
strides=(2,2), kernel_initializer=tf.random_normal_initializer())
return output
#mobilenet_v2的卷积等封装
xavier_initializer = tf.initializers.glorot_uniform(dtype=tf.float16)
leaky_alpha = 0.1
def conv_block(x, filters, stride, out_channel,is_training, name='', relu=True):
"""
:param x: input :nhwc
:param filters: list [f_w, f_h]
:param stride: list int
:param out_channel: int, out_channel
:param net_type: cnn mobilenet
:param is_training: used in BN
:param name: str
:param relu: boolean
:return: depwise and pointwise out
"""
with tf.name_scope('' + name):
in_channel = x.shape[3].value
tmp_channel = out_channel * 3 #中间的临时channel
with tf.name_scope('expand_pointwise'):
pointwise_weight = tf.Variable(xavier_initializer([1, 1, in_channel, tmp_channel]))
x = tf.nn.conv2d(x, pointwise_weight, [1, 1, 1, 1], 'SAME')
x = tf.layers.batch_normalization(x, training=is_training)
x = tf.nn.relu6(x)
with tf.name_scope('depthwise'):
depthwise_weight = tf.Variable(xavier_initializer([filters[0], filters[1], tmp_channel, 1]))
x = tf.nn.depthwise_conv2d(x, depthwise_weight, [1, stride[0], stride[1], 1], 'SAME')
x = tf.layers.batch_normalization(x, training=is_training)
x = tf.nn.relu6(x)
with tf.name_scope('project_pointwise'):
pointwise_weight = tf.Variable(xavier_initializer([1, 1, tmp_channel, out_channel]))
x = tf.nn.conv2d(x, pointwise_weight, [1, 1, 1, 1], 'SAME')
if relu:
x = tf.layers.batch_normalization(x, training=is_training)
else:
bias = tf.Variable(tf.zeros(shape=out_channel),dtype=tf.float16)
x += bias
return x
def residual(x,is_training, out_channel=1, expand_time=1, stride=1,name=''):
shortcut = x
in_channel = x.shape[3].value
tmp_channel = in_channel * expand_time
with tf.name_scope(name):
with tf.name_scope('expand_pointwise'):#点卷积 拓展,生成一个高维信息域 参考《深度可分离卷积文档》
pointwise_weight = tf.Variable(xavier_initializer([1, 1, in_channel, tmp_channel]))
x = tf.nn.conv2d(x, pointwise_weight, [1, 1, 1, 1], 'SAME')
x = tf.layers.batch_normalization(x, training=is_training)
x = tf.nn.relu6(x)
with tf.name_scope('depthwise'):#深度卷积
depthwise_weight = tf.Variable(xavier_initializer([3, 3, tmp_channel, 1]))
x = tf.nn.depthwise_conv2d(x, depthwise_weight, [1, stride, stride, 1], 'SAME')
x = tf.layers.batch_normalization(x, training=is_training)
x = tf.nn.relu6(x)
with tf.name_scope('project_pointwise'):#点卷积
pointwise_weight = tf.Variable(xavier_initializer([1, 1, tmp_channel, out_channel]))
x = tf.nn.conv2d(x, pointwise_weight, [1, 1, 1, 1], 'SAME')
x = tf.layers.batch_normalization(x, training=is_training)
x += shortcut
return x
def mobilenet_v2_body(x,is_training): # 特征检测网络
"""
:param x:
:param is_training:
:return:
"""
with tf.variable_scope('mobilenet_v2'):
# x为 416×416 图像 标准的mobilnet v2 输入为 224 ×224有一定差异
x = conv_block(x, [3, 3],[2, 2],32,is_training=is_training) # conv2d正常卷积,输出208×208×32通道
x = conv_block(x, [3, 3], [2, 2], 16, is_training=is_training) # 残差块卷积,输出104×104×16 下采样
x = conv_block(x, [3, 3], [1, 1], 24, is_training=is_training) # 残差块卷积,输出104×104×24
x = residual(x, is_training, 24, 1) # 残差块卷积,输出104×104×24
x = conv_block(x, [3, 3], [2, 2], 32, is_training=is_training) # 残差块卷积,输出52×52×32 下采样
for i in range(2): # 残差块卷积 输出 52×52×32
x = residual(x, is_training, 32, 1)
route2 = x #[52,52,32]
x = conv_block(x, [3, 3], [2, 2], 64,is_training=is_training) # 残差块卷积,输出26×26×64 下采样
for i in range(3): # 残差块卷积 输出 26×26×64
x = residual(x, is_training, 64, 6)
x = conv_block(x, [3, 3], [1, 1], 96, is_training=is_training) # 残差块卷积,输出26×26×96 更改输出通道
for i in range(2): # 残差块卷积 输出 26×26×64
x = residual(x, is_training, 96, 6)
route1 = x #[26,26,64]
# down sample
x = conv_block(x, [3, 3], [2, 2], 160, is_training=is_training) # 残差块卷积,输出13×13×160 下采样
for i in range(2):
x = residual(x, is_training, 160, 1)
x = conv_block(x, [3, 3], [1, 1], 320, is_training=is_training) # 残差块卷积,输出13×13×320 更改输出通道
return x, route1, route2
def darknet_depthwise(input_data,training): # 特征检测网络
"""
:param x:
:param is_training:
:return:
"""
with tf.variable_scope('darknet_depthwise'):
input_data = conv_block(input_data,filters=[3, 3],stride=[1, 1],out_channel=32, is_training=training, name='conv0')
input_data = conv_block(input_data,filters=[3, 3],stride=[2, 2],out_channel=64, is_training=training, name='conv1')
for i in range(1):
input_data = residual(input_data,is_training=training,out_channel=64,name='residual%d' % (i + 0))
#[?,208,208,64]
input_data = conv_block(input_data, filters=[3, 3],stride=[2,2],out_channel=128, is_training=training, name='conv4')
#[?,104,104,128]
for i in range(2):
input_data = residual(input_data,is_training=training,out_channel=128,name='residual%d' % (i + 1))
# [?,104,104,128]
input_data = conv_block(input_data, filters=[3, 3], stride=[2, 2], out_channel=256, is_training=training,name='conv9')
# [?,52,52,256]
for i in range(8):
input_data = residual(input_data, is_training=training, out_channel=256, name='residual%d' % (i + 3))
input_data = conv_block(input_data, filters=[3, 3], stride=[2, 2], out_channel=512, is_training=training,name='conv26')
# [?,26,26,512]
for i in range(8):
input_data = residual(input_data, is_training=training, out_channel=512, name='residual%d' % (i + 11))
input_data = conv_block(input_data, filters=[3, 3], stride=[2, 2], out_channel=1024, is_training=training,name='conv43')
# [?,13,13,512]
for i in range(4):
input_data = residual(input_data, is_training=training, out_channel=1024, name='residual%d' % (i + 19))
return input_data