Punto de referencia de cómo: leer datos

Estoy usando tensorflow 0.10 y estaba comparando los ejemplos encontrados en el HowTo oficial sobre lectura de datos . Este HowTo ilustra diferentes métodos para mover datos a tensorflow, usando el mismo ejemplo MNIST.

Me sorprendieron los resultados y me preguntaba si alguien tiene suficiente comprensión de bajo nivel para explicar lo que está sucediendo.

En el HowTo hay básicamente 3 métodos para leer en los datos:

  • Feeding : construyendo el mini-lote en python y pasándolo con sess.run(..., feed_dict={x: mini_batch})
  • Reading from files : use las operaciones tf para abrir los archivos y crear mini lotes. (Omita los datos de manejo en Python).
  • Preloaded data : cargue todos los datos en una sola variable tf o constante y use las funciones tf para dividirlos en mini lotes. La variable o constante está anclada a la CPU, no a gpu.

Los scripts que usé para ejecutar mis puntos de referencia se encuentran dentro de tensorflow:

  • Feeding : ejemplos / tutoriales / mnist / fully_connected_feed.py
  • Reading from files : ejemplos / how_tos / reading_data / convert_to_records.py y ejemplos / how_tos / reading_data / fully_connected_reader.py
  • Preloaded data (constant) : ejemplos / how_tos / reading_data / fully_connected_preloaded.py
  • Preloaded data (variable) : ejemplos / how_tos / reading_data / fully_connected_preloaded_var.py

Ejecuté esos scripts sin modificar, excepto los dos últimos porque fallan, al menos para la versión 0.10, a menos que añada un sess.run(tf.initialize_local_variables()) adicional sess.run(tf.initialize_local_variables()) .

Pregunta principal

El tiempo para ejecutar 100 mini lotes de 100 ejemplos que se ejecutan en un GTX1060:

  • Feeding : ~0.001 s
  • Reading from files : ~0.010 s
  • Preloaded data (constant) : ~0.010 s
  • Preloaded data (variable) : ~0.010 s

Esos resultados me sorprenden bastante. Hubiera esperado que la Feeding fuera la más lenta, ya que hace casi todo en python, mientras que los otros métodos utilizan tensorflow / C ++ de nivel inferior para realizar operaciones similares. Es todo lo contrario de lo que esperaba. ¿Alguien entiende lo que está pasando?

Pregunta secundaria

Tengo acceso a otra máquina que tiene un Titan X y controladores NVidia más antiguos. Los resultados relativos estuvieron aproximadamente en línea con lo anterior, excepto por los Preloaded data (constant) que fueron catastróficamente lentos, demorando muchos segundos en un solo mini lote.

¿Es este un problema conocido que el rendimiento puede variar mucho con el hardware / controladores?

Actualice el 9 de octubre, la lentitud se produce porque el cálculo se ejecuta demasiado rápido para que Python se adelante al subproceso de cálculo y programe los subprocesos de precarga. La computación en el hilo principal toma 2 ms y, aparentemente, eso es muy poco para que el hilo de precarga pueda agarrar el GIL. El subproceso de búsqueda previa tiene un retraso mayor y, por lo tanto, siempre puede ser anulado por el subproceso de cálculo. Por lo tanto, el subproceso de cálculo se ejecuta en todos los ejemplos y luego pasa la mayor parte del tiempo bloqueado en GIL a medida que se progtwig un subproceso de captación previa y encola un solo ejemplo. La solución es boost el número de subprocesos de Python, boost el tamaño de la cola para que se ajuste a todo el conjunto de datos, iniciar los corredores de la cola y luego pausar el hilo principal durante un par de segundos para que los corredores de la cola rellenen previamente la cola.

Cosas viejas

Eso es sorprendentemente lento.

Esto parece una especie de casos especiales que hacen que los últimos 3 ejemplos sean innecesariamente lentos (la mayor parte del esfuerzo fue para optimizar modelos grandes como ImageNet, por lo que MNIST no recibió tanta atención).

Puede diagnosticar los problemas obteniendo líneas de tiempo, como se describe aquí

Aquí hay 3 de esos ejemplos con la colección de línea de tiempo habilitada.

Aquí está la línea de tiempo para la implementación de feed_dict

introduzca la descripción de la imagen aquí

Lo importante a tener en cuenta es que matmul toma una buena parte del tiempo, por lo que la sobrecarga de lectura no es significativa

Ahora aquí está la línea de tiempo para la implementación del reader introduzca la descripción de la imagen aquí

Puede ver que la operación está bloqueada en QueueDequeueMany, que lleva la friolera de 45 ms.

Si hace zoom, verá un montón de pequeñas operaciones de MEMCPY y Cast, lo que es una señal de que algunas operaciones son solo CPU ( parse_single_example ), y la salida de la cola tiene que progtwigr múltiples CPU independientes -> transferencias de GPU

Para el ejemplo de var continuación con GPU deshabilitada, no veo pequeñas operaciones pequeñas, pero QueueDequeueMany todavía toma más de 10 ms. El tiempo parece escalar linealmente con el tamaño del lote, por lo que hay una cierta lentitud fundamental allí. Archivado # 4740 introduzca la descripción de la imagen aquí

Yaroslav soluciona bien el problema. Con modelos pequeños, necesitará acelerar la importación de datos. Una forma de hacer esto es con la función Tensorflow, tf.TFRecordReader.read_up_to , que lee múltiples registros en cada llamada session.run() , eliminando así el exceso de sobrecarga causado por varias llamadas.

 enqueue_many_size = SOME_ENQUEUE_MANY_SIZE reader = tf.TFRecordReader(options = tf.python_io.TFRecordOptions(tf.python_io.TFRecordCompressionType.ZLIB)) _, queue_batch = reader.read_up_to(filename_queue, enqueue_many_size) batch_serialized_example = tf.train.shuffle_batch( [queue_batch], batch_size=batch_size, num_threads=thread_number, capacity=capacity, min_after_dequeue=min_after_dequeue, enqueue_many=True) 

Esto también fue abordado en esta pregunta de SO .

La pregunta principal es por qué el ejemplo con los datos de los datos precargados (constante) / how_tos / reading_data / fully_connected_preloaded.py es significativamente más lento que otros códigos de ejemplo de carga de datos cuando se utiliza GPU .

Tuve el mismo problema, que fully_connected_preloaded.py es inesperadamente lento en mi Titan X. El problema era que todo el conjunto de datos estaba precargado en la CPU, no en la GPU.

Primero, déjame compartir mis bashs iniciales. He aplicado los siguientes consejos de rendimiento de Yaroslav.

  • establecer capacity=55000 para tf.train.slice_input_producer . (55000 es el tamaño del conjunto de entrenamiento MNIST en mi caso)
  • establece num_threads=5 para tf.train.batch .
  • establecer la capacity=500 para tf.train.batch .
  • poner time.sleep(10) después de tf.train.start_queue_runners .

Sin embargo, la velocidad media por cada lote se mantiene igual. Intenté la visualización de la timeline para la creación de perfiles, y todavía conseguí que QueueDequeueManyV2 dominara.

El problema fue la línea 65 de fully_connected_preloaded.py . El siguiente código carga el conjunto de datos completo a la CPU, aún proporcionando un cuello de botella para la transmisión de datos de CPU-GPU.

 with tf.device('/cpu:0'): input_images = tf.constant(data_sets.train.images) input_labels = tf.constant(data_sets.train.labels) 

Por lo tanto, cambié la asignación de dispositivos.

 with tf.device('/gpu:0') 

Luego obtuve una velocidad de x100 por cada lote.

Nota:

  1. Esto fue posible porque Titan X tiene suficiente espacio de memoria para precargar un conjunto de datos completo.
  2. En el código original ( fully_connected_preloaded.py ), el comentario en la línea 64 dice “el rest de la canalización es solo para CPU”. No estoy seguro de lo que pretendía este comentario.