首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

TensorFlow Mechanics 101(TensorFlow力学101)

本教程的目标是展示如何使用TensorFlow来使用(经典)MNIST数据集来训练和评估用于手写数字分类的简单前馈神经网络。本教程的目标读者是有兴趣使用TensorFlow的有经验的机器学习用户。

这些教程不适用于一般的机器学习教学。

请确保您已按照说明安装TensorFlow

教程文件

本教程引用了以下文件:

文件

目的

mnist.py

构建完全连接的MNIST模型的代码。

fully_connected_feed.py

使用Feed字典训练构建的MNIST模型与下载的数据集的主要代码。

只需fully_connected_feed.py直接运行该文件即可开始培训:

代码语言:javascript
复制
python fully_connected_feed.py

准备数据

MNIST是机器学习中的一个经典问题。问题在于查看手写数字的灰度28x28像素图像,并确定图像代表哪个数字,所有数字从零到九。

欲了解更多信息,请参阅Yann LeCun的MNIST页面Chris Olah的MNIST可视化

下载

在该run_training()方法的顶部,该input_data.read_data_sets()功能将确保将正确的数据下载到本地培训文件夹,然后解压缩该数据以返回DataSet实例字典。

代码语言:javascript
复制
data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)

注意:该fake_data标志用于单元测试目的,可能会被读者安全忽略。

数据集

目的

data_sets.train

55000图像和标签,用于初级培训。

data_sets.validation

5000个图像和标签,用于迭代验证训练精度。

data_sets.test

10000个图像和标签,用于最终测试的训练精度。

输入和占位符

placeholder_inputs()函数创建了两个tf.placeholder操作,它们定义了输入的形状,包括batch_size图形的其余部分,并将实际训练示例提供给其中。

代码语言:javascript
复制
images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,
                                                       mnist.IMAGE_PIXELS))
labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))

再往下,在训练循环中,将完整图像和标签数据集切片以适合batch_size每个步骤,与这些占位符操作相匹配,然后sess.run()使用feed_dict参数传递给函数。

Build the Graph

产生用于数据的占位符后,图表从内置mnist.py文件根据3级图案:inference()loss(),和training()被建立。

1. inference() - 根据需要构建图表,以便向前运行网络进行预测。

2. loss() - 向推理图添加产生损失所需的操作

3. training() - 将计算和应用渐变所需的操作添加到损失图中。

推理

inference()函数根据需要构建图形以返回将包含输出预测的张量。

它将图像占位符作为输入,并在其上建立一对完全连接的图层,其中包含ReLU(https://en.wikipedia.org/wiki/Rectifier_(neural_networks%29))激活,随后是10节点线性图层,指定输出logits。

每个图层都创建在一个唯一的下方tf.name_scope,作为在该范围内创建的项目的前缀。

代码语言:javascript
复制
with tf.name_scope('hidden1'):

在定义的范围内,这些要使用权重和偏差值的图层每个都会为生成tf.Variable具有所需形状的实例:

代码语言:javascript
复制
weights = tf.Variable(
    tf.truncated_normal([IMAGE_PIXELS, hidden1_units],
                        stddev=1.0 / math.sqrt(float(IMAGE_PIXELS))),
    name='weights')
biases = tf.Variable(tf.zeros([hidden1_units]),
                     name='biases')

例如,当它们在hidden1范围内创建时,给予权重变量的唯一名称就是“ hidden1/weights”。

每个变量被赋予初始化操作作为其构造的一部分。

在这种最常见的情况下,权重用tf.truncated_normal二维张量的形状初始化,其中第一个dim表示权重连接的图层中的单位数,第二个dim表示单位的数量权重连接的层。对于名为的第一层,hidden1尺寸是[IMAGE_PIXELS, hidden1_units]因为权重将图像输入连接到hidden1图层。在tf.truncated_normal初始化生成与给定的平均值和标准偏差的随机分布。

然后,偏置被初始化tf.zeros以确保它们以所有零值开始,并且它们的形状仅仅是它们所连接的层中的单元的数量。

然后创建图的三个主要操作 - 两个tf.nn.reluops tf.matmul隐藏层和一个额外tf.matmul的logits - 分别依次tf.Variable连接到每个输入占位符或前一层的输出张量。

代码语言:javascript
复制
hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)
代码语言:javascript
复制
hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
代码语言:javascript
复制
logits = tf.matmul(hidden2, weights) + biases

最后,logits返回包含输出的张量。

Loss

loss()函数通过添加所需的损耗操作来进一步构建图表。

首先,这些值labels_placeholder被转换为64位整数。然后,tf.nn.sparse_softmax_cross_entropy_with_logits添加op以自动生成单热标签labels_placeholder,并将该inference()功能的输出logits 与那些单热标签进行比较。

代码语言:javascript
复制
labels = tf.to_int64(labels)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
    labels=labels, logits=logits, name='xentropy')

然后它使用tf.reduce_mean平均批量维度(第一维度)上的交叉熵值作为总损失。

代码语言:javascript
复制
loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')

然后返回包含损失值的张量。

注意:交叉熵是信息理论中的一个概念,它允许我们描述相信神经网络的预测有多糟糕,因为实际上是真的。欲了解更多信息,请阅读博客文章视觉信息理论(http://colah.github.io/posts/2015-09-Visual-Information/

训练

training()功能增加了通过渐变下降来减少损失所需的操作。

首先,它从loss()函数中获取损失张量,并将其交给a tf.summary.scalar,当与a一起使用时,将事件文件中的汇总值生成tf.summary.FileWriter(参见下文)。在这种情况下,每次写出摘要时它都会发出损失的快照值。

代码语言:javascript
复制
tf.summary.scalar('loss', loss)

接下来,我们实例化tf.train.GradientDescentOptimizer负责使用请求的学习速率应用渐变。

代码语言:javascript
复制
optimizer = tf.train.GradientDescentOptimizer(learning_rate)

然后,我们生成一个单变量来包含全局训练步骤的计数器,并且该tf.train.Optimizer.minimize操作用于更新系统中的可训练权重并增加全局步长。按照惯例,这个操作被称为train_opTensorFlow会话必须执行的操作,以便引导一个完整的培训步骤(见下文)。

代码语言:javascript
复制
global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)

训练模型

一旦图形被构建,就可以在由用户代码控制的循环中迭代地训练和评估fully_connected_feed.py

图表

run_training()函数的顶部是一个python with命令,用于指示所有构建的操作将与默认的全局tf.Graph实例关联。

代码语言:javascript
复制
with tf.Graph().as_default():

tf.Graph是可以作为一个组一起执行的操作集合。大多数TensorFlow使用只需要依赖单个默认图形。

更复杂的使用多个图是可能的,但超出了这个简单教程的范围。

The Session

一旦完成所有构建准备并生成所有必需的操作,tf.Session就会被创建来运行该图。

代码语言:javascript
复制
sess = tf.Session()

或者,Session可以生成一个with块进行范围界定:

代码语言:javascript
复制
with tf.Session() as sess:

会话的空参数表示此代码将附加到(或创建,如果尚未创建)默认本地会话。

创建会话后所有的tf.Variable实例通过调用tf.Session.run初始化操作被初始化

代码语言:javascript
复制
init = tf.global_variables_initializer()
sess.run(init)

tf.Session.run方法将运行与作为参数传递的op相对应的图的完整子集。在这第一次调用中,initop是一个tf.group仅包含变量的初始值设定项的变量。没有其他的图表在这里运行; 发生在下面的训练循环中。

火车循环

通过会话初始化变量后,可开始培训。

用户代码控制每一步的训练,最简单的可以进行有用训练的循环是:

代码语言:javascript
复制
for step in xrange(FLAGS.max_steps):
    sess.run(train_op)

但是,本教程稍微复杂一些,因为它必须将每个步骤的输入数据切片以匹配先前生成的占位符。

Feed the Graph

对于每一步,代码将生成一个饲料字典,该饲料字典将包含一组步骤的实例,并由它们所代表的占位符操作。

在该fill_feed_dict()函数中,DataSet查询给定的下一batch_size组图像和标签,并且匹配占位符的张量被填充以包含下一个图像和标签。

代码语言:javascript
复制
images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size,
                                               FLAGS.fake_data)

然后生成一个python字典对象,其占位符作​​为关键字并将代表性的张量作为值输入。

代码语言:javascript
复制
feed_dict = {
    images_placeholder: images_feed,
    labels_placeholder: labels_feed,
}

这被传递给sess.run()函数的feed_dict参数,为这一步的训练提供输入示例。

检查状态

该代码指定了两个要在其运行调用中获取的值:[train_op, loss]

代码语言:javascript
复制
for step in xrange(FLAGS.max_steps):
    feed_dict = fill_feed_dict(data_sets.train,
                               images_placeholder,
                               labels_placeholder)
    _, loss_value = sess.run([train_op, loss],
                             feed_dict=feed_dict)

因为有两个值要获取,所以sess.run()返回一个包含两个元素的元组。Tensor在要获取的值列表中的每一个对应于返回元组中的一个numpy数组,在这个训练步骤中填充该张量的值。由于train_opOperation没有输出值,在返回的元组中的相应元件是None,因此,丢弃。然而,loss如果训练期间模型发散,则张量的值可能变为NaN,所以我们捕获这个值用于记录。

假设训练在没有NaN的情况下运行良好,训练循环还会每100步打印一个简单的状态文本,以让用户知道训练状态。

代码语言:javascript
复制
if step % 100 == 0:
    print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))

可视化状态

为了发布由TensorBoard使用的事件文件,所有的摘要(在这种情况下,只有一个)在图构建阶段被收集到一个张量中。

代码语言:javascript
复制
summary = tf.summary.merge_all()

And then after the session is created, a tf.summary.FileWriter may be instantiated to write the events files, which contain both the graph itself and the values of the summaries.

代码语言:javascript
复制
summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)

Lastly, the events file will be updated with new summary values every time the summary is evaluated and the output passed to the writer's add_summary() function.

代码语言:javascript
复制
summary_str = sess.run(summary, feed_dict=feed_dict)
summary_writer.add_summary(summary_str, step)

When the events files are written, TensorBoard may be run against the training folder to display the values from the summaries.

NOTE: For more info about how to build and run Tensorboard, please see the accompanying tutorial Tensorboard: Visualizing Learning.

Save a Checkpoint

In order to emit a checkpoint file that may be used to later restore a model for further training or evaluation, we instantiate a tf.train.Saver.

代码语言:javascript
复制
saver = tf.train.Saver()

In the training loop, the tf.train.Saver.save method will periodically be called to write a checkpoint file to the training directory with the current values of all the trainable variables.

代码语言:javascript
复制
saver.save(sess, FLAGS.train_dir, global_step=step)

在将来的某个时间点,可以通过使用该tf.train.Saver.restore方法重新加载模型参数来恢复培训。

代码语言:javascript
复制
saver.restore(sess, FLAGS.train_dir)

评估模型

每千步,代码将尝试根据训练和测试数据集来评估模型。该do_eval()函数被称为三次,用于训练,验证和测试数据集。

代码语言:javascript
复制
print('Training Data Eval:')
do_eval(sess,
        eval_correct,
        images_placeholder,
        labels_placeholder,
        data_sets.train)
print('Validation Data Eval:')
do_eval(sess,
        eval_correct,
        images_placeholder,
        labels_placeholder,
        data_sets.validation)
print('Test Data Eval:')
do_eval(sess,
        eval_correct,
        images_placeholder,
        labels_placeholder,
        data_sets.test)

请注意,更复杂的用法通常data_sets.test会在大量超参数调整之后将其隔离。然而,为了简单的MNIST问题,我们对所有数据进行评估。

Build the Eval Graph

在进入训练循环之前,Eval op应该已经通过使用与该evaluation()函数mnist.py相同的logits / labels参数调用函数来构建loss()

代码语言:javascript
复制
eval_correct = mnist.evaluation(logits, labels_placeholder)

如果在K个最可能的预测中可以找到真正的标签,该evaluation()函数可以简单地生成一个tf.nn.in_top_kop,它可以自动将每个模型输出评分为正确。在这种情况下,我们将K的值设置为1,以仅考虑真实标签的预测正确性。

代码语言:javascript
复制
eval_correct = tf.nn.in_top_k(logits, labels, 1)

评估输出

然后可以创建用于填充一个循环feed_dict,并调用sess.run()eval_correct来评估在给定数据集模型。

代码语言:javascript
复制
for step in xrange(steps_per_epoch):
    feed_dict = fill_feed_dict(data_set,
                               images_placeholder,
                               labels_placeholder)
    true_count += sess.run(eval_correct, feed_dict=feed_dict)

true_count变量只是积累所有的预测的in_top_kOP已经确定并且正确的。从那里,精确度可以通过简单除以示例的总数来计算。

代码语言:javascript
复制
precision = true_count / num_examples
print('  Num examples: %d  Num correct: %d  Precision @ 1: %0.04f' %
      (num_examples, true_count, precision))

扫码关注腾讯云开发者

领取腾讯云代金券