MNIST卷积神经网络代码实现(三)
0. 引言
前两篇博客总结了感知机和全连接网络实现MNIST手写数字的识别,本篇博客对卷积神经网络的代码实现进行总结。卷积神经网络(CNN)较全连接神经网络而言,其优势在于权值共享和抗形变性,重要的步骤在于卷积和池化两个操作的应用,较全连接神经网络而言,这两种操作能极大的减少网络参数,降低网络的复杂性。
注:以下代码有不清楚的请看博主前面博客,由于相似的太多,故不做累述,敬请见谅!
1. 构建模型之前的准备(导入包)
导入相关包并创建默认的session,进行代码的运行
from tensorflow.examples.tutorials.mnist import input_data // 加载TensorFlow提供的mnist数据集
import tensorflow as tf // 导入TensorFlow模块
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
sess = tf.InteractiveSession()
2. 构建模型
2.1 定义反复用到的操作
// weight_variable()方法用于初始化权重,以便之后调用,这里用截断正太分布进行初始化,随机加入噪声。
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
// bias_variable()用于初始化偏置,初始化为常量0.1
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
// 定义卷积操作,步长为1×1,padding='SAME'会在输入矩阵上下各加几个值为0的行,在左右各加个几个值为0的列,以提取边缘特征,意味着卷积后图像的大小为原尺寸/步长,显然横向除以横向,纵向除以纵向。
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
// 定义池化操作,这里定义最大池化,即在区域里提取最显著的特征。池化核的大小为2×2。池化后图像的大小为原尺寸/步长。
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
2.2 定义输入、输出及真实输出
x = tf.placeholder(tf.float32, [None, 784])// x是特征,即输入的数据,是一个长度为784的数组,None代表行数不确定
x_image = tf.reshape(x, [-1, 28, 28, 1])// reshape()将读入的x输入变成图像的格式,即28*28,以便之后处理,-1和None相对应,即图片的数量不确定,1代表灰度图像。即输入为28*28*1的x_image。
y_ = tf.placeholder(tf.float32, [None, 10])// y_是真实的输出label,是一个长度为10的数组
2.1 网络结构
2.2.1 卷积层1+池化层1
W_conv1 = weight_variable([5, 5, 1, 32])// 卷积核的为5*5,单通道的灰度图像,一共32个不同的卷积核,输出32个特征图
b_conv1 = bias_variable([32])// 每个卷积对应一个偏置,共32个偏置
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1)+b_conv1)// 先进行卷积操作,后用relu激活函数处理,得到处理后的输出28*28*32。
h_pool1 = max_pool_2x2(h_conv1)// 进行最大池化,28*28*32变成了14*14*32
2.2.2 卷积层2+池化层2
W_conv2 = weight_variable([5, 5, 32, 64])// 卷积核尺寸为5*5,32个通道,因为上面得到的是32层,第二个卷积层有64个不同的卷积核
b_conv2 = bias_variable([64])//64个偏置
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2)+b_conv2)// 第一层卷积和池化后的输出h_pool1作为第二层卷积层的输入,由于步长strides为1,填充边界padding为1,故结果为14*14*64
h_pool2 = max_pool_2x2(h_conv2)// 由于步长为2,padding='SAME',故最大池化后为7*7*64
2.2.3 全连接层1
W_fc1 = weight_variable([7*7*64, 1024])// 第一个全连接层的权重矩阵,输入是上层的节点数,上层是7*7*64个结点,输出是1024个结点,1024是自己定义的。
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])// 为方便全连接层计算,把上一层传过来的结果reshape,从三维的变成一维的。
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1)+b_fc1)// 得到一个长度为1024的输出
2.2.4 全连接层2
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)// 使用softmax作为激活函数,softmax是一个多分类器。一般配合交叉熵损失函数使用
2.2 学习规则及评价指标
2.2.1 学习规则
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) // 优化器使用Adam,学习率用一个较小的1e-4
2.2.2 评价指标
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
3. 构建模型后的工作(训练模型、测试模型)
tf.global_variables_initializer().run()
for i in range(2000):
batch = mnist.train.next_batch(50)// 获取50条数据作为一个batch,这里可以叫做mini-batch
train_step.run({x: batch[0], y_: batch[1], keep_prob: 0.5})//训练
// 每100个batch的训练做一个预测
if i % 100 == 0
print("step:", str(i), "测试准确率:", accuracy.eval({x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))// eval()是tf.Tensor的Session.run()的另一种写法,但是eval()只能用于tf.Tensor类对象,即必须是有输出的operation,对于没有输出的operation,可以用run()或Session.run()即可。
注:本代码借鉴黄文坚、唐源的《TensorFlow 实战》一书。