tensorflow: alimentación eficiente de datos eval / train utilizando corredores de cola

Estoy tratando de ejecutar un gráfico de tensorflow para entrenar un modelo y evaluar periódicamente usando un conjunto de datos de evaluación por separado. Tanto los datos de entrenamiento como los de evaluación se implementan utilizando corredores de cola.

Mi solución actual es crear ambas entradas en el mismo gráfico y usar un tf.cond dependiente de un marcador de posición is_training . Mi problema está resaltado por el siguiente código:

 import tensorflow as tf from tensorflow.models.image.cifar10 import cifar10 from time import time def get_train_inputs(is_training): return cifar10.inputs(False) def get_eval_inputs(is_training): return cifar10.inputs(True) def get_mixed_inputs(is_training): train_inputs = get_train_inputs(None) eval_inputs = get_eval_inputs(None) return tf.cond(is_training, lambda: train_inputs, lambda: eval_inputs) def time_inputs(inputs_fn, n_runs=10): graph = tf.Graph() with graph.as_default(): is_training = tf.placeholder(dtype=tf.bool, shape=(), name='is_training') images, labels = inputs_fn(is_training) with tf.Session(graph=graph) as sess: coordinator = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coordinator) t = time() for i in range(n_runs): im, l = sess.run([images, labels], feed_dict={is_training: True}) dt = time() - t coordinator.request_stop() coordinator.join(threads) return dt / n_runs print('Train inputs: %.3f' % time_inputs(get_train_inputs)) print('Eval inputs: %.3f' % time_inputs(get_eval_inputs)) print('Mixed inputs: %.3f' % time_inputs(get_mixed_inputs)) 

También tuve que comentar la línea 133 de tensorflow/models/image/cifar10/cifar10_inputs.py de tensorflow/models/image/cifar10/cifar10_inputs.py .

Esto produjo los siguientes resultados:

 Train inputs: 0.055 Eval inputs: 0.050 Mixed inputs: 0.105 

Parece que en el caso mixto se están leyendo / analizando ambas entradas, aunque solo se use 1. ¿Hay alguna forma de evitar este cómputo redundante? ¿O hay una forma mejor de cambiar entre los datos de capacitación / evaluación que aún aprovechan la configuración del corredor de cola?

¿Ha leído la última sección de este enlace sobre entradas múltiples? Creo que puede agregar un argumento is_training a su función de entrada para distinguir los datos de entrenamiento de los datos de evaluación. Luego, puede reutilizar las variables de uso compartido para obtener los logits para los datos eval y construir una op para eval. Luego, en su gráfica, ejecute valudation_accuracy=sess.run(eval_op) para obtener la precisión de eval.


Actualizar:

Hola, por lo que sé, si quieres entrenar para n lotes, evaluar, entrenar, evaluar, puedes mantener dos operaciones en el mismo gráfico, sin necesidad de construir una nueva. Supongamos que ya ha creado toda la función necesaria, entonces el código debería ser como este:

 #the following two steps will add train and eval input queue to the graph train_inputs,train_labels = inputs(is_train=True) eval_inputs,eval_labels = inputs(is_train=False) with tf.variable_scope("inference") as scope: train_logits = inference(train_inputs) scope.reuse_variables() eval_logits = inference(eval_inputs) loss = loss(train_logits,train_labels) eval_accuracy = accuracy(eval_logits,eval_labels) #...add train op here,start queue runner and train it ... 

Después de algunos experimentos, mi mejor solución actual es tener un gráfico principal con entradas de entrenamiento y un gráfico separado con solo operaciones de datos de evaluación. Abro una sesión separada para obtener los datos de evaluación y los agrego al gráfico de capacitación cuando quiero evaluar. Altamente poco elegante (y las ejecuciones de evaluación tardan más de lo ideal, ya que tienen que venir de una sesión solo para alimentar a otra), pero suponiendo que las ejecuciones de evaluación son raras en comparación con las de capacitación, esto parece preferible a la versión original …

 import tensorflow as tf from tensorflow.models.image.cifar10 import cifar10 from time import time class DataSupplier: def __init__(self, tensor_fn): graph = tf.Graph() with graph.as_default(): with graph.device('/cpu:0'): self.tensor = tensor_fn() self.sess = tf.Session(graph=graph) self.coord = tf.train.Coordinator() self.threads = tf.train.start_queue_runners(sess=self.sess, coord=self.coord) def get_tensor_val(self): return self.sess.run(self.tensor) def clean_up(self): self.coord.request_stop() self.coord.join(self.threads) eval_batcher = DataSupplier(lambda: cifar10.inputs(True)) graph = tf.Graph() with graph.as_default(): images, labels = cifar10.inputs(False) out_images = tf.identity(images) out_labels = tf.identity(labels) n_runs = 100 with tf.Session(graph=graph) as sess: coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess, coord) for i in range(n_runs): sess.run([out_images, out_labels]) t = time() for i in range(n_runs): sess.run([out_images, out_labels]) dt = (time() - t)/n_runs print('Train time: %.3f' % dt) t = time() for i in range(n_runs): eval_images, eval_labels = eval_batcher.get_tensor_val() sess.run([out_images, out_labels], feed_dict={images: eval_images, labels: eval_labels}) dt = (time() - t)/n_runs print('Eval time: %.3f' % dt) coord.request_stop() coord.join(threads) eval_batcher.clean_up() 

Resultados:

 Train time: 0.050 Eval time: 0.064 

Actualización: cuando se utiliza este enfoque en problemas de entrenamiento con tf.contrib.layers y la regularización, encuentro que las pérdidas de regularización son infinitas si el gráfico DataSupplier está en el mismo dispositivo que el gráfico de entrenamiento. Por mi vida no puedo explicar por qué este es el caso, pero configurar explícitamente el dispositivo del DataSupplier a la CPU (dado que el gráfico de entrenamiento está en mi GPU) parece funcionar …