Según la documentación de TensorFlow , los métodos prefetch
y map
de la tf.contrib.data.Dataset
clase, ambos tienen un parámetro llamado buffer_size
.
Por prefetch
método, el parámetro se conoce como buffer_size
y según la documentación:
buffer_size: Un tf.int64 escalar tf.Tensor, que representa el número máximo de elementos que se almacenarán en búfer al realizar la captación previa.
Para el map
método, el parámetro se conoce como output_buffer_size
y según la documentación:
output_buffer_size: (Opcional). Un tf.Tensor escalar tf.int64, que representa el número máximo de elementos procesados que se almacenarán en búfer.
De igual manera para el shuffle
método aparece la misma cantidad y según documentación:
buffer_size: Un tf.int64 escalar tf.Tensor, que representa el número de elementos de este conjunto de datos del que se tomará una muestra del nuevo conjunto de datos.
¿Cuál es la relación entre estos parámetros?
Supongamos que creo un Dataset
objeto de la siguiente manera:
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)
¿Qué papel desempeñan los buffer
parámetros del fragmento anterior?
fuente
Respuestas:
TL; DR A pesar de sus nombres similares, estos argumentos tienen significados bastante diferentes. El
buffer_size
enDataset.shuffle()
puede afectar a la aleatoriedad de su conjunto de datos, y por lo tanto el orden en que se producen los elementos. Elbuffer_size
inDataset.prefetch()
solo afecta el tiempo que se tarda en producir el siguiente elemento.El
buffer_size
argumento entf.data.Dataset.prefetch()
y eloutput_buffer_size
argumento entf.contrib.data.Dataset.map()
proporcionan una manera de ajustar el rendimiento de su canalización de entrada: ambos argumentos le dicen a TensorFlow que cree un búfer de la mayoría de losbuffer_size
elementos y un hilo de fondo para llenar ese búfer en segundo plano. (Tenga en cuenta que eliminamos eloutput_buffer_size
argumento deDataset.map()
cuando se movió detf.contrib.data
atf.data
. El nuevo código debe usarseDataset.prefetch()
despuésmap()
para obtener el mismo comportamiento).Agregar un búfer de captación previa puede mejorar el rendimiento al superponer el procesamiento previo de datos con el cálculo posterior. Normalmente, es más útil agregar un búfer de captación previa pequeño (quizás con un solo elemento) al final de la canalización, pero las canalizaciones más complejas pueden beneficiarse de la captación previa adicional, especialmente cuando el tiempo para producir un solo elemento puede variar.
Por el contrario, el
buffer_size
argumento atf.data.Dataset.shuffle()
afecta la aleatoriedad de la transformación. Diseñamos laDataset.shuffle()
transformación (como latf.train.shuffle_batch()
función que reemplaza) para manejar conjuntos de datos que son demasiado grandes para caber en la memoria. En lugar de mezclar todo el conjunto de datos, mantiene un búfer debuffer_size
elementos y selecciona aleatoriamente el siguiente elemento de ese búfer (reemplazándolo con el siguiente elemento de entrada, si hay uno disponible). Cambiar el valor debuffer_size
afecta la uniformidad de la mezcla: sibuffer_size
es mayor que el número de elementos del conjunto de datos, se obtiene una mezcla uniforme; si esto es1
entonces no tendrás que barajar en absoluto. Para conjuntos de datos muy grandes, un enfoque típico "suficientemente bueno" es dividir aleatoriamente los datos en varios archivos una vez antes del entrenamiento, luego mezclar los nombres de los archivos de manera uniforme y luego usar un búfer aleatorio más pequeño. Sin embargo, la elección adecuada dependerá de la naturaleza exacta de su trabajo de formación.fuente
tf.data.Dataset.shuffle()
. Me gustaría saber el proceso exacto de barajar. Digamos que las primerasbatch_size
muestras se eligen aleatoriamente de los primerosbuffer_size
elementos, y así sucesivamente.buffer_size
que el tamaño del archivo sea igual (y, por supuesto, mezclar los archivos).dataset.shuffle(buffer_size=1)
barajar todavía se produce. ¿Alguna idea?Importancia de
buffer_size
enshuffle()
Quería hacer un seguimiento de la respuesta anterior de @mrry para enfatizar la importancia de
buffer_size
intf.data.Dataset.shuffle()
.Tener un nivel bajo
buffer_size
no solo le dará un barajado inferior en algunos casos: puede arruinar todo su entrenamiento.Un ejemplo práctico: clasificador de gatos
Supongamos, por ejemplo, que está entrenando un clasificador de gatos en imágenes y sus datos están organizados de la siguiente manera (con
10000
imágenes en cada categoría):Una forma estándar de ingresar datos
tf.data
puede ser tener una lista de nombres de archivo y una lista de etiquetas correspondientes, y usarlatf.data.Dataset.from_tensor_slices()
para crear el conjunto de datos: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...
El gran problema con el código anterior es que el conjunto de datos no se barajará de la manera correcta. Durante aproximadamente la primera mitad de una época, solo veremos imágenes de gatos, y durante la segunda mitad solo imágenes que no sean de gatos. Esto dolerá mucho el entrenamiento.
Al comienzo del entrenamiento, el conjunto de datos tomará los primeros
1000
nombres de archivo y los colocará en su búfer, luego elegirá uno al azar entre ellos. Dado que todas las primeras1000
imágenes son imágenes de gatos, solo seleccionaremos imágenes de gatos al principio.La solución aquí es asegurarse de que
buffer_size
sea mayor que20000
, o mezclar de antemanofilenames
ylabels
(con los mismos índices obviamente).Dado que almacenar todos los nombres de archivo y etiquetas en la memoria no es un problema, podemos usarlo
buffer_size = len(filenames)
para asegurarnos de que todo se mezcle. Asegúrese de llamartf.data.Dataset.shuffle()
antes de aplicar las transformaciones pesadas (como leer las imágenes, procesarlas, procesar por lotes ...).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...
La conclusión es siempre verificar dos veces lo que hará la mezcla. Una buena forma de detectar estos errores podría ser trazar la distribución de lotes a lo largo del tiempo (asegúrese de que los lotes contengan aproximadamente la misma distribución que el conjunto de entrenamiento, mitad gato y mitad no gato en nuestro ejemplo).
fuente
filename_01001
) y la agrega. La segunda muestra se toma al azar de estos 1000 nombres de archivo (1001 primeros nombres de archivo menos la primera muestra).tf.summary.histogram
para trazar la distribución de etiquetas a lo largo del tiempo.Código
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()
Salida
[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]
fuente
En realidad, la respuesta de @ olivier-moindrot no es correcta.
Puede verificarlo creando nombres de archivo y etiquetas a medida que mencione e imprima los valores aleatorios.
Verá que cada procedimiento de reproducción aleatoria generará una muestra aleatoriamente con el tamaño igual al tamaño del búfer del conjunto de datos.
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))
fuente
Descubrí que @ olivier-moindrot es realmente correcto, probé el código proporcionado por @Houtarou Oreki, usando las modificaciones señaladas por @max. El código que utilicé fue el siguiente:
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())
La salida del código era de hecho un número que iba de 1 a (buffer_size + (i * batch_size)), donde i es el número de veces que ejecutó next_element . Creo que la forma en que está funcionando es la siguiente. Primero, las muestras de buffer_size se seleccionan en orden de fake_data . Luego, una por una, las muestras de tamaño de lote se extraen del búfer. Cada vez que se toma una muestra por lotes del búfer, se reemplaza por una nueva, tomada en orden de fake_data . Probé esto último usando el siguiente código:
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)
El valor máximo producido por el código fue 109. Por lo tanto, debe garantizar una muestra equilibrada dentro de su tamaño de lote para garantizar un muestreo uniforme durante el entrenamiento.
También probé lo que dijo @mrry sobre el rendimiento, descubrí que batch_size precargará esa cantidad de muestras en la memoria. Probé esto usando el siguiente código:
dataset = dataset.shuffle(buffer_size=20) dataset = dataset.prefetch(10) dataset = dataset.batch(batch_size=5)
Cambiar la cantidad de dataset.prefetch (10) resultó en ningún cambio en la memoria (RAM) utilizada. Esto es importante cuando sus datos no caben en la RAM. Creo que la mejor manera es mezclar sus datos / nombres de archivo antes de alimentarlos a tf.dataset, y luego controlar el tamaño del búfer usando buffer_size .
fuente