Tensorflow队列 - 在列车和验证数据之间切换

时间:2016-12-15 11:23:26

标签: python queue 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 =无

如果我将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.但是这些 对结果没有影响。

我应该采取什么其他方法?

我可以采取哪种其他方法来切换培训和验证数据? 我是否必须创建单独的队列?我不知道如何做到这一点 工作。我是否还需要创建其他协调员和队列运行器?

4 个答案:

答案 0 :(得分:9)

我正在编制一份可能解决此问题的潜在方法列表。其中大多数只是模糊的建议,没有实际的代码示例来展示如何使用它们。

默认占位符

建议here

使用tf.cond()

建议here

sygi也建议在这个非常stackoverflow线程上。 link

使用tf.group()和tf.cond()

建议here

make_template()方法

建议herehere

共享权重方法

sygi在这个stackoverflow线程(link)中建议

。这可能与make_template()方法相同。

QueueBase()方法。

建议的here示例代码here 代码在这个帖子上适应了我的问题。 link

训练桶方法

建议here

答案 1 :(得分:8)

首先,您可以手动阅读代码中的示例(到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})

如果您需要使用队列,您可以尝试有条件地更改队列#34; training"到"测试"之一:

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})

第二种方法是considered unsafe - 在这篇文章中,我们鼓励为培训和测试(共享权重)构建两个单独的图表,这是达到你想要的另一种方式。

答案 2 :(得分:3)

好的,所以我有一个适合我的解决方案。它基于在tensorflow github问题部分中从this post获取的代码。它使用QueueBase.from_list()函数。感觉非常hacky,我对此并不完全满意,但至少我正在努力工作。

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']

我打开这个主题,希望有一个更好的解决方案。

答案 3 :(得分:2)

创建两个不同的队列是discouraged

如果您有两台不同的机器,我建议您使用单独的机器进行培训和验证(如果没有,您可以使用两种不同的流程)。对于2个机器案例:

  1. 第一台机器只有训练数据。它使用队列将数据批量传递给图模型,并具有GPU用于培训。在每个步骤之后,它将新模型(model_iteration)保存在第二台机器可以访问它的地方。
  2. 第二台机器(仅有验证数据)定期用模型轮询该地点,并检查新模型是否可用。在这种情况下,它会运行新模型的推断并检查性能。因为大多数情况下验证数据明显小于训练数据,所以你甚至可以将它全部留在内存中。
  3. 这种方法很少有优点。培训/验证数据是分开的,您不能搞砸它们。您可以使用弱机器进行验证,因为即使验证滞后于培训(不太可能的情况),也不是问题,因为它们是独立的