在使用MNIST这样的小问题进行编码时,我一直在使用feed_dict来引导placeholder
。 TensorFlow还支持使用queue
和queue runner
提供数据,需要一些努力才能学习。
是否有人对这两种方法进行了比较并衡量了效果?是否值得花时间学习使用队列来提供数据?
我想使用队列不仅可以提高性能,还可以使用更清晰的代码,这意味着什么。也许一个数据集的代码可以很容易地用于另一个数据集(一旦我将数据转换为TFRecord)?
但是,this post似乎说队列可能比feed_dict方法慢。现在还是这样吗?如果代码更慢,更难编码,我为什么要使用队列?
感谢您的投入。
答案 0 :(得分:5)
我的NMT模型有2层,512个隐藏单位。我训练的最大句子长度= 50,批量大小= 32,并且看到feed_dict和队列之间的速度相似,大约每秒2400-2500个目标单词(我根据此paper使用此度量标准速度)。
我发现feed_dict非常直观且易于使用。队列很难。使用队列,你必须:
1 /将您的数据转换为tfrecords。我实际上有点谷歌了解如何将我的seq2seq数据转换为tfrecords,因为文档不是很有帮助。
2 /从tfrecords解码您的数据。你会发现用于生成tfrecords的函数和解码它不直观匹配。例如,如果我的每个训练样例都有3个序列(只有3个整数列表)src_input, trg_input, trg_target
,我想记录src_input
的长度(它的一些元素可能是PADDING,所以不要't count),这里是如何从每个例子生成tfrecord:
def _make_example(src_input, src_seq_length, trg_input, trg_seq_length, trg_target, target_weight):
context = tf.train.Features(
feature={
'src_seq_length': int64_feature(src_seq_length)
})
feature_lists = tf.train.FeatureLists(
feature_list={
'src_input': int64_featurelist(src_input),
'trg_input': int64_featurelist(trg_input),
'trg_target': int64_featurelist(trg_target)
})
return tf.train.SequenceExample(context=context, feature_lists=feature_lists)
以下是解码方法:
def _read_and_decode(filename_queue):
reader = tf.TFRecordReader(options=self.tfrecord_option)
_, serialized_ex = reader.read(filename_queue)
context_features = {
'src_seq_length': tf.FixedLenFeature([], dtype=tf.int64)
}
sequence_features = {
'src_input': tf.FixedLenSequenceFeature([], dtype=tf.int64),
'trg_input': tf.FixedLenSequenceFeature([], dtype=tf.int64),
'trg_target': tf.FixedLenSequenceFeature([], dtype=tf.int64)
}
context, sequences = tf.parse_single_sequence_example(
serialized_ex,
context_features=context_features,
sequence_features=sequence_features)
src_seq_length = tf.cast(context['src_seq_length'], tf.int32)
src_input = tf.cast(sequences['src_input'], tf.int32)
trg_input = tf.cast(sequences['trg_input'], tf.int32)
trg_target = tf.cast(sequences['trg_target'], tf.int32)
return src_input, src_seq_length, trg_input, trg_target
并生成每个tfrecord功能/功能列表:
def int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def int64_featurelist(l):
feature = [tf.train.Feature(int64_list=tf.train.Int64List(value=[x])) for x in l]
return tf.train.FeatureList(feature=feature)
3 / Train / dev设置。我认为定期训练你的模型一段时间是常见的做法,然后评估开发设置,然后重复。我不知道怎么用队列做这个。使用feed_dict,您只需在同一会话下构建两个具有共享参数的图形,一个用于列车,一个用于开发。当您评估dev set时,只需将dev数据提供给dev图,就是这样。但是对于队列,队列的输出是图表本身的一部分。要运行队列,您必须启动队列运行器,创建协调器,使用此协调器来管理队列。完成后,队列就会关闭!!!目前,我不知道如何最好地编写我的代码以使train / dev设置符合队列,除了打开新会话,每次我评估时为dev构建新图。同样的问题被提出here,你可以谷歌搜索Stackoverflow上的类似问题。
然而,很多人说队列比feed_dict快。如果你以分布式方式训练,我的猜测是队列是有益的。但对我来说,我经常只在1个GPU上进行训练,到目前为止,我根本没有对队列留下深刻印象。嗯,只是我的猜测。
答案 1 :(得分:4)
这是一个基准:
BasicRNNCell以200个隐藏单位展开20个时间步长。我有250k训练样例并运行了一个批量大小为20的纪元。
feed_dict:597秒
队列:591秒
这是TF v1.0,在i5笔记本电脑上(所以4个CPU)和Ubuntu 16.04。
答案 2 :(得分:4)
我认为您将看到的好处在很大程度上取决于您的问题。当我从feed_dict
切换到队列时,我看到了3倍的加速。至少有两个原因使我的案例有了如此显着的改善:
生成向量的Python代码非常缓慢且未经优化。对于每个训练示例,都有很多中间步骤(分配一些numpy数组,制作Pandas数据帧,调用一堆函数来计算/转换功能)。我总培训时间的25%用于生成Feed数据。
feed_dict
可能很慢的一个原因是它涉及从Python到TF运行时的数据的memcpy。我的例子很大,所以我对此大加赞赏。 (在我的情况下,因为我的例子是seqeuences,并且在给它们之前将它们填充到一个很大的最大长度)。
如果您认为其中任何一个可能适用于您的问题,那么值得考虑使用队列。