页面树结构

2017-11-09 ApacheCN 开源组织,第二期邀请成员活动,一起走的更远 : http://www.apachecn.org/member/209.html


MachineLearning 优酷地址 : http://i.youku.com/apachecn

转至元数据结尾
转至元数据起始

代码:tensorflow/examples/tutorials/mnist/

本教程的目标是如何在MNIST数据集上利用TensorFlow来训练和评估一种简单的前馈神经网络,以分类手写数字。本教程的目标受众是有兴趣使用TensorFlow且经验丰富的机器学习用户。

本教程并不是教授如何学习机器学习。

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

 

教程文件

本教程引用以下文件:

文件目的
mnist.py构建全连接的MNIST模型代码。
fully_connected_feed.py利用数据集训练已搭建好的MNIST模型代码,以数据反馈字典的形式作为模型输入。

直接运行fully_connected_feed.py文件开始训练:

python fully_connected_feed.py

准备数据

MNIST是机器学习中的一个经典问题。机器通过输入大小为28x28像素的手写灰度数字图像,判断这些图像对应0-9中的哪个数字。

有关更多信息,请参阅Yann LeCun的MNIST网页介绍Chris Olah对MNIST的可视化研究

下载

run_training()方法的顶部,input_data.read_data_sets() 函数将确保正确的数据已下载到您的本地用于训练的文件夹中,然后解压缩该数据以返回DataSet实例的字典。

data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)

注意:该fake_data标志用于单元测试,读者可忽略此处。

数据集目的
data_sets.train55000张图片和标签,用于原始训练。
data_sets.validation5000张图片和标签,用于迭代验证训练精度。
data_sets.test10000张图片和标签,用于最终测试训练精度。

输入和占位符

placeholder_inputs()函数创建两个tf.placeholder 操作,定义输入的shape,包括batch_size大小,以及将实际的训练用例传入计算图。

images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,
                                                       mnist.IMAGE_PIXELS))
labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))
接下来,在训练循环中,将整个图片和标签数据集切分以符合每次训练的batch_size大小,占位符操作使之相匹配,然后使用feed_dict参数将其传递到sess.run()函数中。

 

构建图

为数据创建占位符后, mnist.py文件根据3步走的方式构建图:inference()loss(),和 training()

  1. inference() - 构建图,直到图运行前向网络做出预测为止。
  2. loss() - 往inference创建的图中添加生成损失所需要的操作。
  3. training() - 添加需要利用梯度计算的损失图操作。

Inference

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

将图像占位符作为输入,并在其起始地方利用ReLU激活函数构建一对全连接层,然后接一个有着十个节点、指明了输出logtis的线性层。

每个图层都创建在唯一的tf.name_scope下 ,作为在该作用域内创建的元素的前缀。

with tf.name_scope('hidden1'):
在作用域内,每一层所需的权重和偏置都在tf.Variable实例中生成,并且包含了各自需要的shape:
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 初始化,并给出它们的2-D张量shape,其中第一个维度表示权重连接的层输入的单元数,而第二个维度表示权重连接层连接到的单元数。对于第一层命名 hidden1,尺寸是[IMAGE_PIXELS, hidden1_units],因为权重将图像输入连接到hidden1图层。在tf.truncated_normal通过给定均值和标准偏差初始化生成一个随机分布。

然后初始化偏置,tf.zeros 以确保它们以所有初始值为零,并且它们的shape是它们连接到的图层中的单元数。

创建图形的三个主要操作 - 两个封装了tf.matmultf.nn.relu操作和一个的额外tf.matmul操作,然后,依次创建连接到每个输入占位符或上一层的输出张量的独立tf.Variable实例。

hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)

hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)

logits = tf.matmul(hidden2, weights) + biases
最后,返回包含logits输出的张量。

Loss

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

首先,将值labels_placeholder转换为64位整型。然后,从labels_placeholder添加一个tf.nn.sparse_softmax_cross_entropy_with_logits操作以自动生成one-hot labels,并将inference()函数的logits输出与one-hot labels进行比较。

labels = tf.to_int64(labels)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
    labels=labels, logits=logits, name='xentropy')
然后tf.reduce_mean 在batch维度(第一个维度)上对交叉熵值取平均,以此作为训练的loss。
loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')
返回包含loss值的张量。
注意:交叉熵是信息理论中的一个概念,它使我们能够基于事实的情况下描述神经网络的预测有多糟糕。有关更多信息,请阅读博客Visual Information Theory(http://colah.github.io/posts/2015-09-Visual-Information/)

训练

training()函数增加了通过梯度下降最小化损失所需的操作 。

首先,它从loss()函数中输入损失张量,并将其传给 tf.summary.scalar,与SummaryWriter(见下文)一起使用时,向events file中生成summary values 。在这种情况下,每次写入summary values时,它都会释放损失Tensor的当前值。

接下来,我们实例化一个tf.train.GradientDescentOptimizer,按照给定学习率应用于梯度下降。
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
然后,生成一个以包含全局训练步骤计数器的变量,tf.train.Optimizer.minimize 操作用于更新系统中的可训练权重和增加全局步长。按惯例,这个操作被称为train_op,是
运行TensorFlow会话引导一个完整的训练步骤(见下文)必有操作。
global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)

训练模型

一旦图形构建完成,就可以通过fully_connected_feed.py文件中的代码进行循环地迭代,对模型进行训练和评估。

Graph

run_training()函数的开始是一个python中的 with命令,这个命令表明所有已经构建的操作都要与默认的全局 tf.Graph实例关联起来。

with tf.Graph().as_default():
tf.Graph是可以一起作为一个group执行的操作集合。大多数TensorFlow用户只需要依赖于单个默认图形。

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

Session

一旦所有的构建准备已经完成并且生成了所有必要的操作,tf.Session 则创建一个用于运行该图形的操作。

sess = tf.Session()
或者,可以使用with代码块生成Session:
with tf.Session() as sess:
Session的空参数表示此代码将附加到默认的本地Session中(如未创建则创建)。

在创建会话之后,所有的tf.Variable 实例都将通过调用tf.Session.run 中的初始化操作来进行初始化。

init = tf.global_variables_initializer()
sess.run(init)
tf.Session.run 方法将运行参数传递的操作相对应图的完整子集。在第一个调用中,init操作是只包含变量的初始化的tf.group。图的其余部分都不在
这里运行; 而下面的训练循环中发生。

循环训练

在会话初始化变量后,可以开始训练。

用户通过代码控制每一步的训练,而实现有效训练的最简单循环是:

for step in xrange(FLAGS.max_steps):
    sess.run(train_op)
但是,本教程稍微复杂一些,因为它还必须分割每个步骤的输入数据,以匹配先前生成的占位符。

Feed the Graph

对于每个步骤,代码将生成一个Feed字典,其中包含一组示例,用于训练该步骤,由其所代表的占位符操作键入。

fill_feed_dict()函数会查询给定的DataSet,索要下一个batch_size的图像和标签,与占位符相匹配的张量则会包含下一批次的图像和标签。

images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size,
                                               FLAGS.fake_data)
然后生成一个python字典对象,其中占位符作为keys,Feed张量作为values。
feed_dict = {
    images_placeholder: images_feed,
    labels_placeholder: labels_feed,
}
字典被传递到sess.run()函数的feed_dict参数中,为该次训练提供输入示例。

检查状态

该代码在运行调用中获取两个值:[train_op, loss]

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()返回一个包含两个元素的tuple。在values列表中的每个张量在返回的tuple中获取对应的numpy数组,在该次训练中
填充该张量的值。由于train_op一个没有输出值的操作,在返回的tuple中的对应的元素是None,因此会被丢弃。然而,如果模型在训练过程中发生
偏差,loss张量的值可能变为NaN,因此我们捕获该值用于记录。

假设没有NaNs的训练运行良好,循环训练会每隔100次打印简单的状态文本,让用户知道训练状态。

if step % 100 == 0:
    print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))

状态可视化

为了发布TensorBoard使用的events files,在图形构建阶段,所有的summaries(在这种情况下只有一个)被收集到一个Tensor中。

summary = tf.summary.merge_all()
然后在创建会话之后,可以将tf.summary.FileWriter实例化,用于写入包含图形本身和summaries值的events files。
summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)
最后,每次summary评估events files将被更新 ,并将输出传递给读写器的add_summary()函数。
summary_str = sess.run(summary, feed_dict=feed_dict)
summary_writer.add_summary(summary_str, step)
当写入events files时,可以针对训练文件夹运行TensorBoard,以显示summary中的值。

MNIST TensorBoard

注意:有关如何构建和运行Tensorboard的更多信息,请参阅相关教程Tensorboard:可视化学习

保存Checkpoint

为了生成一个可以用于稍后恢复模型进行进一步训练或评估的检查点文件,我们实例化了一个 tf.train.Saver

saver = tf.train.Saver()
在训练循环中,tf.train.Saver.save 将定期调用该方法,在训练目录的一个检查点文件中写入所有可训练变量的当前值。
saver.save(sess, FLAGS.train_dir, global_step=step)
这样在以后我们可以通过使用该tf.train.Saver.restore 方法来重新模型参数来恢复训练 。
saver.restore(sess, FLAGS.train_dir)

评估模型

每一步,代码将尝试针对训练和测试数据集来评估模型。do_eval()函数会被调用三次,用于训练,验证和测试数据集。

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问题,我们对所有数据进行评估。

构建评估图

在进入训练循环之前,Eval 操作应该已经通过在mnist.py文件中调用evaluation()函数被创建好,传入的logits和标签参数要与loss函数的一致。

eval_correct = mnist.evaluation(logits, labels_placeholder)
evaluation()函数生成一个tf.nn.in_top_k 可以自动将每个模型输出打分的操作,如果在K个最有可能的预测中可以找到真正的标签,那么它是正确的。
在这种情况下,我们将K的值设置为1,仅在正确标签的情况下认为预测准确。
eval_correct = tf.nn.in_top_k(logits, labels, 1)

评估输出

然后可以创建一个循环,往里填充feed_dict,在给定数据集上调用sess.run()函数,利用eval_correct操作来评估模型。

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_k操作认为是确定的预测之和。然后简单地除以样本总数来计算精度。
precision = true_count / num_examples
print('  Num examples: %d  Num correct: %d  Precision @ 1: %0.04f' %
      (num_examples, true_count, precision))
  • 无标签