赞
踩
“Hello World”是任何一门编程语言的入门程序,而深度学习中的“Hello World”就是MNIST手写体数字识别问题。
MNIST数据集是一个大型的手写体数字数据库,广泛的用于机器学习领域的训练和测试,由纽约大学的Yann LeCun教授整理,下载链接如下所示:http://yann.lecun.com/exdb/mnist/。MNIST数据集是NIST数据集的一个子集,它包含60000个【训练集和】10000个【测试集】,每张图像都已经进行了归一化、数字居中处理、固定尺寸为28像素x28像素。
如上图所示,MNIST数据集提供了四个下载文件,分别为训练集合,训练集合的标签,测试集,测试集的标签。虽然,这个集合仅仅提供了【训练集】和【测试集】,但是在实际的应用过程,我们会将【测试集】中的一部分数据分割出来作为最终训练模型的【验证集】,以便测试【最终训练出来的模型】在【未知数据集合】上的【预测能力】。在Tensorflow中,Tensorflow对于MNIST数据集合的下载和使用进行了封装,Tensotflow的封装,使得MNIST数据集合的使用更加的方便,具体的封装如下所示:
- #========================================================================================================
- #文件说明:
- # Tensorflow对MNIST数据集合的封装
- #开发环境:
- # Win10+Tensorflow+OpenCv3.3+Python3.5+PyCharm5.0.3
- #时间地点:
- # 陕西师范大学 文集楼 2018.4.20
- #========================================================================================================
- from tensorflow.examples.tutorials.mnist import input_data
- #【1】将MNIST数据集合读入内存,如果指定的路径下没有下载好的MNIST数据集合,那么,Tensorflow会自动从网上下载数据。
- mnist = input_data.read_data_sets('F:/MnistSet/', one_hot=True)
- #【2】打印Training data size 55000
- print("【INFO】Training data size = ",mnist.train.num_examples)
- #【3】打印【验证集】Validating data size: 5000
- print("【INFO】Validating data size:",mnist.validation.num_examples)
- #【4】打印Testing data size:10000
- print("【INFO】Testing data size:",mnist.test.num_examples)
- #【5】打印Example training data:
- print("【INFO】Example training data:",mnist.train.images[0])
- #【6】打印Example training data label
- print("【INFO】Example training data label:",mnist.train.labels[0])
- #========================================================================================================
- #文件说明:
- # 【1】从上面的代码可以看出,通过input_data.read_data_sets函数生成的类会自动的将MNIST数据集划分为【train】、
- # 【Validation】、【Test】三个数据集合,其中train这个集合中有55000张图片,validation这个集合中有5000张图
- # 片,这两个集合组成了MNIST数据集本身提供的训练数据集。Test集合中有10000张图片,这些图片来自MNIST数据集提
- # 供的的【测试集】。处理后的每一张图片是一个长度为784的一维数组,这个数组中的元素对应了图片像素矩阵中的每一
- # 个数字28x28=784.
- # 【2】因为神经网络的输入是一个【特征向量】,所以,在此把一张二维图像的像素矩阵放到一个一维数组中可以方便Tensor-
- # flow将【图片】的【像素矩阵】提供为【神经网络】的【输入层】。
- # 【3】为了方便使用【SGD:随机梯度下降算法】,input_data.read_data_sets函数生成的类还提供了mnist.train.next_batch
- # 函数,它可以从所有的训练数据中读取一小部分作为一个训练batch。下面的这份代码显示了这个功能。
- #========================================================================================================
- from tensorflow.examples.tutorials.mnist import input_data
- #【1】将MNIST数据集合读入内存,如果指定的路径下没有下载好的MNIST数据集合,那么,Tensorflow会自动从网上下载数据。
- mnist = input_data.read_data_sets('F:/MnistSet/', one_hot=True)
- #【2】打印Training data size 55000
- print("【INFO】Training data size = ",mnist.train.num_examples)
- #【3】打印【验证集】Validating data size: 5000
- print("【INFO】Validating data size:",mnist.validation.num_examples)
- #【4】打印Testing data size:10000
- print("【INFO】Testing data size:",mnist.test.num_examples)
- #【5】打印Example training data:
- #print("【INFO】Example training data:",mnist.train.images[0])
- #【6】打印Example training data label
- print("【INFO】Example training data label:",mnist.train.labels[0])
- #========================================================================================================
- #文件说明:
- # 【1】从上面的代码可以看出,通过input_data.read_data_sets函数生成的类会自动的将MNIST数据集划分为【train】、
- # 【Validation】、【Test】三个数据集合,其中train这个集合中有55000张图片,validation这个集合中有5000张图
- # 片,这两个集合组成了MNIST数据集本身提供的训练数据集。Test集合中有10000张图片,这些图片来自MNIST数据集提
- # 供的的【测试集】。处理后的每一张图片是一个长度为784的一维数组,这个数组中的元素对应了图片像素矩阵中的每一
- # 个数字28x28=784.
- # 【2】因为神经网络的输入是一个【特征向量】,所以,在此把一张二维图像的像素矩阵放到一个一维数组中可以方便Tensor-
- # flow将【图片】的【像素矩阵】提供为【神经网络】的【输入层】。
- # 【3】为了方便使用【SGD:随机梯度下降算法】,input_data.read_data_sets函数生成的类还提供了mnist.train.next_batch
- # 函数,它可以从所有的训练数据中读取一小部分作为一个训练batch。下面的这份代码显示了这个功能。
- #========================================================================================================
- batch_size = 100
- xs,ys = mnist.train.next_batch(batch_size)
- #【7】从train的集合中选取batch_size个训练数据
- print("【INFO】X Shape:",xs.shape)
- print("【INFO】Y Shape:",ys.shape)
此块,我们将给出一个完整的Tensorflow程序来解决MNIST手写体数字识别问题。在我们具体设计神经网络的模型之前,我们先回顾以下神经网络设计中的要点,在神经网络结构的设计中,深度学习一方面需要使用使用【激活函数】实现【神经网络 模型】的【去线性化】;另一方面,需要使用一层或者多层的【隐藏层】使得【神经网络的深度】更深,这样便有利于解决更加复杂问题。
在具体【训练神经网络】的时候,我们需要设置合理的【带指数衰减的学习率】、使用【正则化】来避免【过拟合】,以及使用【滑动平均模型】使得最终的模型更加的健壮。下面给出的代码实现了完整功能的在MNIST数据集上的神经网络的训练。
- #coding=UTF-8
- #========================================================================================================
- #文件说明:
- # 完整的MNIST数据集合神经网络的训练和设计
- #开发环境:
- # Win10+Tensorflow+OpenCv3.3+Python3.5+PyCharm5.0.3
- #时间地点:
- # 陕西师范大学 文集楼 2018.4.20
- #========================================================================================================
- import tensorflow as tf
- from tensorflow.examples.tutorials.mnist import input_data
-
- #【1】【MNIST数据集】相关的【常数】
- INPUT_NODE = 784 #[1]输入层的节点数。对于MNIST数据集来说,这个就等于图片的像素点数
- OUTPUT_NODE= 10 #[2]输出层的节点数。这个就等于类别的数目,因为在MNIST数据集中,需要区分的是
- # 0~9这10个数字,所以,这里输出层的节点数为10.
- #【2】【配置神经网络的参数】
- LAYER1_NODE= 500 #[1]隐藏层的节点数。这里使用只有一个隐藏层的网络结构作为样例,这个隐藏层的节
- # 点数为500
- BATCH_SIZE = 100 #[2]一个训练batch中训练数据的个数。数字越小时,训练过程越接近【随机梯度下降】;
- # 数字越大时,训练越接近【梯度下降算法】。
- LEARNING_RATE_BASE = 0.8 #[1]基础的学习率
- LEARNING_RATE_DECAY = 0.99 #[2]学习率的衰减
- REGULARIZATION_RATE = 0.0001 #[3]描述【模型复杂度】的【正则化】在【损失函数】中的【系数】
- TRAINING_STEPS = 30000 #[4]训练轮数
- MOVING_AVERAGE_DECAY= 0.99 #[5]滑动平均模型的衰减率
- #========================================================================================================
- #函数说明:
- # 【1】一个辅助函数,给定【神经网络】的【输入】和【所有参数】,计算【神经网络】的【前向传播结果】。
- # 【2】在这里,定义了一个使用【ReLU激活函数】的三层全连接神经网络。
- # 【3】通过加入【隐藏层】实现【多层网络结构】,通过【RELU激活函数】实现【去线性化】
- # 【4】在这个函数中,也支持传入用于计算参数平均值得类,这样方便在测试时使用【滑动平均模型】
- #========================================================================================================
- def inference(input_tensor,avg_class,weights1,biases1,weights2,biases2):
- #【1】当没有提供【滑动平均类】时,直接使用【参数】的【当前取值】
- if avg_class == None:
- #【2】计算【隐藏层】的【前向传播结果】,这里使用了【ReLU】激活函数
- layer1 = tf.nn.relu(tf.matmul(input_tensor,weights1)+biases1)
- #【3】计算【输出层】的【前向传播结果】。因为在计算【损失函数】时,会一并计算softmax()函数,所以,这里不需要加入
- #激活函数。而且,不加入softmax不会影响预测结果。因为,预测时,使用的不是同类别对应节点输出值得相对大小,有没有
- #softmax层对于最后分类结果的计算没有影响。于是,在计算整个神经网络的前向传播网络时可以不加入最后的softmax层
- return tf.matmul(layer1,weights2)+biases2
- else:
- #【1】首先使用avg_class.average函数来计算得出变量的【滑动平均值】,然后,再计算相应的神经网络前向传播结果。
- layer1 = tf.nn.relu(tf.matmul(input_tensor,avg_class.average(weights1))+avg_class.average(biases1))
- return tf.matmul(layer1,avg_class.average(weights2)+avg_class.average(biases2))
- #========================================================================================================
- #函数说明:
- # 训练模型的过程
- #========================================================================================================
- def train(mnist) -> object:
- x = tf.placeholder(tf.float32,[None,INPUT_NODE],name='x-input') #【1】输入的数据
- y_= tf.placeholder(tf.float32,[None,OUTPUT_NODE],name='y-input') #【2】真实的标签值
- #【1】生成【隐藏层】的【参数】
- #【2】truncated_normal(),生成一个符合【正太分布】的【随机数】,但是,如果生成的随机数偏离平均值超过2个标准差,那
- #么这个随机数将会被重新随机
- weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE,LAYER1_NODE],mean=0,stddev=0.1))
- biases1 = tf.Variable(tf.constant(0.1,shape=[LAYER1_NODE]))
- #【3】生成【输出层】的【参数】
- weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE,OUTPUT_NODE],mean=0,stddev=0.1))
- biases2 = tf.Variable(tf.constant(0.1,shape=[OUTPUT_NODE]))
- #【4】计算在【当前参数】下,【神经网络前向传播】的【结果】。这里给出的用于计算【滑动平均的类】为None,所以,函数不会
- #不会使用【参数】的【滑动平均】
- y = inference(x,None,weights1,biases1,weights2,biases2) #【3】前向传播的【预测结果】
- #【5】定义存储训练轮数的变量。这个变量不需要计算滑动平均值,所以,这里指定这个变量为【不可训练的变量】。在使用Tensorflow
- #训练神经网络时,一般会将代表【轮数的变量】指定为【不可训练的参数】
- global_step = tf.Variable(0,trainable=False)
- #【6】给定【滑动平均衰减率】和【训练轮数的变量】,初始化【滑动平均类】。给定【训练轮数的变量】可以加快【训练早期变量】的
- #更新速度。
- variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,global_step)
- #【7】在所有代表【神经网络参数的变量】上使用【滑动平均】。其他辅助变量,比如global_step就不需要了。tf.trainable_variables
- #返回的就是【图上集合】GraphKeys.TRAINABLE_VARIABLES中的元素。这个集合中的元素就是所有没有指定trainable=False的可训练
- #参数
- variable_averages_op = variable_averages.apply(tf.trainable_variables())
- #【8】计算使用了【滑动平均之后】的【前向传播结果】。经过【滑动平均】不会改变【变量】本身的取值,而是会维护一个【影子变量】
- #来记录其【滑动平均值】。所以,当需要使用这个【滑动平均】时,需要明确的调用average函数。
- average_y = inference(x,variable_averages,weights1,biases1,weights2,biases2)
- #【9】计算【交叉熵】作为刻画【预测值】和【真实值】之间差距的【损失函数】。这里使用了Tensorflow中提供的sparse_softmax_cross
- #_entropy_with_logits函数来计算【交叉熵】。当【分类问题】只有一个正确答案时,可以使用这个函数来加速【交叉熵】的计算。MNIST
- #问题的图片中只包含了0~9中的一个数字,所以,可以使用这个函数来计算【交叉熵损失】。
- # 【1】这个函数的第一个参数是神经网络不包括softmax层的【前向传播结果】
- # 【2】第二个是训练数据的正确答案。因为标准答案是一个长度为10的一维数组,而该函数需要提供的是一个正确答案的数字,所以,
- # 使用tf.argmax函数来得到正确答案对应的编号。
- cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(tf.argmax(1, y_), y)
- #【10】计算当前batch中所有样例的交叉熵平均值
- cross_entropy_mean = tf.reduce_mean(cross_entropy)
- #【11】计算【L2正则化】【损失函数】
- regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
- #【12】计算【模型】的【正则化损失】,一般只计算【神经网络】边上权重的【正则化损失】,而不使用【偏置项】。
- regularization=regularizer(weights1)+regularizer(weights2)
- #【13】【总损失】等于【交叉熵损失】和【正则化损失的和】
- loss = cross_entropy_mean + regularization
- #【14】设置【指数衰减的学习率】
- learning_rate = tf.train.exponential_decay(
- LEARNING_RATE_BASE, #[1]基础的学习率,随着迭代的进行,更新变量时使用的学习率在这个基础上递减
- global_step, #[2]当前迭代的轮数
- mnist.train.num_examples/BATCH_SIZE, #[3]过完所有的训练数据需要的【总迭代次数】
- LEARNING_RATE_DECAY) #[4]学习率衰减速度
-
- #【1】使用tf.train.GradientDescentOptimizer优化算法来优化损失函数。注意,这里的【损失函数】包含了【交叉熵损失】和【L2正则化损失】
- train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
- #【2】在训练【神经网络模型】时,每过一遍数据即需要通过【反向传播】来更新【神经网络】中的【参数】,又要更新每一个【参数】的【滑动平均值】
- #为了一次完成多个操作,Tensorflow提供了tf.control_dependencies和tf.group两种机制。下面两行程序和
- #train_op = tf.group(train_step,variables_averages_op)是等价的
- with tf.control_dependencies([train_step,variables_averages_opl]):
- train_op = tf.no_op(name = 'train')
- #【3】检验使用了【滑动平均模型】的【神经网络前向传播结果】是否正确。
- # [1]tf.argmax(average_y,1)计算每一个样例的【预测答案】。其中average_y是一个batch_size*10的二维数组,每一行表示一个【样例】的
- # 【前向传播结果】;第一个参数1表示选取最大值的操作在第一个维度中进行,也就是说,只在每一行选取最大值对应的下标。于是,得到的结果
- # 是一个长度为batch_size的一维数组,这个一维数组中的值就表示了每一个样例对应的【数字识别结果】。
- # [2]tf.equal判断两个张量的每一维是否相等,如果相等返回True,否则返回False
- correct_prediction = tf.equal(tf.argmax(average_y,1),tf.argmax(y_,1))
- #【4】这个运算,首先讲一个布尔型的数值转换为实数型,然后,计算平均值。这个平均值就是模型在这一组数据上的正确率
- accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
-
- #【1】初始会话并开始训练过程
- with tf.Session() as sess:
- tf.global_variables_initializer().run()
- #【2】准备验证条件。一般在神经网络的训练过程中会通过验证数据来大致判断停止的条件和评判训练的效果
- validate_feed = {x:mnist.validation.images,y_:mnist.validation.labels}
- #【3】准备测试数据。在真实的应用中,这部分数据在训练时是不可见的,这个数据只是作为模型优劣的最后评价标准
- test_feed = {x:mnist.test.images,y_:mnist.test.labels}
- #【4】迭代的训练神经网络
- for i in range(TRAINING_STEPS):
- #【5】每1000轮输出一次在【验证数据集】上的测试结果
- if i%1000 == 0:
- #【6】计算滑动平均模型在验证数据上的结果。因为MNIST数据集比较小,所以一次可以处理所有的验证数据。为了计算方便,本样例程序
- #没有将【验证数据划分】为更小的batch。当神经网络模型比较复杂或者验证数据集合比较大时,太大的batch会导致计算时间过长甚至发
- #生内存溢出的错误
- validate_acc = sess.run(accuracy,feed_dict=validate_feed)
- print("【INFO】After %d training steps,validate accuracy""using average model is %g"%(i,validate_acc))
- #【7】产生这一轮使用的一个batch的训练数据,并运行训练过程
- xs,ys = mnist.train.next_batch(BATCH_SIZE)
- sess.run(train_op,feed_dict={x:xs,y_:ys})
- #【8】在训练数据结束之后,在测试数据上检测神经网络模型的最终正确率
- test_acc = sess.run(accuracy,feed_dict=test_feed)
- print("【INFO】After %d training steps,test accuracy using average""model is %g"%(TRAINING_STEPS,test_acc))
-
- #主程序的入口
- def main(argv=None):
- #声明处理MNIST数据集的类,这个类在初始化时会自动下载数据
- mnist = input_data.read_data_sets("F:/MnistSet/",one_hot = True)
- train(mnist)
- #Tensorflow提供的一个主程序入口,tf.app.run会调用上面定义的main函数
- if __name__ == '__main__':
- tf.app.run()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。