首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一步步提高手写数字的识别率(2)

一步步提高手写数字的识别率(2)

作者头像
云水木石
发布2019-07-01 17:26:45
7720
发布2019-07-01 17:26:45
举报

在前面一篇文章《一步步提高手写数字的识别率(1)》中,我们使用Softmax回归实现了一个简单的手写数字识别程序,在MNIST数据集上的准确率大约为92%。这是一个线性模型,其特点是简单易用,但拟合能力不强。而深度神经网络在线性模型的基础上引入隐藏层,并增加非线性激活函数,使得拟合复杂函数的能力大大增强。

隐藏层是神经网络的一个重要概念,它是指输入、输出层之外的中间层。输入层和输出层是对外可见的,因此也被称作可视层,而中间层不直接暴露出来,是模型的黑箱部分,通常也比较难具有可解释性,所以一般称作隐藏层。

隐藏层需要使用非线性激活函数,有理论可以证明,如果不采用非线性激活函数,神经网络不论有多少节点、多少层,最后组合出来的依然是线性模型,这显然不是我们所希望的。引入非线性激活函数后,理论上只要隐藏节点足够多,即使只有一个隐藏层的神经网络也可以拟合任意函数。同时隐藏层越多,越容易拟合复杂函数。

针对手写数字识别问题,我们加上一个隐藏层,验证能否在识别准确率上有所改善。

加载MNIST数据集

参考上一篇文章,第一步我们还是加载MNIST数据集。

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tfmnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
sess = tf.InteractiveSession()
定义神经网络模型

相较于Softmax回归模型,我们添加一个隐藏层,并使用非线性函数ReLU进行激活,从而构建一个非常浅的深度神经网络,隐藏层的节点数设定为30,模型如下图所示:

ReLU是一个简单的非线性函数,其公式为y = max(0, x)。别小看这样一个简单的函数,在ReLU采用之前,人们通常使用sigmoid函数作为激活函数,但使用sigmoid函数存在梯度消失的问题,特别是神经网络的层数很多时,直到ReLU的出现,才比较完美的解决了梯度消失的问题。目前,ReLU及其变种(EIU, PReLU, RReLUS)已经成了最主流的激活函数。

in_units = 784
h1_units = 300
W1 = tf.Variable(tf.truncated_normal([in_units, h1_units], stddev=0.1))
b1 = tf.Variable(tf.zeros([h1_units]))
W2 = tf.Variable(tf.zeros([h1_units, 10]))
b2 = tf.Variable(tf.zeros([10]))

in_units是输入节点数,其值为28 x 28 = 784,h1_units为隐藏层的节点数,通常指定为一个固定值300。W1和b1是隐藏层的权重和偏置,和Softmax回归模型不同的是,这里的W1不能初始化为全零,否则会引起对称问题,所以这里将权重初始化为截断的正态分布,其标准差为0.1。因为有了非线性激活,所以W2初始化为零不会引起对称问题。至于偏置,都初始化为0即可。

x = tf.placeholder(tf.float32, [None, in_units])
keep_prob = tf.placeholder(tf.float32)hidden1 = tf.nn.relu(tf.matmul(x, W1) + b1)
hidden1_drop = tf.nn.dropout(hidden1, keep_prob)
y = tf.nn.softmax(tf.matmul(hidden1_drop, W2) + b2)

深度神经网络拟合效果好,但存在一个很令人头疼的问题:过拟合,即模型预测准确率在训练集上升高,但是在测试集上反而下降,这通常意味着泛化效果不好,模型只是记忆了当前数据的特征,不具备推广能力。为了解决这一问题,人们提出了一个思路简单但是非常有效的方法:Dropout。Dropout就是在训练时,将网络某一层的输出节点数据随机丢弃一部分,但在测试阶段,我们不希望输出结果是随机的,所以测试阶段不能随机丢弃。

调用tf.nn.dropout可以非常容易的实现Dropout功能,keep_prob参数指定保留数据的比例,在训练阶段其值应该小于1,制造随机性,防止过拟合,在测试阶段,keep_prob值为1。这里定义为placeholder,这样在训练阶段和测试阶段可以输入不同的值。

到目前位置,我们完成了定义算法公式,接下来就是定义损失函数和选择优化器。

定义loss,选择优化器

在这里,我们仍然选择交叉熵作为损失函数,但优化器选择自适应的优化器Adagrad,并把学习率设为0.3。

y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), 1))
train_step = tf.train.AdagradOptimizer(0.3).minimize(cross_entropy)

可以看出这段代码和上一篇文章中的Softmax回归算法差不多,将优化器替换为Adagrad,只是将函数更换了,其它不变。

迭代训练 & 模型评估

这一步和Softmax回归算法一致,除了多了一个keep_prob输入,代码如下:

tf.global_variables_initializer().run()
for i in range(3000)
   batch_xs, batch_ys = mnist.train.next_batch(100)
   train_step.run({x: batch_xs, y_: batch_ys, keep_prob: 0.75})correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
acurracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy, eval({x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

最终,我们在测试集上可以达到98%的准确率,相比之前的Softmax回归,只增加了一个隐藏层就获得了飞跃性的提升。

总结

多层神经网络依靠隐藏层,可以组合出高阶特征,比如横线、竖线、圆圈等,之后可以将这个高阶特征再组合成数字,实现精确的匹配和分类。隐藏层输出的高阶特征经常是可以复用的,所以每一类的判别、概率输出都共享这些高阶特征,而不是各自连接独立的高阶特征。

我们也可以发现,新增一个隐藏层,并使用了Dropout、Adagrad和ReLU,而代码并没有增加很多,这就是TensorFlow的优势。它的代码非常简洁,没有太多冗余,可以方便的将有用的模块拼装在一起。

当然使用全连接的深度神经网络也是有局限的,即使我们使用很深的网络、很多隐藏节点、进行很多次迭代,也很难在MNIST数据集上获得99%以上的准确率。这时就该卷积神经网络(CNN)出场了,在下一篇文章中,我们将使用卷积神经网络来提升手写数字的识别率。

参考
  1. TensorFlow实战,黄文坚、唐源著,电子工业出版社。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云水木石 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 加载MNIST数据集
  • 定义神经网络模型
  • 定义loss,选择优化器
  • 迭代训练 & 模型评估
  • 总结
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档