02-基于TensorFlow的浅层全连接网络的手写字体识别

一、写在前面
本篇将延续并拓展第一篇“01-基于TensorFlow的自定数据的三分类案例”的内容,依托于开源数据集MNIST,编写此篇内容仅为了加强自身学习和巩固知识点,并做一些简单的分享。

二、数据集介绍与获取
MNIST数据集是一种入门级的计算机视觉数据集。其包含各种手写数字图片,图片种类为10类,分别是0~9。该数据集的下载地址为MNIST数据集,可以在官网中手动通过浏览器下载该数据集,也可以通过代码段的读取来下载数据集,推荐使用第一种方式。
这里写图片描述
可以看出该数据集由四个文件组成,从图片信息的描述我们可以看出来上面两个文件是训练集图片和标签,下面两个文件是测试集文件和标签,测试集命名中含有10k,是不是就是其代表着有10k条数据集样本和标签呢?
我们首先把从浏览器下载到的数据集放在当前目录下面的子目录MNIST_data下面,然后通过以下代码来读取和打印数据集信息。

mnist=input_data.read_data_sets('MNIST_data/',one_hot=False)
print(mnist)
===========================================================
==>Datasets(train=<tensorflow.contrib.learn.python.learn.datasets.mnist.DataSet object at 0x7faa6295a438>, 
validation=<tensorflow.contrib.learn.python.learn.datasets.mnist.DataSet object at 0x7fa9d834d080>, 
test=<tensorflow.contrib.learn.python.learn.datasets.mnist.DataSet object at 0x7fa9d837ba20>)

.
通过以上信息我们可以看出整个数据集中间有三个部分的数据集合,分别为train(训练集)、validation(验证集)和test(测试集),并且该数据集的对象保存在相应的存储器地址上。

print(mnist.train.images.shape)
print(mnist.train.labels.shape)
print(mnist.test.images.shape)
print(mnist.test.labels.shape)
print(mnist.validation.images.shape)
print(mnist.validation.labels.shape)

这里写图片描述
这里打印出了数据集信息,显而易见的是MNIST数据集中的训练集有55000条,测试集有10000条,验证集有5000条。每一条数据的维度都是784,这个是因为MNIST数据集中的图片是28×28Pixel,所以,每一幅图就是1行784(28×28)的数据,数据的每一个维度分别表示一个像素点的坐标。
这里有一个小问题,我们是为了完成一个10分类的任务,但是标签的数据只有一个维度,单维度的标签值既无法计算得出softmax的值,也就无法用于损失函数的计算。所以我们必须把标签的值拓展到10个维度数,所以这里就用到了one-hot编码。编码内容是,当图片属于当前维度数则置一,不属于当前维度数则置0,这样便很容易的区分出图片的类别。在代码中我们只需要更改mnist=input_data.read_data_sets('MNIST_data/',one_hot=False)mnist=input_data.read_data_sets('MNIST_data/',one_hot=True) 即可。当然还可以采取另外一种方式,这个在第一篇中有写,就是采用OneHotEncoder 的方式进行转换。这样再次打印,我们便可以发现MNIST的标签维度数的变动。
这里写图片描述

三、网络构建
笔者这里原本选定为三隐层的全连接网络,但是在实际的迭代过程中出现了梯度消失的情况,因为参数一开始笔者是用tf.zeros给定测试的,图片如下所示:
这里写图片描述
由该图可以发现,所给定的第一层隐层的权重参数并没有变化,所以这里修改为双隐层的全连接网络模型来做手写字体的分类。这里虽然有极大的可能是笔者代码的问题,但是由于懒,就先放一放了.

#网络构建(2隐层,单层64节点数的模型构建)
hidden_1=64
hidden_2=64
input_m=784
output_m=10
learning_rate=0.01

#定义输入占位符
x=tf.placeholder(tf.float32,[None,input_m],name='x_ph')
y=tf.placeholder(tf.float32,[None,output_m],name='y_ph')

#构建初始化w,b
weights={
    "w1":tf.Variable(tf.random_normal([input_m,hidden_1],stddev=0.1),dtype=tf.float32,name='w1'),
    "w2":tf.Variable(tf.random_normal([hidden_1,hidden_2],stddev=0.1),tf.float32,name='w2'),
    #"w3":tf.Variable(tf.random_normal([hidden_2,hidden_3],stddev=0.1),tf.float32,name='w3'),
    "out":tf.Variable(tf.random_normal([hidden_2,output_m],stddev=0.1),tf.float32,name='out')
}
b={
    'b1':tf.Variable(tf.zeros([hidden_1]),tf.float32,name='b1'),
    'b2':tf.Variable(tf.zeros([hidden_2]),tf.float32,name='b2'),
    #'b3':tf.Variable(tf.zeros([hidden_3]),tf.float32,name='b3'),
    'b3':tf.Variable(tf.zeros([output_m]),tf.float32,name='b3')
}

layer1=tf.nn.sigmoid(tf.add(tf.matmul(x,weights['w1']),b['b1']))
layer2=tf.nn.sigmoid(tf.add(tf.matmul(layer1,weights['w2']),b['b2']))
#layer3=tf.nn.sigmoid(tf.add(tf.matmul(layer2,weights['w3']),b['b3']))
out=tf.add(tf.matmul(layer2,weights['out']),b['b3'])

.
由上述代码我们可以知道,它的前向传播(FP)网络结构为:

 1. 输入层                    [1,784]
 2. 第一层隐层                 np.dot([1,784],[784,64])=[1,64]
 3. 第二层隐层                 np.dot([1,64],[64,64])=[1,64]
 4. 输出层                     np.dot([1,64],[64,10])=[1,10]

.
最后便可以得出1行10列的one-hot编码形式的分类信息。但是计算出来的结果显然是不正确的,这里需要进行计算交叉熵损失,然后通过梯度下降最小化误差。

#获取softmax的分类概率值
predict=tf.nn.softmax(out)

#计算交叉熵损失函数
loss=tf.reduce_mean(-tf.reduce_sum(y*tf.log(predict),axis=1))

#使用梯度下降求解,最小化误差
train=tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(loss)

.
然后再计算正确率和相关训练信息的定义

#计算正确率
equal=tf.equal(tf.argmax(predict,axis=1),tf.argmax(y,axis=1))
correct_rate=tf.reduce_mean(tf.cast(equal,tf.float32))

print_num=1

#Session()准备信息
init=tf.global_variables_initializer()

training_epochs=100
batch_size=100
batch_num=int(mnist.train.num_examples/batch_size)   #55000/100

saver=tf.train.Saver()

四.迭代训练
创建Session(),开始迭代训练,并打印出输出值

#创建Session()
with tf.Session(config=tf.ConfigProto(log_device_placement=True,allow_soft_placement=True)) as sess:
    sess.run(init)

    for epoch in range(training_epochs):
        average_loss=0
        train_correct_rate=0
        for i in range(batch_num):
            #通过next_batch来获取下一个batch_size的数据
            batch_x,batch_y=mnist.train.next_batch(batch_size)
            sess.run(train,feed_dict={x:batch_x,y:batch_y})
            average_loss+=sess.run(loss,feed_dict={x:batch_x,y:batch_y})/batch_num
            #train_correct_rate+=sess.run(correct_rate,feed_dict={x:batch_x,y:batch_y})/batch_num
            #print("correct_rate:{}".format(train_correct_rate))
        if (epoch+1)%print_num==0:
            #print("epoch:{},average_loss:{},train_correct_rate:{}".format(epoch+1,average_loss,train_correct_rate))
            test_correct_rate=sess.run(correct_rate,feed_dict={x:mnist.test.images,y:mnist.test.labels})
            print("Epoch:{},损失值:{}".format(epoch,average_loss))
            print("测试集准确率:%.9f"%test_correct_rate)
            #print("w={}".format(sess.run(weights)))
            # if(test_correct_rate>=0.95):
    saver.save(sess,'model/model.ckpt')
    print(sess.run([weights,b]))
print("训练完成")

.
这里展示一下最终的训练和测试信息
这里写图片描述

五、实际预测
这里为了不频繁进行训练数据集,这里在当前目录下新建一个.py的文件,并从文件读取到我们之前已经训练好的模型。

saver=tf.train.Saver()
with tf.Session() as sess:
    saver.restore(sess,'model/model.ckpt')

.
为了使用之前的网络结构,这里还需要把上面定义的网络结构放到这个代码中,这里的参数通过模型的读取后会变成我们经过训练之后的参数,所以这里不需要再次进行训练。然后我们需要从测试数据中提取99张图片信息。并转化为数据集原来的形状。

mnist=input_data.read_data_sets('MNIST_data/',one_hot=False)
test_x=mnist.test.images[1:100]
test_lb=mnist.test.labels[1:100]
#print(test_x)
print(test_lb)
test_x=test_x.reshape(99,-1,28)
#print(test_x[0])
# test_test=OneHotEncoder(sparse=True).fit_transform(test_lb.reshape([-1,1])).toarray()
# print(test_test)

.
然后在Session()中预测图片的类别,并依次打印出信息。

    array=[]
    array.append(tf.argmax(sess.run(predict,feed_dict={x:test_x.reshape(-1,784),y:OneHotEncoder(sparse=True).fit_transform(test_lb.reshape([-1,1])).toarray()}),axis=1))

    for i in range(99):
        print("图片如下所示,实际值为:{}".format(test_lb[i]))
        test_label_array=sess.run(array)
        print("预测值为:{}".format(test_label_array[0][i]))
        pylab.imshow(test_x[i])
        pylab.show()

.
最后附5张预测贴图
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
.
经过统计得出,99次判断中仅有6次为错误判断,所贴的第3张图即为一种错误判断,可以判断出该模型基本符合预期的正确率。由于training_epochs的值仅为100,所以在增加迭代次数的基础上,准确率还是可以有所提高的,这个就请读者自己尝试啦。

.
参考文献:李金洪 [2018] 《深度学习之TensorFlow入门、原理与进阶实战》 ISBN 978-7-111-59005-7

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值