Значення buffer_size у Dataset.map, Dataset.prefetch та Dataset.shuffle


100

Відповідно до документації TensorFlow , prefetchі mapметод, і tf.contrib.data.Datasetклас мають обидва параметри buffer_size.

Для prefetchметоду параметр відомий як buffer_sizeі згідно з документацією:

buffer_size: Скалярний tf.Tensor tf.int64, що представляє максимальну кількість елементів, які будуть буферизовані під час попереднього отримання.

Для mapметоду параметр відомий як output_buffer_sizeі згідно з документацією:

output_buffer_size: (Необов’язково.) Скаляр tf.int64 tf.int64, що представляє максимальну кількість оброблених елементів, які будуть буферизовані.

Так само для shuffleметоду, така сама кількість з'являється і згідно з документацією:

buffer_size: Скалярний tf.Tensor tf.int64, що представляє кількість елементів із цього набору даних, з яких буде вибірка нового набору даних.

Яке відношення між цими параметрами?

Припустимо, я створюю Datasetоб'єкт наступним чином:

 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параметри у наведеному фрагменті?


1
404 посилання на "документацію" не знайдено.
Прадіп Сінгх,

Відповіді:


150

TL; DR Незважаючи на схожі назви, ці аргументи мають досить різне значення. buffer_sizeУ Dataset.shuffle()може вплинути на хаотичність в наборі даних, і , отже , порядок , в якому виробляються елементи. buffer_sizeВ Dataset.prefetch()впливає тільки на час, необхідний для отримання наступного елемента.


buffer_sizeАргумент в tf.data.Dataset.prefetch()і output_buffer_sizeаргумент в tf.contrib.data.Dataset.map()забезпечити спосіб для настройки продуктивності вашого вхідного трубопроводу: обидва аргументи сказати TensorFlow створити буфер в більшості buffer_sizeелементів, і фоновий потік , щоб заповнити цей буфер в фоновому режимі. (Зверніть увагу, що ми видалили output_buffer_sizeаргумент з того часу, Dataset.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тоді ви взагалі не перетасовуєте. Для дуже великих наборів даних типовим «досить хорошим» підходом є випадкове подрібнення даних на кілька файлів один раз перед тренуванням, потім рівномірно перемішувати імена файлів, а потім використовувати менший буфер перетасовки. Однак правильний вибір буде залежати від точного характеру вашої навчальної роботи.



Для цього пояснення я все ще маю певні плутанини tf.data.Dataset.shuffle(). Я хотів би знати точний процес перетасовки. Скажімо, перші batch_sizeвибірки випадковим чином вибираються з перших buffer_sizeелементів тощо.
Bs He

1
Перетасування імен файлів @mrry IIUC важливо, оскільки інакше кожна епоха бачитиме один і той же елемент у партіях 0 ... 999; а партіями 1000.1999; тощо, де я припускаю, що 1 файл = 1000 партій. Навіть при перетасовці імен файлів все одно існує деяка невипадковість: це тому, що приклади з файлу #k є близькими один до одного в кожну епоху. Це може бути не надто погано, оскільки сам файл #k є випадковим; все ще в деяких випадках навіть це може зіпсувати навчання. Єдиним способом отримати ідеальне перемішування буде встановити buffer_sizeрівний розмір файлу (і, звичайно, перемішати файли).
максимум

Tensorflow rc 15.0. При dataset.shuffle(buffer_size=1)перетасовці все-таки відбувається. Будь-які думки?
Сергій Бушманов

@SergeyBushmanov це може залежати від перетворення перед вашим перетасовкою, наприклад, list_files (), який за замовчуванням перетасовує імена файлів на початку кожної епохи.
Xiaolong

129

Важливість buffer_sizeвshuffle()

Я хотів , щоб стежити за попередній відповідь від @mrry , щоб підкреслити важливість про buffer_sizeв tf.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_sizeвін більший за 20000, або перетасувати заздалегідь filenamesі labels(з тими ж індексами, очевидно).

Оскільки зберігання всіх імен файлів та міток у пам'яті не є проблемою, ми можемо насправді використовувати, 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...

Винос завжди перевіряти, що буде робити перемішування. Хорошим способом виявити ці помилки може бути побудова графіку розподілу партій за часом (переконайтеся, що партії містять приблизно такий самий розподіл, як навчальний набір, у нашому прикладі наполовину кішка та наполовину не кішка).


1
Наступний зразок завжди вибирається з буфера (розміром тут 1000). Отже, перший зразок взято з перших 1000 імен файлів. Буфер зменшується до розміру 999, тому він бере наступний вхід ( filename_01001) і додає його. Другий зразок береться випадковим чином із цих 1000 імен файлів (1001 ім'я першого файлу мінус перший зразок).
Olivier Moindrot

1
Проблема цього низького розміру буфера полягає в тому, що у вас будуть лише коти у перших партіях. Тож модель тривіально навчиться передбачати лише «кота». Найкращий спосіб тренувати мережу - це мати партії з однаковою кількістю «кота» та «не кота».
Олів'є Мойдрот

1
Ви можете використати tf.summary.histogramдля побудови графіку розподілу міток у часі.
Olivier Moindrot

3
Не друкарська помилка :) Набір даних містить 10 тис. Зображень кожного класу, тому загальний розмір буфера повинен бути вище 20 тис. Але у прикладі вище я взяв розмір буфера 1k, який є занадто низьким.
Олів'є Мойдрот

1
Так, встановлення розміру буфера до розміру набору даних, як правило, чудове. Все, що перевищує розмір набору даних, у будь-якому випадку буде марним (і якщо ви не повторите свій набір даних перед перемішуванням, буфер не може бути більшим за набір даних).
Олів'є Мойдрот,

7

Код

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
Це вказує на те, що для кожного елемента, отриманого ітератором, буфер заповнюється відповідним наступним елементом набору даних, який раніше не був у буфері.
Олексій

2

Насправді відповідь @ olivier-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))

2

Я виявив, що @ olivier-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 + (i * batch_size)), де i - кількість разів, коли ви запускали next_element . Я думаю, що це працює наступним чином. По-перше, вибірки buffer_size відбираються по порядку з фальшивих даних . Потім один за одним вибірки 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)

Зміна обсягу набору даних.prefetch (10) не призвела до змін у використаній пам’яті (ОЗУ). Це важливо, коли ваші дані не поміщаються в оперативну пам’ять. Я думаю, що найкращий спосіб - перетасувати ваші дані / імена файлів, перш ніж подавати їх до tf.dataset, а потім контролювати розмір буфера за допомогою buffer_size .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.