4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)

一、神经元模型 1. 单神经元模型 典型的神经元由“线性方程+激活函数”组合而成,如下图所示

4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)
文章图片
用方程表示即为

2. 激活函数 激活函数是一些非线性方程,目的就是令模型非线性化,以增强模型的表达能力。其他常见的激活函数有Sigmoid、tanh、ReLU等。
(1) Sigmoid函数
也称为S型函数


4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)
文章图片

(2) tanh函数
也称为双曲正切函数


4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)
文章图片

形状与Sigmoid很像,但是最大特点是均值是0
(3) ReLU函数


4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)
文章图片

特点是简单高效,使用非常普遍。
3. 单隐藏层神经网络 下图是一个全连接的单隐藏层神经网络

4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)
文章图片

全连接指相邻两层的节点在横向都有连接(纵向是不连接的)。一个神经网络只有一个输入层和一个输出层,夹在它们之间的就是隐藏层。一般说神经网络有几层,指的就是隐藏层有几层。
含有多个神经元的网络模型比单神经元模型有更好的性能,这次我们就用单层神经网络来解决4.1节的MNIST手写数字识别问题。
二、载入MNIST数据 同4.1节一样的方法读取MNIST数据集

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

三、构建神经网络 1. 构建输入层 其实就是定义特征和标签的占位符,同4.1节一样
x=tf.placeholder(tf.float32,[None,784],name='x') #28*28个像素点的灰度图 y=tf.placeholder(tf.float32,[None,10],name='y') #10个类别以One Hot编码表示

2. 构建隐藏层 这里我们设置单隐藏层包含256各神经元——这是一个超参数,可根据经验和试验来调整。构建隐藏层其实也就是定义变量和前向计算。输入层的每一行经过隐藏层处理后,应得到与隐藏层神经元数量相同结果,即256个。所以权重w1的形状应该是(784,256),偏置b1是(256,)。初始化变量w1随机、b1全0。最后使用激活函数(这里选用ReLU)做非线性处理
H1_NN=256 #单隐藏层神经元的数量w1=tf.Variable(tf.random_normal([784, H1_NN]),name='w1') b1=tf.Variable(tf.zeros([H1_NN]),name='b1')y1=tf.nn.relu(tf.matmul(x,w1)+b1)

3. 构建输出层 中间层的每行有256个元素,而输出的结果是One Hot编码,即每个example(即每一行)输出有10个元素。因此权重w2的形状应该是(256,10),偏置b2是(10,)。初始化变量也是w2随机、b2全0。最后用Softmax函数做分类处理后获得预测值
#构建输出层 w2=tf.Variable(tf.random_normal([H1_NN,10]),name='w2') b2=tf.Variable(tf.zeros(10),name='b2')forward=tf.matmul(y1,w2)+b2 pred=tf.nn.softmax(forward)

4. 定义损失函数 与4.1节一样采用交叉熵损失函数。同样直接调用TensorFlow自带的Softmax交叉熵函数,以免pred接近0时候因log(0)计算问题而出现loss为NaN的情况
loss_function=tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits_v2(logits=forward, labels=y))

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

和4.1节一样准备使用Mini-batch来训练,所以先设置每次样本批量大小为100。
6. 选择优化器 为了对比效果,选择与4.1节同样的梯度下降优化器
optimizer=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_function)

7. 定义准确率 用准确率来观察训练效果(不参与训练),定义同4.1节一样
correct_prediction=tf.equal(tf.argmax(pred,1),tf.argmax(y,1)) accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

8. 训练模型 训练模型与4.1节完全一样,不再赘述。代码如下
loss_rec=[] #记录loss acc_rec=[] #记录accuracy 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!')

运行后结果如下
Epoch: 00 Loss= 8.453162193 Accuracy= 0.8014 Epoch: 01 Loss= 6.002198219 Accuracy= 0.8440 Epoch: 02 Loss= 4.908858776 Accuracy= 0.8598 Epoch: 03 Loss= 4.289498329 Accuracy= 0.8720 Epoch: 04 Loss= 3.805552006 Accuracy= 0.8786 Epoch: 05 Loss= 3.509700775 Accuracy= 0.8822 Epoch: 06 Loss= 3.172487736 Accuracy= 0.8876 Epoch: 07 Loss= 2.975183487 Accuracy= 0.8922 Epoch: 08 Loss= 2.777935266 Accuracy= 0.8928 Epoch: 09 Loss= 2.647064924 Accuracy= 0.8964 Epoch: 10 Loss= 2.491706610 Accuracy= 0.8982 Epoch: 11 Loss= 2.356211185 Accuracy= 0.9006 Epoch: 12 Loss= 2.266038895 Accuracy= 0.9034 Epoch: 13 Loss= 2.166581869 Accuracy= 0.9070 Epoch: 14 Loss= 2.059339762 Accuracy= 0.9088 Epoch: 15 Loss= 1.967479348 Accuracy= 0.9092 Epoch: 16 Loss= 1.916359901 Accuracy= 0.9118 Epoch: 17 Loss= 1.848771930 Accuracy= 0.9132 Epoch: 18 Loss= 1.787561893 Accuracy= 0.9146 Epoch: 19 Loss= 1.738900423 Accuracy= 0.9148 Epoch: 20 Loss= 1.710683584 Accuracy= 0.9158 Epoch: 21 Loss= 1.639987946 Accuracy= 0.9158 Epoch: 22 Loss= 1.602546096 Accuracy= 0.9168 Epoch: 23 Loss= 1.573165417 Accuracy= 0.9162 Epoch: 24 Loss= 1.536014438 Accuracy= 0.9200 Epoch: 25 Loss= 1.483315110 Accuracy= 0.9186 Epoch: 26 Loss= 1.470939636 Accuracy= 0.9200 Epoch: 27 Loss= 1.428285122 Accuracy= 0.9208 Epoch: 28 Loss= 1.405486465 Accuracy= 0.9200 Epoch: 29 Loss= 1.390222907 Accuracy= 0.9230 Epoch: 30 Loss= 1.369023919 Accuracy= 0.9244 Epoch: 31 Loss= 1.328528285 Accuracy= 0.9240 Epoch: 32 Loss= 1.338596702 Accuracy= 0.9234 Epoch: 33 Loss= 1.297055244 Accuracy= 0.9234 Epoch: 34 Loss= 1.297160864 Accuracy= 0.9238 Epoch: 35 Loss= 1.269461513 Accuracy= 0.9254 Epoch: 36 Loss= 1.245946050 Accuracy= 0.9244 Epoch: 37 Loss= 1.249149561 Accuracy= 0.9252 Epoch: 38 Loss= 1.225231290 Accuracy= 0.9252 Epoch: 39 Loss= 1.210901022 Accuracy= 0.9260 Epoch: 40 Loss= 1.196688771 Accuracy= 0.9268 Epoch: 41 Loss= 1.180651903 Accuracy= 0.9256 Epoch: 42 Loss= 1.168871641 Accuracy= 0.9270 Epoch: 43 Loss= 1.152788639 Accuracy= 0.9272 Epoch: 44 Loss= 1.137835145 Accuracy= 0.9272 Epoch: 45 Loss= 1.130878806 Accuracy= 0.9280 Epoch: 46 Loss= 1.127356291 Accuracy= 0.9294 Epoch: 47 Loss= 1.111257315 Accuracy= 0.9284 Epoch: 48 Loss= 1.097258806 Accuracy= 0.9292 Epoch: 49 Loss= 1.097965121 Accuracy= 0.9276 Train finished!

验证集的准确度accuracy达到93%左右,高于4.1节的86%。但是会发现运行时间比4.1高,因为被训练参数多了很多。
四、评估训练结果 1. 测试集精度 和4.1节方法一样,计算已训练模型对整个测试集和整个训练集精度。在同一个session下添加代码
with tf.Session() as sess: ... #用测试集评估训练结果 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)

输出
Test accuracy: 0.9279 Train accuracy: 0.95405453

训练精度略高于验证精度和测试精度2%左右,说明略有过拟合,但是不严重。而且比4.1节测试集的准确度86%要高。
2. 找出错误预测 首先和4.1节一样,保存模型对测试集的所有预测,注意用tf.argmax函数把One Hot转换为实际数字(写在上面同一个session里
with tf.Session() as sess: ... #输出预测结果(把One Hot转换为实际数字) prediction_result=sess.run(tf.argmax(pred,1), feed_dict={x:mnist.test.images})

定义如下函数来打印所有错误预测和总数量
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))

调用函数,输入标签和预测值
print_predict_errs(mnist.test.labels, prediction_result)

输出
Inex=8 label=5 prediction=6 Inex=38 label=2 prediction=3 Inex=59 label=5 prediction=7 Inex=63 label=3 prediction=2 ... Inex=9975 label=3 prediction=8 Inex=9980 label=2 prediction=3 Inex=9982 label=5 prediction=3 Total: 721

3. 可视化结果 可视化函数的定义4.1节一样
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()

然后调用函数,查看测试集第5个example开始的15幅图片,并标出标签值和预测值
plot_images_labels_prediction(mnist.test.images, #测试集图像列表 mnist.test.labels, #测试集标签列表 prediction_result, #预测结果 5, #从第5条example开始显示 15) #显示15幅

输出

4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)
文章图片

【4.2|4.2 TensorFlow实战三(2)(MNIST手写数字识别问题-单层神经网络模型)】可见大部分标签值和预测值是一致的,但也有错误,如第一行第四幅。
附:完整代码
#定义函数:打印错误预测 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()#将数据保存到指定路径 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编码表示#构建单隐藏层 H1_NN=256 #单隐藏层神经元的数量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)#构建输出层 w2=tf.Variable(tf.random_normal([H1_NN,10]),name='w2') b2=tf.Variable(tf.zeros(10),name='b2')forward=tf.matmul(y1,w2)+b2 pred=tf.nn.softmax(forward)#定义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)#定义准确率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 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!')#用测试集评估训练结果 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幅

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

    推荐阅读