tf.datasets input_fn在1个纪元后获得错误

时间:2018-05-10 00:50:26

标签: tensorflow google-cloud-ml tensorflow-datasets

所以我试图使用tf.datasets切换到input_fn(),如question所述。虽然我使用带有下面的input_fn()的tf.datasets可以获得更高的步数/秒,但是在GCMLE上运行此实验时,我似乎在1个纪元后遇到错误。考虑一下这个input_fn():

def input_fn(...):
    files = tf.data.Dataset.list_files(filenames).shuffle(num_shards)

    dataset = files.apply(tf.contrib.data.parallel_interleave(lambda filename: tf.data.TextLineDataset(filename).skip(1), cycle_length=num_shards))
    dataset = dataset.apply(tf.contrib.data.map_and_batch(lambda row:
        parse_csv_dataset(row, hparams = hparams), 
        batch_size = batch_size, 
        num_parallel_batches = multiprocessing.cpu_count())) 
    dataset = dataset.prefetch(1)
    if shuffle:
        dataset = dataset.shuffle(buffer_size = 10000)
    dataset = dataset.repeat(num_epochs)

    iterator = dataset.make_initializable_iterator()
    features = iterator.get_next()
    tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, iterator.initializer)

    labels = {key: features.pop(key) for key in LABEL_COLUMNS}

    return features, labels

我在GCMLE上收到以下错误:

disable=protected-access InvalidArgumentError (see above for traceback): Inputs to operation loss/sparse_softmax_cross_entropy_loss/num_present/Select of type Select must have the same size and shape. Input 0: [74] != input 1: [110] [[Node: loss/sparse_softmax_cross_entropy_loss/num_present/Select = Select[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/device:GPU:0"](loss/sparse_softmax_cross_entropy_loss/num_present/Equal, loss/sparse_softmax_cross_entropy_loss/num_present/zeros_like, loss/sparse_softmax_cross_entropy_loss/num_present/ones_like)]] [[Node: global_step/add/_1509 = _Recv[client_terminated=false, recv_device="/job:localhost/replica:0/task:0/device:CPU:0", send_device="/job:localhost/replica:0/task:0/device:GPU:0", send_device_incarnation=1, tensor_name="edge_3099_global_step/add", tensor_type=DT_INT64, _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

这意味着存在形状不匹配Input 0: [74] != input 1: [110],但是我的旧基于队列的input_fn()在相同的确切数据上工作正常,因此我不认为它与底层数据有任何问题。这是在我认为是时代的结束时发生的(因为当GCMLE错误结束时num_steps正好在num_train_examples/batch_size左右,所以我猜这个问题可能是最后一批不等于batch_size是110(因为它显示在错误中)而只有74个例子。任何人都可以确认这是错误吗?假设它是,是否有一些其他标志我需要设置,以便最后一批可以是除了spcified批量大小110?

以外的其他批次

为了它的价值,我用两个不同的数据集复制了这个行为(基于input_fn的旧队列的多个纪元的列车,在tf.datasets input_fn的第一个纪元的末尾被挂起)

2 个答案:

答案 0 :(得分:1)

您的图表中的某些操作(来自错误消息,可能是sparse_softmax_cross_entropy_loss)似乎需要固定的批量大小。它可能是你的代码(不是input_fn的一部分)强制执行此操作(例如,将batch_size作为op中使用的某个张量的形状传递),或者它可能是TF库之一。

这本身并不总是一个问题。但是,tf.data.Dataset.batch的{​​{3}}是:

  

注意:如果此数据集中的元素数(N)不准确   batch_size的倍数,最后一批包含较小的张量   在批量维度中形成N%batch_size。如果你的程序取决于   在具有相同形状的批次上,考虑使用   改为tf.contrib.data.batch_and_drop_remainder转换。

按照目前的编写,您的(非input_fn)代码属于具有相同形状的批次的类别。

您的选择是跟踪代码通过静态批处理大小的位置或“删除剩余部分”。我相信前者更可取,但更多的工作。

如果您选择后者,请注意您实际上并未使用tf.data.Dataset.batch,而是documented behavior接受drop_remainder参数。

答案 1 :(得分:1)

正如Robbie在other answer中所建议的,看起来您的旧实现始终使用固定批量大小(可能使用类似tf.train.batch()的API或其包装器之一,默认参数为{{1} }),allow_smaller_final_batch=False(通过tf.data.Dataset.batch()tf.contrib.data.map_and_batch())批量处理的默认行为是包含较小的最终批次。

该错误最有可能出现在tf.data中。在没有看到该函数的情况下,很难猜测,但我怀疑通过Tensor.set_shape()(可能在库代码中)存在张量形状的显式(和不正确)断言或者{{的实现中的错误3}}

首先,我假设从model_fn返回的featureslabels张量具有静态未知的批量大小。您是否可以通过打印input_fn()features个对象来确认,并确保其报告的tf.losses.sparse_softmax_cross_entropy()属性的第0维度为labels

接下来,找到None中对tf.losses.sparse_softmax_cross_entropy()的来电。将作为model_fn参数传递的对象打印到此函数,该对象应为weights,并找到其静态形状。鉴于您看到的错误,我怀疑它会有tf.Tensor这样的形状,其中(110,)是您指定的批量大小。如果是这种情况,110中有一个错误,它错误地断言权重的形状是完整的批次,当它可能不是时。 (如果情况并非如此,那么model_fn中就会出现错误!请打开一个Tensor.shape,其中包含一个示例,可让我们重现问题。)

  

旁白:为什么这会解释这个错误?失败tf.losses.sparse_softmax_cross_entropy()操作的GitHub issue看起来像这样(为了便于阅读而编辑):

tf.where()
     

由于历史原因,这种num_present = tf.where(tf.equal(weights, 0.0), # This input is shape [74] tf.zeros_like(weights), # This input is shape [110] tf.ones_like(weights) # This input is probably [110] ) op(在错误消息中名为tf.where()}的味道要求所有三个输入具有相同的大小。从表面上看,"Select"tf.equal(weights, 0.0)tf.ones_like(weights)都具有相同的形状,即tf.zeros_like(weights)的形状。但是,如果静态形状(weights的结果)与code that calls不同,则行为未定义。

     

实际发生了什么?在这种特殊情况下,假设Tensor.shape的静态形状为weights,但动态形状为[110][74]的三个参数的静态形状为tf.where()[110]的实现并不关心是否存在不匹配,因此其动态形状将为tf.equal()[74]tf.zeros_like()的实现使用dynamic shape,当静态形状完全定义时忽略该动态形状,因此它们的动态形状将为tf.ones_like(),从而导致错误你看到了。

正确的解决方法是找到在[110]中声明固定批量大小的代码,然后将其删除。 TensorFlow中的优化和评估逻辑对于可变批量大小是稳健的,这将确保您的所有数据都用于培训和评估过程。

不太理想的短期修复方法是在数据末尾删除小批量。这里有几个选项:

  • 在每个纪元的末尾随机丢弃一些数据:

    • 使用TF 1.8或更高版本,将model_fn传递给drop_remainder=False
    • 使用TF 1.7或更早版本,请在tf.contrib.data.map_and_batch()
    • 之后使用dataset = dataset.filter(lambda features: tf.equal(tf.shape(features[LABEL_COLUMNS[0]])[0], batch_size))
  • 删除最后一批数据:

    • map_and_batch之前移动dataset.repeat(NUM_EPOCHS),然后应用上述两个修复中的一个。