Study-Notes
Study-Notes copied to clipboard
TensorFlow使用技巧
参考自:
1. 使用 timeline 来优化优化性能
timeline
可以分析整个模型在forward
和backward
的时候,每个操作消耗的时间,由此可以针对性的优化耗时的操作。我之前尝试使用 tensorflow 多卡来加速训练的时候, 最后发现多卡速度还不如单卡快,改用tf.data
来 加速读图片还是很慢,最后使用imeline
分析出了速度慢的原因,timeline
的使用如下
run_metadata = tf.RunMetadata()
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
config = tf.ConfigProto(graph_options=tf.GraphOptions(
optimizer_options=tf.OptimizerOptions(opt_level=tf.OptimizerOptions.L0)))
with tf.Session(config=config) as sess:
c_np = sess.run(c,options=run_options,run_metadata=run_metadata)
tl = timeline.Timeline(run_metadata.step_stats)
ctf = tl.generate_chrome_trace_format()
with open('timeline.json','w') as wd:
wd.write(ctf)
然后到谷歌浏览器中打卡chrome://tracing 并导入 timeline.json ,最后可以看得如下图所示的每个操作消耗的时间.
这里横坐标为时间,从左到右依次为模型一次完整的forward and backward
过程中,每个操作分别在cpu,gpu 0, gpu 1
上消耗的时间,这些操作可以放大,非常方便观察具体每个操作在哪一个设备上消耗多少时间。这里我们 cpu 上主要有 QueueDequeue 操作,这是进行图片预期过程,这个时候 gpu 在并行计算的所以 gpu 没有空等;另外我的模型还有一个 PyFunc 在 cpu 上运行,如红框所示,此时 gpu 在等这个结果,没有任何操作运行,这个操作应该要优化的。另外就是如黑框所示,gpu上执行的时候有很大空隙,如黑框所示,这个导致gpu上的性能没有很好的利用起来,最后分析发现是我bn
在多卡环境下没有使用正确,bn
有一个参数updates_collections
我设置为None
这时bn
的参数 mean,var 是立即更新的,也是计算完当前 layer 的 mean,var 就更新,然后进行下一个 layer 的操作,这在单卡下没有问题的, 但是多卡情况下就会写等读的冲突,因为可能存在gpu0
更新(写)mean 但此时gpu1
还没有计算到该层,所以gpu0
就要等gpu1
读完 mean 才能写,这样导致了 如黑框所示的空隙,这时只需将参数设置成updates_collections=tf.GraphKeys.UPDATE_OPS
即可,表示所以的 bn 参数更新由用户来显示指定更新,如
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
train_op = optimizer.minimize(loss)
这样可以在每个卡forward完后,再更新bn参数,此时的写写不存在冲突。优化后,我的2卡训练获得了接近2倍的加速比。
2. 检查 NaN 方法
使用check= tf.add_check_numerics_ops,sess.run([check, ...])
来检查NaN
问题,该操作会报告所有出现NaN
的操作,从而方便找到NaN
的源头。
3. 设置环境变量CUDA_VISIBLE_DEVICES
选择GPU
# 指定哪张卡:0,1,...
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
4. freeze 某层参数
直接通过变量名筛选实现固定某层参数:
def get_train_op(loss, lr=0.01):
optimizer = tf.train.AdamOptimizer(lr)
tvars = tf.trainable_variables()
# 通过变量名筛选
tvars = [v for v in tvars if 'frozen' not in v.name]
grads = tf.gradients(loss, tvars)
train_op = optimizer.apply_gradients(zip(grads, tvars), global_step=tf.train.get_global_step()) return train_op
5. 关于map_fn
map_fn
调用了tf.while_loop
,使得并行计算非常困难,这可以通过timeline
的 profile 发现。
为了避免低效的map_fn
,在一些问题中,如果希望对某个维度进行map
的操作,可以先对原tensor
进行reshape
,将需要map
的维度融入到batch
的维度,操作完成后再reshape
回来。
6. 联合使用pdb
与.get_shape()
快速调试模型
对于调试如下代码:
embeddings = tf.Variable(tf.random_uniform([50000, 64], -1.0, 1.0))
import pdb;
pdb.set_trace()
embed = tf.nn.embedding_lookup(embeddings, inputs)
可以考虑创建embedding
后加入断点,这样方便在pdb
中自由检测各个变量的维度。
pdb>> intputs.get_shape()
pdb>> embed.get_shape()
这样做的好处就是,可以对graph
中各个变量的形状形成一个非常具体的理解,并且可以在pdb
中对各个tensor
进行变换,比如转置等操作。
7. 读取.event
文件
当你有需要从.events文件里读训练过程的各种loss指标,以及evaluation中各种recall,precision,auc时可以用以下代码读取(改自tensorflow实现earlyStop功能的部分源码)
import os
import collections
import tensorflow as tf
_EVENT_FILE_GLOB_PATTERN = 'events.out.tfevents.*'
def _summaries(eval_dir):
"""Yields `tensorflow.Event` protos from event files in the eval dir.
Args:
eval_dir: Directory containing summary files with eval metrics.
Yields:
`tensorflow.Event` object read from the event files.
"""
if tf.gfile.Exists(eval_dir):
for event_file in tf.gfile.Glob(
os.path.join(eval_dir, _EVENT_FILE_GLOB_PATTERN)):
print(event_file)
for event in tf.train.summary_iterator(event_file):
yield event
def read_eval_metrics(eval_dir):
"""Helper to read eval metrics from eval summary files.
Args:
eval_dir: Directory containing summary files with eval metrics.
Returns:
A `dict` with global steps mapping to `dict` of metric names and values.
"""
eval_metrics_dict = {}
for event in _summaries(eval_dir):
if not event.HasField('summary'):
continue
metrics = {}
for value in event.summary.value:
if value.HasField('simple_value'):
metrics[value.tag] = value.simple_value
if metrics:
eval_metrics_dict[event.step] = metrics
return collections.OrderedDict(
sorted(eval_metrics_dict.items(), key=lambda t: t[0]))
if __name__ == '__main__':
dir = '/tmp/eval/'
eval_results = read_eval_metrics(dir)
for step, metrics in eval_results.items():
print(step, metrics)