学习TensorFlow,TensorBoard可视化网络结构和参数

在学习深度网络框架的过程当中,咱们发现一个问题,就是如何输出各层网络参数,用于更好地理解,调试和优化网络?针对这个问题,TensorFlow开发了一个特别有用的可视化工具包:TensorBoard,既能够显示网络结构,又能够显示训练和测试过程当中各层参数的变化状况。本博文分为四个部分,第一部分介绍相关函数,第二部分是代码测试,第三部分是运行结果,第四部分介绍相关参考资料。
html

一. 相关函数

TensorBoard的输入是tensorflow保存summary data的日志文件。日志文件名的形式如:events.out.tfevents.1467809796.lei-All-Series 或 events.out.tfevents.1467809800.lei-All-Series。TensorBoard可读的summary data有scalar,images,audio,histogram和graph。那么怎么把这些summary data保存在日志文件中呢?node

数值如学习率,损失函数用scalar_summary函数。tf.scalar_summary(节点名称,获取的数据)python


accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))  
tf.scalar_summary('accuracy', accuracy)

各层网络权重,偏置的分布,用histogram_summary函数react

preactivate = tf.matmul(input_tensor, weights) + biases  
tf.histogram_summary(layer_name + '/pre_activations', preactivate)

其余几种summary data也是一样的方式获取,只是对应的获取函数名称换一下。这些获取summary data函数节点和graph是独立的,调用的时候也须要运行session。当须要获取的数据较多的时候,咱们一个一个去保存获取到的数据,以及一个一个去运行会显得比较麻烦。tensorflow提供了一个简单的方法,就是合并全部的summary data的获取函数,保存和运行只对一个对象进行操做。好比,写入默认路径中,好比/tmp/mnist_logs (by default)linux

merged = tf.merge_all_summaries()  

train_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/train', sess.graph)  

test_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/test')

SummaryWriter从tensorflow获取summary data,而后保存到指定路径的日志文件中。以上是在创建graph的过程当中,接下来执行,每隔必定step,写入网络参数到默认路径中,造成最开始的文件:events.out.tfevents.1467809796.lei-All-Series 或 events.out.tfevents.1467809800.lei-All-Series。git

for i in range(FLAGS.max_steps):  
if i % 10 == 0:  # Record summaries and test-set accuracy
summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))  
      test_writer.add_summary(summary, i)  
 print('Accuracy at step %s: %s' % (i, acc))  
 else: # Record train set summarieis, and train
      summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))  
      train_writer.add_summary(summary, i)



tensorflow 可视化

tensorflow的可视化是使用summarytensorboard合做完成的.github

基本用法

首先明确一点,summary也是op.算法

输出网络结构api

 
 
 
 
  • 1
  • 2
  • 1
  • 2
with tf.Session() as sess: writer = tf.summary.FileWriter(your_dir, sess.graph)

命令行运行tensorboard --logdir=your_dir,而后浏览器输入127.0.1.1:6006
这样你就能够在tensorboard中看到你的网络结构图了浏览器

可视化参数

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
#ops loss = ... tf.summary.scalar("loss", loss) merged_summary = tf.summary.merge_all() init = tf.global_variable_initializer() with tf.Session() as sess: writer = tf.summary.FileWriter(your_dir, sess.graph) sess.run(init) for i in xrange(100): _,summary = sess.run([train_op,merged_summary], feed_dict) writer.add_summary(summary, i)

这时,打开tensorboard,在EVENTS能够看到loss随着i的变化了,若是看不到的话,能够在代码最后加上writer.flush()试一下,缘由后面说明。

函数介绍

  • tf.summary.merge_all: 将以前定义的全部summary op整合到一块儿

  • FileWriter: 建立一个file writer用来向硬盘写summary数据,

  • tf.summary.scalar(summary_tags, Tensor/variable): 用于标量的 summary

  • tf.summary.image(tag, tensor, max_images=3, collections=None, name=None):tensor,必须4维,形状[batch_size, height, width, channels],max_images(最多只能生成3张图片的summary),觉着这个用在卷积中的kernel可视化很好用.max_images肯定了生成的图片是[-max_images: ,height, width, channels],还有一点就是,TensorBord中看到的image summary永远是最后一个global step

  • tf.summary.histogram(tag, values, collections=None, name=None):values,任意形状的tensor,生成直方图summary

  • tf.summary.audio(tag, tensor, sample_rate, max_outputs=3, collections=None, name=None)

FileWriter

注意:add_summary仅仅是向FileWriter对象的缓存中存放event data。而向disk上写数据是由FileWrite对象控制的。下面经过FileWriter的构造函数来介绍这一点!!!

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
tf.summary.FileWriter.__init__(logdir, graph=None, max_queue=10, flush_secs=120, graph_def=None) Creates a FileWriter and an event file. # max_queue: 在向disk写数据以前,最大可以缓存event的个数 # flush_secs: 每多少秒像disk中写数据,并清空对象缓存

注意

  1. 若是使用writer.add_summary(summary,global_step)时没有传global_step参数,会使scarlar_summary变成一条直线。

  2. 只要是在计算图上的Summary op,都会被merge_all捕捉到, 不须要考虑变量生存空间问题!

  3. 若是执行一次,disk上没有保存Summary数据的话,能够尝试下file_writer.flush()

小技巧

若是想要生成的summary有层次的话,记得在summary外面加一个name_scope

 
 
 
 
  • 1
  • 2
  • 1
  • 2
with tf.name_scope("summary_gradients"): tf.summary.histgram("name", gradients)

这样,tensorboard在显示的时候,就会有一个sumary_gradients一集目录。


主旨

在TensorFlow中每开发一个模型,均可以使用可视化调试工具TensorBoard获得这个session的Graph,这张图的结构和内容都不一样于机器学习教材上介绍的典型神经网络结构图。本文试图经过代码实验理解Graph的含义,用以指导平常调试。

代码和运行环境

代码:https://github.com/wangyaobupt/TF_Graph singleNerualNode.py

运行环境:
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
>>> tf.__version__
‘1.0.0-rc2’

问题介绍

在TensorFlow开发中,TensorBoard是一项颇有用的可视化调试工具。在TensorBoard中,除了开发者自定义输出的数据结构以外,还包括表征神经网络模型的GRAPH。

在常见的机器学习教材中,神经网络的结构通常经过相似于以下的图形表示,下图引用自http://www.extremetech.com/extreme/215170-artificial-neural-networks-are-changing-the-world-what-are-they
这里写图片描述

可是,TensorBoard生成的Graph与上述形态彻底不一样,我本身开发的某个神经网络生成的Graph以下图所示
这里写图片描述

如何理解这张图?本文试图经过代码实验作一些尝试。

案例1:单神经元

为了简化分析场景,咱们设计一个由单个神经元构成的神经网络,这个神经元存在numberOfInputDims输入,神经元的每条输入边都有权重因子wi,此外神经元还有bias偏置项,激活函数为sigmoid. 这个结构能够用以下的结构图描述
这里写图片描述

在TensorFlow中,以下代码便可定义出知足上述结构的神经网络,其中a就是上图中的Y

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
inputTensor = tf.placeholder(tf.float32, [None, numberOfInputDims], name='inputTensor') labelTensor=tf.placeholder(tf.float32, [None, 1], name='LabelTensor') W = tf.Variable(tf.random_uniform([numberOfInputDims, 1], -1.0, 1.0), name='weights') b = tf.Variable(tf.zeros([1]), name='biases') a = tf.nn.sigmoid(tf.matmul(inputTensor, W) + b, name='activation')

使用TensorBoard生成的Graph以下图所示
Fig 0

问题1:Graph中的边(Edge)表明什么

根据TF官方文档对于Graph的说明,上图中实线表示数据依赖(tensor在运算符之间的流动关系),而虚线表示控制依赖关系,原文引用以下

TensorFlow graphs have two kinds of connections: data dependencies and control dependencies. Data dependencies show the flow of tensors between two ops and are shown as solid arrows, while control dependencies use dotted lines.

可是,咱们获得的Graph中全部的实线都没有标出箭头方向,他们之间是谁依赖谁呢?

回答这个问题须要回到代码中,从代码能够知道:weight是由tf.random_uniform([numberOfInputDims, 1]初始化获得的;weight和inputTensor作矩阵乘法获得中间变量;中间变量再加上bias获得激活函数的输入;以此类推。

所以,TensorBoard Graph的上下方位表明了数据依赖的方向:数据老是从下方的节点流向上方的节点,上方节点依赖于下方节点

接下来讨论控制依赖。

上图中weight和bias节点都存在依赖于init运算的虚线,这说明weight和bias节点都须要初始化。虚线指向的运算符(op)是被依赖的运算符(op)

问题2:Graph中的节点表明什么

官方文档对于Graph的说明给出了以下图例表格
这里写图片描述

从中能够看出,不考虑summary node的状况下,节点要么是常数Constant,要么是运算符Operation Node,要么是前二者的组合。

回到咱们这个具体问题,random_uniform/weight/bias就是组合节点,而其余节点就是运算符。注意:从上图能够看到,inputTensor也是被视做一个独立的运算符。

放大其中一个节点weights,咱们能够看到其内部结构以下,包含:赋值(assign)运算符、(weight)运算符、读取(read)运算符

这里写图片描述

结合上述实验和分析,能够初步判断运算符Operation Node包含如下状况

  • 具体的运算操做:例如矩阵乘法(上图中的matmul),赋值(assign),读取(read)
  • 某个Tensor自己:例如上图中的inputTensor和(weights)节点

关于Operation Node表明常量的状况,在本文的案例2中会有体现

案例2:单神经元+损失函数+偏差反向传播梯度降低调整参数

案例1中的神经网络只包含了前向计算逻辑,做为神经网络,其最主要的功能在于被训练知足某一目标,所以损失函数和偏差反向传播梯度降低调整参数是必不可少的。

在此前的代码基础上新增以下两行就能够实现上述功能。这里损失函数使用预测值与目标值的L2距离

 
 
 
 
  • 1
  • 2
loss = tf.nn.l2_loss(a - labels, name='L2Loss') train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)

加入这两个功能后,新生成的Graph以下所示
这里写图片描述

对比上图和案例1中的Graph,在案例2 Graph的Main View(图像左侧)新增了以下信息

  • 节点
    • 常量y:表明label
    • sub运算符
    • L2Loss Tensor
    • gradient命名空间,这个命名空间与除了bias/add/L2Loss以外全部节点都有数据依赖
    • Weights和Bias都新增了对Adam节点的控制依赖

接下来重点讨论Gradient(梯度)和Adam(寻优算法)节点的内部结构

Gradient(梯度)节点
这里写图片描述
Gradient(梯度)节点内部是一个链式结构,分为对L2Loss求导、对减法求导、对激活函数求导……
总结起来,这就是数学上求导数的链式法则的图形化表示,咱们的最终目的是求出损失函数对某个参数的导数,那么根据链式法则,只要从损失函数L2Loss出发,将每一层求导结果相乘就能够获得,笔者前一个系列利用Python实现神经网络中也使用了相似的求导方法

Adam(寻优算法)节点
这里写图片描述
Adam算法自己的原理和实现能够参阅笔者此前的文章:Python实现神经网络Part 2: 训练单个神经元找到最优解
上述图形就是根据Adam算法原理,使用梯度、beta1\beta2,结合每一轮训练中weights和bias的原始值,计算weight和bias的更新值,经过控制依赖关系进一步调节weights和bias

总结

TensorBoard中的Graph不一样于通常的神经网络结构图,它是一种计算图。图中每一个节点要么是Tensor自己,要么是运算符,每一条边要么表明Tensor的流动,要么表明控制关系。这张图完备的表达了经过代码定义的神经网络中全部计算步骤,能够据此说明前向计算、偏差反相传播、梯度降低调整参数等过程。

在实际工做中,理解了上述含义,就能够将Graph利用起来,在Debug过程当中可视化的发现网络计算流程中的问题。复杂程序的调试老是困难的,引入可视化工具对于调试效率会很是有帮助。


image

image

image

image

image

image








参考文献:

http://www.360doc.com/content/17/0414/13/10408243_645540404.shtml

http://blog.csdn.net/helei001/article/details/51842531

http://www.studyai.com/course/play/bb9611e4ca824f9ebbc756fb240f1da8

相关文章
相关标签/搜索