• 实现CNN图像的识别和训练通过tensorflow框架对cifar10数据集等方法的处理


    VGGNet是牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发的深度卷积神经网络
    VGG探索了卷积神经网络的深度与其性能之间的关系,通过反复堆叠33的小型卷积核和22的最大池化层,VGG成功构筑了16-19层深的卷积神经网络
    VGG取得了2014年比赛分类项目第二名和定位项目第一名。同时,VGG拓展性很强,迁移到其他图片数据上的泛化性非常好。VGG的结构简洁, 整个网络都是使用了同样大小的卷积核尺寸33和池化层22.
    VGG现在也还经常被用来提取图像特征,VGGNet训练后的模型参数在其官网上开源了,可用来在图像分类任务上进行再训练,相当于提供了非常好的 初始化权重

    VGG通过加深层次来提升性能,拥有5段卷积,每一段内有2-3个卷积层,同时每段尾部都会连接一个最大池化层来缩小图片尺寸 每段内的卷积核数量一样,越靠后段的卷积核数量越多,64-128-258-512-512 其中经常出现多个完全一样的33的卷积层堆叠在一起的情况,这其实是非常有用的设计,两个33的卷积层串联相当于一个55的卷积层
    即一个像素会和周边5
    5的像素产生关联,可以说感受野大小是55,而三个33的卷积层串联效果相当于一个77的卷积层 另外,3个串联的卷积层拥有比一个77的卷积层更少的参数量,只有后者的 333/(7*7)= 55%,更重要的是,3个串联的层拥有比1个层 更多的非线性变换,前者可以使用三次ReLU激活函数,而后者只有一次,使得CNN对特征的学习能力更强

    def conv_op(input_op, name, kernel_height, kernel_width, n_out, stride_height, stride_width, param_list):
        # 获取输入的通道数
        n_in = input_op.get_shape()[-1].value
    
        with tf.name_scope(name=name) as scope:
            kernel = tf.get_variable(scope+'w',
                                     shape=[kernel_height, kernel_width, n_in, n_out], dtype=tf.float32,
                                     initializer=tf.contrib.layers.xavier_initializer_conv2d())
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    追本溯源:
    ILSVRC(ImageNet Large Scale Visual Recognition Challenge) ImageNet项目于2007年由斯坦福大学华人教授李飞飞创办,目标是收集大量带有标注信息的图片数据供计算机视觉模型训练 ImageNet拥有1500万张标注过的高清图片,总共拥有22000类,其中约有100万张标注了图片中主要物体的定位边框。
    对于Alexnet网络:

    ImageNet项目最早的灵感来自于人类通过视觉学习世界的方式,如果假定儿童的眼睛是生物照相机,它们平均每200ms就拍照一次眼球转动一次的平均时间200ms,那么3岁大的孩子就已经看过了上亿张真实时间的照片,使用亚马逊的土耳其机器人平台实现标注过程 来自世界上167个国家的5万名工作者帮忙一起筛选、标注

    每年ILSVRC比赛数据集中大概有120万张图片,以及1000类的标注,是ImageNet全部数据的一个子集。比赛采用top-5和top-1分类错误率 作为模型性能的评测指标
    AlexNet比赛分类项目的2012年冠军,top5错误率16.4%,8层神经网络 神经网络模型AlexNet可以算是LeNet的一种更深更宽的版本!AlexNet中包含了几个比较新的技术点,首次在CNN中成功应用了ReLU、Dropout、 LRN等Trick,AlexNet包含了6亿3000万个连接,6000多万个参数!和65万个神经元,拥有5个卷积层,3个全连接层,其中3个卷积层后面 连接了最大池化层,输出层是1000类的softmax层做分类,LRN应用在了第一卷积层和第二卷积层后面,ReLU激活函数应用在8层每一层后面

    Alexnet网络模型性能的评测

    1,运用ReLU,解决Sigmoid在网络层次较深时的梯度弥散
    2,训练Dropout,随机忽略一些神经元,避免过拟合
    3,使用重叠的最大池化,此前CNN普遍平均池化,最大池化避免平均池化的模糊化效果
    4,提出了LRN层,局部神经元活动创建竞争机制,响应比较大的值变得更大,抑制其他反馈小的神经元,增强泛化能力
    5,使用CUDA加速深度卷积网络的训练
    6,数据增强,随机地从256256的原始图像中截取224224大小的区域,以及水平翻转的镜像,相当于增加了【(256-224)^2】*2=2048
    倍的数据量。没有数据增强,紧靠原始的数据量,参数众多的CNN会陷入过拟合中

    代码如下:

    import tensorflow as tf
    from tensorflow.examples.tutorials.mnist import input_data
    
    
    mnist = input_data.read_data_sets('MNIST_data_bak/', one_hot=True)
    sess = tf.InteractiveSession()
    
    
    # 截断的正太分布噪声,标准差设为0.1
    # 同时因为我们使用ReLU,也给偏置项增加一些小的正值0.1用来避免死亡节点(dead neurons)
    def weight_variable(shape):
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)
    
    
    def bias_variable(shape):
        initial = tf.constant(0.1, shape=shape)
        return tf.Variable(initial)
    
    
    # 卷积层和池化层也是接下来要重复使用的,因此也为它们定义创建函数
    
    # tf.nn.conv2d是TensorFlow中的2维卷积函数,参数中x是输入,W是卷积的参数,比如[5, 5, 1, 32]
    # 前面两个数字代表卷积核的尺寸,第三个数字代表有多少个channel,因为我们只有灰度单色,所以是1,如果是彩色的RGB图片,这里是3
    # 最后代表核的数量,也就是这个卷积层会提取多少类的特征
    
    # Strides代表卷积模板移动的步长,都是1代表会不遗漏地划过图片的每一个点!Padding代表边界的处理方式,这里的SAME代表给
    # 边界加上Padding让卷积的输出和输入保持同样SAME的尺寸
    def conv2d(x, W):
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
    
    
    # tf.nn.max_pool是TensorFlow中的最大池化函数,我们这里使用2*2的最大池化,即将2*2的像素块降为1*1的像素
    # 最大池化会保留原始像素块中灰度值最高的那一个像素,即保留最显著的特征,因为希望整体上缩小图片尺寸,因此池化层
    # strides也设为横竖两个方向以2为步长。如果步长还是1,那么我们会得到一个尺寸不变的图片
    def max_pool_2x2(x):
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    
    
    # 因为卷积神经网络会利用到空间结构信息,因此需要将1D的输入向量转为2D的图片结构,即从1*784的形式转为原始的28*28的结构
    # 同时因为只有一个颜色通道,故最终尺寸为[-1, 28, 28, 1],前面的-1代表样本数量不固定,最后的1代表颜色通道数量
    x = tf.placeholder(tf.float32, [None, 784])
    y_ = tf.placeholder(tf.float32, [None, 10])
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    
    # 定义我的第一个卷积层,我们先使用前面写好的函数进行参数初始化,包括weights和bias,这里的[5, 5, 1, 32]代表卷积
    # 核尺寸为5*51个颜色通道,32个不同的卷积核,然后使用conv2d函数进行卷积操作,并加上偏置项,接着再使用ReLU激活函数进行
    # 非线性处理,最后,使用最大池化函数max_pool_2*2对卷积的输出结果进行池化操作
    W_conv1 = weight_variable([5, 5, 1, 32])
    b_conv1 = bias_variable([32])
    h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)
    
    # 第二层和第一个一样,但是卷积核变成了64
    W_conv2 = weight_variable([5, 5, 32, 64])
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)
    
    # 因为前面经历了两次步长为2*2的最大池化,所以边长已经只有1/4了,图片尺寸由28*28变成了7*7
    # 而第二个卷积层的卷积核数量为64,其输出的tensor尺寸即为7*7*64
    # 我们使用tf.reshape函数对第二个卷积层的输出tensor进行变形,将其转成1D的向量
    # 然后连接一个全连接层,隐含节点为1024,并使用ReLU激活函数
    W_fc1 = weight_variable([7 * 7 * 64, 1024])
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
    
    # 防止过拟合,使用Dropout层
    keep_prob = tf.placeholder(tf.float32)
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
    
    # 接 Softmax分类
    W_fc2 = weight_variable([1024, 10])
    b_fc2 = bias_variable([10])
    y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
    
    # 定义损失函数
    cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
                                                  reduction_indices=[1]))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    
    correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
    # 训练
    tf.global_variables_initializer().run()
    for i in range(20000):
        batch = mnist.train.next_batch(50)
        if i % 100 == 0:
            train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1],
                                                      keep_prob: 1.0})
            print("step %d, training accuracy %g" % (i, train_accuracy))
        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
    
    print("test accuracy %g" % accuracy.eval(feed_dict={
        x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0
    }))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    最后,这个CNN模型可以得到的准确率约为99.2%,基本可以满足对手写数字识别准确率的要求
    相比之前的MLP的2%的错误率,CNN的错误率下降了大约60%,这里主要的性能提升都来自于更优秀的网络设计
    即卷积网络对图像特征的提取和抽象能力,依靠卷积核的权值共享,CNN的参数数量并没有爆炸,降低计算量的同时
    也减轻了过拟合,因此整个模型的性能有较大的提升,这里我们只是实现了一个简单的卷积神经网络,没有复杂的Trick
    接下来我们实现复杂一点的卷积网络,MNIST数据集已经不适合用来评测其性能
    我们将使用CIFAR-10数据集进行训练,这也是深度学习可以大幅领先其它模型的一个数据集

    本节使用的数据集是CIFAR-10,这是一个经典的数据集,包含60000张32*32的彩色图像,其中训练集50000张,测试集10000张
    一共标注为10类,每一类图片6000张。10类分别是 airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck

    从头开始:
    载入相关库

    import tensorflow as tf
    import numpy as np
    import math
    import time
    from tutorials.image.cifar10 import cifar10
    from tutorials.image.cifar10 import cifar10_input

    初始化参数

    max_steps = 3000
    batch_size = 128
    # 下载cifar10数据集的默认路径
    data_dir = 'D:/cifar10_data/cifar-10-batches-bin'
    
    • 1
    • 2
    • 3
    • 4

    定义初始化weights的函数,和之前一样依然使用tf.truncated_normal截断的正太分布来初始化权值

    def variable_with_weight_losses(shape, stddev, wl):
        # 定义初始化weights的函数,和之前一样依然使用tf.truncated_normal截断的正太分布来初始化权值
        var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
        if wl is not None:
            # 给weight加一个L2的loss,相当于做了一个L2的正则化处理
            # 在机器学习中,不管是分类还是回归任务,都可能因为特征过多而导致过拟合,一般可以通过减少特征或者惩罚不重要特征的权重来缓解这个问题
            # 但是通常我们并不知道该惩罚哪些特征的权重,而正则化就是帮助我们惩罚特征权重的,即特征的权重也会成为模型的损失函数的一部分
            # 我们使用w1来控制L2 loss的大小
            weight_loss = tf.multiply(tf.nn.l2_loss(var), wl, name='weight_loss')
            # 我们使用tf.add_to_collection把weight loss统一存到一个collection,这个collection名为"losses",它会在后面计算神经网络
            # 总体loss时被用上
            tf.add_to_collection("losses", weight_loss)
        return var
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    下载cifar10类下载数据集,并解压,展开到其默认位置

    
    cifar10.maybe_download_and_extract()
    # 使用cifar10_input类中的distorted_inputs函数产生训练需要使用的数据,包括特征及其对应的label,这里是封装好的tensor,
    # 每次执行都会生成一个batch_size的数量的样本。需要注意的是这里对数据进行了Data Augmentation数据增强
    # 具体实现细节查看函数,其中数据增强操作包括随机水平翻转tf.image.random_flip_left_right()
    # 随机剪切一块24*24大小的图片tf.random_crop,随机设置亮度和对比度,tf.image.random_brightness、tf.image.random_contrast
    # 以及对数据进行标准化,白化 tf.image.per_image_standardization() 减去均值、除以方差,保证数据零均值,方差为1
    images_train, labels_train = cifar10_input.distorted_inputs(
        data_dir=data_dir, batch_size=batch_size
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    生成测试数据

    
    # 生成测试数据,不过这里不需要进行太多处理,不需要对图片进行翻转或修改亮度、对比度,不过需要裁剪图片正中间的24*24大小的区块,
    # 并进行数据标准化操作
    images_test, labels_test = cifar10_input.inputs(eval_data=True, data_dir=data_dir, batch_size=batch_size)
    
    # 因为batch_size在之后定义网络结构时被用到了,所以数据尺寸中的第一个值即样本条数需要被预先设定,而不能像以前那样设置为None
    # 而数据尺寸中的图片尺寸为24*24即是剪裁后的大小,颜色通道数则设为3
    image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
    label_holder = tf.placeholder(tf.int32, [batch_size])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    设置卷积初始参数

    
    # 初始设置第一个卷积层,64个卷积核,卷积核大小是5*53通道
    weight1 = variable_with_weight_losses(shape=[5, 5, 3, 64], stddev=5e-2, wl=0.0)
    kernel1 = tf.nn.conv2d(image_holder, filter=weight1, strides=[1, 1, 1, 1], padding='SAME')
    bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
    conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
    # 使用尺寸3*3步长2*2的最大池化层处理数据,这里最大池化的尺寸和步长不一样,可以增加数据的丰富性
    pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
    norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建第二个卷积层

    # 创建第二个卷积层
    # 上面64个卷积核,即输出64个通道,所以本层卷积核尺寸的第三个维度即输入的通道数也需要调整为64
    weight2 = variable_with_weight_losses(shape=[5, 5, 64, 64], stddev=5e-2, wl=0.0)
    kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
    # 还有这里的bias值全部初始化为0.1,而不是0.最后,调换了最大池化层和LRN层的顺序,先进行LRN层处理,再使用最大池化层
    bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
    conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
    norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
    pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后是全连接层

    
    # 两个卷积层之后,是全连接层
    # 先把第二个卷积层之后的输出结果flatten,使用tf.reshape函数将每个样本都变成一维向量,使用get_shape函数获取数据扁平化之后的长度
    reshape = tf.reshape(pool2, [batch_size, -1])
    dim = reshape.get_shape()[1].value
    # 接着初始化权值,隐含节点384个,正太分布的标准差设为0.04,bias的值也初始化为0.1
    # 注意这里我们希望这个全连接层不要过拟合,因此设了一个非零的weight loss值0.04,让这一层具有L2正则所约束。
    weight3 = variable_with_weight_losses(shape=[dim, 384], stddev=0.04, wl=0.004)
    bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
    # 最后我们依然使用ReLU激活函数进行非线性化
    local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)
    
    # 接下来还是全连接层,只是隐含节点只有一半,其他一样
    weight4 = variable_with_weight_losses(shape=[384, 192], stddev=0.04, wl=0.004)
    bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
    local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    最后一层处理

    # 最后一层,依然先创建一层weight,其正太分布标准差设为一个隐含层节点数的倒数,并且不用L2正则
    # 这里没有用之前的softmax输出最后结果,这里把softmax操作放在了计算loss部分,其实我们不需要对inference的输出进行softmax
    # 处理就可以获得最终分类结果(直接比较inference输出的各类的数值大小即可),计算softmax主要是为了计算loss,因此softmax操作整合到后面合理
    weight5 = variable_with_weight_losses(shape=[192, 10], stddev=1/192.0, wl=0.0)
    bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
    logits = tf.add(tf.matmul(local4, weight5), bias5)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    到这里就完成了整个网络inference的部分,梳理整个网络结构,设计性能良好的CNN是有一定规律可循的,但是想要针对某个问题设计最合适的
    网络结构,是需要大量实际摸索的
    完成模型inference的构建,接下来是计算CNN的loss,这里依然是用cross_entropy,这里我们把softmax的计算和cross_entropy的计算
    合在了一起,即 tf.nn.sparse_softmax_cross_entropy_with_logits()
    这里使用 tf.reduce_mean() 对 cross entropy计算均值,再使用 tf.add_to_collection()把cross entropy的loss添加到整体
    losses的collection中,最后,使用tf.add_n将整体losses的collection集合中的全部loss求和,得到最终的loss,其中包括
    cross entropy loss, 还有后两个全连接层中weight的L2 loss

    定义损失函数

    
    def loss(logits, labels):
        labels = tf.cast(labels, tf.int64)
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
            logits=logits, labels=labels, name='cross_entropy_per_example'
        )
        cross_entropy_mean = tf.reduce_mean(cross_entropy,
                                            name='cross_entropy')
        tf.add_to_collection('losses', cross_entropy_mean)
    
        return tf.add_n(tf.get_collection('losses'), name='total_loss')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    模型评估与结果输出

    num_examples = 10000
    # 先计算一共要多少个batch才能将全部样本评测完
    num_iter = int(math.ceil(num_examples / batch_size))
    true_count = 0
    total_sample_count = num_iter * batch_size
    step = 0
    while step < num_iter:
        image_batch, label_batch = sess.run([images_test, labels_test])
        predictions = sess.run([top_k_op], feed_dict={image_holder: image_batch,
                                                      label_holder: label_batch})
        true_count += np.sum(predictions)
        step += 1
    
    precision = true_count / total_sample_count
    print('precision @ 1 = %.3f' % precision)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    最终,在cifar-10数据集上,通过一个短时间小迭代的训练,可以达到大致73%的准确率,持续增加max_steps,可以期望准确率逐渐增加
    如果max_steps比较大,则推荐使用学习速率衰减decay的SGD进行训练,这样训练过程中能达到的准确率峰值会比较高,大致有86%
    其中L2正则以及LRN层的使用都对模型准确率有提升作用,它们都可以提升模型的泛化能力

    数据增强Data Augmentation在我们的训练中作用很大,它可以给单幅图增加多个副本,提高图片的利用率,防止对某一张图片结构的学习过拟合
    这刚好是利用了图片数据本身的性质,图片的冗余信息量比较大,因此可以制造不同的噪声并让图片依然可以被识别出来。如果神经网络可以克服这些
    噪声并准确识别,那么他的泛化能力必然很好。数据增强大大增加了样本量,而数据量的大小恰恰是深度学习最看重的,深度学习可以在图像识别上领先
    其他算法的一大因素就是它对海量数据的利用效率非常高。其他算法,可能在数据量大到一定程度时,准确率就不再上升了,而深度学习只要提供足够
    多的样本,准确率基本持续提升,所以说它是最适合大数据的算法。
    本文相关的代码:
    部分 代码1
    CNN-VGG

    from datetime import datetime
    import math
    import time
    import tensorflow as tf
    def conv_op(input_op, name, kernel_height, kernel_width, n_out, stride_height, stride_width, param_list):
        # 获取输入的通道数
        n_in = input_op.get_shape()[-1].value
    
        with tf.name_scope(name=name) as scope:
            kernel = tf.get_variable(scope+'w',
                                     shape=[kernel_height, kernel_width, n_in, n_out], dtype=tf.float32,
                                     initializer=tf.contrib.layers.xavier_initializer_conv2d())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    完整代码2
    tensorflow_cnn_mnist.py

    import tensorflow as tf
    from tensorflow.examples.tutorials.mnist import input_data
    
    
    mnist = input_data.read_data_sets('MNIST_data_bak/', one_hot=True)
    sess = tf.InteractiveSession()
    
    
    # 截断的正太分布噪声,标准差设为0.1
    # 同时因为我们使用ReLU,也给偏置项增加一些小的正值0.1用来避免死亡节点(dead neurons)
    def weight_variable(shape):
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)
    
    
    def bias_variable(shape):
        initial = tf.constant(0.1, shape=shape)
        return tf.Variable(initial)
    
    
    # 卷积层和池化层也是接下来要重复使用的,因此也为它们定义创建函数
    
    # tf.nn.conv2d是TensorFlow中的2维卷积函数,参数中x是输入,W是卷积的参数,比如[5, 5, 1, 32]
    # 前面两个数字代表卷积核的尺寸,第三个数字代表有多少个channel,因为我们只有灰度单色,所以是1,如果是彩色的RGB图片,这里是3
    # 最后代表核的数量,也就是这个卷积层会提取多少类的特征
    
    # Strides代表卷积模板移动的步长,都是1代表会不遗漏地划过图片的每一个点!Padding代表边界的处理方式,这里的SAME代表给
    # 边界加上Padding让卷积的输出和输入保持同样SAME的尺寸
    def conv2d(x, W):
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
    
    
    # tf.nn.max_pool是TensorFlow中的最大池化函数,我们这里使用2*2的最大池化,即将2*2的像素块降为1*1的像素
    # 最大池化会保留原始像素块中灰度值最高的那一个像素,即保留最显著的特征,因为希望整体上缩小图片尺寸,因此池化层
    # strides也设为横竖两个方向以2为步长。如果步长还是1,那么我们会得到一个尺寸不变的图片
    def max_pool_2x2(x):
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    
    
    # 因为卷积神经网络会利用到空间结构信息,因此需要将1D的输入向量转为2D的图片结构,即从1*784的形式转为原始的28*28的结构
    # 同时因为只有一个颜色通道,故最终尺寸为[-1, 28, 28, 1],前面的-1代表样本数量不固定,最后的1代表颜色通道数量
    x = tf.placeholder(tf.float32, [None, 784])
    y_ = tf.placeholder(tf.float32, [None, 10])
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    
    # 定义我的第一个卷积层,我们先使用前面写好的函数进行参数初始化,包括weights和bias,这里的[5, 5, 1, 32]代表卷积
    # 核尺寸为5*51个颜色通道,32个不同的卷积核,然后使用conv2d函数进行卷积操作,并加上偏置项,接着再使用ReLU激活函数进行
    # 非线性处理,最后,使用最大池化函数max_pool_2*2对卷积的输出结果进行池化操作
    W_conv1 = weight_variable([5, 5, 1, 32])
    b_conv1 = bias_variable([32])
    h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)
    
    # 第二层和第一个一样,但是卷积核变成了64
    W_conv2 = weight_variable([5, 5, 32, 64])
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)
    
    # 因为前面经历了两次步长为2*2的最大池化,所以边长已经只有1/4了,图片尺寸由28*28变成了7*7
    # 而第二个卷积层的卷积核数量为64,其输出的tensor尺寸即为7*7*64
    # 我们使用tf.reshape函数对第二个卷积层的输出tensor进行变形,将其转成1D的向量
    # 然后连接一个全连接层,隐含节点为1024,并使用ReLU激活函数
    W_fc1 = weight_variable([7 * 7 * 64, 1024])
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
    
    # 防止过拟合,使用Dropout层
    keep_prob = tf.placeholder(tf.float32)
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
    
    # 接 Softmax分类
    W_fc2 = weight_variable([1024, 10])
    b_fc2 = bias_variable([10])
    y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
    
    # 定义损失函数
    cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
                                                  reduction_indices=[1]))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    
    correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
    # 训练
    tf.global_variables_initializer().run()
    for i in range(20000):
        batch = mnist.train.next_batch(50)
        if i % 100 == 0:
            train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1],
                                                      keep_prob: 1.0})
            print("step %d, training accuracy %g" % (i, train_accuracy))
        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
    
    print("test accuracy %g" % accuracy.eval(feed_dict={
        x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0
    }))
    
    # 最后,这个CNN模型可以得到的准确率约为99.2%,基本可以满足对手写数字识别准确率的要求
    # 相比之前的MLP的2%的错误率,CNN的错误率下降了大约60%,这里主要的性能提升都来自于更优秀的网络设计
    # 即卷积网络对图像特征的提取和抽象能力,依靠卷积核的权值共享,CNN的参数数量并没有爆炸,降低计算量的同时
    # 也减轻了过拟合,因此整个模型的性能有较大的提升,这里我们只是实现了一个简单的卷积神经网络,没有复杂的Trick
    # 接下来我们实现复杂一点的卷积网络,MNIST数据集已经不适合用来评测其性能
    # 我们将使用CIFAR-10数据集进行训练,这也是深度学习可以大幅领先其它模型的一个数据集
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    完整代码3

    tensorflow_cnn_cifar10.py

    import tensorflow as tf
    import numpy as np
    import math
    import time
    from tutorials.image.cifar10 import cifar10
    from tutorials.image.cifar10 import cifar10_input
    
    
    # 本节使用的数据集是CIFAR-10,这是一个经典的数据集,包含6000032*32的彩色图像,其中训练集50000张,测试集10000张
    # 一共标注为10类,每一类图片6000张。10类分别是 airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck
    
    # 我们载入一些常用库,比如NumPy和time,并载入TensorFlow Models中自动下载、读取CIFAR-10数据的类
    max_steps = 3000
    batch_size = 128
    # 下载cifar10数据集的默认路径
    data_dir = 'D:/cifar10_data/cifar-10-batches-bin'
    
    
    def variable_with_weight_losses(shape, stddev, wl):
        # 定义初始化weights的函数,和之前一样依然使用tf.truncated_normal截断的正太分布来初始化权值
        var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
        if wl is not None:
            # 给weight加一个L2的loss,相当于做了一个L2的正则化处理
            # 在机器学习中,不管是分类还是回归任务,都可能因为特征过多而导致过拟合,一般可以通过减少特征或者惩罚不重要特征的权重来缓解这个问题
            # 但是通常我们并不知道该惩罚哪些特征的权重,而正则化就是帮助我们惩罚特征权重的,即特征的权重也会成为模型的损失函数的一部分
            # 我们使用w1来控制L2 loss的大小
            weight_loss = tf.multiply(tf.nn.l2_loss(var), wl, name='weight_loss')
            # 我们使用tf.add_to_collection把weight loss统一存到一个collection,这个collection名为"losses",它会在后面计算神经网络
            # 总体loss时被用上
            tf.add_to_collection("losses", weight_loss)
        return var
    
    
    # 下载cifar10类下载数据集,并解压,展开到其默认位置
    cifar10.maybe_download_and_extract()
    # 使用cifar10_input类中的distorted_inputs函数产生训练需要使用的数据,包括特征及其对应的label,这里是封装好的tensor,
    # 每次执行都会生成一个batch_size的数量的样本。需要注意的是这里对数据进行了Data Augmentation数据增强
    # 具体实现细节查看函数,其中数据增强操作包括随机水平翻转tf.image.random_flip_left_right()
    # 随机剪切一块24*24大小的图片tf.random_crop,随机设置亮度和对比度,tf.image.random_brightness、tf.image.random_contrast
    # 以及对数据进行标准化,白化 tf.image.per_image_standardization() 减去均值、除以方差,保证数据零均值,方差为1
    images_train, labels_train = cifar10_input.distorted_inputs(
        data_dir=data_dir, batch_size=batch_size
    )
    
    # 生成测试数据,不过这里不需要进行太多处理,不需要对图片进行翻转或修改亮度、对比度,不过需要裁剪图片正中间的24*24大小的区块,
    # 并进行数据标准化操作
    images_test, labels_test = cifar10_input.inputs(eval_data=True, data_dir=data_dir, batch_size=batch_size)
    
    # 因为batch_size在之后定义网络结构时被用到了,所以数据尺寸中的第一个值即样本条数需要被预先设定,而不能像以前那样设置为None
    # 而数据尺寸中的图片尺寸为24*24即是剪裁后的大小,颜色通道数则设为3
    image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
    label_holder = tf.placeholder(tf.int32, [batch_size])
    
    # 初始设置第一个卷积层,64个卷积核,卷积核大小是5*53通道
    weight1 = variable_with_weight_losses(shape=[5, 5, 3, 64], stddev=5e-2, wl=0.0)
    kernel1 = tf.nn.conv2d(image_holder, filter=weight1, strides=[1, 1, 1, 1], padding='SAME')
    bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
    conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
    # 使用尺寸3*3步长2*2的最大池化层处理数据,这里最大池化的尺寸和步长不一样,可以增加数据的丰富性
    pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
    # 使用LRN对结果进行处理
    # LRN最早见于Alex那篇用CNN参加ImageNet比赛的论文,Alex在论文中解释LRN层模仿了生物神经系统的"侧抑制"机制,
    # 对局部神经元的活动创建竞争环境,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力
    # Alex在ImageNet数据集上的实验表明,使用LRN后CNN在Top1的错误率可以降低1.4%,因此其在经典AlexNet中使用了LRN层
    # LRN对ReLU这种没有上限边界的激活函数会比较有用,因为它会从附近的多个卷积核的响应中挑选比较大的反馈
    # 但不适合Sigmoid这种有固定边界并且能抑制过大值得激活函数
    norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
    
    # 创建第二个卷积层
    # 上面64个卷积核,即输出64个通道,所以本层卷积核尺寸的第三个维度即输入的通道数也需要调整为64
    weight2 = variable_with_weight_losses(shape=[5, 5, 64, 64], stddev=5e-2, wl=0.0)
    kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
    # 还有这里的bias值全部初始化为0.1,而不是0.最后,调换了最大池化层和LRN层的顺序,先进行LRN层处理,再使用最大池化层
    bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
    conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
    norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
    pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
    
    # 两个卷积层之后,是全连接层
    # 先把第二个卷积层之后的输出结果flatten,使用tf.reshape函数将每个样本都变成一维向量,使用get_shape函数获取数据扁平化之后的长度
    reshape = tf.reshape(pool2, [batch_size, -1])
    dim = reshape.get_shape()[1].value
    # 接着初始化权值,隐含节点384个,正太分布的标准差设为0.04,bias的值也初始化为0.1
    # 注意这里我们希望这个全连接层不要过拟合,因此设了一个非零的weight loss值0.04,让这一层具有L2正则所约束。
    weight3 = variable_with_weight_losses(shape=[dim, 384], stddev=0.04, wl=0.004)
    bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
    # 最后我们依然使用ReLU激活函数进行非线性化
    local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)
    
    # 接下来还是全连接层,只是隐含节点只有一半,其他一样
    weight4 = variable_with_weight_losses(shape=[384, 192], stddev=0.04, wl=0.004)
    bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
    local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)
    
    # 最后一层,依然先创建一层weight,其正太分布标准差设为一个隐含层节点数的倒数,并且不用L2正则
    # 这里没有用之前的softmax输出最后结果,这里把softmax操作放在了计算loss部分,其实我们不需要对inference的输出进行softmax
    # 处理就可以获得最终分类结果(直接比较inference输出的各类的数值大小即可),计算softmax主要是为了计算loss,因此softmax操作整合到后面合理
    weight5 = variable_with_weight_losses(shape=[192, 10], stddev=1/192.0, wl=0.0)
    bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
    logits = tf.add(tf.matmul(local4, weight5), bias5)
    
    
    # 到这里就完成了整个网络inference的部分,梳理整个网络结构,设计性能良好的CNN是有一定规律可循的,但是想要针对某个问题设计最合适的
    # 网络结构,是需要大量实际摸索的
    # 完成模型inference的构建,接下来是计算CNN的loss,这里依然是用cross_entropy,这里我们把softmax的计算和cross_entropy的计算
    # 合在了一起,即 tf.nn.sparse_softmax_cross_entropy_with_logits()
    # 这里使用 tf.reduce_mean() 对 cross entropy计算均值,再使用 tf.add_to_collection()把cross entropy的loss添加到整体
    # losses的collection中,最后,使用tf.add_n将整体losses的collection集合中的全部loss求和,得到最终的loss,其中包括
    # cross entropy loss, 还有后两个全连接层中weight的L2 loss
    def loss(logits, labels):
        labels = tf.cast(labels, tf.int64)
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
            logits=logits, labels=labels, name='cross_entropy_per_example'
        )
        cross_entropy_mean = tf.reduce_mean(cross_entropy,
                                            name='cross_entropy')
        tf.add_to_collection('losses', cross_entropy_mean)
    
        return tf.add_n(tf.get_collection('losses'), name='total_loss')
    
    
    loss = loss(logits=logits, labels=label_holder)
    # 优化器依然选择Adam Optimizer, 学习速率0.001
    train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)
    
    # 使用 tf.nn.in_top_k()函数求输出结果中 top k的准确率,默认使用top 1,也就是输出分数最高的那一类的准确率
    top_k_op = tf.nn.in_top_k(logits, label_holder, 1)
    
    sess = tf.InteractiveSession()
    tf.global_variables_initializer().run()
    
    # 前面对图像进行数据增强的操作需要耗费大量CPU时间,因此distorted_inputs使用了16个独立的线程来加速任务,函数内部会产生线程池,
    # 在需要使用时会通过TensorFlow queue进行调度
    # 启动图片数据增强的线程队列,这里一共使用了16个线程来进行加速,如果不启动线程,那么后续inference以及训练的操作都是无法开始的
    tf.train.start_queue_runners()
    
    # 进行训练
    for step in range(max_steps):
        start_time = time.time()
        image_batch, label_batch = sess.run([images_train, labels_train])
        _, loss_value = sess.run([train_op, loss],
                                 feed_dict={image_holder: image_batch, label_holder: label_batch})
        duration = time.time() - start_time
        if step % 10 == 0:
            examples_per_sec = batch_size / duration
            sec_per_batch = float(duration)
    
            format_str = 'step %d, loss = %.2f (%.1f examples/sec; %.3f sec/batch)'
            print(format_str % (step, loss_value, examples_per_sec, sec_per_batch))
    
    
    # 评测模型在测试集上的准确率
    # 我们依然像训练时那样使用固定的batch_size,然后一个batch一个batch输入测试数据
    num_examples = 10000
    # 先计算一共要多少个batch才能将全部样本评测完
    num_iter = int(math.ceil(num_examples / batch_size))
    true_count = 0
    total_sample_count = num_iter * batch_size
    step = 0
    while step < num_iter:
        image_batch, label_batch = sess.run([images_test, labels_test])
        predictions = sess.run([top_k_op], feed_dict={image_holder: image_batch,
                                                      label_holder: label_batch})
        true_count += np.sum(predictions)
        step += 1
    
    precision = true_count / total_sample_count
    print('precision @ 1 = %.3f' % precision)
    
    # 最终,在cifar-10数据集上,通过一个短时间小迭代的训练,可以达到大致73%的准确率,持续增加max_steps,可以期望准确率逐渐增加
    # 如果max_steps比较大,则推荐使用学习速率衰减decay的SGD进行训练,这样训练过程中能达到的准确率峰值会比较高,大致有86%
    # 其中L2正则以及LRN层的使用都对模型准确率有提升作用,它们都可以提升模型的泛化能力
    
    # 数据增强Data Augmentation在我们的训练中作用很大,它可以给单幅图增加多个副本,提高图片的利用率,防止对某一张图片结构的学习过拟合
    # 这刚好是利用了图片数据本身的性质,图片的冗余信息量比较大,因此可以制造不同的噪声并让图片依然可以被识别出来。如果神经网络可以克服这些
    # 噪声并准确识别,那么他的泛化能力必然很好。数据增强大大增加了样本量,而数据量的大小恰恰是深度学习最看重的,深度学习可以在图像识别上领先
    # 其他算法的一大因素就是它对海量数据的利用效率非常高。其他算法,可能在数据量大到一定程度时,准确率就不再上升了,而深度学习只要提供足够
    # 多的样本,准确率基本持续提升,所以说它是最适合大数据的算法
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179

    完整代码5
    tensorflow_cnn_alexnet.py

    from datetime import datetime
    import math
    import time
    import tensorflow as tf
    
    
    # ILSVRC(ImageNet Large Scale Visual Recognition Challenge)
    # ImageNet项目于2007年由斯坦福大学华人教授李飞飞创办,目标是收集大量带有标注信息的图片数据供计算机视觉模型训练
    # ImageNet拥有1500万张标注过的高清图片,总共拥有22000类,其中约有100万张标注了图片中主要物体的定位边框
    # ImageNet项目最早的灵感来自于人类通过视觉学习世界的方式,如果假定儿童的眼睛是生物照相机,它们平均每200ms就拍照一次
    # 眼球转动一次的平均时间200ms,那么3岁大的孩子就已经看过了上亿张真实时间的照片,使用亚马逊的土耳其机器人平台实现标注过程
    # 来自世界上167个国家的5万名工作者帮忙一起筛选、标注
    
    # 每年ILSVRC比赛数据集中大概有120万张图片,以及1000类的标注,是ImageNet全部数据的一个子集。比赛采用top-5和top-1分类错误率
    # 作为模型性能的评测指标
    # AlexNet比赛分类项目的2012年冠军,top5错误率16.4%8层神经网络
    # 神经网络模型AlexNet可以算是LeNet的一种更深更宽的版本!AlexNet中包含了几个比较新的技术点,首次在CNN中成功应用了ReLU、Dropout、
    # LRN等Trick,AlexNet包含了6亿3000万个连接,6000多万个参数!和65万个神经元,拥有5个卷积层,3个全连接层,其中3个卷积层后面
    # 连接了最大池化层,输出层是1000类的softmax层做分类,LRN应用在了第一卷积层和第二卷积层后面,ReLU激活函数应用在8层每一层后面
    
    # 1,运用ReLU,解决Sigmoid在网络层次较深时的梯度弥散
    # 2,训练Dropout,随机忽略一些神经元,避免过拟合
    # 3,使用重叠的最大池化,此前CNN普遍平均池化,最大池化避免平均池化的模糊化效果
    # 4,提出了LRN层,局部神经元活动创建竞争机制,响应比较大的值变得更大,抑制其他反馈小的神经元,增强泛化能力
    # 5,使用CUDA加速深度卷积网络的训练
    # 6,数据增强,随机地从256*256的原始图像中截取224*224大小的区域,以及水平翻转的镜像,相当于增加了【(256-224^2*2=2048
    # 倍的数据量。没有数据增强,紧靠原始的数据量,参数众多的CNN会陷入过拟合中
    
    batch_size = 32
    num_batchs = 100
    
    
    # 定义一个现实网络每一层结构的函数print_actications,展示每一个卷积层或池化层输入tensor的尺寸
    # 这个函数接受一个tensor作为输入,并显示其名称 t.op.name 和tensor尺寸 t.get_shape.as_list()
    def print_activations(t):
        print(t.op.name, " ", t.get_shape().as_list())
    
    
    def inference(images):
        parameters = []
    
        with tf.name_scope('conv1') as scope:
            kernel = tf.Variable(tf.truncated_normal([11, 11, 3, 64],
                                                     dtype=tf.float32, stddev=1e-1), name='weights')
            conv = tf.nn.conv2d(images, kernel, [1, 4, 4, 1], padding='SAME')
            biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32),
                                 trainable=True, name='biases')
            bias = tf.nn.bias_add(conv, biases)
            conv1 = tf.nn.relu(bias, name=scope)
            print_activations(conv1)
            parameters += [kernel, biases]
    
        # 这里LRN参数基本都是AlexNet论文中的推荐值,不过目前除了AlexNet,其他经典的卷积神经网络基本都放弃了LRN
        # 主要是效果不明显,使用也会使得前馈、反馈的速度整体下降1/3,可以自主选择是否使用LRN
        lrn1 = tf.nn.lrn(conv1, depth_radius=4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn1')
        pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')
        print_activations(pool1)
    
        with tf.name_scope('conv2') as scope:
            kernel = tf.Variable(tf.truncated_normal([5, 5, 64, 192],
                                                     dtype=tf.float32, stddev=1e-1, name='weights'))
            conv = tf.nn.conv2d(pool1, kernel, [1, 1, 1, 1], padding='SAME')
            biases = tf.Variable(tf.constant(0.0, shape=[192],
                                             dtype=tf.float32), trainable=True, name='biases')
            bias = tf.nn.bias_add(conv, biases)
            conv2 = tf.nn.relu(bias, name=scope)
            parameters += [kernel, biases]
            print_activations(conv2)
    
        lrn2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn2')
        pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool2')
        print_activations(pool2)
    
        with tf.name_scope('conv3') as scope:
            kernel = tf.Variable(tf.truncated_normal([3, 3, 192, 384],
                                                     dtype=tf.float32, stddev=1e-1, name='weights'))
            conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')
            biases = tf.Variable(tf.constant(0.0, shape=[384],
                                             dtype=tf.float32), trainable=True, name='biases')
            bias = tf.nn.bias_add(conv, biases)
            conv3 = tf.nn.relu(bias, name=scope)
            parameters += [kernel, biases]
            print_activations(conv3)
    
        with tf.name_scope('conv4') as scope:
            kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256],
                                                     dtype=tf.float32, stddev=1e-1, name='weights'))
            conv = tf.nn.conv2d(conv3, kernel, [1, 1, 1, 1], padding='SAME')
            biases = tf.Variable(tf.constant(0.0, shape=[256],
                                             dtype=tf.float32), trainable=True, name='biases')
            bias = tf.nn.bias_add(conv, biases)
            conv4 = tf.nn.relu(bias, name=scope)
            parameters += [kernel, biases]
            print_activations(conv4)
    
        with tf.name_scope('conv5') as scope:
            kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 256],
                                                     dtype=tf.float32, stddev=1e-1, name='weights'))
            conv = tf.nn.conv2d(conv4, kernel, [1, 1, 1, 1], padding='SAME')
            biases = tf.Variable(tf.constant(0.0, shape=[256],
                                             dtype=tf.float32), trainable=True, name='biases')
            bias = tf.nn.bias_add(conv, biases)
            conv5 = tf.nn.relu(bias, name=scope)
            parameters += [kernel, biases]
            print_activations(conv5)
    
        pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool5')
        print_activations(pool5)
    
        return pool5, parameters
    
    
    # 最后我们返回这个池化层的输出pool5,这样inference函数就完成了,它可以创建AlexNet的卷积部分,在正式使用AlexNet来训练或预测时
    # 还需要添加3个全连接层,隐含节点数分别为409640961000,由于最后3个全连接层的计算量很小,就没有房子计算速度评测中
    # 大家在使用AlexNet时需要自行加上这3个全连接层
    
    # 评估AlexNet每轮计算时间的函数
    def time_tensorflow_run(session, target, info_string):
        num_steps_burn_in = 10  # 预热轮数
        total_duration = 0.0
        total_duration_squared = 0.0
    
        for i in range(num_batchs + num_steps_burn_in):
            start_time = time.time()
            _ = session.run(target)
            duration = time.time() - start_time
            if i >= num_steps_burn_in:
                if not i % 10:
                    print('%s: step %d, duration = %.3f' %
                          (datetime.now(), i - num_steps_burn_in, duration))
                total_duration += duration
                total_duration_squared += duration * duration
    
        mn = total_duration / num_batchs
        vr = total_duration_squared / num_batchs - mn * mn
        sd = math.sqrt(vr)
        print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' %
              (datetime.now(), info_string, num_batchs, mn, sd))
    
    
    def run_benchmark():
        with tf.Graph().as_default():
            image_size = 224
            images = tf.Variable(tf.random_normal([batch_size,
                                                   image_size,
                                                   image_size,
                                                   3],
                                                  dtype=tf.float32,
                                                  stddev=1e-1))
    
            pool5, parameters = inference(images)
    
            init = tf.global_variables_initializer()
            sess = tf.Session()
            sess.run(init)
    
            time_tensorflow_run(sess, pool5, "Forward")
    
            # 模拟训练过程
            objective = tf.nn.l2_loss(pool5)
            grad = tf.gradients(objective, parameters)
            time_tensorflow_run(sess, grad, "Forward-backward")
    
    
    run_benchmark()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
  • 相关阅读:
    软件项目验收测试报告-软件项目验收流程
    生产环境p0级故障:用户钱付了,订单还是显示未支付,用户把我们投诉了!
    Hive数据库日期格式化
    flink on k8s部署
    简易的贪吃蛇小游戏(以后或许会更新)C++/C语言
    6.S081 附加Lab4 从源代码看进程退出——exit,wait,kill
    数据结构:AVL树的旋转(高度平衡树)
    华为云优惠券介绍、领取入口及使用教程
    ubuntu 18.04 server源码编译安装freeswitch 1.10.7支持音视频通话、收发短信——筑梦之路
    获取dom元素
  • 原文地址:https://blog.csdn.net/dongbao520/article/details/125457704