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

一、神经元模型

1. 单神经元模型

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


4.2 TensorFlow实战三(2):MNIST手写数字识别问题-单层神经网络模型_第1张图片

用方程表示即为

2. 激活函数

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

(1) Sigmoid函数

也称为S型函数

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

(2) tanh函数

也称为双曲正切函数

4.2 TensorFlow实战三(2):MNIST手写数字识别问题-单层神经网络模型_第3张图片

形状与Sigmoid很像,但是最大特点是均值是0

(3) ReLU函数


4.2 TensorFlow实战三(2):MNIST手写数字识别问题-单层神经网络模型_第4张图片

特点是简单高效,使用非常普遍。

3. 单隐藏层神经网络

下图是一个全连接的单隐藏层神经网络


4.2 TensorFlow实战三(2):MNIST手写数字识别问题-单层神经网络模型_第5张图片

全连接指相邻两层的节点在横向都有连接(纵向是不连接的)。一个神经网络只有一个输入层和一个输出层,夹在它们之间的就是隐藏层。一般说神经网络有几层,指的就是隐藏层有几层。

含有多个神经元的网络模型比单神经元模型有更好的性能,这次我们就用单层神经网络来解决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 TensorFlow实战三(2):MNIST手写数字识别问题-单层神经网络模型_第6张图片

可见大部分标签值和预测值是一致的,但也有错误,如第一行第四幅。

附:完整代码

#定义函数:打印错误预测
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

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