Очереди 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) где-нибудь, где вторая машина может получить к ней доступ.
  • Вторая машина (имеет только данные проверки) периодически обследует место с моделью и проверяет, доступна ли новая модель. В этом случае он выполняет вывод новой модели и проверяет производительность. Поскольку большую часть времени данные проверки значительно меньше данных обучения, вы даже можете позволить себе все это в памяти.

Немногие плюсы этого подхода. Данные по обучению/валидации являются отдельными, и вы не можете их испортить. У вас может быть слабая машина для проверки, потому что даже если валидация отстает от обучения (маловероятный сценарий), это не проблема, потому что они независимы