Tensorflow官方文档学习理解 (六)-TensorFlow运作方式入门

我的微信公众号名称:深度学习与智能决策
微信公众号ID:MultiAgent1024
公众号介绍:主要研究强化学习、计算机视觉、深度学习、机器学习等相关内容,分享学习过程中的学习笔记和心得!期待您的关注,欢迎一起学习交流进步!

在这篇文档里面依旧是使用前馈神经网络(feed-forward neural network)来构建MNIST识别的神经网络,只不过神经网络的编写更加规范了。主要是对mnist.pyfully_connected_feed.py 这两个文件代码的一个解释。这两个文件在tensorflow的官方github中都可以找到tensorflow/tensorflow/examples/tutorials/mnist/。在mnist.py里面主要编写的就是构建全连接神经网络-mnist的模型,在fully_connected_feed.py里面主要编写利用mnist数据集的训练代码。运行fully_connected_feed.py就可以开始训练我们的模型。

mnist.py

在开始之前我们需要导入一些模块:

import math
import tensorflow as tf

在mnist.py里面我们经过三阶段的模式函数操作:inference()loss(),和training(),就能够把图表构建完成了。

1.inference() —— 尽可能地构建好图表,满足促使神经网络向前反馈并做出预测的要求。

2.loss() —— 往inference图表中添加生成损失(loss)所需要的操作(ops)。

3.training() —— 往损失图表中添加计算并应用梯度(gradients)所需的操作。

inference():

inferfence()函数会尽可能地构建图表,返回包含了预测结果(out prediction)的Tensor。它以图片的占位符(image placeholder)作为输入,借助ReLu(Rectified Linear Units)激活函数,构建一对全连接层。

每一层都创建于一个唯一的tf.name_scope之下,创建于该作用域之下的所有元素都将带有其前缀。

with tf.name_scope('hidden1'):

接下来我们需要构建第一层的神经元了,我们知道第一层的神经元以图像作为输入,因此我们首先需要去给出图像的大小的参数:

IMAGE_SIZE = 28
IMAGE_PIXELS = IMAGE_SIZE * IMAGE_SIZE

我们有了这个参数之后,我们就可以构建输入层,也就是第一层的神经元的权重(weight)和偏置(bias)了,第一层的输出为隐藏层第一层的输入:

    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')

因此权重W的维度是:[IMAGE_PIXELS, hidden1_units],tf.truncated_normal初始函数将根据所得到的均值和标准差,生成一个随机分布。

定义好了权重和偏置之后,接下来就需要将其和输入图像连接起来并经过激活函数:

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

这样的话第一层神经元就构建好了,接下来我们以同样的方式构建第二层神经元:

  with tf.name_scope('hidden2'):
    weights = tf.Variable(
        tf.truncated_normal([hidden1_units, hidden2_units],
                            stddev=1.0 / math.sqrt(float(hidden1_units))),
        name='weights')
    biases = tf.Variable(tf.zeros([hidden2_units]),
                         name='biases')
    hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)

最后我们希望输出是10个类别,因此我们需要定义一个参数去确定我们的输出是多少维度的:

NUM_CLASSES = 10

之后构建输出层(至于这里为啥没有经过激活函数我也不是很清楚,如果有知道的同学的话还希望可以留言告诉我一下,嘻嘻):

  with tf.name_scope('softmax_linear'):
    weights = tf.Variable(
        tf.truncated_normal([hidden2_units, NUM_CLASSES],
                            stddev=1.0 / math.sqrt(float(hidden2_units))),
        name='weights')
    biases = tf.Variable(tf.zeros([NUM_CLASSES]),
                         name='biases')
    logits = tf.matmul(hidden2, weights) + biases

整个inference函数的构建如下所示:

def inference(images, hidden1_units, hidden2_units):
  with tf.name_scope('hidden1'):
    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 = tf.nn.relu(tf.matmul(images, weights) + biases)
  with tf.name_scope('hidden2'):
    weights = tf.Variable(
        tf.truncated_normal([hidden1_units, hidden2_units],
                            stddev=1.0 / math.sqrt(float(hidden1_units))),
        name='weights')
    biases = tf.Variable(tf.zeros([hidden2_units]),
                         name='biases')
    hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
  with tf.name_scope('softmax_linear'):
    weights = tf.Variable(
        tf.truncated_normal([hidden2_units, NUM_CLASSES],
                            stddev=1.0 / math.sqrt(float(hidden2_units))),
        name='weights')
    biases = tf.Variable(tf.zeros([NUM_CLASSES]),
                         name='biases')
    logits = tf.matmul(hidden2, weights) + biases
  return logits

loss():

loss()函数通过添加所需的op来构建图:

首先,这个来自lables_placeholder是被编码成一个1-hot的向量。例如:如果一个标签是被定义为数字“3”,那么它的表示形式如下所示:

[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]

loss()函数的编写如下:

def loss(logits, labels):
  labels = tf.to_int64(labels)
  return tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

tf.to_int64将张量转换为 int64 类型;

使用tf.nn.sparse_softmax_cross_entropy_with_logits的交叉熵(cross-entropy)loss

  • labels:形状为[d_0, d_1, ..., d_{r-1}]的Tensor(其中,r是labels和结果的秩),并且有dtype int32或int64。labels中的每个条目必须是[0, num_classes)中的索引。当此操作在CPU上运行时,其他值将引发异常,并返回NaNGPU上相应的loss和梯度行。
  • logits:形状为[d_0, d_1, ..., d_{r-1}, num_classes],并且是dtype float32或float64的未缩放的日志概率。

返回与logits具有相同类型的加权损失Tensor。

training():

training的代码如下图所示:

def training(loss, learning_rate):
  tf.summary.scalar('loss', loss)
  optimizer = tf.train.GradientDescentOptimizer(learning_rate)
  global_step = tf.Variable(0, name='global_step', trainable=False)
  train_op = optimizer.minimize(loss, global_step=global_step)
  return train_op

对于tf.summary.scalar()函数的理解:TensorBoard可以将训练过程中的各种绘制数据展示出来,包括标量(scallars),图片(images),音频(Audio),计算图(graph)分布,直方图(histograms)和嵌入式向量。

如果我们需要使用TensorBoard展示数据的话,我们需要在执行Tensorflow就计算图的过程中,将各种类型的数据汇总并记录到日志文件中。然后使用TensorBoard读取这些日志文件,解析数据并产生数据可视化的Web页面,让我们可以在浏览器中观察各种汇总数据。summary_op包括了summary.scalar、summary.histogram、summary.image等操作,这些操作的输出是各种summary protobuf,最后通过summary.writer写入到event文件中。

Tensorflow API中包含系列生成summary数据的API接口,这些函数将汇总信息存放在protobuf中,以字符串形式表达。具体的更加细致的用法可以参考博客。接下来的操作就跟之前的一样,使用梯度下降算法优化迭代更新,不同的是我们生成一个变量用于保存全局训练步骤(global training step)的数值。

之后还多了一个函数,如下所示:

def evaluation(logits, labels):
  correct = tf.nn.in_top_k(logits, labels, 1)
  return tf.reduce_sum(tf.cast(correct, tf.int32))

tf.nn.in_top_k组要是用于计算预测的结果和实际结果的是否相等,返回一个bool类型的张量,tf.nn.in_top_k(prediction, target, K):prediction就是表示你预测的结果,大小就是预测样本的数量乘以输出的维度,类型是tf.float32等。target就是实际样本类别的标签(是mnist标签对应的下标值,一个10维的标签代表一个值(这个十维的标签的最大值的下标),具体的解析参考博客),大小就是样本数量的个数。K表示每个样本的预测结果的前K个最大的数里面是否含有target中的值。一般都是取1。

tf.cast表示将数据转化为tf.int32格式。tf.reduce_sum表示为压缩求和。

fully_connected_feed.py

在开始训练之前我们需要先用tf.placeholder占位符帮手写体数据集占一个位置,这里我们的标签的维度是[batch_size, 1]:

def placeholder_inputs(batch_size):
  images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,
                                                         mnist.IMAGE_PIXELS))
  labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))
  return images_placeholder, labels_placeholder

run_training():

在开始训练之前,我们需要去读取数据:

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

read_data_sets函数是input_data.py源码中的函数,主要用于读取数据,它需要三个参数:

  • train_dir——文件夹的文件夹的位置
  • fake_data——是否使用假数据,默认为False
  • one_hot——是否把标签转为一维向量,默认为False,由于这里没有采用one-hot编码,那么这里的返回值就是图片数字的下标,也就是图片数字到底是几。是一个单纯的数字,而不是一个十维的向量([0, 0, 0, 1, 0, 0, 0, 0, 0, 0])。

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

with tf.Graph().as_default():

tf.Graph实例是一系列可以作为整体执行的操作。TensorFlow的大部分场景只需要依赖默认图表一个实例即可。利用多个图表的更加复杂的使用场景也是可能的,但是超出了本教程的范围。

之后的话,我们需要调用之前写好的神经网络来对其进行训练,在训练之前需要调用之前写好的占位符函数placeholder_input()来给输入数据占个位置:

images_placeholder, labels_placeholder = placeholder_inputs(FLAGS.batch_size)

这样的话我们就假装我们有了输入的图片和其对应的标签(其实是没有的,我们需要之后再将其传进来,嘻嘻),接下来我们构建图的输出公式:

logits = mnist.inference(images_placeholder,FLAGS.hidden1,FLAGS.hidden2)

选择损失和训练参数:

    loss = mnist.loss(logits, labels_placeholder)
    train_op = mnist.training(loss, FLAGS.learning_rate)

评估模型结果指标:

eval_correct = mnist.evaluation(logits, labels_placeholder)

接下来我们把图运行过程中发生的事情(产生的数据记录下来):

summary = tf.summary.merge_all()

初始化变量:

init = tf.global_variables_initializer()

建立一个保存训练中间数据的存档点:

saver = tf.train.Saver()

建立会话:

sess = tf.Session()

创建一个记事本写入器(这里我也想吐槽一下,为什么不之前的创建记事本放在一起):

summary_writer = tf.summary.FileWriter(FLAGS.log_dir, sess.graph)

之后再初始化变量:

sess.run(init)

做完这些基本的操作之后我们就可以对其进行迭代训练了,但是在迭代训练之前我们得处理一下我们的输入数据,之前我们一直都是用tf.placeholder来假装我们有输入数据,现在我们需要将真实数据传入进来:

def fill_feed_dict(data_set, images_pl, labels_pl):
  images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size,FLAGS.fake_data)
  feed_dict = {images_pl: images_feed,labels_pl: labels_feed}
  return feed_dict

fill_feed_dict函数输入是数据,图像placeholder的变量名称,和标签placeholder的变量名称,返回一个字典,即需要传入到会话图中的数据。再接着就可以对其进行迭代训练:

    for step in xrange(FLAGS.max_steps):
      start_time = time.time()
      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数组,而这些数组中包含了当前这步训练中对应Tensor的值。由于train_op并不会产生输出,其在返回的元祖中的对应元素就是None,所以会被抛弃。但是,如果模型在训练中出现偏差,loss Tensor的值可能会变成NaN,所以我们要获取它的值,并记录下来。我们也希望记录一下程序运行的时间:

duration = time.time() - start_time

如果进行顺利的话,我们每隔100步,打印一下步数,损失函数值,和程序运行的时间。

      if step % 100 == 0:
        print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))
        summary_str = sess.run(summary, feed_dict=feed_dict)
        summary_writer.add_summary(summary_str, step)
        summary_writer.flush()

在每次运行summary时,都会往事件文件中写入最新的即时数据,函数的输出会传入事件文件读写器(writer)的add_summary()函数。

我们也希望能够保存我们的训练模型:

      if (step + 1) % 1000 == 0 or (step + 1) == FLAGS.max_steps:
        checkpoint_file = os.path.join(FLAGS.log_dir, 'model.ckpt')
        saver.save(sess, checkpoint_file, global_step=step)

接下来我们定义一个评估模型的函数:

def do_eval(sess,
            eval_correct,
            images_placeholder,
            labels_placeholder,
            data_set):

  true_count = 0  # Counts the number of correct predictions.
  steps_per_epoch = data_set.num_examples // FLAGS.batch_size
  num_examples = steps_per_epoch * FLAGS.batch_size
  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)
  precision = float(true_count) / num_examples
  print('Num examples: %d  Num correct: %d  Precision @ 1: %0.04f' %
        (num_examples, true_count, precision))

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)

每隔一千个训练步骤,我们的代码会尝试使用训练数据集与测试数据集,对模型进行评估。do_eval函数会被调用三次,分别使用训练数据集、验证数据集合测试数据集。

总的代码文件目录如下:

总的代码文档链接:链接:https://pan.baidu.com/s/1TqOJSWeHbIV6kMJBwT1mYA  提取码:2h6h

https://github.com/18279406017/code-of-csdn/tree/master/Tensorflow/six

参考

《大帝之剑》高清免费观看 - 迅雷高清资源 - 悠久影院

https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/g3doc/tutorials/mnist/tf/index.md

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/fully_connected_feed.py

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/mnist.py

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值