文章目录
用过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指定定义好的。
左面的那一栏Run
和 Session 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()
操作,并指定函数中的options
、run_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’