Colas de Tensorflow – Cambio entre el tren y los datos de validación

Estoy tratando de hacer uso de las colas para cargar datos de archivos en Tensorflow.

Me gustaría ejecutar la gráfica con datos de validación al final de cada época para tener una mejor idea de cómo va el entrenamiento.

Ahí es donde me encuentro con problemas. Parece que no puedo entender cómo hacer el cambio entre los datos de entrenamiento y los datos de validación cuando se usan colas.

He reducido mi código a un ejemplo mínimo de juguete para que sea más fácil obtener ayuda. En lugar de incluir todo el código que carga los archivos de imagen, realiza inferencia y entrenamiento, lo he cortado en el punto donde los nombres de archivo se cargan en la cola.

import tensorflow as tf # DATA train_items = ["train_file_{}".format(i) for i in range(6)] valid_items = ["valid_file_{}".format(i) for i in range(3)] # SETTINGS batch_size = 3 batches_per_epoch = 2 epochs = 2 # CREATE GRAPH graph = tf.Graph() with graph.as_default(): file_list = tf.placeholder(dtype=tf.string, shape=None) # Create a queue consisting of the strings in `file_list` q = tf.train.string_input_producer(train_items, shuffle=False, num_epochs=None) # Create batch of items. x = q.dequeue_many(batch_size) # Inference, train op, and accuracy calculation after this point # ... # RUN SESSION with tf.Session(graph=graph) as sess: # Initialize variables sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) # Start populating the queue. coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) try: for epoch in range(epochs): print("-"*60) for step in range(batches_per_epoch): if coord.should_stop(): break train_batch = sess.run(x, feed_dict={file_list: train_items}) print("TRAIN_BATCH: {}".format(train_batch)) valid_batch = sess.run(x, feed_dict={file_list: valid_items}) print("\nVALID_BATCH : {} \n".format(valid_batch)) except Exception, e: coord.request_stop(e) finally: coord.request_stop() coord.join(threads) 

Variaciones y experimentos.

Probando diferentes valores para num_epochs

num_epochs = Ninguno

Si configuro el argumento num_epochs en tf.train.string_input_producer() en None se obtiene la siguiente salida, que muestra que está ejecutando dos épocas como se esperaba, pero está usando datos del conjunto de entrenamiento cuando se ejecuta la evaluación.

 ------------------------------------------------------------ TRAIN_BATCH: ['train_file_0' 'train_file_1' 'train_file_2'] TRAIN_BATCH: ['train_file_3' 'train_file_4' 'train_file_5'] VALID_BATCH : ['train_file_0' 'train_file_1' 'train_file_2'] ------------------------------------------------------------ TRAIN_BATCH: ['train_file_3' 'train_file_4' 'train_file_5'] TRAIN_BATCH: ['train_file_0' 'train_file_1' 'train_file_2'] VALID_BATCH : ['train_file_3' 'train_file_4' 'train_file_5'] 

num_epochs = 2

Si configuro el argumento num_epochs en tf.train.string_input_producer() en 2 se obtiene la siguiente salida, que muestra que ni siquiera está ejecutando los dos lotes completos en absoluto (y la evaluación sigue usando datos de entrenamiento)

 ------------------------------------------------------------ TRAIN_BATCH: ['train_file_0' 'train_file_1' 'train_file_2'] TRAIN_BATCH: ['train_file_3' 'train_file_4' 'train_file_5'] VALID_BATCH : ['train_file_0' 'train_file_1' 'train_file_2'] ------------------------------------------------------------ TRAIN_BATCH: ['train_file_3' 'train_file_4' 'train_file_5'] 

num_epochs = 1

Si configuro el argumento num_epochs en tf.train.string_input_producer() en 1 con la esperanza de que tf.train.string_input_producer() cualquier dato de entrenamiento adicional de la cola para poder utilizar los datos de validación, obtengo el siguiente resultado, que muestra que está terminando tan pronto como llega a través de una época de datos de entrenamiento, y no llega a cargar datos de evaluación.

 ------------------------------------------------------------ TRAIN_BATCH: ['train_file_0' 'train_file_1' 'train_file_2'] TRAIN_BATCH: ['train_file_3' 'train_file_4' 'train_file_5'] 

Ajuste de argumento de capacity a varios valores

También he intentado establecer el argumento de capacity en tf.train.string_input_producer() en valores pequeños, como 3 y 1. Pero esto no tuvo ningún efecto en los resultados.

¿Qué otro enfoque debo tomar?

¿Qué otro enfoque podría tomar para cambiar entre los datos de capacitación y validación? ¿Tendría que crear colas separadas? No sé cómo hacer que eso funcione. ¿Tendría que crear coordinadores adicionales y corredores de cola también?

Estoy comstackndo una lista de posibles enfoques que podrían resolver este problema aquí. La mayoría de estas son solo sugerencias vagas, sin ejemplos de códigos reales que muestren cómo hacer uso de ellas.

Marcador de posición con valor predeterminado

Sugerido aqui

Utilizando tf.cond ()

Sugerido aqui

También sugerido por sygi en este mismo hilo de stackoverflow. enlazar

utilizando tf.group () y tf.cond ()

Sugerido aqui

método make_template ()

Sugerido aquí y aquí.

Método de ponderaciones compartidas

sugerido por sygi en este mismo hilo de stackoverflow ( enlace ). Esto podría ser lo mismo que el método make_template ().

Método QueueBase ().

Sugerido aquí con código de ejemplo aquí Código adaptado a mi problema aquí en este hilo. enlazar

método del cubo de entrenamiento

Sugerido aqui

Primero, puede leer manualmente los ejemplos en su código (para numerar matrices) y pasarlos de la forma que desee:

 data = tf.placeholder(tf.float32, [None, DATA_SHAPE]) for _ in xrange(num_epochs): some_training = read_some_data() sess.run(train_op, feed_dict={data: some_training}) some_testing = read_some_test_data() sess.run(eval_op, feed_dict={data: some_testing}) 

Si necesita usar colas, puede intentar cambiar condicionalmente la cola de “entrenamiento” a “probar” uno:

 train_filenames = tf.string_input_producer(["training_file"]) train_q = some_reader(train_filenames) test_filenames = tf.string_input_producer(["testing_file"]) test_q = some_reader(test_filenames) am_testing = tf.placeholder(dtype=bool,shape=()) data = tf.cond(am_testing, lambda:test_q, lambda:train_q) train_op, accuracy = model(data) for _ in xrange(num_epochs): sess.run(train_op, feed_dict={am_testing: False}) sess.run(accuracy, feed_dict={am_testing: True}) 

Sin embargo, el segundo enfoque se considera inseguro . En esta publicación, se recomienda crear dos gráficos separados para entrenamiento y pruebas (con ponderaciones compartidas), que es otra manera de lograr lo que desea.

Ok, entonces tengo una solución que está funcionando para mí. Se basa en el código tomado de esta publicación en la sección de problemas de github de tensorflow. Hace uso de la función QueueBase.from_list() . Se siente muy mal, y no estoy del todo contento con eso, pero al menos lo estoy haciendo funcionar.

 import tensorflow as tf # DATA train_items = ["train_file_{}".format(i) for i in range(6)] valid_items = ["valid_file_{}".format(i) for i in range(3)] # SETTINGS batch_size = 3 batches_per_epoch = 2 epochs = 2 # ------------------------------------------------ # GRAPH # ------------------------------------------------ graph = tf.Graph() with graph.as_default(): # TRAIN QUEUE train_q = tf.train.string_input_producer(train_items, shuffle=False) # VALID/TEST QUEUE test_q = tf.train.string_input_producer(valid_items, shuffle=False) # SELECT QUEUE is_training = tf.placeholder(tf.bool, shape=None, name="is_training") q_selector = tf.cond(is_training, lambda: tf.constant(0), lambda: tf.constant(1)) # select_q = tf.placeholder(tf.int32, []) q = tf.QueueBase.from_list(q_selector, [train_q, test_q]) # # Create batch of items. data = q.dequeue_many(batch_size) # ------------------------------------------------ # SESSION # ------------------------------------------------ with tf.Session(graph=graph) as sess: # Initialize variables sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) # Start populating the queue. coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) try: for epoch in range(epochs): print("-" * 60) # TRAIN for step in range(batches_per_epoch): if coord.should_stop(): break print("TRAIN.dequeue = " + str(sess.run(data, {is_training: True}))) # VALIDATION print "\nVALID.dequeue = " + str(sess.run(data, {is_training: False})) except Exception, e: coord.request_stop(e) finally: coord.request_stop() coord.join(threads) 

Dando la siguiente salida, que es lo que esperaba.

 ------------------------------------------------------------ TRAIN.dequeue = ['train_file_0' 'train_file_1' 'train_file_2'] TRAIN.dequeue = ['train_file_3' 'train_file_4' 'train_file_5'] VALID.dequeue = ['valid_file_0' 'valid_file_1' 'valid_file_2'] ------------------------------------------------------------ TRAIN.dequeue = ['train_file_0' 'train_file_1' 'train_file_2'] TRAIN.dequeue = ['train_file_3' 'train_file_4' 'train_file_5'] VALID.dequeue = ['valid_file_0' 'valid_file_1' 'valid_file_2'] 

Estoy dejando este hilo abierto con la esperanza de que surja una mejor solución.

Se desaconseja la creación de dos colas diferentes.

Si tiene dos máquinas diferentes, le recomendaría usar máquinas separadas para capacitación y validación (si no, puede usar dos procesos diferentes). Para 2 cajas de máquinas:

  1. La primera máquina solo tiene datos de entrenamiento. Utiliza colas para pasar los datos en lotes al modelo gráfico y tiene GPU para capacitación. Después de cada paso, guarda el nuevo modelo ( model_iteration ) en un lugar donde la segunda máquina puede acceder a él.
  2. La segunda máquina (solo tiene datos de validación) sondea periódicamente el lugar con el modelo y verifica si el nuevo modelo está disponible. En este caso, se ejecuta una inferencia del nuevo modelo y comprueba el rendimiento. Debido a que la mayoría de las veces los datos de validación son significativamente más pequeños que los datos de entrenamiento, incluso puede permitirse tenerlos todos en la memoria.

Pocos pros de este enfoque. Los datos de entrenamiento / validación son separados y no se puede desordenar con ellos. Puede tener una máquina débil para la validación porque incluso si la validación se está retrasando detrás de la capacitación (escenario improbable) no es un problema porque son independientes