机器学习笔记(十三):TensorFlow实战五(经典卷积神经网络: LeNet -5 )

1 - 引言

之前我们介绍了一下卷积神经网络的基本结构——卷积层和池化层。通过这两个结构我们可以任意的构建各种各样的卷积神经网络模型,不同结构的网络模型也有不同的效果。但是怎样的神经网络模型具有比较好的效果呢?

下图展示了CNN的发展历程。

在这里插入图片描述

经过人们不断的尝试,诞生了许多有有着里程碑式意义的CNN模型。因此我们接下来会学习这些非常经典的卷积神经网络

  • LeNet -5
  • AlexNet
  • VGG
  • Inception
  • ResNet

2 - LeNet-5模型

LeNet-5模型是Yann LeCun教授于1998年在论文Gradient-Based Learning Applied to Document Recognition中提出的,它是第一个成功应用与数字识别问题的卷积神经网络。在MNIST数据集上,LeNet-5模型可以达到大约99.2%的正确率,LeNet-5模型如下图所示:
在这里插入图片描述

下面我们来详细介绍一个LeNet-5模型每一层的结构

2.1 第一层:卷积层

数据维数详细说明:

这一层的输入就是原始的图像像素,LeNet-5模型输入层为32X32X1的图像(只能识别灰度图像而不能识别彩色图像)。第一个卷积层的过滤器尺寸为5X5,深度为6(深度既是通道值),不使用padding,步长为1,因为没有使用padding,这一层的输出尺寸为32-5+1=28,深度为6。这一个卷积层总共有5x5x1x6+6=156个参数,其中6个为偏置项参数。本层卷积层总共有28x28x6x(5x5+1)=122304个连接

总结:

输入图片: 32 ∗ 32 ∗ 1 32*32*1 32321

卷积核大小: 5 ∗ 5 5*5 55

卷积核种类:6

输出featuremap大小: 28 ∗ 28 ( 32 − 5 + 1 ) = 28 28*28 (32-5+1)=28 2828325+1=28

神经元数量: 28 ∗ 28 ∗ 6 28*28*6 28286

可训练参数: ( 5 ∗ 5 + 1 ) ∗ 6 ( 每 个 滤 波 器 5 ∗ 5 = 25 个 u n i t 参 数 和 一 个 b i a s 参 数 , 一 共 6 个 滤 波 器 ) (5*5+1) * 6(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器) 55+1)655=25unitbias6

连接数: ( 5 ∗ 5 + 1 ) ∗ 6 ∗ 28 ∗ 28 = 122304 (5*5+1)*6*28*28=122304 55+162828=122304

2.2 第二层:池化层

这一层的输入为第一层的输出,是一个28x28x6的节点矩阵。本层采用的过滤器大小为2x2,步长为2,所以输出矩阵为14x14x6。

输入: 28 ∗ 28 ∗ 6 28*28*6 28286

采样区域: 2 ∗ 2 2*2 22

采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid

采样种类:6

输出featureMap大小: 14 ∗ 14 ( 28 / 2 ) 14*14(28/2) 141428/2

神经元数量: 14 ∗ 14 ∗ 6 14*14*6 14146

连接数: ( 2 ∗ 2 + 1 ) ∗ 6 ∗ 14 ∗ 14 (2*2+1)*6*14*14 22+161414

S2中每个特征图的大小是C1中特征图大小的1/4。

详细说明:第一次卷积之后紧接着就是池化运算,使用$ 2*2$核 进行池化,于是得到了S2,6个 14 ∗ 14 14*14 1414的 特征图(28/2=14)。S2这个pooling层是对C1中的 2 ∗ 2 2*2 22区域内的像素求和乘以一个权值系数再加上一个偏置,然后将这个结果再做一次映射。同时有5x14x14x6=5880个连接。

2.3 第三层:卷积层

本层的输入矩阵大小为14x14x6,使用的过滤器大小为5x5,深度为16。本层不使用padding,步长为1,所以输出节点为10x10x16。所以有5x5x6x16+16 = 2416个参数。10x10x16x(25+1)=41600个连接。

输入:14x14x6

卷积核大小: 5 ∗ 5 5*5 55

卷积核种类:16

输出featureMap大小: 10 ∗ 10 ( 14 − 5 + 1 ) 10*10 (14-5+1) 1010(145+1)

2.4 第四层:池化层

本层输入矩阵大小为10x10x16,采用的过滤器大小为2x2,步长为2,本层输出矩阵大小为5x5x16

2.5 第五层:卷积层

输入:5x5x16

卷积核大小:5*5

卷积核种类:120

输出featureMap大小:1*1(5-5+1)

可训练参数/连接:120*(1655+1)=48120

虽然LeNet-5模型的论文中将这一层成为卷积层,但是因为过滤器的大小就是5x5,所以和全连接层没有区别。

2.6 F6层-全连接层

本层的输入节点个数为120个,输出节点个数为84个,总共参数为120x84+84 = 10164个。

2.7 Output层-全连接层

本层输入节点为84个,输出节点个数为10个,总共参数为84x10+10 = 850个。

3 - TensorFlow实现LeNet-5模型

** mnist_train_LeNet_5.py文件:**

import os

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import  numpy as np
#加载mnist_inference.py中定义的常量和前向传播的函数。
import mnist_inference_LeNet_5

BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8 # 最开始的学习率
LEARNING_RATE_DECAY = 0.99 # 在指数衰减学习率的过程中用到
REGULARIZATION_RATE = 0.0001 # 描述模型复杂度的正则化项在损失函数中的系数
TRAINING_STEPS = 30000 # 训练轮数,注意,训练一个Batch就是一个step
MOVING_AVERAGE_DECAY = 0.99 # 滑动平均模型的衰减率,最后我会讲解滑动平均模型
#模型保存的路径和中文名
MODEL_SAVE_PATH = "/path/to/model/"
MODEL_NAME = "model.ckpt"

def train(mnist):
    # 定义输入输出placeholder。
    x = tf.placeholder(tf.float32,[
        BATCH_SIZE, #第一维表示一个batch中样例的个数
        mnist_inference_LeNet_5.IMAGE_SIZE, #第二维和第三维表示图片的尺寸
        mnist_inference_LeNet_5.IMAGE_SIZE,
        mnist_inference_LeNet_5.NUM_CHANNELS],  #第四维表示图片的深度,对于RBG格式的图片,深度为5
        name='x-input')

    y_ = tf.placeholder(tf.float32, [None, mnist_inference_LeNet_5.OUTPUT_NODE], name='y-input')

    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    # 直接使用mnist_inference.py中定义的前向传播过程
    y = mnist_inference_LeNet_5.inference(x,train, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # 定义损失函数、学习率、滑动平均操作以及训练过程
    variable_averages = tf.train.ExponentialMovingAverage(
        MOVING_AVERAGE_DECAY, global_step
    )
    variable_averages_op = variable_averages.apply(
        tf.trainable_variables()
    )
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits=y, labels=tf.argmax(y_, 1)
    )
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE,
        LEARNING_RATE_DECAY
    )
    train_step = tf.train.GradientDescentOptimizer(learning_rate)\
                   .minimize(loss, global_step=global_step)
    with tf.control_dependencies([train_step, variable_averages_op]):
        train_op = tf.no_op(name='train')

    # 初始化TensorFlow持久化类
    saver = tf.train.Saver()
    with tf.Session() as sess:
        tf.global_variables_initializer().run()

        # 在训练过程中不再测试模型在验证数据上的表现,验证和测试的过程将会有一个独
        # 立的程序来完成。
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            xs = np.reshape(xs,(
                BATCH_SIZE,
                mnist_inference_LeNet_5.IMAGE_SIZE,
                mnist_inference_LeNet_5.IMAGE_SIZE,
                mnist_inference_LeNet_5.NUM_CHANNELS))

            _, loss_value, step = sess.run([train_op, loss, global_step],
                                           feed_dict={x: xs, y_: ys})
            # 每1000轮保存一次模型
            if i % 1000 == 0:
                # 输出当前的训练情况。这里只输出了模型在当前训练batch上的损失
                # 函数大小。通过损失函数的大小可以大概了解训练的情况。在验证数
                # 据集上正确率的信息会有一个单独的程序来生成
                print("After %d training step(s), loss on training "
                      "batch is %g." % (step, loss_value))
                # 保存当前的模型。注意这里给出了global_step参数,这样可以让每个
                # 被保存的模型的文件名末尾加上训练的轮数,比如“model.ckpt-1000”,
                # 表示训练1000轮之后得到的模型。
                saver.save(
                    sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME),
                    global_step=global_step
                )
# 主程序入口
def main(argv=None):
    # 声明处理MNIST数据集的类,这个类在初始化时会自动下载数据。
    mnist = input_data.read_data_sets("/path/to/MNIST_data", one_hot=True)
    train(mnist)

# TensorFlow提供的一个主程序入口,tf.app.run会调用上面定义的main函数
if __name__ == "__main__":
    tf.app.run()

mnist_inference_LeNet_5.py文件:

# _*_ coding: utf-8 _*_
import tensorflow as tf

# 配置神经网络的参数
INPUT_NODE = 784
OUTPUT_NODE = 10

IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10

# 第一个卷积层的尺寸和深度
CONV1_DEEP = 32
CONV1_SIZE = 5
# 第二个卷积层的尺寸和深度
CONV2_DEEP = 64
CONV2_SIZE = 5
# 全连接层的节点个数
FC_SIZE = 512

# 定义卷积神经网络的前向传播过程。这里添加了一个新的参数train,用于区别训练过程和测试过程。在这个程序中将用到dropout方法
# dropout可以进一步提升模型可靠性并防止过拟合(dropout过程只在训练时使用)
def inference(input_tensor, train, regularizer):
    with tf.variable_scope('layer1-conv1'):
        conv1_weights = tf.get_variable('weight', [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
                                        initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv1_biases = tf.get_variable('bias', [CONV1_DEEP],
                                       initializer=tf.constant_initializer(0.0))
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))

    with tf.name_scope('layer2-pool1'):
        pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    with tf.variable_scope('layer3-conv2'):
        conv2_weights = tf.get_variable('weight', [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],
                                        initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable('bias', [CONV2_DEEP],
                                       initializer=tf.constant_initializer(0.0))
        conv2 = tf.nn.conv2d(pool1,conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))

    with tf.name_scope('layer4-pool2'):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    pool2_shape = pool2.get_shape().as_list()
    nodes = pool2_shape[1] * pool2_shape[2] * pool2_shape[3]

    reshaped = tf.reshape(pool2, [pool2_shape[0], nodes])

    with tf.variable_scope('layer5-fc1'):
        fc1_weights = tf.get_variable('weight', [nodes, FC_SIZE],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc1_weights))
        fc1_biases = tf.get_variable('bias', [FC_SIZE],
                                     initializer=tf.constant_initializer(0.0))
        fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
        if train:
            fc1 = tf.nn.dropout(fc1, 0.5)

    with tf.variable_scope('layer6-fc2'):
        fc2_weights = tf.get_variable('weight', [FC_SIZE, NUM_LABELS],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc2_weights))
        fc2_biases = tf.get_variable('bias', [NUM_LABELS],
                                     initializer=tf.constant_initializer(0.0))
        logit = tf.matmul(fc1, fc2_weights) + fc2_biases

    return logit

After 1 training step(s), loss on training batch is 3.2112.
After 1001 training step(s), loss on training batch is 0.231712.
After 2001 training step(s), loss on training batch is 0.182711.
·
·
·
After 27001 training step(s), loss on training batch is 0.0336458.
After 28001 training step(s), loss on training batch is 0.036755.
After 29001 training step(s), loss on training batch is 0.0390648.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值