《深度学习:主流框架和编程实战》——2.3.4 详细代码解析(3)
2.3.4 详细代码解析(3)
5)resnet.py文件定义了ResNet的网络模型,包括输出层、批次正则化层、卷积层、残差模块函数等。
resnet.py
#以np的形式导入numpy,主要用于利用数组表示向量、矩阵数据结构
import numpy as np
#从hyper_parameters导入所有的包,主要包括各种超参的声明
from hyper_parameters import *
#定义BM_EPSILON为0.001
BN_EPSILON = 0.001
#激活总结函数,参数x为一个张量,加入直方图总结和张量稀疏标量总结
def activation_summary(x):
#张量名tensor_name为 x.op.name
tensor_name = x.op.name
#直方图为张量名加激活函数
tf.summary.histogram(tensor_name + '/activations', x)
#标量为张量名加稀疏度
tf.summary.scalar(tensor_name + '/sparsity', tf.nn.zero_fraction(x))
#创建变量函数,包含参数name、shape、initializar
def create_variables(name, shape, initializer=tf.contrib.layers.xavier_initializer(), is_fc_layer=False):
#如果is_fc_layer为真
if is_fc_layer is True:
#正则表达式regularizer 为张量,权重衰减表达式
regularizer = tf.contrib.layers.l2_regularizer(scale=FLAGS.weight_decay)
else:
#正则表达式regularizer 为张量,权重衰减表达式
regularizer = tf.contrib.layers.l2_regularizer(scale=FLAGS.weight_decay)
#定义新变量new_variables名称、外形、初始值、正则表达式
new_variables = tf.get_variable(name, shape=shape,
initializer=initializer,
regularizer=regularizer)
#返回新变量
return new_variables
#输出层函数,包含参数input_layer,类型为2D张量;参数num_labels,类型为整型
def output_layer(input_layer, num_labels):
#输入维度input_dim为输入层重塑
input_dim = input_layer.get_shape().as_list()[-1]
#全连接权重fc_w创建变量,名称为fc_weights,外形为[输入维度,标签数],
#is_fc_layer为真,初始值为同一单元缩放比例初始值
fc_w = create_variables(name='fc_weights',
shape=[input_dim, num_labels],
is_fc_layer=True,
initializer=tf.uniform_unit_scaling_initializer (factor=1.0))
#创建全连接偏置fc_b变量,初始值为零化初始值
fc_b = create_variables(name='fc_bias', shape=[num_labels], initializer=tf.zeros_initializer())
#全连接函数fc_h为输入层矩阵与全连接权重矩阵相乘加全连接偏置
fc_h = tf.matmul(input_layer, fc_w) + fc_b
#返回全连接输出
return fc_h
#批次正则化层函数,参数input_layer,格式为4D张量;参数dimension,格式为4D张量
#返回值为正则化后的4D张量
def batch_normalization_layer(input_layer, dimension):
#平均值mean,方差variance为输入层、轴[0,1,2]的力矩
mean, variance = tf.nn.moments(input_layer, axes=[0, 1, 2])
#定义变量beta,类型为beta,参数包括维度、类型、初始值
beta = tf.get_variable('beta',
dimension,
tf.float32,
initializer=tf.constant_initializer(0.0, tf.float32))
#定义变量gamma,参数包括维度、类型、初始值
gamma = tf.get_variable('gamma',
dimension,
tf.float32,
initializer=tf.constant_initializer(1.0, tf.float32))
#定义批次正则化层为输入层,参数包括平均值、方差、beta、gamma、BN_EPSILON
bn_layer = tf.nn.batch_normalization(input_layer,
mean,
variance,
beta,
gamma,
BN_EPSILON)
#返回bn_layer
return bn_layer
#卷积块,包括卷积、批次正则化、线性调整单元层,
#包含参数:input_layer,4D张量;filter_shape,列表;stride,整型。
#返回值:Y = Relu(batch_normalize(conv(X))),格式为4D张量
def conv_bn_relu_layer(input_layer, filter_shape, stride):
#输出通道out_channel 为滤波器[-1]
out_channel = filter_shape[-1]
#滤波器filter为创建变量,名称为conv,外形为filter_shape
filter = create_variables(name='conv', shape=filter_shape)
#卷积层conv_layer为输入层、滤波器,步长为[1,,stride,stride,1],边框为SAME
conv_layer = tf.nn.conv2d(input_layer,
filter,
strides=[1, stride, stride, 1],
padding='SAME')
#批次正则化层bn_layer为将卷积层和输出通道批次正则化
bn_layer = batch_normalization_layer(conv_layer, out_channel)
#输出层为relu(bn_layer)
output = tf.nn.relu(bn_layer)
#返回输出
return output
#卷积块,批量卷积。先归一化数据,再使用激活函数,最后添加卷积操作
def bn_relu_conv_layer(input_layer, filter_shape, stride):
#输入通道in_channel 输入层外形列表
in_channel = input_layer.get_shape().as_list()[-1]
#批次正则化层bn_layer为将卷积层和输出通道批次正则化
bn_layer = batch_normalization_layer(input_layer, in_channel)
#线性单元层relu_layer为relu(bn_layer)
relu_layer = tf.nn.relu(bn_layer)
#滤波器filter为创建变量,名称为conv,外形为filter_shape
filter = create_variables(name='conv', shape=filter_shape)
conv_layer = tf.nn.conv2d(relu_layer,
filter,
strides=[1, stride, stride, 1],
padding='SAME')
return conv_layer
#定义残差模块函数,在ResNet中定义残差构件
#参数input_layer,格式为4D张量;参数output_channel,格式为整型
#返回4D张量的残差构件
def residual_block(input_layer, output_channel, first_block=False):
#输入通道input_channel为输入层外形列表[-1]
input_channel = input_layer.get_shape().as_list()[-1]
#如果输入通道*2为输出通道
if input_channel * 2 == output_channel:
#增加维度increase_dim为真
increase_dim = True
#步长为2
stride = 2
#又或者输入通道为输出通道
elif input_channel == output_channel:
#增加维度为假
increase_dim = False
#步长为1
stride = 1
else:
#报错Output and input channel does not match in residual blocks!!!
raise ValueError('Output and input channel does not match in residual blocks!!!')
with tf.variable_scope('conv1_in_block'):
#如果是第一块
if first_block:
#创建滤波器filter,名称为conv,尺寸为[3, 3, input_channel, output_channel]
filter = create_variables(name='conv', shape=[3, 3, input_channel, output_channel])
#卷积层1为输入层、滤波器,步长为[1,1,1,1],补丁为SAME的2D卷积
conv1 = tf.nn.conv2d(input_layer,
filter=filter,
strides=[1, 1, 1, 1],
padding='SAME')
else:
#创建卷积块1
conv1 = bn_relu_conv_layer(input_layer,
[3, 3, input_channel, output_channel],
stride)
with tf.variable_scope('conv2_in_block'):
#创建卷积块2
conv2 = bn_relu_conv_layer(conv1, [3, 3, output_channel, output_channel], 1)
#如果增加维度increase_dim为真
if increase_dim is True:
#池化输入pooled_input为输入层
#池化核大小为[1,2,2,1],步长为[1,2,2,1],允许添加边框
pooled_input = tf.nn.avg_pool(input_layer,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='VALID')
#池化后填充池化后的输出
padded_input = tf.pad(pooled_input,
[[0, 0], [0, 0], [0, 0],
[input_channel // 2,
input_channel // 2]])
else:
#填充输入padded_input为输入层
padded_input = input_layer
#输出output为卷积层2+填充输入
output = conv2 + padded_input
return output
#定义ResNet的主函数=1+2n+2n+2n*1=6n+2
#参数input_tensor_batch,格式为4D张量;参数n,格式为整型
#参数reuse,格式为布尔型,reuse为真,能够重复使用
#验证图与训练图共享的权重,返回ResNet残差构件
def inference(input_tensor_batch, n, reuse):
#层数列表layers
layers = []
with tf.variable_scope('conv0', reuse=reuse):
#卷积层0为卷积、批次正则化、线性调整单元层
conv0 = conv_bn_relu_layer(input_tensor_batch, [3, 3, 3, 16], 1)
#激活总结activation_summary(conv0)
activation_summary(conv0)
#更新层layers.append(conv0)
layers.append(conv0)
#对range进行i次遍历
for i in range(n):
with tf.variable_scope('conv1_%d' %i, reuse=reuse):
#如果i==0
if i == 0:
#卷积层1为层[-1]、大小为16的第一残差构件
conv1 = residual_block(layers[-1], 16, first_block=True)
else:
#卷积层1为层[-1]、大小为16的残差构件
conv1 = residual_block(layers[-1], 16)
#激活总结activation_summary(conv1)
activation_summary(conv1)
#更新层layers.append(conv1)
layers.append(conv1)
for i in range(n):
with tf.variable_scope('conv2_%d' %i, reuse=reuse):
conv2 = residual_block(layers[-1], 32)
#激活总结activation_summary(conv1)
activation_summary(conv2)
#更新层layers.append(conv1)
layers.append(conv2)
for i in range(n):
with tf.variable_scope('conv3_%d' %i, reuse=reuse):
#卷积层3为层[-1]、大小为64的残差构件
conv3 = residual_block(layers[-1], 64)
#更新层layers.append(conv1)
layers.append(conv3)
#断言卷积层3外形列表为[8,8,64]
assert conv3.get_shape().as_list()[1:] == [8, 8, 64]
with tf.variable_scope('fc', reuse=reuse):
#输入通道in_channel为层[-1].通道
in_channel = layers[-1].get_shape().as_list()[-1]
#批处理正则化层bn_layer为正则化层[-1],输入通道
bn_layer = batch_normalization_layer(layers[-1], in_channel)
#线性调整单元层为relu(bn_layer)
relu_layer = tf.nn.relu(bn_layer)
#全局池化global_pool为relu_layer、[1, 2]的平均值
global_pool = tf.reduce_mean(relu_layer, [1, 2])
#断言全局池化外形列表为[64]
assert global_pool.get_shape().as_list()[-1:] == [64]
#输出层output与全局池化有关
output = output_layer(global_pool, 10)
#更新output
layers.append(output)
#返回层[-1]
return layers[-1]
#测试图函数,在tensorboard上运行此函数来看图结构
def test_graph(train_dir='logs'):
#输入张量input_tensor为常量[128, 32, 32, 3],类型为float32
input_tensor = tf.constant(np.ones([128, 32, 32, 3]), dtype=tf.float32)
#结果result为输入张量的推理
result = inference(input_tensor, 2, reuse=False)
#初始值ini为初始所有变量
init = tf.initialize_all_variables()
#创建会话sess
sess = tf.Session()
#运行init
sess.run(init)
#总结编辑summary_writer为将sess.graph写入train_dir
summary_writer = tf.train.SummaryWriter(train_dir, sess.graph)
6)hyper_parameters.py文件存放各种变量的值,这样做的好处是可以很方便地修改超参,大大提高了调试的效率。
hyper_parameters.py
#以tf的形式导入TensorFlow
import TensorFlow as tf
#标志FLAGS
FLAGS = tf.app.flags.FLAGS
#定义字符串标志('version',test_110,定义保存日志和检查点的目录的版本号)
tf.app.flags.DEFINE_string('version','test_110',
'''A version number defining the directory to save logs and checkpoints''')
#定义整型标志(report_freq,391,步骤用于输出屏幕上的错误并编写摘要)
tf.app.flags.DEFINE_integer('report_freq',391,
'''Steps takes to output errors on the screen and write summaries''')
#定义浮点数标志train_ema_decay,训练错误移动平均线显示在tensorboard的衰减因子)
tf.app.flags.DEFINE_float('train_ema_decay',0.95,
'''The decay factor of the train error's moving average shown on tensorboard''')
#定义整型标志(train_steps, 80000,想要训练的总步数)
tf.app.flags.DEFINE_integer('train_steps', 80000, '''Total steps that you want to train''')
#定义布尔型标志(is_full_validation',假,验证/全验证设置或随机批次)
tf.app.flags.DEFINE_boolean('is_full_validation', False,
'''Validation w/ full validation set or a random batch''')
#定义整型标志(train_batch_size,128,训练批次大小)
tf.app.flags.DEFINE_integer('train_batch_size', 128, '''Train batch size''')
#定义整型标志(validation_batch_size,250,验证批次大小
tf.app.flags.DEFINE_integer('validation_batch_size', 250,
'''Validation batch size, better to be a divisor of 10000 for this task''')
#定义整型标志(test_batch_size,125,测试批次大小)
tf.app.flags.DEFINE_integer('test_batch_size', 125, '''Test batch size''')
#定义浮点数标志(init_lr,0.1,初始化学习率)
tf.app.flags.DEFINE_float('init_lr', 0.1, '''Initial learning rate''')
#定义浮点数标志(lr_decay_factor,0.1,每次学习率想要衰减多少)
tf.app.flags.DEFINE_float('lr_decay_factor', 0.1,
'''How much to decay the learning rate each time''')
#定义整型标志(decay_step0,40000,在哪一步衰减权重学习率)
tf.app.flags.DEFINE_integer('decay_step0', 40000,
'''At which step to decay the learning rate''')
#定义整型标志(decay_step1,60000,在哪一步衰减偏置学习率)
tf.app.flags.DEFINE_integer('decay_step1', 60000,
'''At which step to decay the learning rate''')
#定义整型标志(num_residual_blocks,5,残差构件的个数)
tf.app.flags.DEFINE_integer('num_residual_blocks', 5,
'''How many residual blocks do you want''')
#定义浮点数标志(weight_decay,0.0002,L2范式的标量)
tf.app.flags.DEFINE_float('weight_decay', 0.0002, '''scale for l2 regularization''')
#定义整型标志(padding_size,2,在数据变量中在图像每边零补丁的层)
tf.app.flags.DEFINE_integer('padding_size', 2,
'''In data augmentation, layers of zero padding on each side of the image''')
#定义字符串标志(ckpt_path,赋初值cache/logs_repeat20/model.ckpt-100000,恢复检查点目录)
tf.app.flags.DEFINE_string('ckpt_path',
'cache/logs_repeat20/model.ckpt-100000',
'''Checkpoint directory to restore''')
#定义布尔型标志(is_use_ckpt,False,是否载入检查点并继续训练)
tf.app.flags.DEFINE_boolean('is_use_ckpt', False,
'''Whether to load a checkpoint and continue training''')
#定义字符串标志(test_ckpt_path,model_110.ckpt-79999,恢复检查点目录)
tf.app.flags.DEFINE_string('test_ckpt_path', 'model_110.ckpt-79999',
'''Checkpoint directory to restore''')
#训练目录train_dir为logs_加标志版本加"/"
train_dir = 'logs_' + FLAGS.version + '/'
#测试目录testdata
test_dir='testdata/'
- 点赞
- 收藏
- 关注作者
评论(0)