Очереди Tensorflow - Переключение между данными поезда и валидации
Я пытаюсь использовать очереди для загрузки данных из файлов в Tensorflow.
Я хотел бы запустить график с данными валидации в конце каждой эпохи, чтобы лучше понять, как проходит обучение.
Вот где я столкнулся с проблемами. Я не могу понять, как
переключение между данными тренировки и данными проверки при использовании очередей.
Я разделил свой код на минимальный пример игрушки, чтобы облегчить
получить помощь. Вместо того, чтобы включать весь код, который загружает файлы изображений, выполняет вывод и обучение, я отрубил его на
где имена файлов загружаются в очередь.
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)
Вариации и эксперименты
Попытка использовать разные значения для num_epochs
num_epochs = None
Если я установил аргумент num_epochs
в tf.train.string_input_producer()
, чтобы
None
он дает следующий вывод,
который показывает, что он выполняет две эпохи, как предполагалось, но использует данные
из набора тренировок при выполнении оценки.
------------------------------------------------------------
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
Если я устанавливаю аргумент num_epochs
в tf.train.string_input_producer()
на 2
это дает следующий результат,
который показывает, что он даже не запускает полные две партии вообще
(и оценка по-прежнему использует учебные данные)
------------------------------------------------------------
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
Если я устанавливаю аргумент num_epochs
в tf.train.string_input_producer()
на 1
в надежде, что он выветрится
любые дополнительные данные обучения из очереди, чтобы он мог использовать проверку
данных, я получаю следующий вывод, который показывает, что он завершается, как только
он проходит через одну эпоху учебных данных и не может пройти
Загрузка оценочных данных.
------------------------------------------------------------
TRAIN_BATCH: ['train_file_0' 'train_file_1' 'train_file_2']
TRAIN_BATCH: ['train_file_3' 'train_file_4' 'train_file_5']
Настройка аргумента capacity
для различных значений
Я также попытался установить аргумент capacity
в
tf.train.string_input_producer()
до небольших значений, таких как 3 и 1. Но эти
не повлияло на результаты.
Какой другой подход следует принять?
Какой другой подход я могу предпринять для переключения между данными обучения и валидации?
Должен ли я создавать отдельные очереди? Я не понимаю, как это сделать.
Работа. Должен ли я создавать дополнительные координаторы и бегуны очереди?
Ответы
Ответ 1
Я составляю список возможных подходов, которые могут решить эту проблему здесь. Большинство из них - просто расплывчатые предложения, без фактических примеров кода, чтобы показать, как их использовать.
Заполнитель по умолчанию
Предлагается здесь
Используя tf.cond()
Предлагается здесь
Также предлагается sygi в этом потоке stackoverflow. ссылка
с использованием tf.group() и tf.cond()
Предлагается здесь
метод make_template()
Предлагается здесь и здесь
Метод общих весов
предложенный sygi в этом потоке stackoverflow (ссылка). Это может быть то же самое, что и метод make_template().
Метод QueueBase().
Предлагается здесь с образцом кода здесь
Код, адаптированный к моей проблеме здесь, в этой теме. ссылка
метод тренировочного ковша
Предлагается здесь
Ответ 2
Сначала вы можете вручную прочитать примеры в своем коде (в массивах numpy) и передать его любым способом:
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})
Если вам нужно использовать Очереди, вы можете попытаться условно изменить очередь от "обучения" до "тестирования":
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})
Второй подход считается небезопасным, хотя - в этом сообщении рекомендуется создавать два отдельных графика для обучения и тестирования (с общим весом), что является еще одним способом достижения желаемого.
Ответ 3
Хорошо, поэтому у меня есть решение, которое работает для меня. Он основан на коде, взятом из этот пост в разделе вопросов github для тензорного потока. Он использует функцию QueueBase.from_list()
. Он чувствует себя очень взломанным, и я не совсем этому доволен, но, по крайней мере, я заставляю его работать.
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)
Предоставление следующего результата, чего я ожидал.
------------------------------------------------------------
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']
Я оставляю эту тему открытой в надежде, что появится лучшее решение.
Ответ 4
Создание двух разных очередей обескуражено.
Если у вас есть две разные машины, я бы рекомендовал использовать отдельные машины для обучения и проверки (если нет, вы можете использовать два разных процесса). Для двух машинных корпусов:
- Первая машина имеет только данные обучения. Он использует очереди для передачи данных партиями в графическую модель и имеет GPU для обучения. После каждого шага он сохраняет новую модель (
model_iteration
) где-нибудь, где вторая машина может получить к ней доступ.
- Вторая машина (имеет только данные проверки) периодически обследует место с моделью и проверяет, доступна ли новая модель. В этом случае он выполняет вывод новой модели и проверяет производительность. Поскольку большую часть времени данные проверки значительно меньше данных обучения, вы даже можете позволить себе все это в памяти.
Немногие плюсы этого подхода. Данные по обучению/валидации являются отдельными, и вы не можете их испортить. У вас может быть слабая машина для проверки, потому что даже если валидация отстает от обучения (маловероятный сценарий), это не проблема, потому что они независимы