面向专家的 MNIST 教程

我是一个很懒的人,我想试试

希望我能坚持到最后,把tensorflow的官方教程全部翻译出来

提高自己,也帮助他人

我的博客:终身学习者

Deep MNIST for Experts

TensorFlow 是一个能进行大规模数值计算的强大的库。它擅长的任务之一就是实现和训练深度神经网络。在本教程中,我们将学习构建基础的 TensorFlow 模块,并将构建深度卷积 MNIST 分类器。

本教程假设你已经熟悉了卷积神经网络和 MNIST 数据库。如果你不了解,那么你应该先看看面向机器学习初学者课程。请确保你在开始本教程之前已安装完TensorFlow

About this tutorial

本教程的第一部分解释了 mnist_softmax.py 代码的含义,这是一个简单的 Tensorflow 模型实现。第二部分介绍了几种方法来提高准确率。

你可以将本教程的每个代码片段分别复制粘贴到你的 Python 环境中,也可以下载整个实现代码 mnist_deep.py

本教程将完成以下内容:

  • 基于遍历图像的每个像素,创建一个 softmax 回归函数来识别 MNIST 数字模型
  • 使用 Tensorflow “查看”上千的样本来训练模型识别数字(并运行我们的第一个 Tensorflow 会话来完成)
  • 使用测试数据来检验模型准确性
  • 构建,训练和测试一个多层卷积网络和提升准确性

Setup

在我们创建模型前,我们首先载入 MNIST 数据集并开始一个 TensorFlow 会话。

Load MNIST Data

如果你打算从本教程中复制粘贴代码,请从以下两行代码开始,它将自动下载和读取数据:

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

这里 mnist 是一个轻量级的类,以 NumPy 数组的形式保存了训练,验证和测试三类数据集。它还提供了一个函数,通过小批量数据迭代,后面我们将会用到的。

Start TensorFlow InteractiveSession

TensorFlow 依赖于高效的 C++ 后端来完成它的计算。与后端的连接称为会话(session)。TensorFlow 程序常见用法是先创建一个图,然后在会话中启动它。

这里我们使用更加方便的InteractiveSession 类,它使得你构造你的代码时能够更灵活的使用 TensorFlow。它允许你在运行图的时候插入一个计算图。这在IPython等交互式环境下工作会显得特别的方便。如果你不使用InteractiveSession的话,你需要在开始会话并 启动图之前构建整个计算图。

import tensorflow as tf
sess = tf.InteractiveSession()

Computation Graph

为了使用 Python 进行高效的数值计算,我们通常使用诸如 NumPy这样的的库来做复杂的操作,例如矩阵乘法,它使用另一种高效的语言来实现。不幸的是,每一次运算的结果都会返回给 Python,这会带来许多额外的开销。如果你希望在GPU或者以分布式计算的方式来运行的话,这其中的转化数据的成本将非常高,带来的开销将更加糟糕。

TensorFlow 也在 Python 之外进行繁重的计算,但是为了避免这种开销,TensorFlow 做了更进一步的完善。TensorFlow 不在 Python 单独运行一个昂贵的操作,而是让我们描述一整个交互操作的图,然后再一起在 Python之外运行。这种方法类似于 Theano 或者 Torch 中使用的方法。

因此 Python 代码的作用是构建这个外部计算图,并决定运行哪部分的计算图。有关更多详细信息,请参考 TensorFlow 使用入门计算图部分。

Build a Softmax Regression Model

在这一部分我们将构建一个只有一个线性层的 softmax 回归模型。在下一部分,我们将扩展成一个有多层卷积的 softmax 回归模型。

Placeholders

我们通过为输入图像和目标输出类创建节点来构建计算图。

x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])

这里 xy_ 不是一个特定值。相反,他们都是占位符 (placeholder) ——是运行 TensorFlow 执行计算时我们输入的值。

输入图像 x 是一个2维浮点数张量。这里我们赋予它的 shape[None, 784],其中 784 是一个28×28的 MNIST 图像像素展开的维度。None表示第一维,与 batch 大小相关,可以是任意尺寸的。输出目标类y_也是一个2维张量,其中每一行是一个10维的 one-hot 向量表示对应的 MNIST 图像是属于哪个数字类别(0到9)。

placeholdershape参数是可选的,但是有了它可以使得 TensorFlow 能够自动捕捉张量形状不一致的错误。

Variables

现在我们定义了模型的权重 W 和偏置 b 。我们也可以设想把他们当作额外的输入,但是 TensorFlow 有一个更好的方式来处理它:VariableVariable是一个存在于 TensorFlow 计算图中的一个值。它可以在计算过程中被使用或者被修改。对于机器学习应用来说,通常将模型参数设置为 Variable

W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))

我们调用 tf.Variable给每个参数传入初始值。在这里,我们使用全为零的张量来初始化 WbW是一个 784×10 的矩阵(因为我们有784个特征和10个输出),b是一个10维的向量(因为我们有10个分类)。

在会话中使用变量前,我们必须先在会话中将其初始化。这一步使用具体的初始值(在这里使用全为零的张量),并赋值给每一个Variable。可以一次性为所有的Variables 完成这个操作:

sess.run(tf.global_variables_initializer())

Predicted Class and Loss Function

现在我们可以实现我们的回归模型了。这只需要短短的一行代码!我们将输入图像x向量化后乘以权重W,再加上偏置 b

y = tf.matmul(x,W) + b

我们可以很容易的指定一个损失函数。损失意味着模型预测一个样本的结果有多差;我们尝试在训练所有样本的过程中使其损失最小化。这里我们的损失函数是模型预测中 softmax 激活函数应用与目标类别之间的交叉熵。正如初级教程所示,我们使用稳定的版本:

cross_entropy = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

注意 tf.nn.softmax_cross_entropy_with_logits 在模型的非正则化模型预测的内部应用了 softmax 并对所有类求和,tf.reduce_mean取这些和的平均值。

Train the Model

现在我们定义了模型和训练损失函数,那么使用 TensorFlow 进行训练就很简单了。因为 TensorFlow 知道整个计算图,所以它可以使用自动微分找到相对每个变量的梯度损失。TensorFlow 内置了大量的优化算法。对于这个例子,我们使用最速梯度下降来降低交叉熵,其中步长为 0.5。

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

在这一行代码里,TensorFlow 实际上是给计算图增加一些新的操作。这些操作包括计算梯度,在每次更新中计算参数,并更新参数。

返回的train_step操作,在运行时会使用梯度下降来更新参数。因此训练模型可以通过重复运行train_step来完成。

for _ in range(1000):
  batch = mnist.train.next_batch(100)
  train_step.run(feed_dict={x: batch[0], y_: batch[1]})

我们在每次寻览迭代中加载 100 个训练样本。然后我们运行train_step操作,用 feed_dict 来使用训练样本替代placeholder张量中的xy_ 。注意你可以在你的计算图中使用feed_dict替代任意张量——不仅限于placeholder

Evaluate the Model

那么我们的模型性能如何?

首先,让我们找出那些我们预测正确的标签。 tf.argmax 是一个非常有用的函数,它能给你一个张量中某个维度上最大值的索引。例如, tf.argmax(y,1) 是 我们模型认为每个输入最有可能的标签,其中tf.argmax(y_,1) 是正确的标签。我们可以使用 tf.equal 来检验我们的预测与真实是否匹配。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

这段代码会给我们一组布尔值。为了确定正确的比例,我们将其转化为浮点数。例如,[True, False, True, True] 变成 [1,0,1,1] ,这样其正确率为0.75。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

最后我们可以在测试集上评估我们的准确性。大概为92%。

print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

Build a Multilayer Convolutional Network

在 MNIST 上得到92%的准确率是很差的。这实在是很糟糕。在这一部分我们将修改一下,从一个非常简单的模型跳到一个中等复杂的模型:一个小型卷积神经网络。这将给我们带来大概99.2%的准确度——虽然不是最好的,但也是可以令人接受的。

下面是一个由 TensorBoard 创建的我们将要构建的模型的图:

《面向专家的 MNIST 教程》 img

Weight Initialization

为了创建这个模型,我们需要创建大量的权重和偏置。一般我们是使用少量的噪声来初始化权重,以打破对称性并避免 0 梯度。由于我们使用 ReLU 神经元,因此使用微小的正整数来初始化偏置以避免 “dead neurons” 是一个好的方法。在构建模型时让我们创建两个方便的函数来初始化权重以免得重复做这件事。

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)

Convolution and Pooling

TensorFlow 在卷积和池化过程中给我们带来了很多的便利。如何处理边界?步长应该是多少?在这个例子里,我们总是选择 vanilla 版本。我们的卷积使用一个步长为 1,0 边距(zero padded),以便输入输出的大小相等。我们的池化使用简单的2×2大小的最大池化。为了保持代码的简洁,让我们抽象这些操作为函数。

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')

First Convolutional Layer

现在我们可以实现我们的第一层了。它由一个卷积连着一个最大池化构成。卷积在每个5×5 patch 上计算出 32 个特征。权重张量的形状是[5, 5, 1, 32]。头两个维度表示 patch 大小,第三个表示输入通道数目,最后一个表示输出通道数目。在每个输出通道都有一个对应的偏置向量。

W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

为了应用这个层我们先将x转为一个4维的张量,其中第二第三维度对应图像的宽和高,最后一维对应这彩色通道数目。

x_image = tf.reshape(x, [-1, 28, 28, 1])

然后我们使用权重张量卷积x_image,加上偏置,然后应用 ReLU 函数,最后进行最大池化。max_pool_2x2 方法将图像大小减小到 14×14 大小。

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

Second Convolutional Layer

为了构建一个深度网络,我们堆叠了几个这样类型的层。第二层在每个 5×5 patch 上将有 64 个特征。

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)

Densely Connected Layer

现在图像的尺寸减小到了 7×7 大小,我们增加了一个拥有1024个神经元的全连接层(fully-connected layer)来处理整个图像。我们从池化层出来的张量转为一些张量,乘以权重,加上偏置,再应用 ReLU。

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。我们创建一个placeholder用于在 dropout 层保持神经元输出的概率。这允许我们在训练时开启 dropout,在测试时关闭 dropout。TensorFlow 的 tf.nn.dropout操作除了可以屏蔽神经元输出外还可以自动处理神经元输出的scale,因此 dropout 工作的时候不需要额外的scale 操作1

keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

Readout Layer

最后我们增加一层,就像上面的 softmax 回归层。

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

Train and Evaluate the Model

这个模型的性能如何呢?为了训练和评估这个模型,我们将使用上面单层 SoftMax 网络相近的代码。

不同之处在于:

  • 我们将使用更复杂的 ADAM 优化算法来替代最速梯度下降优化算法。
  • 我们将添加额外的参数 keep_probfeed_dict 以控制 dropout 比例。
  • 我们将在训练过程中每迭代 100 次打印日志一次。

我们一样使用 tf.Session 而不是 tf.InteractiveSession。这更好的隔开创建图(模型指定)的过程和评估图(模型拟合)的过程。这也将使得代码简洁。tf.Session 是在一个 with block 中创建的,因此它将在离开时自动销毁。

运行这段代码,请注意它会进行 20000 次训练迭代,这将需要一段时间(有可能长达半小时),这取决于你的处理器。

cross_entropy = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
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))

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  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})

  print('test accuracy %g' % accuracy.eval(feed_dict={
      x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

在运行这段代码之后再测试集上的准确的大概为 99.2%。

这里我们学习了如何使用 TensorFlow 快速简单的构建,训练和评估一个稍微复杂的深度学习模型。

1: 对于这个小型的卷积网络,性能实际上几乎相同,而且没有 dropout。Dropout 常常对于减少过度拟合是非常有效的,但是在训练一个非常大的神经网络的时候这是非常有用的。

    原文作者:alvin2015
    原文地址: https://www.jianshu.com/p/d608809e71be
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞