环境:TensorFlow1.8 Python3.6
代码:
# coding=utf-8
# By author MZ
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
## 1.首先处理数据集
## 1.1图片大小为28*28
mnist = input_data.read_data_sets('MNIST_data_bak/', one_hot=True)
## tf.InteractiveSession()是在运行计算图的时候可以插入计算图
##tf.Session()需要在启动这个计算图的时候,必须构建完成
sess = tf.InteractiveSession()
## 1.2定义Weight的生成函数
## tf.truncated_normal截断的正太分布,标准差设为0.1,生成的随机值和均值的差值不会大于两倍的标准差
def weight_variable(shape):
init = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(init)
## 1.3定义bias的生成函数
## tf.truncated_normal截断的正太分布,标准差设为0.1,生成的随机值和均值的差值不会大于两倍的标准差
def bias_variable(shape):
init = tf.constant(0.1, shape=shape)
return tf.Variable(init)
## 1.4卷积层和池化层也是接下来要重复使用的,因此也为它们定义创建函数
## tf.nn.conv2d是TensorFlow中的2维卷积函数,参数中x是输入,W是卷积的参数,比如[5, 5, 1, 32]
## 前面两个数字代表卷积核的尺寸,第三个数字代表有多少个channel
## 因为我们只有灰度单色,所以是1,如果是彩色的RGB图片,这里是3
## 最后代表核的数量,也就是这个卷积层会提取多少类的特征,这里设置为32
## 卷积层:tf.nn.conv2d,表示2维的卷积函数
## Strides代表卷积模板移动的步长,都是1代表会不遗漏地划过图片的每一个点!Padding代表边界的处理方式,
## 这里的SAME代表给边界加上Padding让卷积的输出和输入保持同样SAME的尺寸
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
## 池化层:使用最大值池化
## ksize:池化窗口,维度分别是,batch(一般为1),height,width,channels(通道一般为1)
## strides:步长, 通常batch和channels都为1
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个卷积层+2个池化层+1个Dropout层+1个Hidden_layer+1输出层
## 因为卷积神经网络会利用到空间结构信息,因此需要将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*5,1个颜色通道,32个不同的卷积核,然后使用conv2d函数进行卷积操作,并加上bias,接着再使用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)
## 使用lrn对数据进行随机采样,提高模型的泛化能力
## norm1 = tf.nn.lrn(h_pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
## 第二层卷积层,但是卷积核变成了64
## 通道变为32,是因为上一层卷积核为32,单通道,所以这一层就有32个通道
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)
## 0.976 加上lrn后为0.9788 0.9793
# norm2 = tf.nn.lrn(h_conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
## 第二层池化层
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分类,因为上一层Hidden_layer中有1024个神经元,最终是10个分类,所以shape为[1024,10]
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]))
## adam优化器,使用adam算法, 基于一阶梯度的随机目标函数优化算法
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))
## 3.开始训练,训练次数为2000次,每次训练的batch为50张图片
tf.global_variables_initializer().run()
for i in range(2000):
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})
##4. 测试数据进行测试
print("test accuracy %g" % accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0
}))
打印结果如下:
step 0, training accuracy 0.12
step 100, training accuracy 0.88
step 200, training accuracy 0.88
step 300, training accuracy 0.98
step 400, training accuracy 0.98
step 500, training accuracy 0.92
step 600, training accuracy 0.92
step 700, training accuracy 0.94
step 800, training accuracy 0.98
step 900, training accuracy 0.98
step 1000, training accuracy 0.96
step 1100, training accuracy 0.96
step 1200, training accuracy 0.98
step 1300, training accuracy 0.94
step 1400, training accuracy 0.98
step 1500, training accuracy 0.98
step 1600, training accuracy 1
step 1700, training accuracy 0.98
step 1800, training accuracy 0.96
step 1900, training accuracy 0.98
test accuracy 0.9764
总结:测试MNIST数据集的时候,当使用DNN,不加卷积池化的时候,准确率大概能达到95%,使用两层隐藏层,现在我们使用CNN,准确率能提升2%多点,错误率下降大约60%。
主要的性能提升得益于卷积神经网络对图像特征的提取和抽象能力,依靠卷积核的权值共享,CNN的参数数量并没有爆炸,降低计算量的同时,减轻了过拟合。
注:本文中注释了lrn,lrn加在卷积层之后,也可以提高模型的准确率,原因是对数据进行了局部归一化,但是效果很小,大约能提升准确度的千分之一点。在2015年 Very Deep Convolutional Networks for Large-Scale Image Recognition.提到lrn基本没什么用。