前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一步步提高手写数字的识别率(3)

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

作者头像
云水木石
发布2019-07-02 14:47:40
6470
发布2019-07-02 14:47:40
举报

在前面的两篇文章《一步步提高手写数字的识别率(1)》和《一步步提高手写数字的识别率(2)》中,我们分别介绍了使用Softmax回归和神经网络来实现手写数字识别,其准确率分别在92和98%左右,这在机器学习领域是一个非常不错的准确率,如果我们采用卷积神经网络,准确率还可以进一步提升。

在《一步步构建卷积模型》这篇文章中我们介绍了如何从头构建卷积模型,如果我们使用TensorFlow框架,我们就不需要这么麻烦,只需调用其中的卷积函数即可。不过在开始编程之前,还是简单介绍一下卷积神经网络。

卷积神经网络简介

卷积神经网络(Convolutional Neural Network, CNN)最初是为解决图像识别等问题设计的,到现在不仅限于图像和视频,也可用于时间序列信号,比如音频信号、文本数据等。CNN作为一个深度学习框架被提出的最初诉求,是降低对图像数据预处理的要求,以及避免复杂的特征工程。CNN可以直接使用图像的原始像素作为输入,减轻了使用传统算法必需做的大量重复、繁琐的数据预处理工作。CNN的最大特点在于卷积的权值共享结构,可以大幅减少神经网络的参数量,防止过拟合的同时又降低了神经网络的复杂度。

在卷积神经网络中,第一个卷积层会直接接受图像像素级的输入,每个卷积操作只处理一小块图像,进行卷积变化后再传到后面的网络,每一层卷积(也称作滤波器,filter)都会提取数据中最有效的特征。

一般卷积神经网络由多个卷积层构成,每个卷积层中通常会进行如下几个操作:

  1. 图像通过多个不同的卷积核的滤波,并加偏置(bias),提取局部特征,每个卷积核会映射出一个新的2D图像。
  2. 将卷积核的输出结果,进行非线性的激活函数(ReLU最常用)处理。
  3. 对激活函数的结果再进行池化操作,即降采样,一般采用最大池化方法,保留最显著的特征,并提升模型的畸变容错能力。

这几个步骤就构成了最常见的卷积层,我们还可以将多个卷积层拼在一起,构成卷积神经网络,比如下图就表示包含两个卷积层的卷积神经网络:

TensorFlow实现简单的卷积神经网络

卷积神经网络可以有很多层,比如大名鼎鼎的LeNet5,有一百多个卷积层,具有相当的复杂度。对于手写数字识别问题,我们当然不需要像LeNet5这样复杂的卷积神经网络,这里我们使用两个卷积层加一个全连接层构建一个简单但非常有代表性的卷积神经网络。

和前两个的TensorFlow程序一样,我们先加载MNIST数据集:

代码语言:javascript
复制
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()

接下来定义初始化函数初始化权重和偏置,我们需要给权重制造一些随机的噪声来打破对称,而给偏置增加一些小的正值(0.1)来避免死亡节点(dead neurons)。

代码语言:javascript
复制
def weight_variable(shape):
   initial = tf.truncated_normal(shape, stddev=0.1)
   return tf.Variable(initial)def bias_variable(shape):
   initial = tf.constant(0.1, shape=shape)
   return tf.Variable(initial)

接下来定义卷积层和池化层,TensorFlow中有二维卷积函数tf.nn.conv2d和池化函数tf.nn.max_pool,所以定义起来比较简单:

代码语言:javascript
复制
def conv2d(x, W):
   return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):
   return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

关于strides、padding等卷积运算的概念,请参考《一步步构建卷积模型》。

在正式设计卷积神经网络的结构之前,先定义输入的placeholder,x是特征,y_是真实的标签。因为卷积神经网络会利用到空间结构信息,因此需要将一维的输入向量x转化为2D的图片结构,即从1x784转化为原始的28x28的结构。

代码语言:javascript
复制
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])

接下来定义两个卷积层和全连接层:

代码语言:javascript
复制
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

为了减少过拟合,这里也需要使用到Dropout,正如上一篇文章中所讲到的,最后将Dropout层的输出连接一个Softmax层,得到最后的输出。

代码语言:javascript
复制
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

最后的步骤就是定义损失函数、选择优化器、迭代训练、评估结果,和前面两篇文章的步骤一致,这里就不再详细讨论了。

代码语言:javascript
复制
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))tf.global_variables_initializer().run()
for i in range(20000):
   batch = mnist.train.next_batch(50)
   if i % 100 == 0:
       train_accuracy = accuracy.eval(feed_dict={x:batch[0], y_: batch[1], keep_prob: 1.0})
       print("step %d, training accuracy %g" % (i, train_accuracy))
   train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})batch = mnist.test.next_batch(2000)
print("test accuracy %g" % accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0}))

注意这里没有使用全部的测试数据集,因为在我的GTX 960显卡下,一次性喂入所有的测试数据集,会出现内存不足的问题,所以这里只随机选择了2000个数据集。

总结

这个简单的卷积神经网络模型的准确率大约为99.2%,基本可以满足对手写数字识别准确率的要求。相比之前的深度神经网络2%的错误率,CNN的错误率下降了60%。这其中主要的性能提升都来自更优秀的网络模型设计,充分说明卷积网络对图像特征的提取和抽象能力。依靠卷积核的权值共享,CNN的参数数量并没有爆炸,降低计算量的同时也减轻了过拟合,整个模型的性能有着较大的提升。

至此,一个比较实用的手写数字识别程序就完成了,你也可以尝试增加几个卷积层,检验一下效果。这三篇文章中,我们先设计出了一个简单的机器学习模型,然后逐步优化模型。在实际工作中,我们也通常遵循这样一个流程。通过这一系列的文章,想必对TensorFlow的编程流程也有一定的掌握。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 卷积神经网络简介
  • TensorFlow实现简单的卷积神经网络
  • 总结
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档