【问题标题】:TensorFlow: Feeding data with queue vs with direct feeding with feed_dictTensorFlow:使用队列馈送数据与使用 feed_dict 直接馈送
【发布时间】:2016-07-17 00:23:04
【问题描述】:

在练习 MNIST 等小问题的编码时,我一直在使用 feed_dict 来直接输入 placeholder。 TensorFlow 还支持使用queuequeue runner 喂数据,需要花点功夫去学习。

有没有人比较这两种方法并测量性能?是否值得花时间学习使用队列来提供数据?

我猜想使用队列不仅是为了提高性能,也是为了更简洁的代码,这意味着什么。也许一个数据集的代码可以很容易地用于另一个数据集(一旦我将数据转换为 TFRecord)?

但是,this post 似乎说队列可能比 feed_dict 方法慢。现在还是真的吗?如果队列更慢更难编码,我为什么要使用它?

感谢您的意见。

【问题讨论】:

  • 该教程的第一部分提供了有关何时使用队列的更多指导:indico.io/blog/…

标签: performance tensorflow


【解决方案1】:

我的 NMT 模型有 2 层,512 个隐藏单元。我以最大句子长度 = 50,批量大小 = 32 进行训练,发现 feed_dict 和队列之间的速度相似,大约每秒 2400-2500 个目标词(我根据 paper 使用这个速度指标)。

我发现 feed_dict 非常直观且易于使用。排队很困难。使用队列,您必须:

1/ 将您的数据转换为 tfrecords。我实际上需要用谷歌搜索一下,以了解如何将我的 seq2seq 数据转换为 tfrecords,因为文档不是很有帮助。

2/ 从 tfrecords 中解码您的数据。您会发现用于生成 tfrecord 并对其进行解码的函数在直观上并不匹配。例如,如果我的每个训练示例都有 3 个序列(只有 3 个整数列表)src_input, trg_input, trg_target,并且我也想记录 src_input 的长度(它的一些元素可能是 PADDING,所以不要计算) ,下面是如何从每个示例中生成 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/ 训练/开发设置。我相信定期训练你的模型一段时间,然后在开发集上评估,然后重复是一种常见的做法。我不知道如何用队列做到这一点。使用 feed_dict,您只需在同一会话下构建两个具有共享参数的图表,一个用于训练,一个用于开发。当您评估开发集时,只需将开发数据提供给开发图即可。但是对于队列,队列的输出是图形本身的一部分。要运行队列,你必须启动队列运行器,创建一个协调器,使用这个协调器来管理队列。完成后,队列已关闭!!!!目前,我不知道如何最好地编写代码以使训练/开发设置与队列保持一致,除了打开新会话、每次评估时为开发构建新图表。 here 提出了同样的问题,您可以在 Stackoverflow 上搜索类似问题。

但是,很多人说 queue 比 feed_dict 快。我的猜测是,如果您以分布式方式训练,队列是有益的。但对我来说,我经常只在 1 个 GPU 上训练,到目前为止,我对队列完全没有印象。嗯,只是我的猜测。

【讨论】:

    【解决方案2】:

    我认为您将看到的好处在很大程度上取决于您的问题。当我从 feed_dict 切换到队列时,我看到了 3 倍的加速。在我的情况下,它至少有两个原因可以带来如此显着的改善:

    1. 生成要馈送的向量的 Python 代码非常缓慢且未经优化。对于每个训练示例,都有很多中间步骤(分配一些 numpy 数组,制作 Pandas 数据帧,调用一堆函数来计算/转换特征)。我总训练时间的 25% 用于生成提要数据。

    2. feed_dict 速度慢的原因之一是它涉及从 Python 到 TF 运行时的馈送数据的 memcpy。我的例子非常大,所以我对此很感兴趣。 (在我的例子中,因为我的例子是序列,我在喂它们之前将它们补零到最大长度)。

    如果您认为其中任何一个都可能适用于您的问题,那么值得考虑使用队列。

    【讨论】:

      【解决方案3】:

      这是一个基准:

      BasicRNNCell 展开为 20 个时间步,包含 200 个隐藏单元。我有 250k 个训练示例并运行了 1 个 epoch,批量大小为 20。

      feed_dict:597 秒
      队列:591 秒

      这是使用 TF v1.0,在带有 Ubuntu 16.04 的 i5 笔记本电脑(所以 4 个 CPU)上。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-18
        • 1970-01-01
        • 2010-09-18
        • 1970-01-01
        • 1970-01-01
        • 2020-03-01
        • 2015-01-25
        • 2016-11-17
        相关资源
        最近更新 更多