<jupyter_start><jupyter_text>TensorFlow运作方式入门代码：[tensorflow/examples/tutorials/mnist/](./)本篇教程的目的，是向大家展示如何利用TensorFlow使用（经典）MNIST数据集训练并评估一个用于识别手写数字的简易前馈神经网络（feed-forward neural network）。我们的目标读者，是有兴趣使用TensorFlow的资深机器学习人士。因此，撰写该系列教程并不是为了教大家机器学习领域的基础知识。在学习本教程之前，请确保您已按照[安装TensorFlow](https://www.tensorflow.org/install/index)教程中的要求，完成了安装。  教程使用的文件本教程引用如下文件：文件|目的-----|-----[mnist.py](./mnist.py) | 构建一个完全连接（fully connected）的MINST模型所需的代码。[fully_connected_feed.py](./fully_connected_feed.py)|利用下载的数据集训练构建好的MNIST模型的主要代码，以数据反馈字典（feed dictionary）的形式作为输入模型。只需要直接运行**fully_connected_feed.py**文件，就可以开始训练  准备数据MNIST是机器学习领域的一个经典问题，指的是让机器查看一系列大小为28x28像素的手写数字灰度图像，并判断这些图像代表0-9中的哪一个数字。![mnist_digits](https://www.tensorflow.org/images/mnist_digits.png)更多相关信息，请查阅[Yann LeCun网站中关于MNIST的介绍](http://yann.lecun.com/exdb/mnist/)或者[<NAME>对MNIST的可视化探索](http://colah.github.io/posts/2014-10-Visualizing-MNIST/)。  下载在**run_training()**方法的一开始，**input_data.read_data_sets()**函数会确保你的本地训练文件夹中，已经下载了正确的数据，然后将这些数据解压并返回一个含有**DataSet**实例的字典。<jupyter_code>data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)<jupyter_output><empty_output><jupyter_text>**注意**：**fake_data**标记是用于单元测试的，读者可以不必理会。数据集|目的---|---data_sets.train|55000个图像和标签（labels），作为主要训练集。data_sets.validation|5000个图像和标签，用于迭代验证训练准确度。data_sets.test|10000个图像和标签，用于最终测试训练准确度（trained accuracy）。  输入与占位符**placeholder_inputs()**函数将生成两个[tf.placeholder](https://www.tensorflow.org/api_docs/python/tf/placeholder)操作，定义传入参数的维度，包括batch_size值，后续还会将实际的训练用例传入图。images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,                                                       mnist.IMAGE_PIXELS))labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))<jupyter_code>images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,
                                                       mnist.IMAGE_PIXELS))
labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))<jupyter_output><empty_output><jupyter_text>在训练循环（training loop）的后续步骤中，传入的整个图像和标签数据集会被切片，以符合每一个操作所设置的**batch_size**值，占位符操作将会填补以符合这个batch_size值。然后使用**feed_dict**参数，将数据传入**sess.run()**函数。  构建图在为数据创建占位符之后，就可以运行**mnist.py**文件，经过三阶段的模式函数操作：**inference()**， **loss()**，和**training()**。图就构建完成了。1. **inference()** —— 尽可能地构建好图，满足促使神经网络向前反馈并做出预测的要求。2. **loss()** —— 往inference图中添加生成损失（loss）所需要的操作（ops）。3. **training()** —— 往损失图中添加计算并应用梯度（gradients）所需的操作。![mnist_subgraph](https://www.tensorflow.org/images/mnist_subgraph.png) 推理**inference()**函数会尽可能地构建图，做到返回包含了预测结果（output prediction）的Tensor。它接受图像占位符为输入，在此基础上借助[ReLu(Rectified Linear Units)](https://en.wikipedia.org/wiki/Rectifier_(neural_networks))激活函数，构建一对完全连接层（layers），以及一个有着十个节点（node）、指明了输出logits模型的线性层。每一层都创建于一个唯一的[tf.name_scope](https://www.tensorflow.org/api_docs/python/tf/name_scope)之下，创建于该作用域之下的所有元素都将带有其前缀。<jupyter_code>with tf.name_scope('hidden1'):<jupyter_output><empty_output><jupyter_text>在定义的作用域中，每一层所使用的权重和偏差都在[tf.Variable](https://www.tensorflow.org/api_docs/python/tf/Variable)实例中生成，并且包含了各自期望的维度：<jupyter_code>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')<jupyter_output><empty_output><jupyter_text>例如，当这些层是在**hidden1**作用域下生成时，赋予权重变量的独特名称将会是"**hidden1/weights**"。每个变量在构建时，都会获得初始化操作（initializer ops）。在这种最常见的情况下，通过[tf.truncated_normal](https://www.tensorflow.org/api_docs/python/tf/truncated_normal)函数初始化权重变量，给赋予的shape则是一个二维tensor，其中第一个维度代表该层中权重变量所连接（connect from）的单元数量，第二个维度代表该层中权重变量所连接到的（connect to）单元数量。对于名叫**hidden1**的第一层，相应的维度则是**[IMAGE_PIXELS, hidden1_units]**，因为权重变量将图像输入连接到了hidden1层。**tf.truncated_normal**初始函数将根据所得到的均值和标准差，生成一个随机分布。然后，通过[tf.zeros](https://www.tensorflow.org/api_docs/python/tf/zeros)函数初始化偏差变量（biases），确保所有偏差的起始值都是0，而它们的shape则是其在该层中所接到的（connect to）单元数量。图的三个主要操作，分别是两个[tf.nn.relu](https://www.tensorflow.org/api_docs/python/tf/nn/relu)操作，它们中嵌入了隐藏层所需的[tf.matmul](https://www.tensorflow.org/api_docs/python/tf/matmul)；以及logits模型所需的另外一个tf.matmul。三者依次生成，各自的**tf.Variable**实例则与输入占位符或下一层的输出tensor所连接。<jupyter_code>hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)
hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
logits = tf.matmul(hidden2, weights) + biases<jupyter_output><empty_output><jupyter_text>最后，程序会返回包含了输出结果的**logits**张量。  损失loss()函数通过添加所需的损失操作，进一步构建图。首先，**labels_placeholer**中的值将被转化为64位整型，然后，自动使用[tf.nn.sparse_softmax_cross_entropy_with_logits](https://www.tensorflow.org/api_docs/python/tf/nn/sparse_softmax_cross_entropy_with_logits)操作，将**labels_placeholer**编码为一个独热码，并与**inference()**的输出logits比较。<jupyter_code>labels = tf.to_int64(labels)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
    labels=labels, logits=logits, name='xentropy')<jupyter_output><empty_output><jupyter_text>然后，使用[tf.reduce_mean](https://www.tensorflow.org/api_docs/python/tf/reduce_mean)函数，计算batch维度（第一维度）下交叉熵（cross entropy）的平均值，将将该值作为总损失。<jupyter_code>loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')<jupyter_output><empty_output><jupyter_text>最后，程序会返回包含了损失值的张量。> 注意：交叉熵是信息理论中的概念，可以让我们描述如果基于已有事实，相信神经网络所做的推测最坏会导致什么结果。更多详情，请查阅博文《可视化信息理论》(http://colah.github.io/posts/2015-09-Visual-Information/)  训练**training()**函数添加了通过梯度下降（gradient descent）将损失最小化所需的操作。首先，该函数从**loss()**函数中获取损失张量，将其交给[tf.summary.scalar](https://www.tensorflow.org/api_docs/python/tf/summary/scalar)，后者在与[tf.summary.FileWriter](https://www.tensorflow.org/api_docs/python/tf/summary/FileWriter)（见下文）配合使用时，可以向事件文件（events file）中生成汇总值（summary values）。在本篇教程中，每次写入汇总值时，它都会释放损失Tensor的当前值（snapshot value）。<jupyter_code>tf.summary.scalar('loss', loss)<jupyter_output><empty_output><jupyter_text>接下来，我们实例化一个[tf.train.GradientDescentOptimizer](https://www.tensorflow.org/api_docs/python/tf/train/GradientDescentOptimizer)，负责按照所要求的学习效率（learning rate）应用梯度下降法（gradients）。<jupyter_code>optimizer = tf.train.GradientDescentOptimizer(learning_rate)<jupyter_output><empty_output><jupyter_text>之后，我们生成一个变量用于保存全局训练步骤（global training step）的数值，并使用[tf.train.Optimizer.minimize](https://www.tensorflow.org/api_docs/python/tf/train/Optimizerminimize)操作更新系统中的训练权重（trainable weights）、增加全局步骤。根据惯例，这个操作被称为**train_op**，是TensorFlow会话（session）诱发一个完整训练步骤所必须运行的操作（见下文）。<jupyter_code>global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)<jupyter_output><empty_output><jupyter_text>训练模型一旦图构建完毕，就通过**fully_connected_feed.py**文件中的用户代码进行循环地迭代式训练和评估。  图在**run_training()**这个函数的一开始，是一个Python语言中的**with**命令，这个命令表明所有已经构建的操作都要与默认的[tf.Graph](https://www.tensorflow.org/api_docs/python/tf/Graph)全局实例关联起来。<jupyter_code>with tf.Graph().as_default():<jupyter_output><empty_output><jupyter_text>**tf.Graph**实例是一系列可以作为整体执行的操作。TensorFlow的大部分场景只需要依赖默认图一个实例即可。利用多个图的更加复杂的使用场景也是可能的，但是超出了本教程的范围。  会话完成全部的构建准备、生成全部所需的操作之后，我们就可以创建一个[tf.Session](https://www.tensorflow.org/api_docs/python/tf/Session)，用于运行图。<jupyter_code>sess = tf.Session()<jupyter_output><empty_output><jupyter_text>另外，也可以利用**with**代码块生成**Session**，限制作用域：<jupyter_code>with tf.Session() as sess:<jupyter_output><empty_output><jupyter_text>Session函数中没有传入参数，表明该代码将会依附于（如果还没有创建会话，则会创建新的会话）默认的本地会话。生成会话之后，所有**tf.Variable**实例都会立即通过调用各自初始化操作中的[tf.Session.run](https://www.tensorflow.org/api_docs/python/tf/Sessionrun)函数进行初始化。<jupyter_code>init = tf.global_variables_initializer()
sess.run(init)<jupyter_output><empty_output><jupyter_text>[tf.Session.run](https://www.tensorflow.org/api_docs/python/tf/Sessionrun)方法将会运行图中与作为参数传入的操作相对应的完整子集。在初次调用时，**init**操作只包含了变量初始化程序[tf.group](https://www.tensorflow.org/api_docs/python/tf/group)。图的其他部分不会在这里，而是在下面的训练循环运行。  训练循环完成会话中变量的初始化之后，就可以开始训练了。训练的每一步都是通过用户代码控制，而能实现有效训练的最简单循环就是：<jupyter_code>for step in xrange(FLAGS.max_steps):
    sess.run(train_op)<jupyter_output><empty_output><jupyter_text>但是，本教程中的例子要更为复杂一点，原因是我们必须把输入的数据根据每一步的情况进行切分，以匹配之前生成的占位符。  向图输入参数执行每一步时，我们的代码会生成一个输入字典（feed dictionary），其中包含对应步骤中训练所要使用的例子，这些例子的键就是其所代表的占位符操作。**fill_feed_dict**函数中，会查询给定的**DataSet**，给下一**batch_size**批次的图像和标签，与占位符相匹配的张量则会载入下一批次的图像和标签。<jupyter_code>images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size,
                                               FLAGS.fake_data)<jupyter_output><empty_output><jupyter_text>然后，以占位符为键，创建一个Python字典对象，键值则是其代表的输入张量。<jupyter_code>feed_dict = {
    images_placeholder: images_feed,
    labels_placeholder: labels_feed,
}<jupyter_output><empty_output><jupyter_text>这个字典随后作为**feed_dict**参数，传入**sess.run()**函数中，为这一步的训练提供输入样例。  检查状态代码中明确其需要获取的两个值：**[train_op, loss]**。<jupyter_code>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)<jupyter_output><empty_output><jupyter_text>因为要获取这两个值，**sess.run()**会返回一个有两个元素的元组。其中每一个**张量**，对应了返回的元组中的numpy数组，而这些数组中包含了当前这步训练中对应张量的值。由于**train_op**并不会产生输出，其在返回的元祖中的对应元素就是**None**，所以会被抛弃。但是，如果模型在训练中出现偏差，**loss**张量的值可能会变成NaN，所以我们要获取它的值，并记录下来。假设训练一切正常，没有出现NaN，训练循环会每隔100个训练步骤，就打印一行简单的状态文本，告知用户当前的训练状态。<jupyter_code>if step % 100 == 0:
    print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))<jupyter_output><empty_output><jupyter_text>状态可视化为了释放[TensorBoard](https://www.tensorflow.org/get_started/summaries_and_tensorboard)所使用的事件文件（events file），所有的即时数据（在这里只有一个）都要在图构建阶段合并至一个操作（op）中。<jupyter_code>summary = tf.summary.merge_all()<jupyter_output><empty_output><jupyter_text>在创建好会话（session）之后，可以实例化一个[tf.summary.FileWriter](https://www.tensorflow.org/api_docs/python/tf/summary/FileWriter)，用于写入包含了图表本身和即时数据具体值的事件文件。<jupyter_code>summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)<jupyter_output><empty_output><jupyter_text>最后，每次运行**summary**时，都会往事件文件中写入最新的即时数据，函数的输出会传入事件文件读写器（writer）的**add_summary()**函数。<jupyter_code>summary_str = sess.run(summary, feed_dict=feed_dict)
summary_writer.add_summary(summary_str, step)<jupyter_output><empty_output><jupyter_text>事件文件写入完毕之后，可以就训练文件夹打开一个TensorBoard，查看即时数据的情况。![mnist_tensorboard](https://www.tensorflow.org/images/mnist_tensorboard.png)**注意**：了解更多如何构建并运行TensorBoard的信息，请查看相关教程[Tensorboard：训练过程可视化]()。  保存检查点为了得到可以用来后续恢复模型以进一步训练或评估的检查点文件（checkpoint file），我们实例化一个[tf.train.Saver](https://www.tensorflow.org/api_docs/python/tf/train/Saver)。<jupyter_code>saver = tf.train.Saver()<jupyter_output><empty_output><jupyter_text>在训练循环中，将定期调用[tf.train.Saver.save](https://www.tensorflow.org/api_docs/python/tf/train/Saversave)方法，向训练文件夹中写入包含了当前所有可训练变量值得检查点文件。<jupyter_code>saver.save(sess, FLAGS.train_dir, global_step=step)<jupyter_output><empty_output><jupyter_text>这样，我们以后就可以使用[tf.train.Saver.restore](https://www.tensorflow.org/api_docs/python/tf/train/Saverrestore)方法，重载模型的参数，继续训练。<jupyter_code>saver.restore(sess, FLAGS.train_dir)<jupyter_output><empty_output><jupyter_text>评估模型每隔一千个训练步骤，我们的代码会尝试使用训练数据集与测试数据集，对模型进行评估。**do_eval**函数会被调用三次，分别使用训练数据集、验证数据集合测试数据集。<jupyter_code>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)<jupyter_output><empty_output><jupyter_text>> 注意，更复杂的使用场景通常是，先隔绝**data_sets.test**测试数据集，只有在大量的超参数优化调整（hyperparameter tuning）之后才进行检查。但是，由于MNIST问题比较简单，我们在这里一次性评估所有的数据。  构建评估图在进入训练循环之前，我们应该先调用**mnist.py**文件中的**evaluation()**函数，传入的logits和标签参数要与**loss()**函数的一致。这样做事为了先构建Eval操作。<jupyter_code>eval_correct = mnist.evaluation(logits, labels_placeholder)<jupyter_output><empty_output><jupyter_text>**evaluation()**函数会生成[tf.nn.in_top_k](https://www.tensorflow.org/api_docs/python/tf/nn/in_top_k)操作，如果在K个最有可能的预测中可以发现真的标签，那么这个操作就会将模型输出标记为正确。在本文中，我们把K的值设置为1，也就是只有在预测是真的标签时，才判定它是正确的。<jupyter_code>eval_correct = tf.nn.in_top_k(logits, labels, 1)<jupyter_output><empty_output><jupyter_text>评估输出之后，我们可以创建一个循环，往其中添加**feed_dict，**并在调用**sess.run()**函数时传入**eval_correct**操作，目的就是用给定的数据集评估模型。<jupyter_code>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)<jupyter_output><empty_output><jupyter_text>**true_count**变量会累加所有**in_top_k**操作判定为正确的预测之和。接下来，只需要将正确测试的总数，除以例子总数，就可以得出准确率了。<jupyter_code>precision = true_count / num_examples
print('  Num examples: %d  Num correct: %d  Precision @ 1: %0.04f' %
      (num_examples, true_count, precision))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1430029040106742, 'fraction_numerical': 0.006121968448316459, 'mean_word_length': 9.55675227837614, 'pattern_counts': {'":': 0, '<': 122, '<?xml version=': 0, '>': 124, 'https://': 29, 'lorem ipsum': 0, 'www.': 28, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '30', 'dir': 'jupyter-structured-clean-dedup', 'id': '625897', 'license': 'Apache-2.0', 'n_tokens_mistral': 6759, 'n_tokens_neox': 7366, 'n_words': 214, 'path': 'mnist/TensorFlow_Mechanics_101.ipynb', 'repo_name': 'ling7334/tensorflow-get-started', 'size': '12745'}