TensorFlow 实战 02:用 DNN 做 MNIST 手写数字识别

学一门新的语言,第一个实战例子总是打印 hello world;在机器学习领域,也有一个 hello world,那就是 MNIST 手写数字识别——当然,它可比打印字符串有意义多了。

什么是 MNIST

MNIST 是深度学习巨巨 Yann LeCun 维护的一套手写数字图像数据库,包含 60000 个训练样本和 10000 个测试样本,所有这些图像已经做过规范化及居中处理,拥有同样的固定尺寸,使用起来非常方便。下面是其中几个例子。

MNIST

我们要做什么

很明显,我们马上要开始编写第一个 TensorFlow 程序,一个有实际应用价值的深度神经网络(DNN)——虽然只有 2 个隐层,做 MNIST 手写数字识别,484 很鸡冻……

Let's do it, but how?

万事开头难,迈出第一步非常关键。为了避免陷入对无限发散的未知知识点(技能树)的恐慌,我们尽量将注意力集中在主线任务上,旁枝末节以后慢慢看,当然,楼主假设你还是会写基本的 Python 的。废话不多说,动手吧!

读取 MNIST 数据

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

input_data 封装了 MNIST 数据的下载、解析功能,read_data_sets() 返回三部分数据:55000 个训练样本 mnist.train,5000 个验证样本 mnist.validation(注意到没有,这 5000 是从原始的 60000 个训练样本上拿出来的),10000 个测试样本 mnist.test

每个样本都包含一个手写数字图像 x 和一个对应的标签 y,训练集的图像和标签可以通过 mnist.train.imagesmnist.train.labels 读取,验证集和测试集同理。图像的尺寸是 28x28 像素,每个像素值代表 [0,1] 的笔划强度,我们可以把图像数据理解为一个长度为 784 的数组,也就是一个 784 维的向量。标签的取值为 [0,9] ,表示从 0 到 9 这 10 个数字,这里我们把标签处理成 10 维 one-hot 向量以方便对应 DNN 的输出。

建立 DNN 模型

接下来我们搭建一个拥有 2 个隐层的 DNN 模型,第一层拥有 1024 个神经元,第二层拥有 625 个神经元,使用 ReLU 作为激活函数,并在输入层和隐层各层都使用 dropout 机制避免模型发生过拟合,这些概念暂时不懂也没有关系,把实验做完,不懂的再去查。

import tensorflow as tf

X = tf.placeholder(tf.float32, [None, 784])
Y = tf.placeholder(tf.float32, [None, 10])
p_keep_input = tf.placeholder(tf.float32)
p_keep_hidden = tf.placeholder(tf.float32)


def init_weights(shape):
    return tf.Variable(tf.random_normal(shape, stddev=0.01))


w_h = init_weights([784, 1024])
w_h2 = init_weights([1024, 625])
w_o = init_weights([625, 10])


def model(X, w_h, w_h2, w_o, p_keep_input, p_keep_hidden):
    X = tf.nn.dropout(X, p_keep_input)

    h = tf.nn.relu(tf.matmul(X, w_h))
    h = tf.nn.dropout(h, p_keep_hidden)

    h2 = tf.nn.relu(tf.matmul(h, w_h2))
    h2 = tf.nn.dropout(h2, p_keep_hidden)

    return tf.matmul(h2, w_o)


py_x = model(X, w_h, w_h2, w_o, p_keep_input, p_keep_hidden)

训练模型

为了训练模型,必须有一个指标衡量模型的好坏,这个指标就是损失(loss 或 cost),loss 越接近于 0 表明模型的输出越接近于真实的标签,我们选择最常用的交叉熵 cross-entropy 作为损失函数, RMSProp 优化算法做梯度下降

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)

评估模型

我们把模型输出的标签与真实的标签进行比较,并将比较结果转换为一个取值 [0, 1] 的浮点数作为准确率指标。

predict_acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(py_x, 1), tf.argmax(Y, 1)), tf.float32))

接下来,我们该启动程序了:

epoch_count = 20000
batch_size = 50
keep_input = 0.8
keep_hidden = 0.75

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    step = 0
    for i in xrange(epoch_count):
        step += 1
        batch_x, batch_y = mnist.train.next_batch(batch_size)
        sess.run(train_op, feed_dict={X: batch_x, Y: batch_y, p_keep_input: keep_input, p_keep_hidden: keep_hidden})
        if step % 100 == 0:
            loss, acc = sess.run([cost, predict_acc], feed_dict={X: batch_x, Y: batch_y, p_keep_input: 1., p_keep_hidden: 1.})
            print("Epoch: {}".format(step), "\tLoss: {:.6f}".format(loss), "\tTraining Accuracy: {:.5f}".format(acc))
    print("Testing Accuracy: {:0.5f}".format(sess.run(predict_acc, feed_dict={X: mnist.test.images, Y: mnist.test.labels, p_keep_input: 1., p_keep_hidden: 1.})))

整个训练过程共 20000 步,每步使用 50 个一组的随机样本做训练,每训练 100 步输出一次训练准确率,全部训练结束后使用测试集 mnist.test 评估准确率。

我们的 DNN 模型很简单,同时也很争气,准确率大概是 98.26%,有没有感觉到一种成就感……

小提示

虽然实验做完了,结果也还算不错,但此刻我们应该冷静下来认真做一下回顾,把整篇文章中不太明白的概念整理出来,深入地去做一下理论知识学习。这里推荐一本免费的电子书:Neural Networks and Deep Learning,拿来入门非常合适。

0 Comments

No comments yet.

Leave a Reply