Tensorflow深度学习之八:再探CNN解决mnist手写数字识别问题

前几天碰到的第二个深度学习的程序就是CNN(卷积神经网络)解决mnist手写数字的识别问题,但是碍于个人能力,很多地方不是很理解,现在学习了一些基本的概念之后,重新将加了注释的源代码贴上,与大家分享,基本上每一条语句都标注了注释,很多参数的设置也有相应的说明。
(本人也是最近刚刚接触Tensorflow,如有相关概念理解错误,还请大家不吝指出。)

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

# 下载并导入mnist数据
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# Tensorflow默认会话
sess = tf.InteractiveSession()

# 打印训练集,测试集,验证集的image和label的shape
print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape)

# 定义权重变量的函数,参数是需要使用的权重的shape
def weight_variable(shape):
    # 使用正态分布填充变量,标准差设置为0.1
    initial = tf.truncated_normal(shape, stddev=0.1)
    #返回相应的变量
    return tf.Variable(initial)

# 定义一个返回全部由0.1组成的指定shape的tensor的函数
def bias_Variable(shape):
    # 定义一个常量该常量全部由0.1组成,shape由参数shape指定
    initial = tf.constant(0.1, shape=shape)
    # 返回一个变量,变量的值由刚刚定义的常量决定
    return tf.Variable(initial)

# 卷积函数,这里把Tensorflow提供的卷积函数做了一个小小的封装。
# 这里默认strides是[1,1,1,1],即只能将卷积核每次都移动一步。
# 默认使用边缘填充0的策略(padding="SAME"),这样保持卷积前后的矩阵在第一维第二维上是相等的,注意这里深度可能是不同的。
# 参数x表示的是待卷积的矩阵,一般是一张图片。
# 参数W表示的是卷积核的相关信息。W一般是一个由4个数字组成的list:[a,b,c,d]。
# a,b,c参数限制的卷积核的结构,a,b表示的是平面上的大小,c表示深度,一般可以理解为图片的通道数目。
# d表示的是卷积核的深度,可以理解为卷积核的数目,即你想要提取的特征的数目。
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1,1,1,1],padding="SAME")

# 池化函数,同卷积函数,这里也将池化函数做了一个小小的封装。
# 这里x是待池化的矩阵。
# ksize是池化核的大小,和卷积核一样,一般是一个四个数字组成的list,第一个数字和最后一个数字应为1。
# strdides是步长参数,一般第一个数字和最后一个数字都是1。
# padding参数和卷积核一样,这里使用"SAME",表示使用边缘填充0的策略。
# 池化操作一般不改变矩阵的深度,但是会改变矩阵的大小。
# 这里采用的是最大池化,同时还有平均池化方法。
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1],padding="SAME")

# 定义传入数据的placeholder
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])

# 因为CNN网络需要考虑像素的空间位置,因此需要把其空间位置还原。
# 这里-1表示样本数量不固定,两个28表示空间尺寸,1表示1个通道数目。
x_image = tf.reshape(x, [-1, 28, 28, 1])

# 获取卷积核,这里卷积核的大小是5x5,1个通道,32个卷积核,提取32个特征。
W_conv1 = weight_variable([5,5,1,32])

# 获取偏差值,偏差值得数目应该和卷积核的数目保持一致。
b_conv1 = bias_Variable([32])

# 将输入的图像做卷积操作,最有加上偏差值,使用relu函数去线性化。
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)

# 对去线性化的矩阵进行池化操作。
h_pool1 = max_pool_2x2(h_conv1)

# 类似的,重复上述操作,注意这里输入的矩阵的深度是32,与上一次卷积过程卷积核的数目相同。
# 这里我们使用的卷积核的数目是64。
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)
h_pool2 = max_pool_2x2(h_conv2)

# 下面定义一个全连接层。

# 我们经过两次卷积和池化操作,最后图片的大小变为7x7,原因请参考池化操作时的池化核和池化步长。
# 因为均为2x2,所以每次池化之后,大小均会长宽均会缩小至原来的一半。
# 这里设置1024个隐藏节点用于保存参数。
W_fc1 = weight_variable([7*7*64,1024])

# 设置1024个偏置量。
b_fc1 = bias_Variable([1024])

# 将经过两次卷积池化操作之后的矩阵整理为一维向量,长度为7*7*64。
h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])

# 矩阵相乘并加上相关的偏置量,最后使用Relu函数去线性化。
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)

# 定义一个保留的概率,这里是一个placeholder。
keep_prob = tf.placeholder(tf.float32)

# 使用dropout方法按照一定概率(keep_prob指定)使得矩阵的某些数据不起作用。
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)

# 定义又一个全连接层。因为我们需要将结果映射到10个类别上,因此第二个参数是10。
W_fc2 = weight_variable([1024,10])

# 定义偏置量。
b_fc2 = bias_Variable([10])

# 矩阵相乘,并加上偏置量,随后使用softmax处理。
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)

# 定义交叉熵函数,即我们使用的loss函数,我们的目的就是在训练过程中使得loss函数尽可能的小。
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv),reduction_indices=[1]))

# 使用AdamOptimizer进行loss函数的优化。
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))

# 初始化所有的全局变量。
tf.global_variables_initializer().run()


# 从这里开始训练我们的模型。
# 我们一共训练20000次。
for i in range(20000):
    # 随机在训练集中产生50个数据。
    batch = mnist.train.next_batch(50)
    # 这一部分用于输出训练过程中的准确率变化。 
    if i % 100 == 0:
        # 定义训练中的准确率计算,并将数据feed给相应的placeholder,用于训练。
        train_accuracy = accuracy.eval(feed_dict = {x:batch[0] , y_:batch[1], keep_prob: 1.0})
        # 输出
        print("step %d,train accuracy %g"%(i,train_accuracy))
    # feed数据,并训练模型。    
    train_step.run(feed_dict = {x:batch[0] , y_:batch[1], keep_prob: 0.5})

# 将测试集的书记feed给相应的placeholder,然后测试模型的训练结果。
print("test accuracy %g"%accuracy.eval(feed_dict = {x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))

程序运行结果如下:(运行结果或有不同)

/home/bryan/anaconda3/bin/python /home/bryan/Desktop/CNN/main.py
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
(55000, 784) (55000, 10)
(10000, 784) (10000, 10)
(5000, 784) (5000, 10)
step 0,train accuracy 0.06
step 100,train accuracy 0.84
step 200,train accuracy 0.94
step 300,train accuracy 0.92
step 400,train accuracy 0.96
step 500,train accuracy 0.96
step 600,train accuracy 0.88
step 700,train accuracy 0.94
step 800,train accuracy 0.92
step 900,train accuracy 1
step 1000,train accuracy 0.94
step 1100,train accuracy 0.94
step 1200,train accuracy 0.96
step 1300,train accuracy 0.96
step 1400,train accuracy 1
step 1500,train accuracy 0.96
……
step 19200,train accuracy 1
step 19300,train accuracy 1
step 19400,train accuracy 1
step 19500,train accuracy 1
step 19600,train accuracy 1
step 19700,train accuracy 0.98
step 19800,train accuracy 1
step 19900,train accuracy 1
test accuracy 0.9934
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值