tensorflow之debug和可视化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014665013/article/details/86987827

用过tensorflow的小伙伴应该都清楚,因为静态图的缘故,tensorflow的debug是比较麻烦的,这篇帖子也是我入坑以来常用方法的一些小总结,现在自己整理一下~

1.实用小技巧之InteractiveSession

可能都会知道,TensorFlow的前后端的连接依靠于session,使用TensorFlow程序的流程构建计算图完成之后,在session中启动运行。
会话创建的方法有两种(常用的):InteractiveSession()Session()

  • InteractiveSession()先构建一个session,然后再构建计算图;也就是说,InteractiveSession()能够在运行图时,插入一些计算图,比较方便。

  • Session()先构建整个计算图,然后构建session,并在session中启动已经构建好的计算图;也就是说,在会话构建之前,构建好计算图。

因此可见,InteractiveSession()对于动态调试还是很方便的,搭配Jupyter有奇效…
示例(session()):

#Session
import tensorflow as tf
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x,W) + b)
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(1000):
  batch = mnist.train.next_batch(50)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})

示例(InteractiveSession()):


import tensorflow as tf
sess = tf.InteractiveSession()
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
sess.run(tf.initialize_all_variables())
y = tf.nn.softmax(tf.matmul(x,W) + b)
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
for i in range(1000):
  batch = mnist.train.next_batch(50)
  train_step.run(feed_dict={x: batch[0], y_: batch[1]})
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})

2.logging日志

TensorFlow用五个不同级别的日志信息。为了升序的严重性,他们是调试DEBUG,信息INFO,警告WARN,错误ERROR和致命FATAL的。当你配置日志记录在任何级别,TensorFlow将输出对应于更高程度的严重性和所有级别的日志信息。例如,如果设置错误的日志记录级别,将得到包含错误和致命消息的日志输出,并且如果设置了调试级别,则将从所有五个级别获取日志消息。

默认情况下,TensorFlow配置在日志记录级别的WARN,但当跟踪模型的训练,你会想要调整水平到INFO,这将提供额外的反馈如进程中的fit操作。

将下面的行添加到代码的开头(在导入之后):

tf.logging.set_verbosity(tf.logging.INFO)

现在运行代码时,将看到如下的附加日志输出:

INFO:tensorflow:loss = 1.18812, step = 1
INFO:tensorflow:loss = 0.210323, step = 101
INFO:tensorflow:loss = 0.109025, step = 201

带有INFO级别的日志,tf.contrib.learn每100步骤自动输出training-loss metrics到stderr。

tf.logging.info(msg, *args, **kwargs) 

记录INFO级别的日志. args 是配合msg中的占位符用的. 比如 info("I have been in love with %s for %d years.","yichu",7)

3.tensorboard(summary)

3.1.tensorboard 概述

TensorBoard 是如何工作的?
简单来说,TensorBoard 是通过一些操作(summary operations)将数据记录到文件(event files)中,然后再读取文件来完成作图的。想要在浏览器上看到 TensorBoard 页面,大概需要这几步:

  • summary。在定义计算图的时候,在适当的位置加上一些 summary 操作 。
  • merge。你很可能加了很多 summary 操作,我们需要使用 tf.summary.merge_all 来将这些 summary 操作聚合成一个操作,由它来产生所有 summary 数据。
  • run。在没有运行的时候这些操作是不会执行任何东西的,仅仅是定义了一下而已。在运行(开始训练)的时候,我们需要通过 tf.summary.FileWriter() 指定一个目录来告诉程序把产生的文件放到哪。然后在运行的时候使用 add_summary() 来将某一步的 summary 数据记录到文件中。

当训练完成后,在命令行使用 tensorboard --logdir=path/to/log-directory来启动 TensorBoard,按照提示在浏览器打开页面,注意把 path/to/log-directory 替换成你上面指定的目录。这里尤其注意路径中不要有中文路径 。并且如果运行了多次可以创建多个同级文件夹,比如我们再root文件夹下创建了三个log文件夹,然后我们可以指定路径的时候到root,那么这样tensorboard里面就包含三个log,对比比较方便(当然,也可以指定到某一个log文件夹,这样的就是只有一个log,没有对比的),运行效果是这样的:
在这里插入图片描述
总体上,目前 TensorBoard 主要包括下面几个面板:
在这里插入图片描述
其中 TEXT 是 最新版(应该是 1.3)才加进去的,实验性功能,官方都没怎么介绍。除了 AUDIO(没用过)、EMBEDDINGS可参考链接) 和 TEXT(没用过) 这几个,这篇博客主要说剩下的几个,其他的等回头熟练了再来说,尽量避免误人子弟。

TensorBoard 的工作原理是读取模型训练时产生的 TensorFlow events 文件,这个文件包括了一些 summary 数据(就是作图时用的数据)。

TensorBoard 打开时默认直接进入 SCALARS,并且默认使用 .* 正则表达式显示所有图(其他面板同理,下面就不再赘述),你用到的面板会在顶部导航栏直接显示,而其他用不到的(你代码中没有相关代码)则会收起到 INACTIVE中。
在这里插入图片描述

3.2.graph

很多时候我们的模型很复杂,包含很多层,我们想要总体上看下构建的网络到底是什么样的,这时候就用到 GRAPHS 面板了,在这里可以展示出你所构建的网络整体结构,显示数据流的方向和大小,也可以显示训练时每个节点的用时、耗费的内存大小以及参数多少。

默认显示的图分为两部分:主图(Main Graph)和辅助节点(Auxiliary Nodes)。其中主图显示的就是网络结构,辅助节点则显示的是初始化、训练、保存等节点。我们可以双击某个节点或者点击节点右上角的 + 来展开查看里面的情况,也可以对齐进行缩放,每个节点的命名都是我们在代码中使用 tf.name_scope()或者直接Variable中name指定定义好的。

左面的那一栏RunSession Run 分别是不同的训练和迭代步数。比如我这里以不同的超参训练了 6 次,那么 就有 6 个 run,而你所记录的迭代次数(实际是所有的sess.run(tf.summary.merge_all())的个数,并不是每一步都会记录当前状态的,那样的话太多了,一般都是每隔多少次记录一次)则显示在 Session Run 里。

在构建graph的时候,在指定 tf.name_scope()或者Variable中name之后,要注意先后顺序,需要将tf.summary.FileWriter("logs/", sess.graph)这个writer放在整张graph构建完成之后,sess.run()之前,这样才是完整的静态图。

完整代码详见小博zsweet github

# View more python learning tutorial on my Youtube and Youku channel!!!

# Youtube video tutorial: https://www.youtube.com/channel/UCdyjiB5H8Pu7aDTNVXTTpcg
# Youku video tutorial: http://i.youku.com/pythontutorial

"""
Please note, this code is only for python 3+. If you are using python 2+, please modify the code accordingly.
"""
from __future__ import print_function
import tensorflow as tf


def add_layer(inputs, in_size, out_size, activation_function=None):
    # add one more layer and return the output of this layer
    with tf.name_scope('layer'):
        with tf.name_scope('weights'):
            Weights = tf.Variable(tf.random_normal([in_size, out_size]), name='W')
        with tf.name_scope('biases'):
            biases = tf.Variable(tf.zeros([1, out_size]) + 0.1, name='b')
        with tf.name_scope('Wx_plus_b'):
            Wx_plus_b = tf.add(tf.matmul(inputs, Weights), biases)
        if activation_function is None:
            outputs = Wx_plus_b
        else:
            outputs = activation_function(Wx_plus_b, )
        return outputs


# define placeholder for inputs to network
with tf.name_scope('inputs'):
    xs = tf.placeholder(tf.float32, [None, 1], name='x_input')
    ys = tf.placeholder(tf.float32, [None, 1], name='y_input')

# add hidden layer
l1 = add_layer(xs, 1, 10, activation_function=tf.nn.relu)
# add output layer
prediction = add_layer(l1, 10, 1, activation_function=None)

# the error between prediciton and real data
with tf.name_scope('loss'):
    loss = tf.reduce_mean(tf.reduce_sum(tf.square(ys - prediction),
                                        reduction_indices=[1]))

with tf.name_scope('train'):
    train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

sess = tf.Session()
init = tf.global_variables_initializer()

'''
下面这句话是summary写文件的核心,将将态度写入文件
下面这句话可以直接写成tf.summary.FileWriter("logs/", sess.graph),因为这里是简单的例子,没有动态运行,在后面不需要add_summary(也就是不需要再使用writer了)。关于复杂的后面介绍~
'''
writer = tf.summary.FileWriter("logs/", sess.graph) 
sess.run(init)

# direct to the local dir and run this in terminal:
# $ tensorboard --logdir=logs

运行效果图:
在这里插入图片描述

TensorBoard 默认是不会记录每个节点的用时、耗费的内存大小等这些信息的,那么如何才能在图上显示这些信息呢?关键就是如下这些代码,主要就是在 sess.run() 中加入 options 和 run_metadata 参数。

示例代码:

run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()
s, lss, acc , _ = sess.run([merged_summary, loss, accuracy, train_step], 
                           feed_dict={x: batch_x, y: batch_y, phase: 1},
                           options=run_options,
                           run_metadata=run_metadata)
summary_writer.add_run_metadata(run_metadata, 'step{}'.format(i))
summary_writer.add_summary(s, i)

这里注意到,需要动态执行才能测试memory和运行时间,而run的就需要有实际的执行,这里执行的是train,如上例执行的就是merged_summary, loss, accuracy, train_step

如果没有sess.run(),tensorboard中会出现parsing metadata…的错误。
这里我没有验证过的是,run_metadata作为参数传入sess.run()函数,然后再sess.run()的时候,修改run_metadata变量,最后通过writer.add_run_metadata()方法写入tensorboard。目前还没能得到验证,网上资料比较少,有时间撸源码~

完整源码详见zsweet github

这里注意只有选择左边面板的Session run之后,才能选择查看某一步的memory和compute time(我竟然找了半天的错误。。。)
在这里插入图片描述

3.3.scalar用法

SCALARS 面板主要用于记录诸如准确率、损失和学习率等单个值的变化趋势。在代码中用 tf.summary.scalar() 来将其记录到文件中。对应于我的代码中,我是使用其记录了训练准确率和损失。

每个图的右下角都有 3 个小图标,第一个是查看大图,第二个是是否对 y 轴对数化,第三个是如果你拖动或者缩放了坐标轴,再重新回到原始位置。
在这里插入图片描述
左面的导航栏解释如下:

  • Smoothing 指的是作图时曲线的平滑程度,使用的是类似指数平滑的处理方法。如果不平滑处理的话,有些曲线波动很大,难以看出趋势。0 就是不平滑处理,1 就是最平滑,默认是 0.6。

  • Horizontal Axis 顾名思义指的是横轴的设置:

    • STEP:默认选项,指的是横轴显示的是训练迭代次数
    • RELATIVE:这个相对指的是相对时间,相对于训练开始的时间,也就是说是训练用时 ,单位是小时
    • WALL:指训练的绝对时间
  • 最下面的 Runs 列出了各个 run,你可以选择只显示某一个或某几个。

效果如下:
在这里插入图片描述

简单demo:

import tensorflow as tf
 
a = tf.placeholder(tf.float32, shape=[])
b = tf.constant(1, dtype=tf.int32)
 
tf.summary.scalar("a", a)
tf.summary.scalar("b", b)
 
sess = tf.Session()
 
init_op = tf.global_variables_initializer()
merged_summaries = tf.summary.merge_all()
writer = tf.summary.FileWriter("train", sess.graph)
 
sess.run(init_op)
 
for step in range(6):
    feed_dict = {a: step}
    summary = sess.run(merged_summaries, feed_dict=feed_dict)

更加详细的源码详见3.4.tensorboard 小结中的链接

3.3.distributions & histogram

3.3.1.distributions

DISTRIBUTIONS 主要用来展示网络中各参数随训练步数的增加的变化情况,可以说是 多分位数折线图 的堆叠。下面我就下面这张图来解释下。
在这里插入图片描述

横轴表示训练步数,纵轴表示权重值。而从上到下的折现分别表示权重分布的不同分位数:[maximum, 93%, 84%, 69%, 50%, 31%, 16%, 7%, minimum]。

3.3.2.HISTOGRAMS

HISTOGRAMS 和 DISTRIBUTIONS 是对同一数据不同方式的展现。与 DISTRIBUTIONS 不同的是,HISTOGRAMS 可以说是 频数分布直方图 的堆叠。
横轴表示权重值,纵轴表示训练步数。颜色越深表示时间越早,越浅表示时间越晚(越接近训练结束)。除此之外,HISTOGRAMS 还有个 Histogram mode,有两个选项:OVERLAY 和 OFFSET。选择 OVERLAY 时横轴为权重值,纵轴为频数,每一条折线为训练步数。颜色深浅与上面同理。默认为 OFFSET 模式。

3.3.2.示例代码

部分代码:

    with tf.name_scope(layer_name):
        with tf.name_scope('weights'):
            Weights = tf.Variable(tf.random_normal([in_size, out_size]), name='W')
            tf.summary.histogram(layer_name + '/weights', Weights)  ### histogram
        with tf.name_scope('biases'):
            biases = tf.Variable(tf.zeros([1, out_size]) + 0.1, name='b')
            tf.summary.histogram(layer_name + '/biases', biases)### histogram
        with tf.name_scope('Wx_plus_b'):
            Wx_plus_b = tf.add(tf.matmul(inputs, Weights), biases)
        if activation_function is None:
            outputs = Wx_plus_b
        else:
            outputs = activation_function(Wx_plus_b, )
        tf.summary.histogram(layer_name + '/outputs', outputs)### histogram
    return outputs

完整代码详见2.4.tensorboard 小结 中的链接

3.4.tensorboard 小结

graph:作为一个比较基础,但是特别常用的模块,添加步骤如下:

  • 对想在tensorboard中显示的代码块添加tf.name_scope()方法,对于Variable或者op操作添加name属性
  • 在sess=tf.Session()之后添加tf.summary.FileWriter("path\to\log_file", sess.graph)

如果想查看model执行时的memory、cpu执行情况,可采用"动态"graph的方式,即在上面步骤添加:

  • sess.run()操作,并指定函数中的optionsrun_metadata以记录运行时的mem和cpu
  • 通过writer(tf.summary.FileWriter),调用writer.add_run_metadata()方法写入文件

scalar和histogram(同distribution):这两种添加流程相同,流程如下:

  • 在需要追踪的地方添加相关函数,scalar(tf.summary.scalar())、histogram(tf.summary.histogram())、distribution((tf.summary.histogram()))
  • 在图构建完毕(即sess=tf.Session()执行)之后,前后端连接(即sess.run()执行)之前,调用merged(tf.summary.merge_all() )和writer(tf.summary.FileWriter()),第一个方法是merge所有summary数据,第二个是写文件。(tf.summary.FileWriter()执行后,graph已经存储文件完毕,但是summary的scalar和histogram并没有添加到文件)
  • 在执行model(train或者eval)时,执行sess.run(merged),并把执行结果通过writer.add_summary()写文件。

题外话:
这里说点和今天主题无关的,这里tf.Session()是构建图的,很多的操作都要构建完图才能操作,比如今天的tensorboard里面所有的操作,因为图都没有构建完何谈graph,没有图何谈数据流;再比如tf.train.Saver()也是,只有在图构建完(tf.Session())才能save或者restore

histogram & scalar & graph综合效果代码:zsweet gihub

4.assert

这个也称不上什么方法,但是是一种技巧吧,在撸代码的时候经常性的assert,比如assert input.get_shape() == [batch_size, 20, 20, 256]

如何为assert断言语句添加异常参数
assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题。格式如下:
assert expression [, arguments]
assert 表达式 [, 参数]

assert len(lists) >=5,‘列表元素个数小于5’
assert 2==1,‘2不等于1’

参考文献

猜你喜欢

转载自blog.csdn.net/u014665013/article/details/86987827
今日推荐