什么是Tensorflow?
“TensorFlow™ 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。”
简单地说,Tensorflow允许你使用一系列张量和张量之间的各种计算关系组织一张计算图,用以描述各种算法的数据关系。计算图或者「数据流图」是一张有向图,节点为张量(Tensors)或者操作(Operations),边为节点之间的连接关系。
Tensorflow的强大之处在于自动求取微分,使得你在定义神经网络ForwardPropagation部分之后,不必手动编写BackwardPropagation部分代码;此外,它集成了常见的操作,如卷积,池化,丢弃(dropout)等,各种激活函数,如sigmoid,ReLU,softmax等,这可以使你专注模型结构的搭建,无需考虑手动编写各种数学操作。
如果你对Tensorflow搭建神经网络模型有兴趣的话,可以试试这个Tensorflow游乐场:https://playground.tensorflow.org
什么是MNIST?
MNIST数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST)。 训练集由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局的工作人员, 测试集也是同样比例的手写数字数据。
MNIST手写体数字识别,这个任务相当于是机器学习中的HelloWorld程序,给定输入为28*28*1的图片,输出为10的向量标签(one-hot编码)。
环境配置
这个任务需要配置的环境十分简单我们使用Python3.6和Tensorflow1.5来实现。首先安装Python3.6和pip,之后安装tensorflow-gpu和matplotlib这两个包,使用
命令安装即可,当然使用Ananconda配置这个环境也可以。需要注意的是,Tensorflow1.5的GPU版本需要搭配CUDA9.0和cudnn7来使用。
然后开发环境随意,PyCharm、Eclipse甚至是Visual Studio,本示例使用VS2017进行编写并调试通过。GPU就是笔记本上的960M显卡,跑一下这种极小规模的网络还是完全没问题的。
模型说明
网络的架构十分简单,2个5*5卷积+2*2最大池化层,2个全连接层,除最后用Softmax分类外,其他层的激活函数均采用ReLU函数。
卷积层(Convolutional layer)的目的是提取输入的不同特征,前几层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网路能从低级特征中迭代提取更复杂的特征。
池化(Pooling)是卷积神经网络中另一个重要的概念,它实际上是一种形式的降采样。最大池化层是将输入的图像划分为若干个矩形区域,对每个子区域输出最大值。直觉上,这种机制能够有效地原因在于,在发现一个特征之后,它的精确位置远不及它和其他特征的相对位置的关系重要。池化层会不断地减小数据的空间大小,因此参数的数量和计算量也会下降,这在一定程度上也控制了过拟合。通常来说,CNN的卷积层之间都会周期性地插入池化层。
线性整流函数(Rectified Linear Unit, ReLU)又称修正线性单元,就是单位斜变函数,是一种人工神经网络中常用的激活函数,
线性整流被认为有一定的生物学原理,并且由于在实践中通常有着比其他常用激活函数(Sigmoid,tanh等)更好的效果,而被如今的深度神经网络广泛使用于诸如图像识别等领域。
编写代码
首先从引入环境开始,在我们的编辑器中写下:
这些代码将tensorflow、numpy、matplotlib引入程序,就像在类C语言程序中引用库函数一样。之后设定一些模型与算法的基本参数,如下:
这里我们定义了一个函数load_data()用于将MNIST数据集下载好并载入程序。设定参数INPUT_NODE与OUTPUT_NODE为输入数据与输出数据的维度,EPOCHS_MAX为训练时期数,定为10次,就是对整个训练数据集完成遍历10次。BATCH_SIZE为应用batch方法进行训练的批的大小,这里是512。之后的LEARNING_RATE、BETA1、BETA2以及EPSILON为AdaM优化算法的参数。
接下来定义了网络的初始结构,x、y和keep_prob作为外界的输入量,用tf.placeholder进行占位操作。x为输入,维数由INPUT_NODE确定,y为输出,维数由OUTPUT_NODE决定。然后初始化卷积层和全连接层的权重矩阵和偏置矩阵,5*5卷积核确定好之后的其他矩阵的维度不难计算。
定义网络结构,将输入的向量重新展开成28*28的图像,之后分别定义卷积层1和2的结构以及其中各个张量的连接关系。卷积层分为线性卷积操作(其实根据卓老师课上讲的应该叫相关),ReLU(单位斜变,线性整流)函数完成非线性映射,接入最大池化层进行降采样。两层卷积后进行一波dropout,这个技巧是防止模型过拟合于给定的训练数据,而降低其泛化能力的问题出现的关键。dropout通过随机地将某些神经元的输出置零使得过拟合的问题的得到缓解,控制因子为keep_prob。之后接入两层全连接层做分类,最后使用Softmax函数将神经原输出压缩到(0,1)之间,给出分类的结果。
在这个部分,我们定义了损失函数为交叉熵,这种损失函数常见于分类问题。训练步骤为使用Adam优化算法使得损失函数最小。定义预测的有效与否为比较输出与ground truth,并将这个量的平均值作为准确率用于评估。
这一部分用于执行实际训练,with语句启动了一个Session用于执行计算图,实际的训练步骤在sess.run()函数中完成,而每执行一次epoch使用eval函数计算训练准确率。注意到实际训练时keep_prob赋值为0.5,而测试时为1.0,这是因为我们不希望在评估准确率时也进行dropout操作。训练的过程是从训练集中取出BATCH_SIZE个数据分批次进行训练,合理的批次大小可以提升训练速度。
训练结束之后在交叉验证集上进行准确率测试,来评估这个模型。
最后将测试集导出,并送入这个模型,抽取其中的10张图片可以查看模型实际的效果。
运行程序
在VS中按下F5直接运行,在Tensorflow的提示信息之外,可以得到如下结果:
这是训练过程的情况,首先程序提取了数据集,然后经过10个Epoch我们看到准确率逐渐上升,达到1也许是因为训练数据量本身不大。整个过程持续大概3分钟,如果加大训练的Epoch数量,时间会变得更长。
交叉验证的结果是0.9932的准确率,也就是说这个模型的准确率是可以接受的。接下来看几个实际的识别结果:
任务完成?
事实上,MNIST手写数字识别只是一种"Hello World!"般的存在,真正的计算机视觉所面临问题要更加复杂,所应用的网络模型也更加宏大而设计精巧,所涉及的算法和学习技巧也更加丰富而强大。但尽管如此,通过一个简单的MNIST示例上手Tensorflow这一强大的深度学习工具也是一次十分有趣的体验。愿我们以此为起点,开启对更广阔领域的探索,发现人工智能的新乐趣。
本文代码可见于https://github.com/Vladimir2506/MNIST-Demo
本文基于tensorflow教程,可在https://www.tensorfly.cn/tfdoc/tutorials/overview.html找到所有样例。
本文的其他相关代码,可见
https://github.com/yhlleo/mnist
供稿 | 夏卓凡
THU自动化系学生科协(ASTA)信息推送平台,搭建科研之路,带你走向人生巅峰