4.3 TensorFlow实战三(3):MNIST手写数字识别问题-多层神经网络模型

一、多层神经网络解决MNIST问题

1. 构建多层神经网络模型

在4.2节我们使用了单层神经网络来解决MNIST手写数字识别问题,提高了识别性能。很容易想到,能否增加隐藏层数量来进一步提高模型预测的的准确率。这一节我们尝试构建两层神经网络模型。


代码方面,只需要修改隐藏层构建到输出层构建的一部分即可

#构建多隐藏层(2层)
H1_NN=256 #第1隐藏层神经元的数量
w1=tf.Variable(tf.random_normal([784, H1_NN]),name='w1')
b1=tf.Variable(tf.zeros([H1_NN]),name='b1')
z1=tf.matmul(x,w1)+b1
y1=tf.nn.relu(z1) #z1,y1的shape(none,H1_NN)

H2_NN=64 #第2隐藏层神经元的数量
w2=tf.Variable(tf.random_normal([H1_NN, H2_NN]),name='w2')
b2=tf.Variable(tf.zeros([H2_NN]),name='b2')
z2=tf.matmul(y1,w2)+b2
y2=tf.nn.relu(z2) #z2,y2的shape(none,H2_NN)

#构建输出层
w3=tf.Variable(tf.random_normal([H2_NN,10]),name='w3')
b3=tf.Variable(tf.zeros(10),name='b3')

forward=tf.matmul(y2,w3)+b3
pred=tf.nn.softmax(forward) #forward,pred的shape(none,10)

可见第2层神经网络构建的方法和第1层完全一样,只是神经元个数变为64。须要注意的是输入、变量和输出的形状:
第1隐藏层
输入 x是(none, 784);
变量 w1是(784, 256),b是(256,);
输出 z1是(none, 256),y1和z1一样
第2隐藏层
输入 x是(none, 784);
变量 w1是(784, 256),b是(256,);
输出 z1是(none, 256),y1和z1一样

2. 结果输出和分析

运行结果如下(为简洁不再打印错误预测)

Epoch: 00 Loss= 2.863374233 Accuracy= 0.5942
Epoch: 01 Loss= 1.736896873 Accuracy= 0.5742
Epoch: 02 Loss= 1.537292242 Accuracy= 0.5942
Epoch: 03 Loss= 1.360914946 Accuracy= 0.6322
Epoch: 04 Loss= 1.317842960 Accuracy= 0.6406
Epoch: 05 Loss= 1.197029829 Accuracy= 0.6716
Epoch: 06 Loss= 1.208800435 Accuracy= 0.6686
Epoch: 07 Loss= 1.142614245 Accuracy= 0.6818
Epoch: 08 Loss= 1.026190042 Accuracy= 0.7250
Epoch: 09 Loss= 0.983034194 Accuracy= 0.7254
Epoch: 10 Loss= 0.983637333 Accuracy= 0.7290
Epoch: 11 Loss= 0.998256624 Accuracy= 0.7438
Epoch: 12 Loss= 0.869820714 Accuracy= 0.7650
Epoch: 13 Loss= 0.850869536 Accuracy= 0.7696
Epoch: 14 Loss= 0.890440524 Accuracy= 0.7496
Epoch: 15 Loss= 0.820125401 Accuracy= 0.7812
Epoch: 16 Loss= 0.809774816 Accuracy= 0.7812
Epoch: 17 Loss= 0.942296088 Accuracy= 0.7712
Epoch: 18 Loss= 0.853635132 Accuracy= 0.7614
Epoch: 19 Loss= 0.745617390 Accuracy= 0.7986
Epoch: 20 Loss= 0.784108520 Accuracy= 0.7900
Epoch: 21 Loss= 0.697698414 Accuracy= 0.8162
Epoch: 22 Loss= 0.722510159 Accuracy= 0.8082
Epoch: 23 Loss= 0.780704618 Accuracy= 0.7998
Epoch: 24 Loss= 0.698084891 Accuracy= 0.8168
Epoch: 25 Loss= 0.703638196 Accuracy= 0.8140
Epoch: 26 Loss= 0.722524762 Accuracy= 0.7930
Epoch: 27 Loss= 0.660884261 Accuracy= 0.8320
Epoch: 28 Loss= 0.717998624 Accuracy= 0.8208
Epoch: 29 Loss= 0.682168424 Accuracy= 0.8248
Epoch: 30 Loss= 0.656583905 Accuracy= 0.8312
Epoch: 31 Loss= 0.690692604 Accuracy= 0.8186
Epoch: 32 Loss= 0.696775079 Accuracy= 0.8288
Epoch: 33 Loss= 0.645569742 Accuracy= 0.8392
Epoch: 34 Loss= 0.627729952 Accuracy= 0.8388
Epoch: 35 Loss= 0.727829814 Accuracy= 0.8236
Epoch: 36 Loss= 0.622332513 Accuracy= 0.8444
Epoch: 37 Loss= 0.599031925 Accuracy= 0.8490
Epoch: 38 Loss= 0.609254837 Accuracy= 0.8512
Epoch: 39 Loss= 0.612630367 Accuracy= 0.8484
Epoch: 40 Loss= 0.593185604 Accuracy= 0.8512
Epoch: 41 Loss= 0.616054416 Accuracy= 0.8438
Epoch: 42 Loss= 0.625913560 Accuracy= 0.8492
Epoch: 43 Loss= 0.561142325 Accuracy= 0.8638
Epoch: 44 Loss= 0.609652936 Accuracy= 0.8400
Epoch: 45 Loss= 0.602623045 Accuracy= 0.8464
Epoch: 46 Loss= 0.577677369 Accuracy= 0.8582
Epoch: 47 Loss= 0.567311525 Accuracy= 0.8576
Epoch: 48 Loss= 0.581219554 Accuracy= 0.8572
Epoch: 49 Loss= 0.560301960 Accuracy= 0.8652
Train finished!
Test accuracy: 0.8605
Train accuracy: 0.8614182

会发现,结果并不如单层神经网络模型——单层的三种accuracy均超过92%。

3. 模型优化

上面的结果说明,神经网络的层数并不是越多越好,可能结果更差,而且会因训练难度增大而降低训练效率。要提高模型性能,应选择合适的超调量和处理函数,包括神经元个数、神经网络层数、学习率、激活函数、损失函数、优化器等等,这就需要反复调试优化的过程。
比如我们在上面的基础上,使用Adam优化器来代替梯度下降优化器

#选择优化器(Adam)
optimizer=tf.train.AdamOptimizer(learning_rate).minimize(loss_function)

其他设置全都不变,运行结果为

Epoch: 00 Loss= 1.359953761 Accuracy= 0.6332
Epoch: 01 Loss= 1.098942995 Accuracy= 0.7356
Epoch: 02 Loss= 0.869957805 Accuracy= 0.7574
Epoch: 03 Loss= 0.788484216 Accuracy= 0.8142
Epoch: 04 Loss= 0.707541227 Accuracy= 0.8494
Epoch: 05 Loss= 0.589031637 Accuracy= 0.8624
Epoch: 06 Loss= 0.428204983 Accuracy= 0.8966
Epoch: 07 Loss= 0.393920511 Accuracy= 0.8982
Epoch: 08 Loss= 0.357491851 Accuracy= 0.9168
Epoch: 09 Loss= 0.261545718 Accuracy= 0.9296
Epoch: 10 Loss= 0.283064246 Accuracy= 0.9242
Epoch: 11 Loss= 0.242876947 Accuracy= 0.9420
Epoch: 12 Loss= 0.270045996 Accuracy= 0.9394
Epoch: 13 Loss= 0.243025586 Accuracy= 0.9442
Epoch: 14 Loss= 0.227809906 Accuracy= 0.9498
Epoch: 15 Loss= 0.194676608 Accuracy= 0.9514
Epoch: 16 Loss= 0.294435561 Accuracy= 0.9396
Epoch: 17 Loss= 0.246601224 Accuracy= 0.9568
Epoch: 18 Loss= 0.232908204 Accuracy= 0.9574
Epoch: 19 Loss= 0.220450491 Accuracy= 0.9580
Epoch: 20 Loss= 0.228046492 Accuracy= 0.9578
Epoch: 21 Loss= 0.255377233 Accuracy= 0.9566
Epoch: 22 Loss= 0.228124321 Accuracy= 0.9612
Epoch: 23 Loss= 0.232139006 Accuracy= 0.9586
Epoch: 24 Loss= 0.206230372 Accuracy= 0.9596
Epoch: 25 Loss= 0.335204601 Accuracy= 0.9600
Epoch: 26 Loss= 0.269430697 Accuracy= 0.9590
Epoch: 27 Loss= 0.188264996 Accuracy= 0.9606
Epoch: 28 Loss= 0.199349329 Accuracy= 0.9690
Epoch: 29 Loss= 0.246823296 Accuracy= 0.9636
Epoch: 30 Loss= 0.250041127 Accuracy= 0.9608
Epoch: 31 Loss= 0.240920603 Accuracy= 0.9610
Epoch: 32 Loss= 0.240424410 Accuracy= 0.9646
Epoch: 33 Loss= 0.208946735 Accuracy= 0.9714
Epoch: 34 Loss= 0.290345877 Accuracy= 0.9682
Epoch: 35 Loss= 0.231242090 Accuracy= 0.9718
Epoch: 36 Loss= 0.208569869 Accuracy= 0.9706
Epoch: 37 Loss= 0.242159471 Accuracy= 0.9606
Epoch: 38 Loss= 0.297732055 Accuracy= 0.9660
Epoch: 39 Loss= 0.285338253 Accuracy= 0.9686
Epoch: 40 Loss= 0.240815669 Accuracy= 0.9680
Epoch: 41 Loss= 0.273060739 Accuracy= 0.9636
Epoch: 42 Loss= 0.318290919 Accuracy= 0.9718
Epoch: 43 Loss= 0.264123738 Accuracy= 0.9666
Epoch: 44 Loss= 0.282791466 Accuracy= 0.9630
Epoch: 45 Loss= 0.248179272 Accuracy= 0.9718
Epoch: 46 Loss= 0.325113714 Accuracy= 0.9680
Epoch: 47 Loss= 0.343051672 Accuracy= 0.9720
Epoch: 48 Loss= 0.345522314 Accuracy= 0.9554
Epoch: 49 Loss= 0.290476650 Accuracy= 0.9674
Train finished!
Test accuracy: 0.973
Train accuracy: 0.9878

可见,准确度超过了97%,效果有了极大提升。

二、优化代码

每构建一层神经网络就要写如下代码

H1_NN=256 #第1隐藏层神经元的数量
w1=tf.Variable(tf.random_normal([784, H1_NN]),name='w1')
b1=tf.Variable(tf.zeros([H1_NN]),name='b1')
z1=tf.matmul(x,w1)+b1
y1=tf.nn.relu(z1) #z1,y1的shape(none,H1_NN)

比较麻烦,可以写一个函数将其封装

#定义全连接层函数
def fcn_layer(inputs, #输入数据
              input_dim, #输入神经元数量
              output_dim, #输出神经元数量
              w_name,
              b_name,
              activation=None): #激活函数(不设置则不使用)
    w=tf.Variable(tf.truncated_normal([input_dim, output_dim],#以阶段正态分布随机初始化
                                      stddev=0.1),name=w_name)#偏置0.1
    b=tf.Variable(tf.zeros([output_dim]),name=b_name) ##以0初始化b
    z=tf.matmul(inputs,w)+b
    if activation is None:
        return z
    else:
        return activation(z)

这样,本文最开始定义神经网络的代码就可以简写为

#构建多隐藏层(2层)
y1=fcn_layer(x,784,256,'w1','b1',tf.nn.relu) #构建第1隐藏层
y2=fcn_layer(y1,256,64,'w2','b2',tf.nn.relu) #构建第2隐藏层

#构建输出层
forward=fcn_layer(y2,64,10,'w3','b3') #前向传播
pred=tf.nn.softmax(forward) #forward,pred的shape(none,10)

三、保存和还原模型

1. 保存模型

训练好的模型变量只在会话期间有效。我们需要保存,在需要使用时再还原。
在打开会话之前,先创建一个saver对象

#创建saver对象以保存模型文件
saver=tf.train.Saver()

然后在会话中模型训练完成之后保存模型

with tf.Session() as sess:
    ...
    # 保存模型
    saver.save(sess,'./ckpt_dir/mnist_model.ckpt')
    print('Model saved!')

运行后,会在脚本所在目录的ckpt_dir文件夹内看到如下四个文件

checkpoint
mnist_model.ckpt.data-00000-of-00001
mnist_model.ckpt.index
mnist_model.ckpt.meta

说明保存成功

2. 还原模型

新建一个脚本,先定义神经网络

import tensorflow as tf

#定义全连接层函数
def fcn_layer(inputs, #输入数据
              input_dim, #输入神经元数量
              output_dim, #输出神经元数量
              w_name,
              b_name,
              activation=None): #激活函数(不设置则不使用)
    w=tf.Variable(tf.truncated_normal([input_dim, output_dim],#以阶段正态分布随机初始化
                                      stddev=0.1),name=w_name)#偏置0.1
    b=tf.Variable(tf.zeros([output_dim]),name=b_name) ##以0初始化b
    z=tf.matmul(inputs,w)+b
    if activation is None:
        return z
    else:
        return activation(z)
    
#构建输入层(定义占位符)
x=tf.placeholder(tf.float32,[None,784],name='x') #28*28个像素点的灰度图
y=tf.placeholder(tf.float32,[None,10],name='y') #10个类别以One Hot编码表示

#构建多隐藏层(2层)
y1=fcn_layer(x,784,256,'w1','b1',tf.nn.relu) #构建第1隐藏层
y2=fcn_layer(y1,256,64,'w2','b2',tf.nn.relu) #构建第2隐藏层

#构建输出层
forward=fcn_layer(y2,64,10,'w3','b3') #前向传播
pred=tf.nn.softmax(forward) #forward,pred的shape(none,10)

定义准确度并引入MNIST以引入examples做对比

#定义准确率accuracy
correct_prediction=tf.equal(tf.argmax(pred,1),tf.argmax(y,1))
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
import tensorflow.examples.tutorials.mnist.input_data as input_data
#将数据保存到指定路径
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

要注意变量名('x', 'y', 'w1', 'b1'等),一定要和之前的脚本一样!
创建saver对象以读取模型

#创建saver对象以读取模型文件
saver=tf.train.Saver()

建立和运行session以还原模型,并计算测试集准确度来验证效果

with tf.Session() as sess:
    init=tf.global_variables_initializer()
    sess.run(init) #变量初始化
    
    ckpt=tf.train.get_checkpoint_state('./ckpt_dir/')
    saver.restore(sess,ckpt.model_checkpoint_path) #从已保存的模型中读取参数
    
    #训练集精度
    acc_test=sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels})
    print('Train accuracy:',acc_test)

输出

Train accuracy: 0.973

和之前的结果一样,说明正确。

附1:完整代码 - MNIST多层神经网络建立和保存

import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
import numpy as np
import matplotlib.pyplot as plt

#定义函数:打印错误预测
def print_predict_errs(labels, #标签值列表(One Hot)
                       prediction_list): #预测值列表(实际数字)
    #找出错误预测
    label_list=np.argmax(labels,1)
    compare_list=prediction_list==label_list
    error_list=np.nonzero((~compare_list).astype(int))
    error_list=list(error_list[0]) #转换为列表
    for k in error_list:
        print('Inex=%d label=%d prediction=%d'
              %(k,label_list[k],prediction_list[k]))
    print('Total:',len(error_list))
    
#定义可视化函数(查看训练完成后结果)
def plot_images_labels_prediction(images, #图像列表
                                  labels, #标签列表
                                  prediction, #预测值
                                  index, #从第index个开始显示
                                  num=10): #显示几幅图像,缺省10
    fig=plt.gcf() #获取当前图表,Get Current Figure
    fig.set_size_inches(10,12) #设置图像尺寸,1inch=2.54cm
    if num>25:
        num=25 #限制最多显示25个图像
    for i in range(0,num):
        ax=plt.subplot(5,5, i+1) #获取当前要处理的子图
        ax.imshow(np.reshape(images[index],(28,28)),cmap='binary') #显示第index个图像
        title='label='+str(np.argmax(labels[index])) #在当前图title上显示信息
        if len(prediction)>0:
            title+=',predict'+str(prediction[index])
        ax.set_title(title,fontsize=10) #显示title信息
        ax.set_xticks([]) #不显示坐标轴
        ax.set_yticks([])
        index+=1
    plt.show()
    
#定义全连接层函数
def fcn_layer(inputs, #输入数据
              input_dim, #输入神经元数量
              output_dim, #输出神经元数量
              w_name,
              b_name,
              activation=None): #激活函数(不设置则不使用)
    w=tf.Variable(tf.truncated_normal([input_dim, output_dim],#以阶段正态分布随机初始化
                                      stddev=0.1),name=w_name)#偏置0.1
    b=tf.Variable(tf.zeros([output_dim]),name=b_name) ##以0初始化b
    z=tf.matmul(inputs,w)+b
    if activation is None:
        return z
    else:
        return activation(z)
    

#将数据保存到指定路径
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

#构建输入层(定义占位符)
x=tf.placeholder(tf.float32,[None,784],name='x') #28*28个像素点的灰度图
y=tf.placeholder(tf.float32,[None,10],name='y') #10个类别以One Hot编码表示

#构建多隐藏层(2层)
#H1_NN=256 #第1隐藏层神经元的数量
#w1=tf.Variable(tf.random_normal([784, H1_NN]),name='w1')
#b1=tf.Variable(tf.zeros([H1_NN]),name='b1')
#z1=tf.matmul(x,w1)+b1
#y1=tf.nn.relu(z1) #z1,y1的shape(none,H1_NN)
y1=fcn_layer(x,784,256,'w1','b1',tf.nn.relu) #构建第1隐藏层

#H2_NN=64 #第2隐藏层神经元的数量
#w2=tf.Variable(tf.random_normal([H1_NN, H2_NN]),name='w2')
#b2=tf.Variable(tf.zeros([H2_NN]),name='b2')
#z2=tf.matmul(y1,w2)+b2
#y2=tf.nn.relu(z2) #z2,y2的shape(none,H2_NN)
y2=fcn_layer(y1,256,64,'w2','b2',tf.nn.relu) #构建第2隐藏层

#构建输出层
#w3=tf.Variable(tf.random_normal([H2_NN,10]),name='w3')
#b3=tf.Variable(tf.zeros(10),name='b3')

#forward=tf.matmul(y2,w3)+b3
forward=fcn_layer(y2,64,10,'w3','b3') #前向传播
pred=tf.nn.softmax(forward) #forward,pred的shape(none,10)

#定义Softmax交叉熵损失函数
loss_function=tf.reduce_mean(
        tf.nn.softmax_cross_entropy_with_logits_v2(logits=forward,
                                                   labels=y))

#设置超参数
train_epochs=50 #训练轮数
learning_rate=0.01 #学习率
batch_size=100 #单次训练样本批量大小

#选择优化器(SGD)
#optimizer=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_function)
#选择优化器(Adam)
optimizer=tf.train.AdamOptimizer(learning_rate).minimize(loss_function)

#定义准确率accuracy
correct_prediction=tf.equal(tf.argmax(pred,1),tf.argmax(y,1))
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

#训练模型
loss_rec=[] #记录loss
acc_rec=[] #记录accuracy

#创建saver对象以保存模型文件
saver=tf.train.Saver()

with tf.Session() as sess:
    init=tf.global_variables_initializer()
    sess.run(init) #变量初始化
    
    total_batch=int(mnist.train.num_examples/batch_size) #一轮训练多少批次
    for epoch in range(train_epochs):
        for batch in range(total_batch):
            xs,ys=mnist.train.next_batch(batch_size) #读取小批量数据
            sess.run(optimizer,feed_dict={x:xs,y:ys}) #执行训练
            
        #每轮训练完成,使用验证集计算loss值和accuracy,并记录
        loss,acc=sess.run([loss_function,accuracy],
                          feed_dict={x:mnist.validation.images,
                                     y:mnist.validation.labels})
        loss_rec.append(loss)
        acc_rec.append(acc)
        
        #打印每轮训练结果的信息
        print('Epoch:','%02d'%(epoch),'Loss=','{:.9f}'.format(loss),
              'Accuracy=','{:.4f}'.format(acc))
    #训练结束
    print('Train finished!')
    
    # 保存模型
    saver.save(sess,'./ckpt_dir/mnist_model.ckpt')
    print('Model saved!')
    
    #用测试集评估训练结果
    acc_test=sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels})
    print('Test accuracy:',acc_test)
    #训练集精度
    acc_train=sess.run(accuracy,feed_dict={x:mnist.train.images,y:mnist.train.labels})
    print('Train accuracy:',acc_train)

    #输出预测结果(把One Hot转换为实际数字)
    prediction_result=sess.run(tf.argmax(pred,1),
                               feed_dict={x:mnist.test.images})
    
    

#打印错误预测
#print_predict_errs(mnist.test.labels, prediction_result)

#查看测试集index=5开始的15幅图片的结果
plot_images_labels_prediction(mnist.test.images, #测试集图像列表
                              mnist.test.labels, #测试集标签列表
                              prediction_result, #预测结果
                              5, #从index=5的example开始显示
                              15) #显示15幅

附2:完整代码 - MNIST多层神经网络还原

import tensorflow as tf

#定义全连接层函数
def fcn_layer(inputs, #输入数据
              input_dim, #输入神经元数量
              output_dim, #输出神经元数量
              w_name,
              b_name,
              activation=None): #激活函数(不设置则不使用)
    w=tf.Variable(tf.truncated_normal([input_dim, output_dim],#以阶段正态分布随机初始化
                                      stddev=0.1),name=w_name)#偏置0.1
    b=tf.Variable(tf.zeros([output_dim]),name=b_name) ##以0初始化b
    z=tf.matmul(inputs,w)+b
    if activation is None:
        return z
    else:
        return activation(z)
    
#构建输入层(定义占位符)
x=tf.placeholder(tf.float32,[None,784],name='x') #28*28个像素点的灰度图
y=tf.placeholder(tf.float32,[None,10],name='y') #10个类别以One Hot编码表示

#构建多隐藏层(2层)
y1=fcn_layer(x,784,256,'w1','b1',tf.nn.relu) #构建第1隐藏层
y2=fcn_layer(y1,256,64,'w2','b2',tf.nn.relu) #构建第2隐藏层

#构建输出层
forward=fcn_layer(y2,64,10,'w3','b3') #前向传播
pred=tf.nn.softmax(forward) #forward,pred的shape(none,10)

#定义准确率accuracy
correct_prediction=tf.equal(tf.argmax(pred,1),tf.argmax(y,1))
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
import tensorflow.examples.tutorials.mnist.input_data as input_data
#将数据保存到指定路径
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

#创建saver对象以读取模型文件
saver=tf.train.Saver()

with tf.Session() as sess:
    init=tf.global_variables_initializer()
    sess.run(init) #变量初始化
    
    ckpt=tf.train.get_checkpoint_state('./ckpt_dir/')
    saver.restore(sess,ckpt.model_checkpoint_path) #从已保存的模型中读取参数
    
    #训练集精度
    acc_test=sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels})
    print('Train accuracy:',acc_test)

Reference:
https://www.icourse163.org/learn/ZUCC-1206146808#/learn/content?type=detail&id=1214536583&cid=1218338042

你可能感兴趣的:(4.3 TensorFlow实战三(3):MNIST手写数字识别问题-多层神经网络模型)