Согласно TensorFlow документации , то prefetch
и map
методы tf.contrib.data.Dataset
класса, оба имеют параметр с именем buffer_size
.
Для prefetch
метода параметр известен как buffer_size
и согласно документации:
buffer_size: скаляр tf.int64 tf.Tensor, представляющий максимальное количество элементов, которые будут буферизованы при предварительной выборке.
Для map
метода параметр известен как output_buffer_size
и согласно документации:
output_buffer_size: (Необязательно.) Скаляр tf.int64 tf.Tensor, представляющий максимальное количество обрабатываемых элементов, которые будут помещены в буфер.
Аналогично для shuffle
метода появляется такое же количество и согласно документации:
buffer_size: скаляр tf.int64 tf.Tensor, представляющий количество элементов из этого набора данных, из которых будет выполняться выборка нового набора данных.
Какая связь между этими параметрами?
Предположим, я создаю 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
параметры в приведенном выше фрагменте?
источник
Ответы:
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
аргумент totf.data.Dataset.shuffle()
влияет на случайность преобразования. Мы разработалиDataset.shuffle()
преобразование (как иtf.train.shuffle_batch()
функцию, которую оно заменяет) для обработки наборов данных, которые слишком велики для размещения в памяти. Вместо перетасовки всего набора данных он поддерживает буферbuffer_size
элементов и случайным образом выбирает следующий элемент из этого буфера (заменяя его следующим входным элементом, если он доступен). Изменение значенияbuffer_size
влияет на равномерность перемешивания: еслиbuffer_size
оно больше, чем количество элементов в наборе данных, вы получите равномерное перемешивание; если это1
тогда у вас вообще не будет перетасовки. Для очень больших наборов данных типичным «достаточно хорошим» подходом является случайное разделение данных на несколько файлов один раз перед обучением, затем равномерное перемешивание имен файлов и использование меньшего буфера перемешивания. Однако правильный выбор будет зависеть от конкретного характера вашей учебной работы.источник
tf.data.Dataset.shuffle()
. Я хотел бы знать точный процесс перетасовки. Скажем, первыеbatch_size
образцы выбираются случайным образом из первыхbuffer_size
элементов и так далее.buffer_size
равный размеру файла (и, конечно же, перемешать файлы).dataset.shuffle(buffer_size=1)
перетасовкой все равно происходит. есть идеи?Важность
buffer_size
вshuffle()
Я хотел , чтобы следить за предыдущий ответ от @mrry , чтобы подчеркнуть важность о
buffer_size
вtf.data.Dataset.shuffle()
.В некоторых случаях низкий уровень
buffer_size
не только приведет к плохой маневренности : он может испортить всю вашу тренировку.Практический пример: классификатор кошек
Предположим, например, что вы обучаете классификатор кошек на изображениях, и ваши данные организованы следующим образом (с
10000
изображениями в каждой категории):Стандартный способ ввода данных
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...
Вывод - всегда дважды проверять, что будет делать перетасовка. Хорошим способом отловить эти ошибки может быть построение графика распределения пакетов во времени (убедитесь, что пакеты содержат примерно такое же распределение, что и обучающий набор, половина кота и половина не кота в нашем примере).
источник
filename_01001
) и добавляет его. Вторая выборка выбирается случайным образом из этих 1000 имен файлов (1001 первое имя файла минус первая выборка).tf.summary.histogram
для построения графика распределения меток во времени.Код
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]
источник
На самом деле ответ @ 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))
источник
Я обнаружил, что @ 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 выбираются по порядку из 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). Это важно, когда ваши данные не помещаются в ОЗУ. Я думаю, что лучший способ - перетасовать ваши данные / имена_файлов перед их передачей в tf.dataset, а затем контролировать размер буфера с помощью buffer_size .
источник