TensorFlow-神经网络手写数字体识别

本文可用于学习基于TensorFlow编写神经网络模型进行手写数字体识别!!!

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

#载入MNIST数据集,自动划分训练、验证数据集
mnist = input_data.read_data_sets('MNIST_data/',one_hot=True)

#获得数据集信息
print("训练集样本:",mnist.train.num_examples)
print("验证集样本:",mnist.validation.num_examples)
print("测试集样本:",mnist.test.num_examples)

# print("训练样本:",mnist.train.images[0])
# print("训练样本标签:",mnist.train.labels[0])

print("训练样本维度:",mnist.train.images[0].shape)
print("测试样本维度:",mnist.test.images[0].shape)

#配置神经网络的参数
INPUT_NODE = 784#输入层节点数
OUTPUT_NODE = 10#输出层节点数
LAYER1_NODE = 500#隐层节点数
BATCH_SIZE = 100#每次训练的样本数
LEARNING_RATE_BASE = 0.8#初始学习速率
LEARNING_RATE_DECAY = 0.99#学习率的衰减率
LAMBDA = 0.0001#正则化系数
TRAINING_STEPS = 10000#训练轮数
MOVING_AVERAGE_DECAY = 0.99#滑动平均衰减率

#前向传播过程
def forward(x, avg_class, w1, b1, w2, b2):
	#如果没有提供滑动平均类时,直接使用参数当前的取值
	if avg_class == None:
		#隐层输出
		layer1 = tf.nn.relu( tf.matmul(x, w1) + b1 )
		#返回输出层输出,不需要进行非线性.因为预测使用的是输出节点相对大小的列表索引,所以可以不需要softmax层
		return tf.matmul( layer1, w2 ) + b2
	else:
		#首先计算各变量的滑动平均值,再计算隐层输出
		layer1 = tf.nn.relu( tf.matmul( x, avg_class.average(w1) ) + avg_class.average(b1) )
		return tf.matmul( layer1, avg_class.average(w2) ) + avg_class.average(b2)

#1.定义batch训练集的占位变量
x = tf.placeholder( tf.float32, [None, INPUT_NODE], name = 'x_input' )
y_ = tf.placeholder( tf.float32, [None, OUTPUT_NODE], name = 'y_input' )

#2.定义权重参数和偏置参数,正态分布初始化
w1 = tf.Variable( tf.truncated_normal( [INPUT_NODE, LAYER1_NODE], stddev = 0.1) )
w2 = tf.Variable( tf.truncated_normal( [LAYER1_NODE, OUTPUT_NODE], stddev = 0.1) )
b1 = tf.Variable( tf.constant(0.1, shape = [LAYER1_NODE]) )
b2 = tf.Variable( tf.constant(0.1, shape = [OUTPUT_NODE]) )

#3.获得没有滑动平均类的前向传播结果
y = forward( x, None, w1, b1, w2, b2 )

#4.定义当前训练轮数的变量,指定此参数为不可训练
global_step = tf.Variable( 0, trainable = False )
#初始化滑动平均类,给定滑动平均衰减率和当前训练的轮数
variable_averages = tf.train.ExponentialMovingAverage( MOVING_AVERAGE_DECAY, global_step )
#更新滑动平均值得操作,在所有代表神经网络参数的变量上使用滑动平均,global_step不需要,通过trainable=False指定
variable_averages_op = variable_averages.apply( tf.trainable_variables() )

#5.获得使用滑动平均类的前向传播结果
average_y = forward( x, variable_averages, w1, b1, w2, b2 )

#6.交叉熵损失函数   tf.argmax(y_, 1)获得二维列表y_每行列表的最大值索引,最终变成一维列表
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( logits=y, labels=tf.argmax(y_, 1) )
#batch的交叉熵损失均值
cross_entropy_mean = tf.reduce_mean( cross_entropy )

#7.L2正则化,一般只计算各层模型的正则化损失,不计算各层偏置损失
regularizer = tf.contrib.layers.l2_regularizer( LAMBDA )
regularition = regularizer(w1) + regularizer(w2)

#8.计算总损失
loss = cross_entropy_mean + regularition

#9.设置指数衰减的学习速率 (起始学习速率,当前迭代的轮数,训练完所有数据的轮数,学习率衰减速度)
learning_rate = tf.train.exponential_decay( LEARNING_RATE_BASE, global_step, mnist.train.num_examples/BATCH_SIZE, LEARNING_RATE_DECAY )

#10.梯度下降算法优化损失函数
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize( loss, global_step=global_step)

#11.同时进行多个操作。训练神经网络时,每过一遍数据既需要完成反向传播更新神经网络参数,又需要更新每一个参数的滑动平均值
with tf.control_dependencies( [train_step, variable_averages_op] ):
	train_op = tf.no_op( name='train' )

#12.计算滑动平均模型的前向传播结果是否正确,tf.argmax(averabe_y, 1),返回维度为batch的一维数组,每个元素为预测数组的最大值索引
correct_prediction = tf.equal( tf.argmax(average_y, 1), tf.argmax(y_, 1) )
#计算准确率,将布尔型强制类型转换为实数型
accuracy = tf.reduce_mean( tf.cast( correct_prediction, tf.float32 ) )

#13.创建有上下文管理器的会话
with tf.Session() as sess:
	#初始化变量
	tf.initialize_all_variables().run()
	#验证数据集
	validate_feed = { x:mnist.validation.images, y_:mnist.validation.labels }
	#测试数据集
	test_feed = { x:mnist.test.images, y_:mnist.test.labels }
	#迭代训练神经网络
	for i in range(TRAINING_STEPS):
		#14.每1000轮输出一次在验证集、测试集上的测试结果
		if i%1000==0:
			#验证集上的准确率
			validate_acc = sess.run( accuracy, feed_dict = validate_feed )
			print("当前模型在验证集上的准确率:", validate_acc)
			#测试集上的准确率
			test_acc = sess.run( accuracy, feed_dict=test_feed )
			print("当前训练模型在测试集上的准确率:", test_acc)

		#15.每一轮使用一个batch训练集
		xs,ys = mnist.train.next_batch(BATCH_SIZE)
		sess.run( train_op, feed_dict={ x:xs, y_:ys } )

	#16.最终测试集上的准确率
	test_acc = sess.run( accuracy, feed_dict=test_feed )
	print("最终训练模型在测试集上的准确率:", test_acc)

所用知识:

1.回归问题和分类问题的损失函数?
分类问题的损失函数常用交叉熵损失函数,输出层节点数量为类别数量;回归问题的损失函数常用均方误差(MSE,mean squared error)损失函数,输出层节点数量一般为一个节点,节点的输出值即为预测值。

2.交叉熵损失函数?

刻画的是实际输出p(概率)与期望输出q(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。在神经网络中,交叉熵常常与Sorfmax函数组合使用。在TensorFlow中,其交叉熵损失函数获得nxm矩阵,n为batch数量,m为类别数量,其最终结果为整个矩阵的均值(先对行求均值,再对列求均值),即为整个batch的平均交叉熵;

2.梯度下降算法?

批量梯度下降:不能保证被优化的函数(凸函数可以)达到全局最优解;计算时间长,因为损失函数J是在所有训练数据上的损失和。

随机梯度下降:为了加速训练过程,可以使用随机梯度下降算法,在每一轮迭代中,随机优化某一条训练数据上的损失函数。但是可能无法达到局部最优;

batch梯度下降:综合批量梯度下降算法和随机梯度下降算法,提出了batch梯度下降,每次计算一个batch训练数据上的损失函数,优化神经网络上的参数。

4.验证数据的作用?

神经网络模型的最终目标是对未知数据的预测。所以一般从训练数据集中获取一部分验证数据集用于调试模型参数,(当数据量较小时,可使用交叉验证的方法)然后用此模型对测试数据进行预测。

参考资料:

1.《TensorFlow实战Google深度学习框架》


猜你喜欢

转载自blog.csdn.net/attitude_yu/article/details/80045990