一、多层神经网络解决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