《深度学习:主流框架和编程实战》——2.3.4 详细代码解析(3)

举报
华章计算机 发表于 2019/06/05 16:56:38 2019/06/05
【摘要】 本书摘自《深度学习:主流框架和编程实战》——书中第2章,第2.3.4节,作者是赵涓涓、强彦。

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/'


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。