TensorFlow 与深度学习 (6) 阅读代码

单层神经网络的代码已经写好了。请打开 mnist_1.0_softmax.py 文件并按说明进行操作。

你在本节的任务是理解代码,以便之后对其改进。

你应该看到,在文档中的说明和启动代码只有微小的差别。它们对应于可视化的函数,并且在注释中被标记。此处可忽略。

1
2
3
4
5
6
7
import tensorflow as tf

X = tf.placeholder(tf.float32, [None, 28, 28, 1])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

init = tf.initialize_all_variables()

我们首先定义 TensorFlow 的变量和占位符。变量是你希望训练算法为你确定的所有的参数,在我们的例子中参数有权重和偏置。

占位符是在训练期间填充实际数据的参数,通常是训练图像。持有训练图像的张量的形式是 [None, 28, 28, 1],其中的参数代表

  • 28, 28, 1: 图像是 28x28 像素 x 1(灰度)。最后一个数字对于彩色图像是 3,但在这里不是必须的。
  • None: 这是代表 mini-batch 中的图像数量。在训练时可以得到。
1
2
3
4
5
6
7
8
9
10
11
# model
Y = tf.nn.softmax(tf.matmul(tf.reshape(X, [-1, 784]), W) + b)
# placeholder for correct labels
Y_ = tf.placeholder(tf.float32, [None, 10])

# loss function
cross_entropy = -tf.reduce_sum(Y_ * tf.log(Y))

# % of correct answers found in batch
is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

第一行是我们单层神经网络的模型。公式是我们在前面的理论部分建立的。tf.reshape 命令将我们的 28×28 的图像转化成 784 个像素的单向量。在 reshape 中的 “-1” 意思是:“帮我算出这个数,只有一种可能”。实际上,这就是 mini-batch 中的图像数量。

然后,我们需要一个额外的占位符用于存放训练集的 labels,这些 label 与训练图像一起被提供。

现在我们有模型预测和正确的 label,可以计算交叉熵了。tf.reduce_sum 是对向量的所有元素求和。

最后两行计算了正确识别数字的百分比。这是留给读者的理解练习,参考 TensorFlow API reference。你可以先跳过这部分。

1
2
optimizer = tf.train.GradientDescentOptimizer(0.003)
train_step = optimizer.minimize(cross_entropy)

这才是 TensorFlow 发挥力量的地方。你选择一个 optimiser(有许多可供选择)并用它最小化 cross-entrophy loss。在这一步中,TensorFlow 计算损失函数对于所有权重和偏置的偏导数,也就是梯度。这是一个形式推导的过程,而非耗时的数值方法计算。

梯度然后被用来更新权重和偏置,学习率为 0.003。

最后,是时候来启动训练循环(training loop)了!目前为止,这些 TensorFlow 指令已经在内存中准备了一个计算图,但是还未开始计算。

TensorFlow 的延迟执行(deferred execution)”模型:TensorFlow 是为分布式计算构建的。它必须知道你要计算的是什么、你的执行图(execution graph),然后才开始发送计算任务到各种计算机。这就是为什么它有一个延迟执行模型——你首先使用 TensorFlow 函数在内存中创造一个计算图,然后启动一个执行 Session 并且使用 Session.run 执行实际计算任务。此刻,图形无法被更改。

多亏了这个模型,TensorFlow 能接管分布式计算的大部分逻辑。举例而言,假如你指示它在计算机 1 上运行计算的一部分 ,而在计算机 2 上运行另一部分,它可以自动进行必要的数据传输。

计算需要将实际数据填进你在 TensorFlow 代码中定义的占位符。这是以 Python 的字典类型给出的,其中 key 是占位符的名字。

1
2
3
4
5
6
7
8
9
10
sess = tf.Session()
sess.run(init)

for i in range(1000):
# load batch of images and correct answers
batch_X, batch_Y = mnist.train.next_batch(100)
train_data={X: batch_X, Y_: batch_Y}

# train
sess.run(train_step, feed_dict=train_data)

这里的 train_step 是我们之前定义的,意思是让 TensorFlow 最小化交叉熵。这就是计算梯度以及更新权重、偏置的那一步。

最终,我们还需要显示一些值,以便我们看到模型性能的变化。

在训练循环里,我们用这行代码来计算准确度和交叉熵(例如每 10 次迭代):

1
2
# success ?
a,c = sess.run([accuracy, cross_entropy], feed_dict=train_data)

通过在 feed_dict 中提供测试数据(而不是训练数据),我们可以对测试数据进行同样的计算(例如每 100 次迭代计算一次。有 10,000 个测试数字,所以会耗费一些时间):

1
2
3
# success on test data ?
test_data={X: mnist.test.images, Y_: mnist.test.labels}
a,c = sess.run([accuracy, cross_entropy], feed_dict=test_data)

TensorFlow 和 Numpy 是朋友:在准备计算图时,你只需要操纵 TensorFlow 张量和命令,比如 tf.matmul, tf.reshape 等。

然而,只要执行 Session.run 命令,它的返回值就是 Numpy 张量,即 Numpy 可以使用的 numpy.ndarray 对象以及基于它的所有科学计算库。这就是为什么我们能用 matplotlib(基于 Numpy 的标准 Python 绘图库)为实验构建实时可视化。

这个简单的模型已经能识别 92% 的数字了!不错,但是你现在要显著地改善它。