页面树结构

版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

目录

 

TensorFlow是进行大规模数值计算的强大库。其优点之一是实现和训练深层神经网络。在本教程中,我们将学习TensorFlow模型的基本构建模块,

并构造一个深度卷积神经网络的MNIST分类器。

本教程假设你已经熟悉神经网络和MNIST数据集。如果你尚未了解这些,请查看 初学者的介绍。开始之前,请务必 安装TensorFlow

关于本教程

本教程的第一部分解释了mnist_softmax.py 中的代码 ,这是Tensorflow模型的基本实现。第二部分显示了一些提高精度的方法。

您可以将本教程中的每个代码段复制并粘贴到Python环境中,或者你能下载mnist_deep.py代码以实现一个深度网络。

我们将在本教程中完成什么:

  • 基于图片中的每个像素,创建一个用于识别数字模型的softmax回归函数

  • 使用TensorFlow“学习”成千上万个案例来训练模型并识别数字(运行我们的第一个TensorFlow会话)

  • 使用测试数据检测模型的精度

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

 

构建

在创建我们的模型之前,我们将首先加载MNIST数据集,并启动TensorFlow会话。

加载MNIST数据

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

代码块
languagepy
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
mnist是一个存储训练、验证和测试集为NumPy数组的轻量级类。它还提供了一个迭代数据服务的函数,我们将在下面使用。

启动TensorFlow InteractiveSession

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

我们使用方便的InteractiveSession类,这使得TensorFlow更加灵活地构建您的代码。它允许您将运行图的时候插入计算图 。在IPython这样的交互式环境中工作时,这是非常方便的。如果您没有使用InteractiveSession,则应在开始会话前构建整个计算图,然后 启动图

代码块
languagepy
import tensorflow as tf
sess = tf.InteractiveSession()

计算图

为了在Python中进行有效的数值计算,我们通常使用像 NumPy这样的数据库,会把类似矩阵乘法这样的复杂运算使用其他外部语言实现。不幸的是,每次操作都需要重新切换到Python的计算开销很大。如果要在GPU上运行计算或以分布式方式运行计算,那么这种开销尤其糟糕,传输数据的成本很高。

TensorFlow也在Python之外做了大量的工作,但它需要进一步的工作来避免这种开销。Tensorflow不单独地运行单一的复杂计算,而是利用图(graph)描述一系列可交互的计算操作,然后整个图在Python之外运行。这种方法类似于Theano或Torch所使用的方法。

因此,Python代码的作用是构建这个外部计算图,并指定计算图应该运行的部分。有关详细信息,请参阅“ 入门指南 ”的“ 计算图” 部分。  

 

建立一个Softmax回归模型

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

占位符

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

代码块
languagepy
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
这里xy_没有特定的值。相反,它们都是占位符,我们在TensorFlow运行计算时输入的值。

输入图像x是一个2D的浮点数张量。在这里,我们分配给它的shape[None, 784]784由28*28像素构成的单一MNIST图片平铺而成的维度,None表示第一个维度值大小不定,对应批量大小(batch size)。目标输出类别y_也将由二维张量组成,其中每一行为一个10维的one-hot向量,指示对应的MNIST图像属于哪个数字类(零到九)。

placeholder的shape参数是可选的,但它能够让TensorFlow自动捕捉张量不一致而产生的错误。

变量

我们现在定义模型的权重W和偏置b。我们可以将他们当做额外的输入量,但TensorFlow有一个更好的方式来处理它们:VariableVariable是一个位于TensorFlow的交互操作图中的可变张量。它可以被用来计算输入值甚至修改。在机器学习应用中,通常有一个模型参数为 Variables。

代码块
languagepy
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
我们通过tf.Variable初始化这些值:在本案例中,我们初始化Wb张量,使其全部置0。 W是一个784x10的矩阵(因为我们有784个输入特征和10个输出),b是一个10维向量(因为我们
有10个类)。

Variables被用作会话之前,必须初始化它们。此步将已经指定的初始值(本案例中为零),并将其分配给每个 Variable,这样可以一次性为所有变量完成此操作:

代码块
languagepy
sess.run(tf.global_variables_initializer())

预测类和损失函数

我们现在可以实现我们的回归模型。它只需要一行!我们将矢量化输入图像x乘以权重矩阵W,添加偏置b

代码块
languagepy
y = tf.matmul(x,W) + b
我们可以很容易地指定一个损失函数。损失函数可以表明模型在一个例子上的预测到底有多糟糕; 我们在训练的过程中尝试最小化损失函数。本例中,我们的损失函数是目标类别和softmax激活
函数应用于模型预测之间的交叉熵。在初学者教程中,我们使用稳定的形式:
代码块
languagepy
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的最快梯度下降法来降低交叉熵。

代码块
languagepy
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
TensorFlow在这一行中实际做的是向计算图添加新的操作。这些操作包括计算梯度,计算每个参数的步长变化,对变化的参数进行更新。

返回train_step操作时,将梯度下降更新应用于参数。因此,训练模型可以通过反复运行来实现train_step

代码块
languagepy
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用来检查我们的预测是否正确。

代码块
languagepy
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
这给了我们一个布尔的列表。为了确定哪个部分是正确的,我们转换为浮点数,然后取平均值。例如, [True, False, True, True]会变成[1,0,1,1],概率即为0.75
代码块
languagepy
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
最后,我们可以评估我们对测试数据的准确性。这应该是大约92%正确。
代码块
languagepy
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

构建多层卷积网络

在MNIST上获得92%的准确率,这个结果并不好,在本节中,我们将改善这个问题,从一个非常简单的模型跳到中等程度:一个小的卷积神经网络。这将使我们达到约99.2%的准确度 - 虽然不是最高的识别率,但是已经很了不起了。

权重初始化

要创建该模型,我们将需要创建很多权重和偏差。通常应该用少量的噪声来初始化重量以进行对称断裂,并且防止0梯度。由于我们使用 要创建该模型,我们需要创建很多权重置和偏置量。通常,权重在初始化时应该加入少量的噪声来打破对称性以及避免0梯度。由于我们使用 ReLU神经元,所以初始化它们也是一个很好的初始偏倚,以避免“死神经元”。而不是在构建模型时反复执行,我们创建两个方便的功能来为我们做。神经元,因此较好的做法是用一个较小的正数来初始化偏置量,以避免神经元节点输出恒为0(dead neurons)。为了不在构建模型时反复执行初始化操作,我们定义两个函数用于初始化。

代码块
languagepy
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还为卷积和集合操作提供了很大的灵活性。我们如何处理边界?我们的步幅是多少?在这个例子中,我们总是选择香草版本。我们的卷积使用一个步长,零填充,使输出与输入的大小相同。我们的游泳池是超过2个2个街区的普通老人群。为了使代码更清洁,我们还将这些操作抽象为函数。TensorFlow还为卷积和池化操作提供了很大的灵活性。我们如何处理图片边界?我们的步幅是多少?在本例中,我们总是选择vanilla版本。我们的卷积使用1步长,零填充,使输出与输入的大小相同。我们的池化用简单传统的2x2大小的模板做最大池化,为了使代码更简介,我们还将这些操作抽象为函数。

代码块
languagepy
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个功能。它的重量张量将具有一个形状现在我们可以实现第一层卷积。它由一个卷积接一个最大池化构成。卷积在每5x5的patch中算出32个特征。它的权重张量形状[5, 5, 1, 32]。前两个维度是补丁大小,下一个是输入通道的数量,最后一个是输出通道的数量。我们还将拥有一个带有每个输出通道分量的偏置矢量。。前两个维度是patch大小,第三个维度是图片颜色输入通道的数量(灰度图片为1,彩色图片为3),最后一个是输出通道的数量。我们还将为每个输出通道添加一个偏置量。

代码块
languagepy
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
为了应用层,我们首先重新x形成4d张量,第二和第三维对应于图像的宽度和高度,最后的尺寸对应于颜色通道的数量。为了将层应用于图中,我们首先reshape x形成4d张量,第二和第三维对应于图片的宽度和高度,最后的尺寸对应于颜色通道的数量。
代码块
languagepy
x_image = tf.reshape(x, [-1,28,28,1])
然后我们x将x_image与权重张量进行卷积,添加偏差,应用ReLU函数,最后再加入最大值。该与权重张量进行卷积,添加偏置量,应用于ReLU函数,最后再加入最大池化。该max_pool_2x2方法将图像大小减小到14x14。
代码块
languagepy
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

第二卷积层

为了构建一个深层次的网络,我们堆叠这种类型的几层。第二层将为每个5x5补丁提供64个功能。为了构建一个深层次的网络,我们堆叠几层这种类型的模块。第二层将为每个5x5patch提取64特征。

代码块
languagepy
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。现在图像尺寸已经缩小到7x7,我们添加了一个具有1024个神经元的全连接层,以便对整个图像进行处理。我们从池化层将张量reshape成一些向量,然后乘以权重矩阵,添加偏置量并应用于ReLU。

代码块
languagepy
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

为了减少过度拟合,我们将在读出层之前应用压差。我们创建一个placeholder神经元在输出期间输出的概率。这样可以让我们在训练过程中辍学,并在测试过程中将其关闭。TensorFlow的为了避免过拟合,我们在输出层之前加入dropout 。我们创建一个占位符表示在神经元dropuot时保持不变的概率。这样可以让我们在训练过程中应用dropout,并在测试过程中将其关闭。TensorFlow的tf.nn.dropoutop自动处理缩放神经元输出,除了掩盖它们,所以退出只是工作没有任何额外的缩放。op除了可以屏蔽神经元的输出外,还可以自动处理神经元输出scale,所以用dropout的时候可以不用考虑任何额外scale。1

代码块
languagepy
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

 

读出层

输出层

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

代码块
languagepy
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

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

训练和评估模型

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

差异在于:

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

  • 我们将包括额外的参数我们将用更复杂的ADAM优化器替换梯度最快下降优化器。

  • 我们将在feed_dict中加入额外的参数keep_probfeed_dict,控制辍学率。

  • 我们将在培训过程中每隔100次添加日志记录。

随意运行这段代码,但是它会执行20
  • ,以便控制dropout。

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

随后运行这段代码,但是它会执行20,000次训练迭代,并且可能需要一段时间(可能长达半小时),这取决于您的处理器。

代码块
languagepy
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))
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运行此代码后的最终测试集精度大约为99.2%。

我们已经学会了如何使用TensorFlow快速,轻松地构建,训练和评估一个相当复杂的深度学习模型。

1:对于这个小型卷积网络,性能实际上几乎是一样的,没有退出。辍学通常在减少过拟合方面非常有效,但是在训练非常大的神经网络时最有用。:对于这个小型卷积网络,即使没有dropout,性能也几乎是一样的。dropout通常在减少过拟合方面非常有效,在训练非常大的神经网络时最有用。