名字作用域和抽象节点
抽象节点,它不是一个具体的操作,而是代表一组特定操作的集合。将同一层网络或具有相同功能的操作整合为一个抽象节点,以简化数据流图的网络结构。
虽然ANN模型非常简单,但是如果不做任何处理直接可视化,它的graph仍然稍显复杂。如果进一步可视化深度神经网络,可能就会陷入节点间的复杂网络惋惜,难以迅速定位graph结构问题。因此,针对可视化深度神经网络的场景,我们应该尽可能的使用抽象节点整合相同的网络层或功能模块。抽象节点是graph上的subgraph,其内部展开后没有丢失节点间的拓扑关系。
一般使用名字作用域定义graph上抽象节点。相比于只作用于存储节点的变量作用域,名字作用域更通用,因为他对计算、存储和数据节点都有效。同一名字作用域下的所有节点集成相同的名字前缀,在graph上体现为该作用域下的所有节点都汇聚成一个抽象节点。
示例代码:
x = tf.placeholder(tf.float32, [None, 784])
with tf.name_scope('layer1'):
w1 = tf.Variable(tf.random_normal([784, 1024]), 'weight1')
b1 = tf.Variable(tf.zeros[1024], name='bias1')
y1 = tf.nn.relu(tf.matmul(x, w1) + b1
此外name_scope还支持多层嵌套,例如
x = tf.placeholder(tf.float32, [None, 784])
with tf.name_scope('layer1'):
with tf.name_scope('weight'):
w1 = tf.Variable(tf.random_normal([784, 1024]), 'weight1')
with tf.name_scope('bias'):
b1 = tf.Variable(tf.zeros[1024], name='bias1')
with tf.name_scop('activation'):
y1 = tf.nn.relu(tf.matmul(x, w1) + b1
可视化数据流图示例
以MNIST softmax模型为例,介绍使用TB可视化其数据流图的步骤和方法。
可视化数据流图可分为以下三个步骤:
- 创建graph
创建graph,并使用name_scope将同一层网络或相同功能模块的节点应以为同一个抽象节点,如输入模块、softmax网络层、交叉熵、优化器和评估指标。同时,对于同一个抽象内部包含多个节点的情况,可以嵌套name_scope,使得graph的网络结构更加清晰。from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
def model(lr=0.01):
with tf.name_scope('input'):
x = tf.placeholder(tf.float32, [None, 784], name='x')
gt = tf.placeholder(tf.float32, [None, 10], name='ground-truth')
with tf.name_scope('layer1'):
with tf.name_scope('weight'):
w1 = tf.Variable(tf.random_normal([784, 1024]))
with tf.name_scope('bias'):
b1 = tf.Variable(tf.zeros([1024])
with tf.name_scope('output'):
y1 = tf.nn.relu(tf.matmul(x, w1) + b1)
with tf.name_scope('layer2'):
with tf.name_scope('weight'):
w2 = tf.Variable(tf.random_normal([1024, 10])
with tf.name_scope('bias'):
b2 = tf.Variable(tf.zeros([10])
with tf.name_scope('output'):
y2 = tf.nn.softmax(tf.matmul(y1, w2) + b2)
with tf.name_scope('cross_entropy'):
loss = -tf.reduce_sum(gt * tf.log(tf.clip_by_value(y2, 1e-10, 1.0)))
tf.summary.scalar('loss', loss)
with tf.name_scope('train'):
optimizer = tf.train.AdamOptimizer(lr)
train_ops = optimizer.minimize(loss)
with tf.name_scope('metric'):
with tf.name_scope('correct_prediction'):
correct_prediction = tf.equal(tf.argmax(y2, 1), tf.argmax(gt, 1))
with tf.name_scope('accuracy'):
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)
return x, gt, train_ops, accuracy
- 创建FileWriter
先将上一步创建的graph写入event file。FileWriter构造方法的第一个参数logdir是必须设置的,他表示FileWriter实例创建的event file的目录。由于同一个模型可能训练多次,FileWriter会自动为每次训练创建各自的事件文件。当我们向FileWriter实例的构造方法传入graph时,他也会自动将graph写入evnet file中。 示例代码如下:
with tf.Session() as sess:
writer = tf.summary.FileWriter('/tmp/log', sess.graph)
tf.global_variables_initializer().run()
...
writer.close()
- 启动TB程序
启动TB程序,并从命令行给出logdir路径。当同一个模型训练多次之后,TB可以将该目录下的所有event file的数据进行fusion。在TB web界面上呈现的便是连续变化的数据。 命令行指令如下:$: tensorboard --logdir='/tmp/log'
TB程序启动后默认使用6006端口,在web browser中输入localhost:6006,即可看到程序可视化结果。
汇总数据和事件数据
汇总数据
汇总数据是tf.summary.Summary类或其内嵌类的实例。tf.summary.Summary包含3个内嵌类:
-
Image 用于表示图像数据 message Image {
int32 height = 1;
int32 width = 2;
int 32 colorspace = 3;
bytes encoded_image_string = 4;
}
-
Audio 用于表示音频数据 message Audio{
float sample_rate = 1;
int64 num_channels = 2;
int64 length_frames = 3;
bytes encoded_audio_string = 4;
string content_type = 5;
}
-
Value 用于表示具名数值 message Value {
string node_name = 7
string tag = 1;
oneof value{
float simple_value = 2;
bytes = obsolete_old_style_histogram = 3;
Image image = 4;
HistogramProto histo = 5;
Audio audio = 6;
TensorProto tensor = 8;
}
}
事件数据
事件数据是tf.summary.Event()类的实例。事件数据表示在session中执行操作是产生的event info,包括时间戳、全局步数、以及oneof(多选一)方式定义的具体event info。对于一个特定的事件实例,event info只能从以下字段中选择一种:文件版本号、编码后的graph,summary data、TB log、Session status、metadata、以及编码后的meta-graph。它们分别代表相应类型对象的创建event。
部分原型定义如下:
message Event {
double wall_time = 1;
int64 step = 2;
oneof what {
string file_version = 3;
bytes graph_def = 4;
Summary summary = 5;
LogMessage log_message = 6;
SessionLog session_log = 7;
TaggedRunMetadata tagged_run_metadata = 8;
bytes meta_graph_def = 9;
}
}
tf.summary.FileWriter工作原理
FileWriter向事件文件中写入的是event data,那么summary data 和其他序列化数据就需要转化为event data。此外,因为训练过程会产生大量数据,所以FileWriter还得支持并行地更新event file。
FileWriter的构造方法如下:
class FileWriter(SummartyToEventTransformer):
def __init__(self, logdir, graph=None, max_queue=10, flush_secs=120, graph_def=None,filename_suffix=None):
event_writer = EventFileWriter(logdir, max_queue, flush_secs)
super(FileWriter, self).__init__(event_writer, graph, graph_def)
FileWriter类继承自SummaryToEvntTrasnforer类。FileWriter实例包含event_writer成员对象,后者是EventFileWriter的实例,提供向事件文件中写入event data的能力。因此,FileWriter内部实际调用的是event_write的成员方法来完成事件文件的创建和更新。
主要参数介绍:
- logdir:存储event file的目录
- graph:session中加载的graph
- max_queue:缓存event data的队列大小
- flush_secs:定期刷新event data缓存队列的时间
- graph_def:被遗弃,为了兼容低版本TF,所以保留了参数
- filename_suffix:设置事件文件的名称后缀
对于可视化数据的写入,一般使用基类SummaryToEventTransforer的成员方法:
SummaryToEventTransforer.add_event() ,添加事件数据到事件文件SummaryToEventTransforer.add_graph() ,添加数据流图到事件文件SummaryToEventTransforer.add_meta_graph() ,添加元数据流图到事件文件SummaryToEventTransforer.add_run_metadata() ,添加元数据信息到事件文件SummaryToEventTransforer.add_session_log() ,添加会话日志到事件文件SummaryToEventTransforer.add_summary() ,添加summary data to event file。
上述方法并不会立即将数据写入事件文件,如果用户设置了flush_secs参数,FileWriter便会定期将event data缓存队列中的数据写入event file。用户也可以调用FileWriter.flush() 手动刷新event data 缓存队列。
FileWriter类其他常用的成员方法:
- FileWriter.get_logdir(),获取event file 的目录
- FileWriter.flush(),将event data缓存队列中的所有数据添加到event file中
- FileWriter.close(),先调用flush*(方法,然后关闭event filestream
- FileWriter.reopen(),如果调用了close方法,则重新打开event filestream,并在logdir目录下新建一个event文件;否则不做任何操作。
|