python - train - Colas de Tensorflow-Cambio entre el tren y los datos de validación
tensorflow keras tutorial (4)
Estoy compilando 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 here
Utilizando tf.cond ()
Sugerido here
También sugerido por sygi en este mismo hilo de stackoverflow. link
utilizando tf.group () y tf.cond ()
Sugerido here
método make_template ()
Método de ponderaciones compartidas
sugerido por sygi en este mismo hilo de stackoverflow ( link ). Esto podría ser lo mismo que el método make_template ().
Método QueueBase ().
Sugerido here con código de ejemplo here Código adaptado a mi problema aquí en este hilo. link
método del cubo de entrenamiento
Sugerido here
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?
Ok, entonces tengo una solución que está funcionando para mí. Se basa en el código tomado de here 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.
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.
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:
- 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. - 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