代码:tensorflow / examples / tutorials / mnist /
本教程的目标是展示如何使用TensorFlow来使用(经典的)MNIST数据集来训练和评估一个简单的前馈神经网络,用于手写数字分类。本教程的目标受众是有兴趣使用TensorFlow的经验丰富的机器学习用户。
这些教程一般不用于教授机器学习。
请确保您已按照说明 安装TensorFlow。
教程文件
本教程引用以下文件:
文件 | 目的 |
---|---|
mnist.py | 构建完全连接的MNIST模型的代码。 |
fully_connected_feed.py | 使用Feed字典对下载的数据集训练构建的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. | 55000张图片和标签,用于初级培训。 |
data_sets. | 5000张图像和标签,用于训练准确性的迭代验证。 |
data_sets. | 10000张图像和标签,用于最终测试训练准确度。 |
输入和占位符
该placeholder_inputs()
函数创建两个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))
进一步下来,在训练循环中,将完整图像和标签数据集切片以适应batch_size
每个步骤,与这些占位符操作相匹配,然后sess.run()
使用feed_dict
参数传递到函数中。
构建图
产生用于数据的占位符后,将图表从内置 mnist.py
文件根据3级图案:inference()
,loss()
,和 training()
。
inference()
- 建立图表,直到运行网络前进来做出预测。loss()
- 添加推理图生成损失所需的操作。training()
- 添加损耗图表计算和应用渐变所需的操作。
推理
该inference()
函数根据需要构建图形,以返回包含输出预测的张量。
它将图像占位符作为输入,并在其顶部构建一对完全连接的层,ReLU激活后跟一个指定输出逻辑的十个节点线性层。
每个图层都创建在唯一的下面tf.name_scope
,作为在该范围内创建的项目的前缀。
with tf.name_scope('hidden1'):
在定义的范围内,每个这些层要使用的权重和偏差被生成为tf.Variable
具有所需形状的实例:
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张量的形状,其中第一个dim表示权重连接的层中的单元数,而第二个dim表示在重量连接到的层。对于第一层命名 hidden1
,尺寸是[IMAGE_PIXELS, hidden1_units]
因为权重将图像输入连接到hidden1图层。在 tf.truncated_normal
初始化生成与给定的平均值和标准偏差的随机分布。
然后初始化偏置,tf.zeros
以确保它们以所有零值开始,并且它们的形状只是它们连接到的图层中的单位数。
然后创建图形的三个主要操作 - 为隐藏层tf.nn.relu
包装两个操作tf.matmul
和一个额外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()
函数通过添加所需的损失操作进一步构建图。
首先,将值labels_placeholder
转换为64位整数。然后,tf.nn.sparse_softmax_cross_entropy_with_logits
添加一个操作以自动生成1个热标签labels_placeholder
,并将该inference()
功能的输出逻辑与这些1个热标签进行比较。
labels = tf.to_int64(labels)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
labels=labels, logits=logits, name='xentropy')
然后tf.reduce_mean
,它将整个批量维度(第一维度)的交叉熵值作为总损耗进行平均。
loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')
然后返回包含损失值的张量。
注意: 交叉熵是信息理论中的一个想法,它使我们能够描述相信神经网络的预测有多糟糕,因为实际上是真的。有关更多信息,请阅读博客文章Visual Information Theory(http://colah.github.io/posts/2015-09-Visual-Information/)
训练
该training()
功能增加了通过渐变下降最小化损失所需的操作 。
首先,它使用loss()
函数中的损失张量,并将其转换为a tf.summary.scalar
,用于在与a tf.summary.FileWriter
(见下文)一起使用时将事件文件生成摘要值 。在这种情况下,每次写出摘要时,它将发出丢失的快照值。
tf.summary.scalar('loss', loss)
接下来,我们实例化一个tf.train.GradientDescentOptimizer
负责应用所需学习率的渐变。
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
然后,我们生成一个单个变量以包含全局训练步骤的计数器,而tf.train.Optimizer.minimize
op用于更新系统中的可训练权重并增加全局步长。按惯例,这个操作被称为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
。
图表
在run_training()
函数的顶部是一个python with
命令,它指示所有的构建的操作都与默认的全局tf.Graph
实例相关联。
with tf.Graph().as_default():
A tf.Graph
是可以一起作为一组执行的操作的集合。大多数TensorFlow用户只需要依赖于单个默认图形。
更复杂的使用多个图形是可能的,但超出了这个简单教程的范围。
会议
一旦所有的构建准备已经完成并且生成了所有必要的操作,tf.Session
则创建一个用于运行该图形的操作。
sess = tf.Session()
或者,Session
可以将a生成到with
用于范围的块中:
with tf.Session() as sess:
会话的空参数表示此代码将附加到默认本地会话(或创建尚未创建)。
在创建会话之后,所有的tf.Variable
实例都将通过调用tf.Session.run
其初始化操作来初始化。
init = tf.global_variables_initializer()
sess.run(init)
该tf.Session.run
方法将运行与作为参数传递的操作对应的图形的完整子集。在这第一个调用中,init
op是一个tf.group
只包含变量的初始化器的操作。图的其余部分都不在这里运行; 这在下面的训练循环中发生。
火车回路
在会话初始化变量后,可以开始训练。
用户代码控制每一步的培训,最简单的循环可以做有用的培训是:
for step in xrange(FLAGS.max_steps):
sess.run(train_op)
但是,本教程稍微复杂一些,因为它还必须分割每个步骤的输入数据,以匹配先前生成的占位符。
馈送图表
对于每个步骤,代码将生成一个Feed字典,其中包含一组示例,用于训练该步骤,由其所代表的占位符操作键入。
在该fill_feed_dict()
功能中,DataSet
查询给定的下一 batch_size
组图像和标签,填充与占位符匹配的张量,其中包含下一个图像和标签。
images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size,
FLAGS.fake_data)
然后生成一个python字典对象,其中占位符作为键,代表性的Feed张量作为值。
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()
返回一个包含两个项的元组。Tensor
要获取的值列表中的每一个对应于返回的元组中的numpy数组,在该培训步骤中填充该张量的值。由于train_op
是Operation
没有输出值,在返回的元组中的相应元件是None
,因此,丢弃。然而,loss
如果模型在训练过程中发生分歧,则张量的值可能变为NaN,因此我们捕获该值用于记录。
假设没有NaNs的训练运行良好,训练循环还会每100个步骤打印一个简单的状态文本,让用户知道训练状态。
if step % 100 == 0:
print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))
可视化状态
为了发布TensorBoard使用的事件文件,在图形构建阶段,所有的摘要(在这种情况下只有一个)被收集到一个Tensor中。
summary = tf.summary.merge_all()
然后在创建会话之后,tf.summary.FileWriter
可以将a实例化为写入事件文件,其中包含图形本身和摘要的值。
summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)
最后,每次summary
评估事件文件将被更新, 并将输出传递给作者的add_summary()
功能。
summary_str = sess.run(summary, feed_dict=feed_dict)
summary_writer.add_summary(summary_str, step)
当写入事件文件时,可以针对训练文件夹运行TensorBoard,以显示摘要中的值。
注意:有关如何构建和运行Tensorboard的更多信息,请参阅随附的教程Tensorboard:可视化学习。
保存检查点
为了发出一个可以用于稍后恢复模型进行进一步培训或评估的检查点文件,我们实例化了一个 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 op应该是通过使用与该evaluation()
函数mnist.py
相同的logits / labels参数调用该函数来构建的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
OP已经确定是正确的。从那里可以从简单地除以实例的总数来计算精度。
precision = true_count / num_examples
print(' Num examples: %d Num correct: %d Precision @ 1: %0.04f' %
(num_examples, true_count, precision))