我用数据集API方法替换了项目中的CIFAR-10预处理管道,导致性能下降了大约10-20%。
准备工作相当标准: - 从磁盘读取图像 - 随机/裁剪和翻转 - 洗牌,批量 - 送给模特
总的来说,我看到现在的批处理处理速度提高了15%,但每隔一段时间(或者更确切地说,每当我重新初始化数据框或预期重新洗牌时)批处理都会被长时间(30秒)阻止,这总计很长减慢每个纪元的处理时间。
这种行为似乎与内部哈希有关。如果我在ds.shuffle(buffer_size = N)中减少N,则延迟较短但比例更频繁。删除shuffle会导致延迟,就像buffer_size设置为数据集大小一样。
在阅读/缓存方面,有人可以解释数据集API的内部逻辑吗?是否有任何理由期望数据集API比手动创建的队列更快地工作?
我正在使用TF 1.3。
答案 0 :(得分:10)
如果使用tf.data.Dataset
API并使用队列实施相同管道,则数据集版本的性能应优于基于队列的版本。
但是,为了获得最佳性能,需要遵循一些性能最佳实践。我们在performance guide for tf.data
收集了这些内容。以下是主要问题:
预取很重要:默认情况下基于队列的管道预取,而数据集管道则不预取。将dataset.prefetch(1)
添加到管道的末尾将为您提供预取的大部分好处,但您可能需要进一步调整它。
shuffle运算符在开头有一个延迟,而它填充缓冲区。基于队列的管道混合了所有历元的串联,这意味着缓冲区仅填充一次。在数据集管道中,这相当于dataset.repeat(NUM_EPOCHS).shuffle(N)
。相比之下,您也可以编写dataset.shuffle(N).repeat(NUM_EPOCHS)
,但这需要重新开始每个时代的混乱。后一种方法稍微好一点(例如,对于SGD的定义更为正确),但如果您的数据集很大,则差异可能不明显。
我们正在添加一个融合版本的shuffle-and-repeat,不会产生延迟,每晚构建的TensorFlow将包含相当于dataset.shuffle(N).repeat(NUM_EPOCHS)
的自定义tf.contrib.data.shuffle_and_repeat()
转换但在每个时代开始时都没有遭受延误。
话虽如此,如果您使用tf.data
时的管道明显比队列慢,请提交GitHub issue详细信息,我们就来看看吧! / p>
答案 1 :(得分:0)
建议的事情在过去并不能解决我的问题,但是我想为那些不想了解队列并仍然充分利用TF数据管道的人提供一些建议:< / p>
。
files = tf.data.Dataset.list_files(data_dir)
ds = tf.data.TFRecordDataset(files, num_parallel_reads=32)
ds = (ds.shuffle(10000)
.repeat(EPOCHS)
.map(parser_fn, num_parallel_calls=64)
.batch(batch_size)
)
dataset = dataset.prefetch(2)
必须注意3个主要组成部分的地方:
num_parallel_read=32
并行化磁盘IO操作num_parallel_calls=64
使对解析器函数的调用并行化prefetch(2)