Tensorflow实现MNIST手写数字识别
之前我们讲了神经网络的起源、单层神经网络、多层神经网络的搭建过程、搭建时要注意到的具体问题、以及解决这些问题的具体方法。本文将通过一个经典的案例:MNIST手写数字识别,以代码的形式来为大家梳理一遍神经网络的整个过程。
一 、MNIST手写数字数据集介绍
MNIST手写数字数据集来源于是美国国家标准与技术研究所,是著名的公开数据集之一,通常这个数据集都会被作为深度学习的入门案例。数据集中的数字图片是由250个不同职业的人纯手写绘制,数据集获取的网址为:http://yann.lecun.com/exdb/mnist/。(下载后需解压)
具体来看,MNIST手写数字数据集包含有60000张图片作为训练集数据,10000张图片作为测试集数据,且每一个训练元素都是28*28像素的手写数字图片,每一张图片代表的是从0到9中的每个数字。该数据集样例如下图所示:

如果我们把每一张图片中的像素转换为向量,则得到长度为28*28=784的向量。因此我们可以把MNIST数据训练集看作是一个[60000,784]的张量,第一个维度表示图片的索引,第二个维度表示每张图片中的像素点。而图片里的每个像素点的值介于0-1之间。

此外,MNIST数据集的类标是介于0-9的数字,共10个类别。通常我们要用独热编码(One_Hot Encoding)的形式表示这些类标。所谓的独热编码,直观的讲就是用N个维度来对N个类别进行编码,并且对于每个类别,只有一个维度有效,记作数字1 ;其它维度均记作数字0。例如类标1表示为:([0,1,0,0,0,0,0,0,0,0]);同理标签2表示为:([0,0,1,0,0,0,0,0,0,0])。最后我们通过softmax函数输出的是每张图片属于10个类别的概率。
二 、网络结构的设计
接下里通过Tensorflow代码,实现MINIST手写数字识别的过程。首先,如程序1所示,我们导入程序所需要的库函数、数据集:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
接下来,我们读取MNIST数据集,并指定用one_hot的编码方式;然后定义batch_size、batch_num两个变量,分别代表一次性传入神经网络进行训练的批次大小,以及计算出训练的次数。如程序2所示:
程序2:
mnist_data=input_data.read_data_sets("MNIST.data",one_hot=True)
batch_size=100
batch_num=mnist_data.train.num_examples//batch_size
我们需要注意的是:在执行第一句命令时,就会从默认的地方下载MNIST数据集,下载下来的数据集会以压缩包的形式存到指定目录,如下图所示。这些数据分别代表了训练集、训练集标签、测试集、测试集标签。

接着我们定义两个placeholder,程序如下所示:
程序3:
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])
其中,x代表训练数据,y代表标签。具体来看,我们会把训练集中的图片以batch_size批次大小,分批传入到第一个参数中(默认为None);X的第二个参数代表把图片转换为长度为784的向量;Y的第二个参数表示10个不同的类标。
接下来我们就可以开始构建一个简单的神经网络了,首先定义各层的权重w和偏执b。如程序4所示:
程序4:
weights = {
'hidden_1': tf.Variable(tf.random_normal([784, 256])),
'out': tf.Variable(tf.random_normal([256, 10]))
}
biases = {
'b1': tf.Variable(tf.random_normal([256])),
'out': tf.Variable(tf.random_normal([10]))
}
因为我们准备搭建一个含有一个隐藏层结构的神经网络(当然也可以搭建两个或是多个隐层的神经网络),所以先要设置其每层的w和b。如上程序所示,该隐藏层含有256个神经元。接着我们就可以开始搭建每一层神经网络了:
程序5:
def neural_network(x):
hidden_layer_1 = tf.add(tf.matmul(x, weights['hidden_1']), biases['b1'])
out_layer = tf.matmul(hidden_layer_1, weights['out']) + biases['out']
return out_layer
如程序5所示,我们定义了一个含有一个隐藏层神经网络的函数neural_network,函数的返回值是输出层的输出结果。
接下来我们定义损失函数、优化器以及计算准确率的方法。
程序6:
#调用神经网络
result = neural_network(x)
#预测类别
prediction = tf.nn.softmax(result)
#平方差损失函数
loss = tf.reduce_mean(tf.square(y-prediction))
#梯度下降法
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)
#预测类标
correct_pred = tf.equal(tf.argmax(y,1),tf.argmax(prediction,1))
#计算准确率
accuracy = tf.reduce_mean(tf.cast(correct_pred,tf.float32))
#初始化变量
init = tf.global_variables_initializer()
如程序6所示:首先使用softmax函数对结果进行预测,然后选择平方差损失函数计算出loss,再使用梯度下降法的优化方法对loss进行最小化(梯度下降法的学习率设置为0.2)。接着使用argmax函数返回最大的值所在的位置,再使用equal函数与正确的类标进行比较,返回一个bool值,代表预测正确或错误的类标;最后使用cast函数把bool类型的预测结果转换为float类型(True转换为1,False转换为0),并对所有预测结果统计求平均值,算出最后的准确率。要注意:最后一定不要忘了对程序中的所有变量进行初始化。
最后一步,我们启动Tensorflow默认会话,执行上述过程。代码如下所示:
程序7:
step_num=400
with tf.Session() as sess:
sess.run(init)
for step in range(step_num+1):
for batch in range(batch_num):
batch_x,batch_y = mnist_data.train.next_batch(batch_size)
sess.run(train_step,feed_dict={x:batch_x,y:batch_y})
acc = sess.run(accuracy,feed_dict={x:mnist_data.test.images,y:mnist_data.test.labels})
print("Step " + str(step) + ",Training Accuracy "+ "{:.3f}" + str(acc))
print("Finished!")
上述程序定义了MNIST数据集的运行阶段,首先我们定义迭代的周期数,往往开始的时候准确率会随着迭代次数快速提高,但渐渐地随着迭代次数的增加,准确率提升的幅度会越来越小。而对于每一轮的迭代过程,我们用不同批次的图片进行训练,每次训练100张图片,每次训练的图片数据和对应的标签分别保存在 batch_x、batch_y中,接着再用run方法执行这个迭代过程,并使用feed_dict的字典结构填充每次的训练数据。循环往复上述过程,直到最后一轮的训练结束。
最后我们利用测试集的数据检验训练的准确率,feed_dict填充的数据分别是测试集的图片数据和测试集图片对应的标签。输出结果迭代次数和准确率,完成训练过程。我们截取400次的训练结果,如下图所示:

以上我们便完成了MNIST手写数字识别模型的训练,接下来可以从以下几方面对模型进行改良和优化,以提高模型的准确率。
首先,在计算损失函数时,可以选择交叉熵损失函数来代替平方差损失函数,通常在Tensorflow深度学习中,softmax_cross_entropy_with_logits函数会和softmax函数搭配使用,是因为交叉熵在面对多分类问题时,迭代过程中权值和偏置值的调整更加合理,模型收敛的速度更加快,训练的的效果也更加好。代码如下所示:
程序8:
######预测类别
prediction = tf.nn.softmax(result)
######交叉熵损失函数
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=prediction))
######梯度下降法
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)
#####预测类标
correct_pred = tf.equal(tf.argmax(y,1),tf.argmax(prediction,1))
#####计算准确率
accuracy = tf.reduce_mean(tf.cast(correct_pred,tf.float32))
如程序8所示:我们把两个参数:类标y以及模型的预测值prediction,传入到交叉熵损失函数softmax_cross_entropy_with_logits中,然后对函数的输出结果求平均值,再使用梯度下降法进行优化。最终的准确率如下图所示:

我们可以明显看到,使用交叉熵损失函数对于模型准确率的提高还是显而易见的,训练过程迭代200次的准确率已经超过了平方差损失函数迭代400次的准确率。
除了改变损失函数,我们还可以改变优化算法。例如使用adam优化算法代替随机梯度下降法,因为它的收敛速度要比随机梯度下降更快,这样也能够使准确率有所提高。如下程序所示,我们使用学习率为0.001的AdamOptimizer作为优化算法(其它部分不变):
程序9:
#Adam优化算法
train_step = tf.train.AdamOptimizer(1e-2).minimize(loss)
此外,如果你了解过拟合的概念,那么很容易可以联想到测试集准确率不高的原因,可能是因为训练过程中发生了“过拟合”的现象。所以我们可以从防止过拟合的角度出发,提高模型的准确率。我们可以采用增加数据量或是增加正则化项的方式,来缓解过拟合。这里,我们为大家介绍dropout的方式是如何缓解过拟合的。
Dropout是在每次神经网络的训练过程中,使得部分神经元工作而另外一部分神经元不工作。而测试的时候激活所有神经元,用所有的神经元进行测试。这样便可以有效的缓解过拟合,提高模型的准确率。具体代码如下所示:
程序10:
def neural_network(x):
hidden_layer_1 = tf.add(tf.matmul(x, weights['hidden_1']), biases['b1'])
L1 = tf.nn.tanh(hidden_layer_1)
dropout1 = tf.nn.dropout(L1,0.5)
out_layer = tf.matmul(dropout1, weights['out']) + biases['out']
return out_layer
如程序10所示,我们在隐藏层后接了dropout,随机关掉50%的神经元,最后的测试结果如下图所示,我们发现准确率取得了显著的提高,在神经网络结构中没有添加卷积层和池化层的情况下,准确率达到了92%以上。

Tensorflow实现MNIST手写数字识别的更多相关文章
- Android+TensorFlow+CNN+MNIST 手写数字识别实现
Android+TensorFlow+CNN+MNIST 手写数字识别实现 SkySeraph 2018 Email:skyseraph00#163.com 更多精彩请直接访问SkySeraph个人站 ...
- 基于tensorflow的MNIST手写数字识别(二)--入门篇
http://www.jianshu.com/p/4195577585e6 基于tensorflow的MNIST手写字识别(一)--白话卷积神经网络模型 基于tensorflow的MNIST手写数字识 ...
- 基于TensorFlow的MNIST手写数字识别-初级
一:MNIST数据集 下载地址 MNIST是一个包含很多手写数字图片的数据集,一共4个二进制压缩文件 分别是test set images,test set labels,training se ...
- Tensorflow之MNIST手写数字识别:分类问题(1)
一.MNIST数据集读取 one hot 独热编码独热编码是一种稀疏向量,其中:一个向量设为1,其他元素均设为0.独热编码常用于表示拥有有限个可能值的字符串或标识符优点: 1.将离散特征的取值扩展 ...
- Tensorflow之MNIST手写数字识别:分类问题(2)
整体代码: #数据读取 import tensorflow as tf import matplotlib.pyplot as plt import numpy as np from tensorfl ...
- 基于TensorFlow的MNIST手写数字识别-深入
构建多层卷积神经网络时需要多组W和偏移项b,我们封装2个方法来产生W和b 初级MNIST中用0初始化W和b,这里用噪声初始化进行对称打破,防止产生梯度0,同时用一个小的正值来初始化b避免dead ne ...
- TensorFlow——MNIST手写数字识别
MNIST手写数字识别 MNIST数据集介绍和下载:http://yann.lecun.com/exdb/mnist/ 一.数据集介绍: MNIST是一个入门级的计算机视觉数据集 下载下来的数据集 ...
- mnist手写数字识别——深度学习入门项目(tensorflow+keras+Sequential模型)
前言 今天记录一下深度学习的另外一个入门项目——<mnist数据集手写数字识别>,这是一个入门必备的学习案例,主要使用了tensorflow下的keras网络结构的Sequential模型 ...
- mnist 手写数字识别
mnist 手写数字识别三大步骤 1.定义分类模型2.训练模型3.评价模型 import tensorflow as tfimport input_datamnist = input_data.rea ...
随机推荐
- LeetCode 225题用队列实现栈(Implement Stack using Queues) Java语言求解
链接 https://leetcode-cn.com/problems/implement-stack-using-queues/ 思路 首先演示push()操作:将元素依次进入队1,进入时用top元 ...
- tomcat&servlet初记
tomcat&servlet初记 1. web相关概念 2. web服务器软件的学习:tomcat 3. servlet入门学习 web相关概念 1,软件架构 1,cs架构:客户端/服务器端 ...
- 《javascript高级程序设计》笔记:文档模式
文档模式是用于指定浏览器使用什么样的标准来正确的显示网页,各个标准的解析存在着差异 文档类型的分类 文档模式大致分为三种类型: 混杂模式(quirks mode) 标准模式(standards mod ...
- 处理 Vue 单页面 SEO 的另一种思路
vue-meta-info 官方地址: https://github.com/monkeyWang... (设置vue 单页面meta info信息,如果需要单页面SEO,可以和 prerender- ...
- input type="file"使用
问题: 在实际开发过程中,会遇到上传文件的一些需求.但是使用原生的<input type="file" />在使用中存在一些问题 在未上传文件时,显示"no ...
- docker 学习(四)
1.Dockerfile简介 1)什么是Dockerfile Dockerfile是一个包含用于组合映像的命令的文本文档.可以使用在命令行中调用任何命令. Docker通过读取Dockerfile中的 ...
- 看逐浪CMS技术小哥做SVG动画(附使用Bodymovin和Lottie将Adobe After Effects(AE)程式转为 HTML5/Android/iOS原生的动画全过程-即AE转svg\canvas\html5动画)
名词解解释 adobe After Effects AE:adobe After Effects,adobe公司的专业视频制作软件. Bodymovin插件预览 Bodymovin:是一个AE的插 ...
- openwrt 外挂usb 网卡 RTL8188CU 及添加 RT5572 kernel支持
RT5572 原来叫 Ralink雷凌 现在被 MTK 收购了,淘宝上买的很便宜50块邮,2.4 5G 双频.在 win10 上插了试试,果然是支持 5G.这上面写着 飞荣 是什么牌子,有知道的和我说 ...
- 编译 ijg JPEG V8 库 GIF 库
libjpeg-turbo-1.2.1太老了,不支持,从内存解压,这里编译支持 jpeg_mem_src 的 JPEG V9 wget http://www.ijg.org/files/jpegsrc ...
- SpringBoot内置的各种Starter是怎样构建的?--SpringBoot源码(六)
注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 温故而知新 本篇接 外部配置属性值是如何被绑定到XxxProperties类属性上的?--SpringBoot源码(五) 温 ...