使用tf.Profiler监控图

显示图节点的占用空间、耗时等信息

Posted by xhhszc on November 28, 2018

使用tf.profiler监控图


参考:

1. 引入相关包

import tensorflow as tf
from tensorflow.python.profiler import model_analyzer
from tensorflow.python.profiler import option_builder

注意:只有tf 1.3以上的版本才有profiler功能

2. 创建相关实例

#... 省略图的构建代码...
sess.run(init) # 初始化变量
graph_profiler = model_analyzer.Profiler(graph=sess.graph)# 创建tf.profiler实例,作为记录、处理和显示数据的主体
run_metadata = tf.RunMetadata() # 创建RunMetadata, 用于在每次session.Run()时汇总统计数据
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)#定义trace level为FULL_TRACE,这样我们才能搜集到包括GPU硬件在内的最全统计数据

3. 执行sess.run,在想要记录的iteration中搜集统计数据并添加到tfprofiler实例中

feed_dict = dict()
for step in range(max_step):
    feed_dict = {
                 images_placeholder: images_feed,
                 labels_placeholder: labels_feed,
                }    
    
    if step % 5 == 0:
        _, loss_value = sess.run(fetches=[train_op, loss],feed_dict=feed_dict, options=run_options, run_metadata=run_metadata)#每 5步,搜集一下统计数据:

        #将本步搜集的统计数据添加到tfprofiler实例中     
        graph_profiler.add_step(step=step, run_meta=run_metadata)
    else:
        _, loss_value = sess.run(fetches=[train_op, loss],
                               feed_dict=feed_dict)

4. 定义option

option用于设置过滤条件、显示字段,完整option 参见Options

比较常用的有:

=========================Options=============================
- max_depth 4       命名空间的深度阈值,超过该阈值的分支不予展示
- min_bytes 0       展示占用内存超过该阈值的OP
- min_micros 10       展示耗时超过该阈值的OP
- min_params  0       展示超过该阈值的参数大小的OP
- min_float_ops 0       展示超过该阈值的浮点计算量的 OP
- min_occurrence  0        展示超过该阈值的出现次数的 OP
- step -1       展示训练时候哪一步的统计情况,默认为最后一步
- order_by micros       统计结果排序字段设定
- account_type_regexes _trainable_variables 
    #Selectively counting statistics based on node types ,比如这里设定展示可训练变量;account_type_regexes=['.*gpu:0.*'] 设定展示 GPU 运行的变量;
    以下四个选项可以更加灵活的设置展示哪些 OP 
    - start_name_regexes .* #从哪儿可以开始匹配
          
    - trim_name_regexes #从哪儿停止匹配
    - show_name_regexes siamese.* #展示什么字段的 OP
    - hide_name_regexes #隐藏什么字段的 OP
- account_displayed_op_only false
- select micros       选择展示的属性,这里选择查看计算耗时(micros)/查看内存占用则设置为bytes,
- output stdout:       输出方式,这里是标准输出

下面为一个例子:


# a readout of memory usage for each node in graph
profile_scope_opts_builder = option_builder.ProfileOptionBuilder(
  option_builder.ProfileOptionBuilder.trainable_variables_parameter())

# set to timeline output
profile_scope_opts_builder.with_timeline_output(timeline_file='/tmp/train_profiler.json')
profile_scope_opts_builder.with_min_memory(min_bytes=1024*1024) # only show >1Mb
profile_scope_opts_builder.select(['params','bytes','input_shapes','micros','device'])
profile_scope_opt_builder.order_by('bytes')

其中with_timeline_output表示要以Timeline形式输出,可选的方法还有:

  • with_file_output(outfile=”filename”) # 以文件形式输出
  • with_stdout_output() # print输出

6.选择视图模式

tf.Profiler有4种视图模式为:

  • ‘code’: 统计按照每行代码进行聚集,最终看到的是每行代码的耗时和内存占用;
  • ‘op’: 统计按照每种OP 的类型,最终看到每种操作耗时和内存占用;
  • ‘scope’: 统计按照命名空间的层级进行,最后看到每个命名空间及其子空间的情况;
  • ‘graph’: 统计按照计算图上的顺序进行,也就是每个节点的输入输出等情况; 各模式的详情例子见上面的参考blog链接

一下我们选择‘scope’模式

graph_profiler.profile_name_scope(profile_scope_opt_builder.build())

其它模式对应的函数为:

  • ‘code’: profile_python(builder.build())
  • ‘op’: profile_operations(builder.build())
  • ‘scope’: profile_name_scope(builder.build())
  • ‘graph’: profile_graph(builder.build())

5. 查看记录内容

在运行我们的python程序后,我们就可以在浏览器中查看profiler记录的内容:

打开chrome浏览器,输入about:tracing, 然后load “/tmp/train_profiler.json” 文件,显示如图: Alt text

不幸的是,如果你是在服务器上运行的程序,你只能在将服务器上的“/tmp/train_profiler.json”下载到本地,然后再进行查看。

6. 我遇到的bug

运行程序时出现错误提示:

Couldn't open CUDA library libcupti.so.8.0. LD_LIBRARY_PATH: /usr/local/cuda/lib64:

解决方法: 根据提示,应该是LD_LIBRARY_PATH的路径不太对,现有路径“/usr/local/cuda/lib64:”下为cuda 9.0 版本,因此需要修改LD_LIBRARY_PATH中的内容

直接修改LD_LIBRARY_PATH内容可能会有一定的风险,(也有权限等问题),所以建议修改当前用户的.bashrc 文件:

#  在.bashrc中添加下面这行
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64:/usr/local/cuda-8.0/extras/CUPTI/lib64/
#  或者添加下面这一行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-8.0/lib64:/usr/local/cuda-8.0/extras/CUPTI/lib64/

其中[/usr/local/cuda-8.0/lib64]是linux中cuda 8.0的位置,[/usr/local/cuda-8.0/extras/CUPTI/lib64/]是linux中libcupti.so.8.0的位置。

当然,如果你没有安装cuda 8.0,那就从安装cuda开始吧。

后记


后来我发现了有比上面更简便的方法,但所谓简便,即封装的更好,灵活性也就更差,但仍记录在此,以防以后忘记。

1. 引入相关包

import tensorflow as tf

2. 创建option实例,并进行相关设置

# Create options to profile the time and memory information.
builder = tf.profiler.ProfileOptionBuilder
builder_opts = builder(builder.time_and_memory())
# builder_opts = builder(builder.time_and_memory())
builder_opts.with_timeline_output(timeline_file='tmp/%s_profiler.json'%FLAGS.dataset)
builder_opts.with_min_memory(min_bytes=1024*1024) # only show >1Mb
builder_opts.select(['params','bytes','input_shapes','micros','device'])
builder_opts.order_by('bytes')
builder_opts = builder_opts.build()

3. 使用tfprof进行变量跟踪

with tf.contrib.tfprof.ProfileContext('tmp/%s_profiler.json'%FLAGS.dataset, trace_steps=range(0,20,5), dump_steps=range(0,20,5)) as pctx:
    # Initialize session
    config = tf.ConfigProto()
    config.gpu_options.allow_growth = True
    with tf.Session(config=config) as sess:
        # Init variables
        sess.run(tf.global_variables_initializer())
        # Enable tracing for next session.run.
        pctx.trace_next_step()
        # Dump the profile to '/tmp/train_dir' after the step.
        pctx.dump_next_step()
        for epoch in range(FLAGS.epochs):
            outs = sess.run([model.opt_op, model.loss, model.accuracy], feed_dict=feed_dict)
            pctx.profiler.profile_graph(options=builder_opts)
            # pctx.add_auto_profiling('op', builder_opts, [1, 9, 19])

4. 查看记录内容

这一步跟之前一模一样。