附:课程链接
第五讲.全连接网络基础
5.3手写数字识别准确率输出
由于个人使用Win7系统,并未完全按照课程所讲,以下记录的也基本是我的结合课程做的Windows系统+PyCharm操作。且本人有python基础,故一些操作可能简略。并未完全按照网课。
记住编写代码时,除注释内容外,字符均使用英文格式。
本节目标:搭建神经网络,在MNIST数据集上训练模型,输出手写数字识别准确率。
五、实例学习
实现手写体mnist数据集的识别任务,共分为三个模块文件:
描述网络结构的前向传播文件mnist_forward.py
描述网络参数优化方法的反向传播文件mnist_backward.py
验证模型准确率的测试过程文件mnist_test.py
1.前向传播文件mnist_forward.py
在前向传播过程中,需要定义网络模型输入层个数、隐藏层节点数、输出层个数,定义网络参数w、偏置b,定义由输入到输出的神经网络架构。
"""
前向传播
"""
import tensorflow as tf
#在前向传播中,首先定义了神经网络的相关参数
INPUT_NODE = 784 #神经网络输入节点为784个
#(代表每张图片输入的像素点个数28*28=784,每个像素点是0-1之间的浮点数)。
#这784个点组成了一个一维数组
OUTPUT_NODE = 10 #输出10个数,每个数表示对应索引号出现的概率,实现了数字0-9的十分类
LAYER1_NODE = 500 #定义了隐藏层的节点个数
"""
与参数w有关
get_weight有两个参数,一个是w的形状shape,一个是正则化权重regularizer
"""
def get_weight(shape,regularizer):
w = tf.Variable(tf.truncated_normal(shape,stddev=0.1)) #通过tf.Variable()函数在训练神经网络时随机生成参数w
if regularizer != None: tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(regularizer)(w))
#如果使用正则化(regularizer != None),则把每一个w的正则化计入到总losses
return w
"""
与参数b有关
get_bias有一个参数,一个是b的形状shape,其实就是某层中b的个数
"""
def get_bias(shape):
b = tf.Variable(tf.zeros(shape)) #给b赋初值,()里写赋初值的方法。这些方法在3.2中讲过
return b
"""
搭建神经网络,描述从输入到输出的数据流
"""
def forward(x,regularizer):
#第一层参数w1、偏置b1、输出y1
w1 = get_weight([INPUT_NODE,LAYER1_NODE],regularizer)
b1 = get_bias(LAYER1_NODE)
y1 = tf.nn.relu(tf.matmul(x,w1) + b1)
# 第二层参数w2、偏置b2、输出y
w2 = get_weight([LAYER1_NODE,OUTPUT_NODE], regularizer)
b2 = get_bias(OUTPUT_NODE)
y = tf.matmul(y1,w2) + b2 #此y直接输出。因为输出y要经过softmax函数,使其符合概率分布,故输出y不经过relu函数
return y
2.反向传播文件mnist_backward.py
反向传播过程实现利用训练数据集对神经网络的模型训练,通过降低损失函数值,实现网络模型参数的优化,从而得到准确率高且泛化能力强的神经网络模型。
"""
反向传播
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_forward
import os #os模块
BATCH_SIZE = 200 #定义每轮喂入神经网络多少张图片
LEARNING_RATE_BASE = 0.1 #最开始的学习率
LEARNING_RATE_DECAY = 0.99 #学习率衰减率
REGULARIZER = 0.0001 #正则化系数
STEPS = 50000 #共训练多少轮
MOVING_AVERAGE_DECAY = 0.99 #滑动平均衰减率
MODEL_SAVE_PATH = "mnist_model" #模型的保存路径
MODEL_NAME = "mnist_model" #模型保存的文件名
#对于反向传播而言,要训练网络参数
def backward(mnist): #在backward函数中读入mnist
x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE]) #给x占位
y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE]) #给y_占位
y = mnist_forward.forward(x, REGULARIZER) #调用前向传播程序计算输出y
global_step = tf.Variable(0,trainable=False) #给轮数计数器赋初值,设定为不可训练
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1)) # 实现softmax和交叉熵的协同使用
cem = tf.reduce_mean(ce)
loss = cem + tf.add_n(tf.get_collection('losses')) #调用包含正则化的损失函数loss
#定义指数衰减学习率
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples / BATCH_SIZE,
LEARNING_RATE_DECAY,
staircase=True)
#定义训练过程
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
#定义滑动平均
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
ema_op = ema.apply(tf.trainable_variables())
with tf.control_dependencies([train_step, ema_op]):
train_op = tf.no_op(name='train')
#实例化saver
saver = tf.train.Saver() #tf.compat.v1.train.Saver
#在with结构中初始化所有变量
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
#在for循环中迭代STEPS轮
for i in range(STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE) #每次读入BATCH_SIZE组图片和标签
_,loss_value,step = sess.run([train_op,loss,global_step],feed_dict={x:xs,y_:ys}) #喂入神经网络,执行相关过程
if i % 1000 == 0: #每1000轮打印loss值
print("After %d training steps(s),loss on training batch is %g"%(step,loss_value))
saver.save(sess,os.path.join(MODEL_SAVE_PATH,MODEL_NAME),global_step=global_step) #保存模型到当前会话
def main():
mnist = input_data.read_data_sets("mnist_work/", one_hot=True)
backward(mnist)
if __name__ == '__main__':
main()
3.测试文件mnist_test.py
当训练完模型后,给神经网络模型输入测试集验证网络的准确性和泛化性,注意,所用的测试集和训练集是相互独立的。
import time #为了延时,导入了time模块
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_forward
import mnist_backward
TEST_INTERVAL_SECS = 5 #定义程序循环的间隔时间是5s
def test(mnist):
with tf.Graph().as_default() as g: #绘制计算图中的节点
#给输入图像x和y_占位
x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])
y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])
#用前向传播过程计算出y的值
y = mnist_forward.forward(x, None)
#实例化可还原滑动平均的saver,这样所有参数在会话中北加载时会被赋值为各自的滑动平均值
ema = tf.train.ExponentialMovingAverage(mnist_backward.MOVING_AVERAGE_DECAY)
ema_restore = ema.variables_to_restore()
saver = tf.train.Saver(ema_restore)
#计算正确率
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
while True:
with tf.Session() as sess:
#加载训练好的模型ckpt,也就是把滑动平均值赋给各个参数
ckpt = tf.train.get_checkpoint_state(mnist_backward.MODEL_SAVE_PATH)
#如果已有ckpt模型则恢复
if ckpt and ckpt.model_checkpoint_path:
#恢复会话
saver.restore(sess,ckpt.model_checkpoint_path)
#恢复轮数
global_step = int(ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1])
#计算准确率
accuracy_score = sess.run(accuracy,feed_dict = {x:mnist.test.images,y_:mnist.test.labels})
#打印提示
print("After %s training step(s),test accuracy = %g"%(global_step,accuracy_score))
#如果没有模型
else:
print('No checkpoint file found') #模型不存在提示
return
time.sleep(TEST_INTERVAL_SECS)
def main():
mnist = input_data.read_data_sets("mnist_work/", one_hot=True)
test(mnist)
if __name__ == '__main__':
main()
【注意:
对于global_step = int(ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1])
这句话,课程中原本是global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
,但是在实际运行代码结果时,会出现这样的结果:
我们发现训练轮数并没有改变,而老师在print("After %s training step(s),test accuracy = %g"%(global_step,accuracy_score))
中使用的占位符也是%s,即为一个字符串string占位,但事实并非如此。现在大部分人使用的都是python3.x,在python2.x中老师的例子是成功的,训练轮数在改变,但是在我们这里就不适用了,所以要改成:global_step = int(ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1])
将字符串变作整形,这样训练轮数才会改变:
我竟然把这个debug出来了,哭泣】
先运行mnist_backward.py文件
首先下载了数据集(无视WARNING):
开始训练且从打印出来的内容可以看到loss在减小(仅显示首尾部分训练过程):
运行mnist_test.py文件(运行了一段时间mnist_backward文件后,再同时运行mnist_test.py文件。这样可以同时观察准确率的提高),最后准确率达到:
第五讲至此结束。