欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tensorflow实战:VGGNet16原理及实现(多注释)

程序员文章站 2024-03-14 09:43:46
...

参考《Tensorflow实战》黄文坚,并添加了自己的理解。欢迎提问!!

下图为VGG结构:

                            Tensorflow实战:VGGNet16原理及实现(多注释)

下表为VGGNet各级别网络结构图:

                                                   Tensorflow实战:VGGNet16原理及实现(多注释)

 

下图为本文代码组织结构图:

  Tensorflow实战:VGGNet16原理及实现(多注释)                                             Tensorflow实战:VGGNet16原理及实现(多注释)                                                                                     Tensorflow实战:VGGNet16原理及实现(多注释) 

本文使用VGGNet16-D的结构及参数,进行了前向计算和反向计算的测评,代码及详细注释如下:

from datetime import datetime
import math
import time
import tensorflow as tf


'''############################################02《TensorFlow实战》实现VGGNet16-D########################################################'''

batch_size = 16 #一个批次的数据
num_batches = 100   #测试一百个批次的数据


'''卷积层创建函数,并将本层参数存入参数列表
input_op:输入的tensor  name:这一层的名称  kh:kernel height即卷积核的高    kw:kernel width即卷积核的宽
n_out:卷积核数量即输出通道数   dh:步长的高     dw:步长的宽     p:参数列表
'''
def conv_op(input_op, name, kh, kw, n_out, dh, dw, p):
    # 获取输入数据的通道数
    n_in = input_op.get_shape()[-1].value

    with tf.name_scope(name) as scope:
        #创建卷积核,shape的值的意义参见alexNet
        kernel = tf.get_variable(scope+"w", shape=[kh, kw, n_in, n_out], dtype=tf.float32,
                                 initializer=tf.contrib.layers.xavier_initializer_conv2d())
        #卷积操作
        conv = tf.nn.conv2d(input_op, kernel, (1, dh, dw, 1), padding='SAME')
        #初始化bias为0
        bias_init_val = tf.constant(0.0, shape=[n_out], dtype=tf.float32)
        biases = tf.Variable(bias_init_val, trainable=True, name='b')
        #将卷积后结果与biases加起来
        z = tf.nn.bias_add(conv, biases)
        #使用**函数relu进行非线性处理
        activation = tf.nn.relu(z, name=scope)
        #将卷积核和biases加入到参数列表
        p += [kernel, biases]
        # tf.image.resize_images()
        #卷积层输出作为函数结果返回
        return activation

'''全连接层FC创建函数'''
def fc_op(input_op, name, n_out, p):
    #获取input_op的通道数
    n_in = input_op.get_shape()[-1].value

    with tf.name_scope(name) as scope:
        #初始化全连接层权重
        kernel = tf.get_variable(scope+"w", shape=[n_in, n_out], dtype=tf.float32,
                                 initializer=tf.contrib.layers.xavier_initializer())
        #初始化biases为0.1而不为0,避免dead neuron
        biases = tf.Variable(tf.constant(0.1, shape=[n_out], dtype=tf.float32), name='b')
        #Computes Relu(x * weight + biases)
        activation = tf.nn.relu_layer(input_op, kernel, biases, name=scope)
        #将权重和biases加入到参数列表
        p += [kernel, biases]
        #activation作为函数结果返回
        return activation


'''最大池化层创建函数'''
def mpool_op(input_op, name, kh, kw, dh, dw):
    return tf.nn.max_pool(input_op,
                          ksize=[1, kh, kw, 1], #池化窗口大小
                          strides=[1, dh, dw, 1],   #池化步长
                          padding='SAME',
                          name= name)


'''创建VGGNet-16-D的网络结构
input_op为输入数据,keep_prob为控制dropoout比率的一个placeholder
'''
def inference_op(input_op, keep_prob):
    p = []
    '''D-第一段'''
    #第一段卷积网络第一个卷积层,输出尺寸224*224*64,卷积后通道数(厚度)由3变为64
    conv1_1 = conv_op(input_op, name="conv1_1", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p)
    #第一段卷积网络第二个卷积层,输出尺寸224*224*64
    conv1_2 = conv_op(conv1_1, name="conv1_2", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p)
    #第一段卷积网络的最大池化层,经过池化后输出尺寸变为112*112*64
    pool1 = mpool_op(conv1_2, name="pool1", kh=2, kw=2, dw=2, dh=2)

    '''D-第二段'''
    #第二段卷积网络第一个卷积层,输出尺寸112*112*128,卷积后通道数由64变为128
    conv2_1 = conv_op(pool1, name="conv2_1", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
    #第二段卷积网络第二个卷积层,输出尺寸112*112*128
    conv2_2 = conv_op(conv2_1, name="conv2_1", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
    #第二段卷积网络的最大池化层,经过池化后输出尺寸变为56*56*128
    pool2 = mpool_op(conv2_2, name="pool2", kh=2, kw=2, dw=2, dh=2)

    '''D-第三段'''
    #第三段卷积网络第一个卷积层,输出尺寸为56*56*256,卷积后通道数由128变为256
    conv3_1 = conv_op(pool2, name="conv3_1", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
    #第三段卷积网络第二个卷积层,输出尺寸为56*56*256
    conv3_2 = conv_op(conv3_1, name="conv3_2", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
    #第三段卷积网络第三个卷积层,输出尺寸为56*56*256
    conv3_3 = conv_op(conv3_2, name="conv3_3", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
    #第三段卷积网络的最大池化层,池化后输出尺寸变为28*28*256
    pool3 = mpool_op(conv3_3, name="pool3", kh=2, kw=2, dh=2, dw=2)

    '''D-第四段'''
    #第四段卷积网络第一个卷积层,输出尺寸为28*28*512,卷积后通道数由256变为512
    conv4_1 = conv_op(pool3, name="conv4_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
    # 第四段卷积网络第二个卷积层,输出尺寸为28*28*512
    conv4_2 = conv_op(conv4_1, name="conv4_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
    # 第四段卷积网络第三个卷积层,输出尺寸为28*28*512
    conv4_3 = conv_op(conv4_2, name="conv4_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
    #第四段卷积网络的最大池化层,池化后输出尺寸为14*14*512
    pool4 = mpool_op(conv4_3, name="pool4", kh=2, kw=2, dh=2, dw=2)

    '''D-第五段'''
    #第五段卷积网络第一个卷积层,输出尺寸为14*14*512
    conv5_1 = conv_op(pool4, name="conv5_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
    # 第五段卷积网络第二个卷积层,输出尺寸为14*14*512
    conv5_2 = conv_op(conv5_1, name="conv5_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
    # 第五段卷积网络第三个卷积层,输出尺寸为14*14*512
    conv5_3 = conv_op(conv5_2, name="conv5_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
    #第五段卷积网络的最大池化层,池化后尺寸为7*7*512
    pool5 = mpool_op(conv5_3, name="conv5_3", kh=2, kw=2, dh=2, dw=2)

    '''对卷积网络的输出结果进行扁平化,将每个样本化为一个长度为25088的一维向量'''
    shp = pool5.get_shape()
    flattened_shape = shp[1].value * shp[2].value * shp[3].value    #图像的长、宽、厚度相乘,即7*7*512=25088
    # -1表示该样本有多少个是自动计算得出的,得到一个矩阵,准备传入全连接层
    resh1 = tf.reshape(pool5, [-1,flattened_shape], name="resh1")

    '''全连接层,共三个'''
    fc6 = fc_op(resh1, name="fc6", n_out=4096, p=p)
    fc6_drop = tf.nn.dropout(fc6, keep_prob, name="fc6_drop")   #dropout层,keep_prob数据待外部传入

    fc7 = fc_op(fc6_drop, name="fc7", n_out=4096, p=p)
    fc7_drop = tf.nn.dropout(fc7, keep_prob, name="fc7_drop")

    #最后一个全连接层
    fc8 = fc_op(fc7_drop, name="fc8", n_out=1000, p=p)
    softmax = tf.nn.softmax(fc8)    #使用softmax进行处理得到分类输出概率
    predictions = tf.argmax(softmax,1)  #求概率最大的类别

    #返回参数
    return predictions, softmax, fc8, p


'''评测函数'''
def time_tensorflow_run(session, target, feed, info_string):    #target:需要评测的运算算字, info_string:测试的名称
    num_steps_burn_in = 10  #给程序热身,头几轮迭代有显存的加载、cache命中等问题因此可以跳过,我们只考量10轮迭代之后的计算时间
    total_duration = 0.0    #总时间
    total_duration_squared = 0.0    #平方和

    for i in range(num_batches + num_steps_burn_in):
        start_time = time.time()
        _ = session.run(target, feed_dict=feed)
        duration = time.time() - start_time

        if i>= num_steps_burn_in:   #程序热身完成后,记录时间

            if not i % 10:  #每10轮 显示  当前时间,迭代次数(不包括热身),用时
                print('%s: step %d, duration = %.3f' % (datetime.now(), i-num_steps_burn_in, duration))

            # 累加total_duration和total_duration_squared
            total_duration += duration
            total_duration_squared += duration * duration

    # 循环结束后,计算每轮迭代的平均耗时mn和标准差sd,最后将结果显示出来
    mn = total_duration / num_batches
    vr = total_duration_squared / num_batches - mn * mn
    sd = math.sqrt(vr)
    print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' %
          (datetime.now(), info_string, num_batches, mn, sd))

'''评测的主函数,不使用ImageNet数据集来训练,只使用随机图片测试前馈和反馈计算的耗时'''
def run_benchmaek():
    with tf.Graph().as_default():
        image_size = 224
        #利用tf.random_normal()生成随机图片
        images = tf.Variable(tf.random_normal([batch_size,  #每轮迭代的样本数
                                               image_size, image_size,  #图片的size:image_size x image_size
                                               3],  #图片的通道数
                                              dtype=tf.float32,
                                              stddev=1e-1))
        #创建keep_prob的placeholder
        keep_prob = tf.placeholder(tf.float32)
        predictions, softmax, fc8, p = inference_op(images, keep_prob)
        #创建Session并初始化全局参数
        init = tf.global_variables_initializer()
        sess = tf.Session()
        sess.run(init)

        #前向计算测评
        time_tensorflow_run(sess, predictions, {keep_prob:1.0}, "Forward")
        #前向和反向计算测评
        objective = tf.nn.l2_loss(fc8)
        grad = tf.gradients(objective, p)
        time_tensorflow_run(sess, grad, {keep_prob:0.5}, "Forward-backward")

run_benchmaek()

结果如下:

2018-09-01 13:03:13.532583: step 0, duration = 0.157
2018-09-01 13:03:15.089366: step 10, duration = 0.156
2018-09-01 13:03:16.643247: step 20, duration = 0.156
2018-09-01 13:03:18.199118: step 30, duration = 0.156
2018-09-01 13:03:19.756954: step 40, duration = 0.156
2018-09-01 13:03:21.312730: step 50, duration = 0.157
2018-09-01 13:03:22.873557: step 60, duration = 0.157
2018-09-01 13:03:24.432437: step 70, duration = 0.156
2018-09-01 13:03:25.988297: step 80, duration = 0.157
2018-09-01 13:03:27.545107: step 90, duration = 0.157
2018-09-01 13:03:28.943368: Forward across 100 steps, 0.156 +/- 0.001 sec / batch
2018-09-01 13:03:36.774692: step 0, duration = 0.548
2018-09-01 13:03:42.234097: step 10, duration = 0.540
2018-09-01 13:03:47.725417: step 20, duration = 0.567
2018-09-01 13:03:53.208759: step 30, duration = 0.546
2018-09-01 13:03:58.711079: step 40, duration = 0.552
2018-09-01 13:04:04.209350: step 50, duration = 0.560
2018-09-01 13:04:09.662770: step 60, duration = 0.545
2018-09-01 13:04:15.174038: step 70, duration = 0.546
2018-09-01 13:04:20.668350: step 80, duration = 0.550
2018-09-01 13:04:26.160701: step 90, duration = 0.550
2018-09-01 13:04:31.113427: Forward-backward across 100 steps, 0.549 +/- 0.007 sec / batch