【问题标题】:How to inject data into a graph when using an input pipeline?使用输入管道时如何将数据注入图形?
【发布时间】:2018-09-28 10:06:05
【问题描述】:

我在我的代码中使用了一个可初始化的迭代器。迭代器从具有 20.000 个条目的 csv 数据集中返回大小为 100 的批次。然而,在训练过程中,我遇到了一个问题。考虑这段代码:

def get_dataset_iterator(batch_size): 

    # parametrized with batch_size
    dataset = ... 
    return dataset.make_initializable_iterator()


## build a model and train it (x is the input of my model)
iterator = get_dataset_iterator(100)
x = iterator.get_next()
y = model(x)

## L1 norm as loss, this works because the model is an autoencoder
loss = tf.abs(x - y)

## training operator
train_op = tf.train.AdamOptimizer(0.01).minimize(loss)

with tf.Session() as sess:

    for epoch in range(100):

        sess.run(iterator.initializer)

        # iterate through the whole dataset once during the epoch and 
        # do 200 mini batch updates

        for _ in range(number_of_samples // batch_size):
            sess.run(train_op)

        print(f'Epoch {epoch} training done!')

        # TODO: print loss after epoch here

我对完成 epoch 后的训练损失感兴趣。对我来说最有意义的是我计算整个训练集的平均损失(例如,通过网络输入所有 20.000 个样本并平均它们的损失)。我可以在这里重用数据集迭代器,批量大小为 20.000,但我已将 x 声明为输入。

所以问题是:

1.) 对所有 20.000 个示例进行损失计算是否有意义?我见过有些人只用一个小批量(时代的最后一批)进行计算。

2.) 如何使用输入管道计算整个训练集的损失?我必须以某种方式注入所有的训练数据,这样我就可以运行sess.run(loss),而不需要仅计算 100 个样本(因为 x 被声明为输入)。

编辑澄清:

如果我按以下方式编写训练循环,会有一些事情困扰我:

with tf.Session() as sess:

    for epoch in range(100):

        sess.run(iterator.initializer)

        # iterate through the whole dataset once during the epoch and 
        # do 200 mini batch updates

        for _ in range(number_of_samples // batch_size):
            _, current_loss = sess.run([train_op, loss])

        print(f'Epoch {epoch} training done!')
        print(current_loss)

首先,在进行最后一次权重更新之前,仍会评估损失。这意味着出现的任何东西都不是最新的值。其次,退出 for 循环后我将无法访问current_loss,因此无法打印。

【问题讨论】:

    标签: python python-3.x tensorflow


    【解决方案1】:

    1) 整个训练集的损失计算(在更新权重之前)确实有意义,称为批量梯度下降(尽管使用整个训练集而不是小批量)。

    但是,在更新权重之前计算整个数据集的损失会很慢(尤其是对于大型数据集),而且训练需要很长时间才能收敛。因此,通常使用小批量数据来计算损失和更新权重。尽管使用小批量会产生一个嘈杂的损失估计,但它实际上是足够好的估计来训练具有足够训练迭代的网络。

    编辑:

    我同意您打印的损失值不会是具有最新更新权重的最新损失。可能在大多数情况下,它确实不会产生太大的不同或改变结果,所以人们只是按照您编写上面代码的方式进行。但是,如果您真的想在完成训练(打印出来)后获得真正的最新损失值,那么您只需要在完成训练操作后再次运行损失操作,例如:

    for _ in range(number_of_samples // batch_size):
                sess.run([train_op])
                current_loss = sess.run([loss])
    

    这将获得您真正的最新价值。当然,这不会出现在整个数据集上,仅适用于 100 个小批量。同样,该值可能是一个足够好的估计值,但如果您希望计算整个数据集的准确损失,则必须遍历整个数据集,例如另一个循环,然后平均损失:

        ...
        # Train loop
        for _ in range(number_of_samples // batch_size):
            _, current_loss = sess.run([train_op, loss])
    
        print(f'Epoch {epoch} training done!')
    
        # Calculate loss of whole train set after training an epoch.
        sess.run(iterator.initializer)
        current_loss_list = []
        for _ in range(number_of_samples // batch_size):
                _, current_loss = sess.run([loss])
                current_loss_list.append(current_loss)
        train_loss_whole_dataset = np.mean(current_loss_list)
        print(train_loss_whole_dataset)
    

    编辑 2: 正如所指出的那样,对 train_op 进行串行调用,然后 loss 将调用迭代器两次,因此事情可能不会很好地进行(例如,数据用完)。因此我的第二段代码会更好用。

    【讨论】:

    • 关于 1.):不幸的是,我不清楚为什么在权重更新之前计算损失是有意义的(例如这里的 train_op),因为我想知道更新的损失值而不是以前是什么。我在这里想念什么?关于 2.):我希望通过这种方式一次性获得整个数据集,并用它来计算所有样本的损失。
    • 好的,我想我明白你现在在问什么,正在更新答案。
    • 谢谢,但是train_oploss 的串行评估问题都需要x。这意味着get_next() 方法被调用了两次,循环实际上以OutOfRangeException 失败。那是因为计算出的小批量更新数量是 20000/100 = 200,但我只达到了 100 次迭代。这就是让输入管道对我来说难以处理的原因。如果您可以保存一次get_next 的返回值并重复使用它,那就太好了。但是,我将测试您的第二个解决方案。
    • 是的,这很好,它会再次调用 get_next(),这样你的数据就会用完,事情就不会很好地工作。可能我的第二种方法会更好,您必须在循环进行损失计算之前重新初始化数据集。
    【解决方案2】:

    我认为以下代码将回答您的问题: (A)如何在执行训练步骤后打印批量损失? (B) 即使数据集迭代器每次只给出一个批次,你如何计算整个训练集的损失?

    import tensorflow as tf
    import numpy as np
    
    dataset_size = 200
    batch_size= 5
    dimension = 4
    
    # create some training dataset
    dataset = tf.data.Dataset.\
        from_tensor_slices(np.random.normal(2.0,size=(dataset_size,dimension)).
        astype(np.float32))
    
    dataset = dataset.batch(batch_size) # take batches
    
    iterator = dataset.make_initializable_iterator()
    x = tf.cast(iterator.get_next(),tf.float32)
    w = tf.Variable(np.random.normal(size=(1,dimension)).astype(np.float32))
    
    loss_func = lambda x,w: tf.reduce_mean(tf.square(x-w)) # notice that the loss function is a mean!
    loss = loss_func(x,w) # this is the loss that will be minimized
    train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
    
    # we are going to use control_dependencies so that we know that we have a loss calculation AFTER the train step
    with tf.control_dependencies([train_op]):
        loss_after_train_op = loss_func(x,w) # this is an identical loss, but will only be calculated AFTER train_op has
                                             # been performed
    
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
    
        # train one epoch
        sess.run(iterator.initializer)
        for i in range(dataset_size//batch_size):
            # the training step will update the weights based on ONE batch of examples each step
           loss1,_,loss2 = sess.run([loss,train_op,loss_after_train_op])
           print('train step {:d}.  batch loss before step: {:f}.  batch loss after step: {:f}'.format(i,loss1,loss2))
    
        # evaluate loss on entire training set. Notice that this calculation assumes the the loss is of the form 
        # tf.reduce_mean(...)
        sess.run(iterator.initializer)
        epoch_loss = 0
        for i in range(dataset_size // batch_size):
            batch_loss = sess.run(loss)
            epoch_loss += batch_loss*batch_size
        epoch_loss = epoch_loss/dataset_size
        print('loss over entire training dataset: {:f}'.format(epoch_loss))
    

    至于您的问题,计算整个训练集的损失是否有意义 - 是的,出于评估目的,这是有意义的。执行基于所有训练集的训练步骤通常没有意义,因为该集通常非常大,并且您希望更频繁地更新权重,而无需每次都检查整个训练集。

    【讨论】:

    • 这是一个很好的答案,我不知道control_dependencies 的存在。虽然您的示例完美无缺,但我很难让它适用于我的代码(之前和之后的损失似乎一直都是一样的)。尽管如此,我会从中得到两件事:首先,使用批量损失作为数据集损失的估计是常见的做法(即使它甚至不是最后一批之后的损失)。其次,也有可能在最后一批之后得到损失,但在实践中并不普遍。这是正确的吗?
    • 关于我的第二个问题:TensorFlow 中是否有可能在训练期间将数据注入此图中?您通过总结答案中的批处理结果来避免这种情况。我正在寻找类似的东西,而不是将数据集中的批次用作模型的 x,而是将变量 y 中的值注入其中。
    • 请注意,您可能需要计算损失函数有两个原因:(1)出于评估目的,查看损失是否足够小以及训练是否仍在减少损失等。( 2)进行梯度计算,然后你使用这个梯度来更新权重(梯度下降)。
    • 对于选项(2),您通常只计算小批量的损失。这意味着您的计算只是一个估计,但估计通常足够准确(如果批量大小不是太小),以便学习以合理的速度进行。使用(2)中的整个训练数据集通常会使训练时间更长,因为在每次权重更新之前计算损失需要很长时间。
    • 对于选项 (1),如果您对所有训练数据集的损失感兴趣,那很好,但请确保只偶尔执行一次此评估步骤(例如,每隔几个 epoch ),因为此评估需要很长时间(而且您通常还希望对验证测试执行评估,这也需要时间)。
    猜你喜欢
    • 2011-05-24
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-20
    相关资源
    最近更新 更多