【问题标题】:Tensorflow dataset with changing batch size to compute test loss during training具有改变批量大小的 TensorFlow 数据集以计算训练期间的测试损失
【发布时间】:2018-05-29 04:16:37
【问题描述】:

我正在尝试运行一个训练循环,定期确定当前的平均损失并将其打印到控制台。为了确定损失,我想使用不同的批量大小。所以它是这样的:

dataset = create_dataset().shuffle(1000).repeat().batch(minibatch_size)
iterator = dataset.make_one_shot_iterator() # using this iterator in the graph

while ...:
 session.run(...) # perform training

 if epoch % 10 = 0:
  test_avg_loss = session.run(avg_loss) # want a different number of items here

我希望在训练期间小批量大小为 10,但我想用 100 个数据点进行测试,以获得对平均损失的更好估计。如何让数据集在这里返回不同数量的项目?我尝试将placeholder 传递给batch,但似乎不受支持。错误是:

'ValueError : 无法按值捕获占位符(名称:batchSize,类型:占位符)。'

如果这似乎是一个更好的解决方案,我愿意完全使用不同的代码结构。我知道出于性能原因不使用feedDict 传递数据很重要,因此使用dataset 似乎是可行的方法。我不是在寻求某种 hack,但我想知道这样做的正确方法是什么。

【问题讨论】:

    标签: python tensorflow


    【解决方案1】:

    一个好的解决方案是使用可重新初始化的迭代器,它可以让您在两个(或多个)Datasets 之间切换,通常一个用于训练,一个用于验证。

    The example in the documentation其实很简洁:

    # Define training and validation datasets with the same structure.
    training_dataset = tf.data.Dataset.range(100).map(
        lambda x: x + tf.random_uniform([], -10, 10, tf.int64))
    validation_dataset = tf.data.Dataset.range(50)
    
    # A reinitializable iterator is defined by its structure. We could use the
    # `output_types` and `output_shapes` properties of either `training_dataset`
    # or `validation_dataset` here, because they are compatible.
    iterator = tf.data.Iterator.from_structure(training_dataset.output_types,
                                               training_dataset.output_shapes)
    next_element = iterator.get_next()
    
    training_init_op = iterator.make_initializer(training_dataset)
    validation_init_op = iterator.make_initializer(validation_dataset)
    
    # Run 20 epochs in which the training dataset is traversed, followed by the
    # validation dataset.
    for _ in range(20):
      # Initialize an iterator over the training dataset.
      sess.run(training_init_op)
      for _ in range(100):
        sess.run(next_element)
    
      # Initialize an iterator over the validation dataset.
      sess.run(validation_init_op)
      for _ in range(50):
        sess.run(next_element)
    

    只要确保您创建的迭代器的批量大小未知。

    【讨论】:

    • 我明白了!重新初始化会带来什么样的性能成本?这可行吗?我想初始化可能需要将数据复制到 GPU 并重新洗牌。
    • 成本更多的是内存,因为您通常会为两个数据集缓冲。我想说,转换本身几乎没有成本。
    • 那将是理想的。我将等待其他答案,但这个似乎非常好。
    • 我刚刚对每个运行调用进行了基准测试。事实证明,初始化的成本随着shuffle 中指定的元素数量而增加。初始化操作以及初始化后的第一次测试/训练运行调用都变得更加昂贵。所以这不是一个恒定的时间开关。不过,对于我的目的而言,成本已经足够低了。
    • 不知道。这是否意味着切换时会丢弃缓冲区?那会让我难过。
    【解决方案2】:

    根据您的评论,您应该查看可以与 tf.placeholder 一起使用的 feedable iterator,以通过熟悉的 feed_dict 机制选择在每次调用 tf.Session.run 时使用的迭代器。它提供与reinitializable iterator 相同的功能,但它不需要您在迭代器之间切换时从数据集的开头初始化迭代器。

    # Training and validation datasets
    training_dataset = tf.data.Dataset.range(100).repeat().batch(100)
    validation_dataset = tf.data.Dataset.range(150, 200).repeat().batch(10)
    
    # A feedable iterator to toggle between validation and training dataset
    handle = tf.placeholder(tf.string, shape=[])
    iterator = tf.data.Iterator.from_string_handle(
    handle, training_dataset.output_types, training_dataset.output_shapes)
    next_element = iterator.get_next()
    
    training_iterator = training_dataset.make_one_shot_iterator()
    validation_iterator = validation_dataset.make_one_shot_iterator()
    
    with tf.Session() as sess:
    
       # The `Iterator.string_handle()` method returns a tensor that can be evaluated
       # and used to feed the `handle` placeholder.
       training_handle = sess.run(training_iterator.string_handle())
       validation_handle = sess.run(validation_iterator.string_handle())
    
       # Run 20 epochs in which the training dataset is traversed, followed by the
       # validation dataset.
       for _ in range(20):
          for _ in range(100):
            out = sess.run(next_element, feed_dict={handle: training_handle})
    
          for _ in range(50):
            out = sess.run(next_element, feed_dict={handle: validation_handle})
    

    【讨论】:

    • 看起来效果最好。我会调查的。
    【解决方案3】:

    用 [None, None] 塑造你的占位符

    现在在评估和训练期间做这样的事情:

    给你的训练文件一个结构:

    import tensorflow as tf
    
    
    def shape(dataset):
    
        #shape your data here 
        return {'input':np.array(input_data),'label':np.array(labels)}
    
    
    def evaluate(model,batch_size=100):
        sess = tf.get_default_graph()
        iteration = len(dataset) // batch_size
    
        loss = []
    
        for j in iteration:
            dataset = dataset[j * batch_size:(j + 1) * batch_size]
    
            #shape it here before feeding to network 
            dataset=shape(dataset)
    
            out = sess.run(model, feed_dict={input_place: dataset['input'], labels: data['labels']})
            loss.append(out['loss'])
    
        return np.mean(loss)
    
    def train(model,batch_size=10):
    
    
    
        iteration=len(dataset)//batch_size
    
        with tf.Session() as sess:
    
            for i in epoch(epoch):
                for j in iteration:
                    dataset = dataset[j * batch_size:(j + 1) * batch_size]
    
                    dataset = shape(dataset)
    
                    # shape it here before feeding to network 
    
    
                    out = sess.run(model, feed_dict={input_place: dataset['input'], labels: data['labels']})
    
                print(out['loss'], out['training_accuracy'])
    
            print(evaluate(model))
    

    【讨论】:

    • 试图理解代码...这不是通过feedDict传递数据吗?我知道这会带来极高的性能成本。请注意,我使用的是tensorflow.data.dataset,因此我现在不必通过 feedDict 传递任何数据。如果我可以通过 feedDict 发送数据,问题就很容易解决了。
    • 它正在通过 feeddict 传递数据。
    • 我没有测量它,但据我所知,如果你关心性能,这种方法是不可能的。您必须构建图表,以便图表本身执行数据馈送。 dataset 这样做。
    • @boot4life 你能提供任何来源吗,我想了解更多。
    • 这似乎是一篇关于它的知名文章:towardsdatascience.com/…
    猜你喜欢
    • 1970-01-01
    • 2017-08-24
    • 2019-02-06
    • 1970-01-01
    • 2017-01-19
    • 1970-01-01
    • 2016-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多