TensorFlow官方教程学习 Deep MNIST for Experts

写在前面的话

其实有人组织了翻译TensorFlow文档,在github上的项目,但是我还是比较喜欢看原始的文档,边看边写,纯粹自娱自乐,也是看看自己是否真的认真看了并且有些理解了。

开始正文

TensorFlow是一个对于大规模数字计算十分强力的库。其使命之一就是实现和训练深度神经网络。在这个教程中我们将会学习建立一个深度卷积MNIST分类器的TensorFlow模型的基本步骤。

这个介绍假定你熟悉神经网络和MNIST数据库。如果你对此没有背景,查阅为初学者的指导,请确认你在开始前安装了TensorFlow

关于这个教程

教程的第二部分解释了在mnist_softmax.py代码中发生了什么,这是对于TensorFlow模型最基本的实现。第二部分描述了一些提高正确性的方法。

你能够拷贝和黏贴每个代码段到一个Python环境中,或者你能够选择只是通读这些代码。

我们在这个教程中将会实现:
- 创建一个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数组形式训练,验证和测试集。其也提供了一个函数以小批量的形式遍历这些数据,我们将会在底下使用到它。

开始TensorFlow InteractiveSession

TensorFlow依赖于一个高效的C++后端来进行计算。到后端的连接被称为一个会话(session)。对于TensorFlow编程的通常做法是首先创建一个图然后在会话中启动它。

这里我们使用了方便的InteractiveSession类,这将使得你更加灵活构建你的TensorFlow代码。其允许你能够不断交替地构建一个计算图然后运行这个图。这在像IPython这样交互式上下文工作环境中有为便利。如果你不适用一个InteractiveSession,你可以在开始一个会话之前建立整个运算图然后运行这个图

import tensorflow as tf
sess = tf.InteractiveSession()

运算图

为了在Python中进行有效的数字计算,我们通常适用类似于NumPy这样使用一些其他高效语言来实现复杂计算的库。不幸的是,这样仍然在每个切换回Python的操作中有着巨大开销。如果在GPU上或者进行分布式计算这样有着大量传输数据损耗的时候,这种开销更为严重。

TensorFlow也是使用非Python语言来做这些重活,但是其更进一步来避免了这些开销。TensorFlow让我们描述一个相互交互操作的图,然后将其这个运行在Python之外(类似做法在Theano或者Torch中也能看到),避免了每步运行时候和Python切换的开销。

Python代码的角色就是建立整个运算图,然后指挥运算图中哪个部分应该运行。查看基本使用中的运算图章节来获取更多的细节。

建立一个Softmax回归模型

在这个部分我们将会使用单个线性层构建softmax回归模型。在下一个张部分我们将会扩展其为使用多层卷积网络的softmax回归模型。

Placeholders

我们从创建输入图像和目标输出类的节点来创建计算图。

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

这里x和y_并不是制定的值,它们每个都是一个placeholder,我们在使用TensorFlow来运行计算时候我们将会给其赋予值。

输入图像x会包含一个2d浮点数张量。这里我们赋予其大小为[None, 784],其中784是来自于28*28像素的MNIST图像拍平之后的维度。第一个维度的None说明了其大小可以是任意值。目标输出类y_将会包含一个2d张量,每一行是一个One-hot 10维向量,指示MNIST图像应该属于哪个数字类别(0到9)。

对于placeholder,shape参数是可选的,但是其允许TensorFlow自动捕获来自于张量大小不一致的错误。

Variables

我们现在为我们的模型定义权重W和偏差b。我们能够将这些设想为额外的输入,但是TensorFlow有着一个更好的办法来处理它们:Variable。一个Variable是一个存活于TensorFlow运算图中的值。其能够被使用甚至在计算过程中修改。在机器学习应用中,通常模型的参数就是Variables。

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

我们在调用tf.Variable的时候给每个参数一个初始化值。在这个例子中,我们初始化W和b为都是0的张量。W是一个784*10矩阵(因为我们有784个输入特性和10个输出)同时b是一个10维向量(因为我们有10个类)。

在Variables能够在会话中被使用之前,他们必须被会话初始化。这个步骤获取已经初始化的值(在这个例子中都是0),然后赋予给每个Variable。这个能够一次性为所有的Variables进行。

sess.run(tf.initialize_all_variables())

预测的类和损失函数

我们现在可以完成我们的回归模型。其仅仅使用一行代码!我们将向量化后的输入图像成语权重矩阵W,添加偏差b:

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

我们也能够同样容易地制定一个损失函数。损失指的是这个模型的预测对于一个单独例子的糟糕横渡。我们尝试在所有例子进行训练的时候最小化损失。这里,我们的损失函数是目标和用于模型预测的softmax激活函数之间的交叉熵。像在初学者教程提到的,我们使用数值稳定的方式:

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y, 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 i 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张量x和y_。注意你可以使用feed_dict来替换你计算图中的任何一个张量,并不仅仅限于替换placeholder。

模型评估

我们模型运行得怎么样?

首先让我们指出我们预测对了的标签。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))

最后我们检验我们测试数据上的正确性:

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

结果大约为92%。

建立一个多层卷积网络

在MNIST上得到92%的正确率很糟糕。这是一个糟糕得让人尴尬的结果。在这个不烦,我们将会提高正确率。我们从一个简单的模型跳到一个中等复杂的模型:一个小的卷积神经网络。这将会给予我们大约99.2%的正确率 – 并不够完美,但是可以接受。

权重初始化

在创建这个模型之前,我们将会需要许多的权重和偏差。我们应当产生有着小许随机噪声的权重来防止symmetry breaking,阻止0梯度的情况产生。由于我们使用了ReLU神经网络,初始化神经网络的权重的时候使用一些微小的正初始化偏差是一个很好的做法,这样可以避免出现“死神经元”。我们不在构建模型的时候反复做这样的事情,我们构建两个函数来帮我们做这些事情。

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)

卷积和池化(Pooling)

TensorFlow在卷积和池化操作上给予我们很多变量。我们该如何处理这些边界问题?我们的步幅应该是多少?在这个例子中,我们总是选择vanilla的做法。我们卷积使用使用一个步幅,并填充零使得输出与输入大小相同。我们的池化使用的是在2*2块上的max pooling。为了保持我们的代码干净整洁,让我们也将这些操作抽象为函数:

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

第一个卷积层

我们可以实现我们的第一个层了。其包含跟随者max pooling的卷积。这个卷积将会为每个5*5块计算32个特征,其权重张量形式为[5, 5, 1, 32]。前两个维度是块的大小,然后是输入通道的数量,最后一个是输出通道的数量。我们会有一个偏差向量,对于每个输出会有一个分量。

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 pooling。

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

第二个卷积层

为了建立一个深度网络,我们堆叠这种类型的几个层。第二个层将会对于每5*5块有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)

密集连接层

现在图像的大小已经被降低到7*7,我们添加一个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)
Dropout

为了降低过拟合,我们在读出层之前应用dropout。我们创建一个placeholder来保存一个神经元输入在dropout时候被保存的概率。这允许我们在训练的时候启用dropout,在测试的时候关闭dropout。TensorFlow的tf.nn.dropout操作自动处理缩放神经元的输出以及屏蔽它们。所以dropout可以在没有任何额外缩放的情况下工作。[1]

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网络几乎相同的代码:

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_conv, y_))
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.initialize_all_variables())
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的表现实际上相当。Dropout十分高效并且能降低过拟合,但是其在训练非常大型的神经网络的时候才十分有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值