Tensorflow命名空间与计算图可视化

Tensorflow命名空间与计算图可视化

觉得有用的话,欢迎一起讨论相互学习~

我的微博我的github我的B站

参考文献
强烈推荐Tensorflow实战Google深度学习框架
实验平台:
Tensorflow1.4.0
python3.5.0

  • Tensorflow可视化得到的图并不仅是将Tensorflow计算图中的节点和边直接可视化,它会根据每个Tensorflow计算节点的命名空间来整理可视化得到效果图,使得神经网络的整体结构不会被过多的细节所淹没。除了显示Tensorflow计算图的结构,Tensorflow还可以展示Tensorflow计算节点上的信息进行描述统计,包括频数统计和分布统计。
  • 为了更好的组织可视化效果图中的计算节点,Tensorboard支持通过Tensorflow命名空间来整理可视化效果图上的节点。在Tensorboard的默认视图中,Tensorflow计算图中同一个命名空间下的所有节点会被缩略为一个节点,而顶层命名空间的节点才会被显示在Tensorboard可视化效果图中。

tf.variable_scope和tf.name_scope函数区别

  • tf.variable_scope和tf.name_scope函数都提供了命名变量管理的功能,这两个函数在大部分情况下是等价的,唯一的区别在于使用tf.get_variable函数时:
import tensorflow as tf
# 不同的命名空间
with tf.variable_scope("foo"):
    # 在命名空间foo下获取变量"bar",于是得到的变量名称为"foo/bar"
    a = tf.get_variable("bar", [1])
    print(a.name)
    # foo/bar:0
with tf.variable_scope("bar"):
    # 在命名空间bar下获取变量"bar",于是得到的变量名称为"bar/bar".此时变量在"bar/bar"和变量"foo/bar"并不冲突,于是可以正常运行
    b = tf.get_variable("bar", [1])
    print(b.name)
    # bar/bar:0
#  tf.Variable和tf.get_variable的区别。

with tf.name_scope("a"):
    # 使用tf.Variable函数生成变量时会受到tf.name_scope影响,于是这个变量的名称为"a/Variable"
    a = tf.Variable([1])
    print(a.name)
    # a/Variable: 0

    # tf.get_variable函数不受头tf.name_scope函数的影响,于是变量并不在a这个命名空间中
    a = tf.get_variable("b", [1])
    print(a.name)
    # b:0

# with tf.name_scope("b"):
    # 因为tf.get_variable不受tf.name_scope影响,所以这里将试图获取名称为"a"的变量。然而这个变量已经被声明了,于是这里会报重复声明的错误。
    # tf.get_variable("b",[1])
  • 通过对变量命名空间进行管理,使用Tensorboard查看模型的结构时更加清晰
import tensorflow as tf
with tf.name_scope("input1"):
    input1 = tf.constant([1.0, 2.0, 3.0], name="input2")
with tf.name_scope("input2"):
    input2 = tf.Variable(tf.random_uniform([3]), name="input2")
output = tf.add_n([input1, input2], name="add")

writer = tf.summary.FileWriter("log/simple_example.log", tf.get_default_graph())
writer.close()
  • 这样程序中定义的加法运算都被清晰的展示出来,并且变量初始化等基本操作都被折叠起来。

Tensorboard展示图片

  • 点击input2上的加号按钮,能够看到关于input2变量初始化的全过程。

input2节点操作

可视化MNIST程序

MNIST基本程序

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# mnist_inference中定义的常量和前向传播的函数不需要改变,因为前向传播已经通过
# tf.variable_scope实现了计算节点按照网络结构的划分
import mnist_inference

# #### 1. 定义神经网络的参数。
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 3000
MOVING_AVERAGE_DECAY = 0.99


# #### 2. 定义训练的过程并保存TensorBoard的log文件。
def train(mnist):
    #  将处理输入数据的计算都放在名字为"input"的命名空间中
    with tf.name_scope('input'):
        x = tf.placeholder(tf.float32, [None, mnist_inference.INPUT_NODE], name='x-input')
        y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input')
    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    y = mnist_inference.inference(x, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # 将处理滑动平均相关的计算都放在名为moving average 的命名空间下。
    with tf.name_scope("moving_average"):
        variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
        variables_averages_op = variable_averages.apply(tf.trainable_variables())  # 对可训练变量集合使用滑动平均

    # 将计算损失函数相关的计算都放在名为loss function 的命名空间下。
    with tf.name_scope("loss_function"):
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
        cross_entropy_mean = tf.reduce_mean(cross_entropy)
        # 将交叉熵加上权值的正则化
        loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))

    # 将定义学习率、优化方法以及每一轮训练需要执行的操作都放在名字为"train_step"的命名空间下。
    with tf.name_scope("train_step"):
        learning_rate = tf.train.exponential_decay(
            LEARNING_RATE_BASE,
            global_step,
            mnist.train.num_examples/BATCH_SIZE, LEARNING_RATE_DECAY,
            staircase=True)

        train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
        # 在反向传播的过程中更新变量的滑动平均值
        with tf.control_dependencies([train_step, variables_averages_op]):
            train_op = tf.no_op(name='train')
    # 将结果记录进log文件夹中
    writer = tf.summary.FileWriter("log", tf.get_default_graph())

    # 训练模型。
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)

            if i%1000 == 0:
                # 配置运行时需要记录的信息。
                run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
                # 运行时记录运行信息的proto。
                run_metadata = tf.RunMetadata()
                # 将配置信息和记录运行信息的proto传入运行的过程,从而记录运行时每一个节点的时间空间开销信息
                _, loss_value, step = sess.run(
                    [train_op, loss, global_step], feed_dict={x: xs, y_: ys},
                    options=run_options, run_metadata=run_metadata)
                writer.add_run_metadata(run_metadata=run_metadata, tag=("tag%d"%i), global_step=i)
                print("After %d training step(s), loss on training batch is %g."%(step, loss_value))
            else:
                _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})

    writer.close()


# #### 3. 主函数。
def main(argv=None):
    mnist = input_data.read_data_sets("../../datasets/MNIST_data", one_hot=True)
    train(mnist)


if __name__ == '__main__':
    main()

可视化效果图

  • input节点代表了训练神经网络需要的输入数据,这些输入数据会提供给神经网络的第一层layer1.然后神经网络第一层layerl的结果会被传到第二层layer2,经过layer2的计算得到前向传播的结果.loss function节点表示计算损失函数的过程,这个过程既依赖于前向传播的结果来计算交叉熵(layer2到loss_function的边,又依赖于每一层中所定义的变量来计算L2 正则化损失(layer1和layer2到loss_function的边).loss_function 的计算结果会提供给神经网络的优化过程,也就是图中位train_step 所代表的节点。

  • 发现节点之间有两种不同的边。一种边是通过实线表示的,这种边刻画了数据传输,边上箭头方向表达了数据传输的方向。比如layerl和layer2之间的边表示了layer1的输出将会作为layer2的输入。TensorBoard 可视化效果图的边上还标注了张量的维度信息。

  • 从图中可以看出,节点input和layer1之间传输的张量的维度为*784。这说明了训练时提供的batch大小不是固定的(也就是定义的时候是None),输入层节点的个数为784。当两个节点之间传输的张量多于1时,可视化效果图上将只显示张量的个数。效果图上边的粗细表示的是两个节点之间传输的标量维度的总大小,而不是传输的标量个数。比如layer2和train_step之间虽然传输了6个张量,但其维度都比较小,所以这条边比layerl和moving_average之间的边(只传输了4个张量〉还要细。当张量的维度无法确定时,TensorBoard会使用最细的边来表示。比如layer1与layer2之间的边。

  • Tensor Board可视化效果图上另外一种边是通过虚线表示的,比如图中所示的moving_ average 和train_step 之间的边.虚边表达了计算之间的依赖关系,比如在程序中,通过tf.control_dependencies函数指定了更新参数滑动平均值的操作和通过反向传播更新变量的操作需要同时进行,于是moving_average 与train_step 之间存在一条虚边.

  • 除了手动的通过TensorFlow中的命名空间来调整TensorBoard的可视化效果图,TensorBoard也会智能地调整可视化效果图上的节点.TensorFlow中部分计算节点会有比较多的依赖关系,如果全部画在一张图上会便可视化得到的效果图非常拥挤.于是TensorBoard将TensorFlow计算图分成了主图(Main Graph)和辅助图(Auxiliary nodes)两个部分来呈现。TensorBoard 会自动将连接比较多的节点放在辅助图中,使得主图的结构更加清晰。

  • 除了自动的方式,TensorBoard也支持手工的方式来调整可视化结果。右键单击可视化效果图上的节点会弹出一个选项,这个选项可以将节点加入主图或者从主图中删除。左键选择一个节点并点击信息框下部的选项也可以完成类似的功能。注意TensorBoard 不会保存用户对计算图可视化结果的手工修改,页面刷新之后计算图可视化结果又会回到最初的样子。

posted @ 2018-07-16 21:11  WUST许志伟  阅读(2591)  评论(0编辑  收藏  举报