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

2020-08-14 14:28:58 浏览数 (1)

参考文献 强烈推荐Tensorflow 实战 Google 深度学习框架[1]实验平台: 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 函数时:
代码语言:javascript复制
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 查看模型的结构时更加清晰
代码语言:javascript复制
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()
  • 这样程序中定义的加法运算都被清晰的展示出来,并且变量初始化等基本操作都被折叠起来。
  • 点击 input2 上的加号按钮,能够看到关于 input2 变量初始化的全过程。

可视化 MNIST 程序

MNIST 基本程序[2]

代码语言:javascript复制
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 i00 == 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 不会保存用户对计算图可视化结果的手工修改,页面刷新之后计算图可视化结果又会回到最初的样子。

参考资料

[1]

Tensorflow实战Google深度学习框架: https://github.com/caicloud/tensorflow-tutorial/tree/master/Deep_Learning_with_TensorFlow/1.4.0

[2]

MNIST基本程序: https://blog.csdn.net/u013555719/article/details/81066425

0 人点赞