BUFFER_SIZE在Dataset.map,Dataset.prefetch和Dataset.shuffle意义

问题描述 投票:62回答:5

按照TensorFlow documentationprefetch类的maptf.contrib.data.Dataset方法,都有一个名为buffer_size参数。

对于prefetch的方法,所述参数被称为buffer_size并且根据文档:

BUFFER_SIZE:甲tf.int64标量tf.Tensor,表示预取时将被缓冲的最大数量的元素。

对于map的方法,所述参数被称为output_buffer_size并且根据文档:

output_buffer_size:(可选)。A tf.int64标量tf.Tensor,代表将被缓冲处理的元素的最大数量。

类似地,对于shuffle方法中,相同的数量和出现根据文档:

BUFFER_SIZE:甲tf.int64标量tf.Tensor,表示从该数据集从该新数据集将采样元件的数量。

什么是这些参数之间的关系?

假设我创建aDataset对象,如下所示:

 tr_data = TFRecordDataset(trainfilenames)
    tr_data = tr_data.map(providefortraining, output_buffer_size=10 * trainbatchsize, num_parallel_calls\
=5)
    tr_data = tr_data.shuffle(buffer_size= 100 * trainbatchsize)
    tr_data = tr_data.prefetch(buffer_size = 10 * trainbatchsize)
    tr_data = tr_data.batch(trainbatchsize)

什么样的角色正在由buffer参数在上面的代码片段播放?

tensorflow tensorflow-gpu tensorflow-datasets
5个回答
104
投票

TL; DR尽管它们的名称相似,这些论点有相当差异的含义。在buffer_sizeDataset.shuffle()可以影响你的数据集的随机性,因此在其中的元件所产生的顺序。在buffer_sizeDataset.prefetch()只影响才能产生下一个元素的时间。


buffer_sizetf.data.Dataset.prefetch()参数和output_buffer_sizetf.contrib.data.Dataset.map()参数调整提供了一种方式您输入管线的性能:这两个参数告诉TensorFlow创造最多buffer_size元素的缓冲区,一个后台线程来填补背景缓冲区。 (请注意,我们删除从output_buffer_sizeDataset.map()参数时,它从tf.contrib.data搬到tf.data。之后Dataset.prefetch()新代码应该使用map()来获得相同的行为。)

添加预取缓冲器可以通过数据的预处理与下游的计算重叠,提高性能。通常,它是最有用的在管线的最末端添加一个小的预取缓冲液(含也许只是一个单一的元素),但更复杂的管道可以从附加的预取获益,特别是当时间以产生单一元件可以改变。

相比之下,buffer_size参数tf.data.Dataset.shuffle()影响转型的随机性。我们设计的Dataset.shuffle()变换(就像它取代了tf.train.shuffle_batch()函数)来处理是太大,无法在内存中的数据集。代替洗牌整个数据集的,它维持buffer_size元件的缓冲器中,并且随机选择来自缓冲器(与下一个输入元件代替它,如果有一个可用)的下一个元素。更改buffer_size的值会影响洗牌如何统一是:如果buffer_size比数据集中要素的数量越多,你得到一个统一的洗牌;如果是1那么你没有洗牌的。对于非常大的数据集,一个典型的“足够好”的做法是在训练前一次随机分片数据到多个文件,然后均匀洗牌的文件名,然后使用一个较小的移缓冲器。但是,适当的选择将取决于你的训练作业的确切性质。



95
投票

Importance of buffer_size in shuffle()

我想跟进从@mrry以前的答案强调buffer_sizetf.data.Dataset.shuffle()的重要性。

具有低buffer_size不会只给你不如洗牌有些情况是:它可以搞砸你的整个训练。


一个实际的例子:猫分类

假设,例如,你在训练上的图像猫的分类,你的数据在下面的方式(在每个分类均具有10000图片)组织:

train/
    cat/
        filename_00001.jpg
        filename_00002.jpg
        ...
    not_cat/
        filename_10001.jpg
        filename_10002.jpg
        ...

tf.data输入数据的标准方法可以有文件名列表和相应标签的列表,并使用tf.data.Dataset.from_tensor_slices()创建数据集:

filenames = ["filename_00001.jpg", "filename_00002.jpg", ..., 
             "filename_10001.jpg", "filename_10002.jpg", ...]
labels = [1, 1, ..., 0, 0...]  # 1 for cat, 0 for not_cat

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.shuffle(buffer_size=1000)  # 1000 should be enough right?
dataset = dataset.map(...)  # transform to images, preprocess, repeat, batch...

与上面的代码中最大的问题是,数据集将实际上不以正确的方式洗牌。关于一个时代的前半部分,我们将只能看到猫的图片,并为下半年唯一的非猫的图像。这会伤害培养了很多。 在训练开始时,数据集将采取先1000文件名,并把它们在其缓冲区,然后选择一个在其中随机的。由于所有的第一1000图像是猫的图片,我们才刚刚开始挑猫的图像。

这里的解决方法是确保buffer_size20000大,或提前filenameslabels(具有相同指数明显)洗牌。

由于存储所有的文件名和标签内存是不是一个问题,我们实际上可以使用buffer_size = len(filenames),以确保一切都将被洗牌在一起。确保将沉重的转换(如读取图像,处理它们,配料...)之前调用tf.data.Dataset.shuffle()

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.shuffle(buffer_size=len(filenames)) 
dataset = dataset.map(...)  # transform to images, preprocess, repeat, batch...

外卖是始终仔细检查一下洗牌会做。捕捉这些错误的一个好方法可能是绘制批次随时间的分布(确保批次包含关于同一配送为一体的训练集,半猫在我们的例子中一半非猫)。


5
投票

import tensorflow as tf
def shuffle():
    ds = list(range(0,1000))
    dataset = tf.data.Dataset.from_tensor_slices(ds)
    dataset=dataset.shuffle(buffer_size=500)
    dataset = dataset.batch(batch_size=1)
    iterator = dataset.make_initializable_iterator()
    next_element=iterator.get_next()
    init_op = iterator.initializer
    with tf.Session() as sess:
        sess.run(init_op)
        for i in range(100):
            print(sess.run(next_element), end='')

shuffle()

产量

[298][326][2][351][92][398][72][134][404][378][238][131][369][324][35][182][441][370][372][144][77][11][199][65][346][418][493][343][444][470][222][83][61][81][366][49][295][399][177][507][288][524][401][386][89][371][181][489][172][159][195][232][160][352][495][241][435][127][268][429][382][479][519][116][395][165][233][37][486][553][111][525][170][571][215][530][47][291][558][21][245][514][103][45][545][219][468][338][392][54][139][339][448][471][589][321][223][311][234][314]


2
投票

我发现,@奥利维尔 - moindrot确实是正确的,我试图通过@Houtarou Oreki提供的代码,使用@Max指出的修改。我使用的代码是以下几点:

fake_data = np.concatenate((np.arange(1,500,1),np.zeros(500)))

dataset = tf.data.Dataset.from_tensor_slices(fake_data)
dataset=dataset.shuffle(buffer_size=100)
dataset = dataset.batch(batch_size=10)
iterator = dataset.make_initializable_iterator()
next_element=iterator.get_next()

init_op = iterator.initializer

with tf.Session() as sess:
    sess.run(init_op)
    for i in range(50):
        print(i)
        salida = np.array(sess.run(next_element))
        print(salida)
        print(salida.max())

该代码输出确实是一个数从1到(BUFFER_SIZE +(我的batch_size *)),其中i是的次数,你跑next_element。我认为这是工作的方式如下。首先,BUFFER_SIZE样本,以便从fake_data采摘。然后一个一个的样品的batch_size从缓冲器拾取。每一批样品从缓冲拾取时间它是由一个新的,从fake_data为了采取更换。我用下面的代码测试了这最后一件事:

aux = 0
for j in range (10000):
    with tf.Session() as sess:
        sess.run(init_op)
        salida = np.array(sess.run(next_element))
        if salida.max() > aux:
            aux = salida.max()

print(aux)

由代码生成的最大值为109。所以,你需要保证你内的batch_size平衡样品,以确保培训过程中均匀采样。

我还测试说过性能什么@mrry,我发现将batch_size时预取样品的量到内存中。我测试了这个使用下面的代码:

dataset = dataset.shuffle(buffer_size=20)
dataset = dataset.prefetch(10)
dataset = dataset.batch(batch_size=5)

改变dataset.prefetch(10)量导致存储器(RAM)中使用没有变化。这是重要的,当你的数据不适合没有到RAM中。我认为最好的办法就是喂养它们tf.dataset前洗你的数据/ file_names,然后使用buffer_size控制的缓冲区大小。


1
投票

实际上是由@奥利维尔 - moindrot的答案是不正确的。

您可以通过创建文件名和标签,因为他/她提和打印洗牌值验证。

你会看到大小等于从数据集缓冲区大小各洗牌过程将随机生成的样本。

dataset = dataset.shuffle(buffer_size=1000)
iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()
with tf.Session() as sess:
    for i in range(1000):
        print(sess.run(next_element))
© www.soinside.com 2019 - 2024. All rights reserved.