您的位置:首页 > 其它

TensorFlow实战系列13--LeNet-5 模型

2018-02-06 15:11 281 查看
 LeNet-5 模 型 是 Yann LeCun 教 授 于 1998 年 在 论 文 Gradient-basedlearning applied to document recognition 中提出的,它是第一个成功应用于数字识别问题的卷积神经网络。在 MNIST 数据集上,LeNet-5
模型可以达到大约 99.2% 的正确率。LeNet-5 模型总共有 7 层,图 7 展示了 LeNet-5模型的架构。



 在 下 面 的 篇 幅 中 将 详 细 介 绍 LeNet-5 模 型 每 一 层 的 结 构。 论 文GradientBased Learning Applied to Document Recognition 提 出 的 LeNet-5模型中,卷积层和池化层的实现与上文中介绍的
TensorFlow 的实现有细微的区别,这里不过多的讨论具体细节,而是着重介绍模型的整体框架。

第一层,卷积层
 这一层的输入就是原始的图像像素,LeNet-5 模型接受的输入层大小为 32×32×1。第一个卷积层过滤器的尺寸为 5×5,深度为 6,不使用全0 填充,步长为 1。因为没有使用全 0 填充,所以这一层的输出的尺寸为32-5+1=28,深度为 6。这一个卷积层总共有 5×5×1×6+6=156
个参数,其中 6 个为偏置项参数。因为下一层的节点矩阵有 28×28×6=4704 个节点,每个节点和 5×5=25 个当前层节点相连,所以本层卷积层总共有 4704×(25+1)=122304 个连接。

第二层,池化层
 这一层的输入为第一层的输出,是一个 28×28×6 的节点矩阵。本层采用的过滤器大小为 2×2,长和宽的步长均为 2,所以本层的输出矩阵大小为 14×14×6。原始的 LeNet-5 模型中使用的过滤器和本文中介绍的有些细微差别,这里不做具体介绍。

第三层,卷积层

 本 层 的 输 入 矩 阵 大 小 为 14×14×6, 使 用 的 过 滤 器 大 小 为 5×5,深度为 16。本层不使用全 0 填充,步长为 1。本层的输出矩阵大小为10×10×16。按照标准的卷积层,本层应该有 5×5×6×16+16=2416 个参数,10×10×16×(25+1)=41600
个连接。

第四层,池化层
 本层的输入矩阵大小为 10×10×16,采用的过滤器大小为 2×2,步长为 2。本层的输出矩阵大小为 5×5×16。

第五层,全连接层本层的输入矩阵大小为 5×5×16,在 LeNet-5 模型的论文中将这一层称为卷积层,但是因为过滤器的大小就是 5×5,所以和全连接层没有区别,在之后的 TensorFlow 程序实现中也会将这一层看成全连接层。如果将 5×5×16 矩阵中的节点拉成一个向量,那么这一层和前面介绍的全连接层输入就一样了。本层的输出节点个数为 120,总共有5×5×16×120+120=48120 个参数。

第六层,全连接层

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

第七层,全连接层

本层的输入节点个数为 84 个,输出节点个数为 10 个,总共参数为84×10+10=850 个。
 上 面 介 绍 了 LeNet-5 模 型 每 一 层 结 构 和 设 置, 下 面 给 出 一 个TensorFlow 的程序来实现一个类似 LeNet-5 模型的卷积神经网络来解决MNIST 数字识别问题。通过 TensorFlow 训练卷积神经网络的过程和前面介绍的训练全连接神经网络是完全一样的。损失函数的计算、反向传播过程的实现都可以复用上一篇中给出的
mnist_train.py 程序。唯一的区别在于因为卷积神经网络的输入层为一个三维矩阵,所以需要调整一下入数据的格式:

# 调整输入数据placeholder的格式,输入为一个四维矩阵。
x = tf.placeholder(tf.float32, [
  BATCH_SIZE,
  # 第一维表示一个batch中样例的个数。
  mnist_inference.IMAGE_SIZE,
  # 第二维和第三维表示图片的尺寸。
  mnist_inference.IMAGE_SIZE,
  mnist_inference.NUM_CHANNELS],
  name='x-input')
  # 第四维表示图片的深度,对于RBG格
  #式的图片,深度为5。
# 类似地将输入的训练数据格式调整为一个四维矩阵,并将这个调整后的数据传入sess.run过程。
reshaped_xs = np.reshape(xs, (BATCH_SIZE,
   mnist_inference.IMAGE_SIZE,
   mnist_inference.IMAGE_SIZE,
   mnist_inference.NUM_CHANNELS))

 在调整完输入格式之后,只需要在程序 mnist_inference.py 中实现类似 LeNet-5 模型结构的前向传播过程即可。下面给出了修改后的 mnist_infernece.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):

# 声明第一层卷积层的变量并实现前向传播过程。这个过程和6.3.1小节中介绍的一致。
# 通过使用不同的命名空间来隔离不同层的变量,这可以让每一层中的变量命名只需要
# 考虑在当前层的作用,而不需要担心重名的问题。和标准LeNet-5模型不大一样,这里
# 定义的卷积层输入为28×28×1的原始MNIST图片像素。因为卷积层中使用了全0填充,
# 所以输出为28×28×32的矩阵。
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))
# 使用边长为5,深度为32的过滤器,过滤器移动的步长为1,且使用全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))
# 实现第二层池化层的前向传播过程。这里选用最大池化层,池化层过滤器的边长为2,
# 使用全0填充且移动的步长为2。这一层的输入是上一层的输出,也就是28×28×32
# 的矩阵。输出为14×14×32的矩阵。
with tf.name_scope('layer2-pool1'):
pool1 = tf.nn.max_pool(
relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 声明第三层卷积层的变量并实现前向传播过程。这一层的输入为14×14×32的矩阵。
# 输出为14×14×64的矩阵。
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))
# 使用边长为5,深度为64的过滤器,过滤器移动的步长为1,且使用全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))
# 实现第四层池化层的前向传播过程。这一层和第二层的结构是一样的。这一层的输入为
# 14×14×64的矩阵,输出为7×7×64的矩阵。
with tf.name_scope('layer4-pool2'):
pool2 = tf.nn.max_pool(
relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 将第四层池化层的输出转化为第五层全连接层的输入格式。第四层的输出为7×7×64的矩阵,
# 然而第五层全连接层需要的输入格式为向量,所以在这里需要将这个7×7×64的矩阵拉直成一
# 个向量。pool2.get_shape函数可以得到第四层输出矩阵的维度而不需要手工计算。注意
# 因为每一层神经网络的输入输出都为一个batch的矩阵,所以这里得到的维度也包含了一个
# batch中数据的个数。
pool_shape = pool2.get_shape().as_list()
# 计算将矩阵拉直成向量之后的长度,这个长度就是矩阵长宽及深度的乘积。注意这里
# pool_shape[0]为一个batch中数据的个数。
nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
# 通过tf.reshape函数将第四层的输出变成一个batch的向量。
reshaped = tf.reshape(pool2, [pool_shape[0], nodes])
# 声明第五层全连接层的变量并实现前向传播过程。这一层的输入是拉直之后的一组向量,
# 向量长度为3136,输出是一组长度为512的向量。这一层和之前在第五章中介绍的基本
# 一致,唯一的区别就是引入了dropout的概念。dropout在训练时会随机将部分节点的
# 输出改为0。dropout可以避免过拟合问题,从而使得模型在测试数据上的效果更好。
# dropout一般只在全连接层而不是卷积层或者池化层使用。
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.1))
fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
if train: fc1 = tf.nn.dropout(fc1, 0.5)
# 声明第六层全连接层的变量并实现前向传播过程。这一层的输入为一组长度为512的向量,
# 输出为一组长度为10的向量。这一层的输出通过Softmax之后就得到了最后的分类结果。
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.1))
logit = tf.matmul(fc1, fc2_weights) + fc2_biases
# 返回第六层的输出。
return logit

运行修改后的 mnist_train.py 和 mnist_eval.py,可以得到一下测试结果:

$ python mnist_train.py
Extracting /home/tianlei/notebook/MNIST_data/train-images-idx3-ubyte.gz

Extracting /home/tianlei/notebook/MNIST_data/train-labels-idx1-ubyte.gz

Extracting /home/tianlei/notebook/MNIST_data/t10k-images-idx3-ubyte.gz

Extracting /home/tianlei/notebook/MNIST_data/t10k-labels-idx1-ubyte.gz

After 1 training step(s), loss on training batch is 6.45373.

After 1001 training step(s), loss on training batch is 0.824825.

After 2001 training step(s), loss on training batch is 0.646993.

After 3001 training step(s), loss on training batch is 0.759975.

After 4001 training step(s), loss on training batch is 0.68468.

After 5001 training step(s), loss on training batch is 0.630368.

上面的程序可以将 MNIST 正确率达到 ~99.4%。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐