在线文档教程

Deep MNIST for Experts(针对专家的深度MNIST)

Deep MNIST for Experts

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

本介绍假定您熟悉神经网络和MNIST数据集。如果你没有他们的背景,请查看 初学者 的介绍。开始之前一定要安装TensorFlow

关于本教程

本教程的第一部分解释了 mnist_softmax.py 代码中发生的事情,这是Tensorflow模型的基本实现。第二部分展示了一些提高准确性的方法。

您可以将本教程中的每个代码片段复制并粘贴到Python环境中,或者您可以从 mnist_deep.py 下载完全实施的深网。

我们将在本教程中完成的任务:

  • 基于查看图像中的每个像素,创建一个softmax回归函数,该函数是识别MNIST数字的模型

  • 使用Tensorflow来训练模型,通过让数字“看”数千个示例来识别数字(并运行我们的第一个Tensorflow会话来完成)

  • 用我们的测试数据检查模型的准确性

  • 构建,训练和测试多层卷积神经网络以改善结果

建立

在我们创建模型之前,我们将首先加载MNIST数据集,然后开始一个TensorFlow会话。

加载MNIST数据

如果您要复制并粘贴本教程中的代码,请从这里开始,这两行代码将自动下载并读取数据:

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

这里mnist是一个轻量级类,它将训练,验证和测试集存储为NumPy数组。它还提供了一个函数来迭代数据minibatches,我们将在下面使用它。

启动TensorFlow InteractiveSession

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

在这里,我们改为使用便利的InteractiveSession类,这使TensorFlow更加灵活地了解如何构建代码。它允许您将构建计算图的操作与运行图的操作交错。在IPython等交互式上下文中工作时,这特别方便。如果你没有使用一个InteractiveSession,那么你应该在开始一个会话并启动该图之前构建整个计算图。

import tensorflow as tf sess = tf.InteractiveSession()

计算图

为了在Python中进行高效的数值计算,我们通常使用像 NumPy 这样的库来执行昂贵的操作,例如使用Python之外的矩阵乘法,使用以另一种语言实现的高效代码。不幸的是,每次操作都会返回Python,但仍然会有很多开销。如果您想要在GPU上运行计算或以分布式方式运行计算,则这种开销尤其糟糕,因为传输数据的成本很高。

TensorFlow也在Python之外进行繁重的工作,但为了避免这种开销,它需要更进一步。TensorFlow不是独立于Python运行一个昂贵的操作,而是让我们描述一个完全在Python之外运行的交互操作图。这种方法类似于Theano或Torch中使用的方法。

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

建立一个Softmax回归模型

在本节中,我们将建立一个单线性层的softmax回归模型。在下一节中,我们将扩展到具有多层卷积网络的softmax回归的情况。

占位符

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

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

这里xy_没有特定的值。相反,它们都是placeholder我们要求TensorFlow运行计算时输入的值。

输入图像x将由浮点数的二维张量组成。在这里,我们分配给它一个shape[None, 784],在那里784是单个的维数由28像素MNIST图像扁平28,以及None指示所述第一尺寸,对应于批量大小,可以是任何大小的。目标输出类y_也将由2d张量组成,其中每行是一个单热的10维向量,指示对应的MNIST图像属于哪个数字类(0到9)。

placeholdershape参数是可选的,但它允许TensorFlow自动捕捉由于从张量不一致而产生的形状而产生的错误。

变量

我们现在定义我们模型的权重W和偏差b。我们可以想象将这些视为额外的输入,但TensorFlow有更好的方法来处理它们:VariableVariable是一个存在于TensorFlow计算图中的值。它可以被使用,甚至被计算修改。在机器学习应用中,通常人们的模型参数是Variables。

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

我们将呼叫中每个参数的初始值传递给tf.Variable。在这种情况下,我们都初始化Wb为张量全是零。W是一个784x10矩阵(因为我们有784个输入特征和10个输出)并且b是一个10维向量(因为我们有10个类)。

在会话中可以使用Variable s之前,必须使用该会话对它们进行初始化。此步骤将已经指定的初始值(在这种情况下,张量填满零),并将它们分配给每个Variable值。这样可以一次完成所有Variables

sess.run(tf.global_variables_initializer())

预测类别和损失函数

我们现在可以实现我们的回归模型。它只需要一行!我们将矢量化的输入图像乘以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取这些和的平均值。

训练模型

现在我们已经定义了我们的模型和训练损失函数,使用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- 它不仅限于placeholders ,而是可以替换计算图中的任何张量。

评估模型

我们的模型有多好?

首先我们要弄清楚我们在哪里预测了正确的标签。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}))

构建一个多层卷积网络

在MNIST上获得92%的准确性是不好的。这几乎是令人尴尬的坏事。在本节中,我们将解决这个问题,从一个非常简单的模型跳到适度复杂的模型:一个小的卷积神经网络。这将使我们的准确率达到99.2%左​​右 - 这不是最先进的技术,而是值得尊敬的。

这是一张用TensorBoard创建的,关于我们将要构建的模型的图表:

重量初始化

要创建这个模型,我们需要创建很多权重和偏见。通常应该用少量的噪声初始化权重,以防止对称性破坏,并防止0梯度。由于我们使用的是ReLU(https://en.wikipedia.org/wiki/Rectifier_(neural_networks%29))神经元,所以初始化它们也是一个很好的做法,用一个稍微正面的初始偏倚来避免“死神经元”,而不是在我们构建模型的过程中重复这样做,让我们创建两个方便的函数来为我们做。

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也为卷积和合并操作提供了很大的灵活性。我们如何处理边界?我们的步幅是多少?在这个例子中,我们总是选择香草版本。我们的卷积使用一个步长,并填充零,以便输出与输入大小相同。我们的游泳池是普通游泳池,超过2x2块。为了保持我们的代码更清晰,我们也将这些操作抽象为函数。

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

第一卷积层

我们现在可以实现我们的第一层。它将由卷积组成,然后是最大池。卷积将为每个5x5补丁计算32个特征。它的重量张量将有一个形状[5, 5, 1, 32]。前两个维度是色块大小,下一个是输入通道的数量,最后一个是输出通道的数量。我们还将为每个输出通道分配一个偏置矢量。

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

为了应用该层,我们首先重塑x为4d张量,第二和第三维对应于图像的宽度和高度,并且最终尺寸对应于颜色通道的数量。

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

然后x_image,我们用权重张量进行卷积,添加偏差,应用ReLU函数,最后使用最大值池。该max_pool_2x2方法将图像尺寸减小到14x14。

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

第二卷积层

为了建立一个深层网络,我们堆叠了这种类型的几个层。第二层将为每个5x5修补程序提供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)

密集连接层

现在图像尺寸已经减小到7x7,我们添加了一个具有1024个神经元的完全连接图层,以允许在整个图像上进行处理。我们将池中的张量重塑为一批向量,乘以权重矩阵,添加偏差,并应用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)

丢失

为了减少过拟合,我们将在读出层之前应用丢失。我们创建了placeholder一个神经元输出在丢失期间保持的概率。这可以让我们在训练过程中关闭失误,并在测试过程中将其关闭。TensorFlow的tf.nn.dropoutop会自动处理缩放神经元输出,并掩盖它们,所以dropout在没有任何额外缩放的情况下工作。

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

读出层

最后,我们添加一个图层,就像上面的一层softmax回归一样。

W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

培训和评估模型

这个模型有多好?为了训练和评估它,我们将使用与上述简单的一层SoftMax网络几乎相同的代码。

不同之处在于:

  • 我们将用更复杂的ADAM优化器替换最陡梯度下降优化器。

  • 我们将包括额外的参数keep_probfeed_dict,控制丢失率。

  • 我们将在训练过程中每100次迭代添加日志记录。

我们也将使用tf.Session而不是tf.InteractiveSession。这更好地分离了创建图的过程(模型规范)和评估图的过程(模型拟合)。它通常会使代码更简洁。tf.Session是在一个with块内创建的,所以一旦块被退出,它就会自动销毁。

随意运行此代码。请注意,它会进行20,000次训练迭代,可能需要一段时间(可能长达半小时),具体取决于您的处理器。

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:对于这个小型卷积网络来说,性能实际上几乎相同,没有丢失。丢手通常在减少过度拟合方面非常有效,但在训练非常大的神经网络时它是最有用的。↩