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

1 - 引言

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

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

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

经过人们不断的尝试,诞生了许多有有着里程碑式意义的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模型如下图所示:
机器学习笔记(十三):TensorFlow实战五(经典卷积神经网络: LeNet -5 )_第2张图片

下面我们来详细介绍一个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.

你可能感兴趣的:(机器学习笔记)