【问题标题】:Keras' `model.fit_generator()` behaves different than `model.fit()`Keras 的 `model.fit_generator()` 的行为与 `model.fit()` 不同
【发布时间】:2018-02-07 06:45:38
【问题描述】:

我有一个庞大的数据集,我需要以生成器的形式提供给 Keras,因为它不适合内存。但是,使用fit_generator,我无法复制我在使用model.fit 进行常规训练时获得的结果。而且每个纪元的持续时间也相当长。

我实现了一个最小的示例。也许有人可以告诉我问题出在哪里。

import random
import numpy

from keras.layers import Dense
from keras.models import Sequential

random.seed(23465298)
numpy.random.seed(23465298)

no_features = 5
no_examples = 1000


def get_model():
    network = Sequential()
    network.add(Dense(8, input_dim=no_features, activation='relu'))
    network.add(Dense(1, activation='sigmoid'))
    network.compile(loss='binary_crossentropy', optimizer='adam')
    return network


def get_data():
    example_input = [[float(f_i == e_i % no_features) for f_i in range(no_features)] for e_i in range(no_examples)]
    example_target = [[float(t_i % 2)] for t_i in range(no_examples)]
    return example_input, example_target


def data_gen(all_inputs, all_targets, batch_size=10):
    input_batch = numpy.zeros((batch_size, no_features))
    target_batch = numpy.zeros((batch_size, 1))
    while True:
        for example_index, each_example in enumerate(zip(all_inputs, all_targets)):
            each_input, each_target = each_example
            wrapped = example_index % batch_size
            input_batch[wrapped] = each_input
            target_batch[wrapped] = each_target
            if wrapped == batch_size - 1:
                yield input_batch, target_batch


if __name__ == "__main__":
    input_data, target_data = get_data()
    g = data_gen(input_data, target_data, batch_size=10)
    model = get_model()
    model.fit(input_data, target_data, epochs=15, batch_size=10)  # 15 * (1000 / 10) * 10
    # model.fit_generator(g, no_examples // 10, epochs=15)        # 15 * (1000 / 10) * 10

在我的电脑上,model.fit 总是以 0.6939 的损失完成第 10 个 epoch,并且在 ca 之后。 2-3 秒。

然而,model.fit_generator 方法运行的时间要长得多,并以 不同 损失 (0.6931) 结束最后一个 epoch。

我一般不明白为什么两种方法的结果不同。这可能看起来差别不大,但我需要确保具有相同网络的相同数据产生相同的结果,独立于常规训练或使用生成器。

更新:@Alex R. 为部分原始问题提供了答案(一些性能问题以及每次运行的结果变化)。然而,由于核心问题仍然存在,我只是相应地调整了问题和标题。

【问题讨论】:

  • 我认为您在面向 Python 编程的网站上可能会更好。
  • 您的训练数据集有多大?如果你在 fit 生成器中增加批量大小会发生什么?
  • @AlexR。我有大约 250 万个例子。如果我增加批量大小,损失仍然不稳定,并且与我使用model.fit() 得到的损失仍然不同。
  • @mdewey 如果你知道在没有 Python 的情况下使用 Keras 的方法,我期待听到它。
  • Also each epoch lasts considerably longer. 其原因显然是与 I/O 操作相关的开销。它随领土而来。为了缩短它,您可能需要一个固态硬盘。

标签: python keras generator


【解决方案1】:

确保您的生成器每次都实际返回不同的批次。我的发电机遇到了这个问题。 当您在 while 循环之前初始化批处理 numpy 占位符时,即使您可能在 for 循环内更改这些变量,初始化的变量也可能在第一个 for 循环期间仅更改一次。我的问题正是如此。我有一个类似结构的生成器,但我在 for 循环之后返回了批次:Why is this python generator returning the same value everytime?

您可以使用此 sn-p 检查生成器是否工作,该 sn-p 检查所有生成的批次是否确实不同:

g = data_gen(input_data, target_data, batch_size=10)
input_list = []
target_list = []
for _ in range(100):
    input, target = next(g)
    input_list.append(input)
    target_list.append(target)
inputs = np.concatenate(input_list, axis=0)   
targets = np.concatenate(target_list, axis=0)

all_different = True
for i in range(1, inputs.shape[0]):
    if np.array_equal(inputs[0], inputs[i]):
        all_different = False
print('All batches different') if all_different else print('Generator broken. Initialize your numpy arrays inside the while loop or yield input.copy, target.copy()')

【讨论】:

    【解决方案2】:

    希望我没有迟到。我要补充的最重要的一点:

    在 Keras 中,使用 fit() 适合可以加载到内存中的较小数据集。对于大多数实际用例,几乎所有数据集都很大,无法一次加载到内存中。

    对于更大的数据集,我们必须使用fit_generator()

    【讨论】:

    • 如果你不介意我说,问题不在于何时使用fit()fit_generator(),每个人都同意这一点,而是为什么他们的行为不同。
    【解决方案3】:

    确保在生成器中对批次进行洗牌。

    此讨论建议您在迭代器中打开随机播放:https://github.com/keras-team/keras/issues/2389。我有同样的问题,这解决了它。

    【讨论】:

      【解决方案4】:

      至于损失,这可能是由于已经讨论过的批量大小差异。

      至于训练时间的差异,model.fit_generator()允许你指定“工人”的数量。此参数是指同时在数据集中的不同区域中训练模型的多少实例。如果您的计算机体系结构得到了正确优​​化,您应该能够将工作人员参数更改为 4 或 8,并看到训练时间大大减少。

      【讨论】:

        【解决方案5】:

        批量大小

        • fit 中,您使用的是标准批量大小 = 32。
        • fit_generator 中,您使用的批量大小 = 10。

        Keras 可能会在每个批次之后运行权重更新,因此,如果您使用不同大小的批次,则可能会在两种方法之间获得不同的梯度。而且一旦有不同的权重更新,这两个模型将永远不会再见面。

        尝试将 fit 与 batch_size=10 一起使用,或将生成器与 batch_size=32 一起使用。


        种子问题?

        您是否正在为每个案例创建一个带有get_model() 的新模型?

        如果是这样,两个模型中的权重是不同的,那么两个模型自然会得到不同的结果。 (好吧,你已经设置了一个种子,但是如果你使用的是 tensorflow,也许你面对的是this issue

        不过,从长远来看,它们会趋于一致。两者之间的差异似乎并没有那么大。


        检查数据

        如果您不确定您的生成器产生的数据是否与您预期的相同,请在其上执行一个简单的循环并打印/比较/检查它产生的数据:

        for i in range(numberOfBatches):
            x,y = g.next() #or next(g)
            #print or compare x,y here. 
        

        【讨论】:

        • 感谢您的回答。我猜 tensorflow 问题并非如此,因为model.fit 在每次运行中返回相同的损失。我比较了两个输出:它们是相同的:(
        • 好的,你试过相同的批量吗?请参阅我的答案中的更新。
        • 在上面的代码中,可以看到batch_sizes都设置为10
        • 好的,还有两件事我可以想象(但我没有检查,如果我错了,请见谅): 1 - 从列表到 numpy 数组的更改可能正在更改数据类型在 float32 和 float64 之间?也许尝试在numpy数组中转换get_data()? --- 2 - 生成器中的批次大小在创建结束时真的是 10 吗?
        【解决方案6】:

        我不明白批量越大,损失如何不稳定,因为批量越大,波动应该越小。然而,看看Keras documentationfit() 例程看起来像:

        fit(self, x, y, batch_size=32, epochs=10, verbose=1, callbacks=None, validation_split=0.0, 
            validation_data=None, shuffle=True, class_weight=None, sample_weight=None, 
            initial_epoch=0)
        

        它有一个默认的batch_size=32epochs=10。而fit_generator() 看起来像:

        fit_generator(self, generator, steps_per_epoch, epochs=1, verbose=1,
                      callbacks=None, validation_data=None, validation_steps=None, 
                      class_weight=None, max_queue_size=10, workers=1,
                      use_multiprocessing=False, initial_epoch=0)
        

        “step_per_epoch”具体定义为:

        steps_per_epoch:要产生的步骤总数(样本批次) 在声明一个纪元完成并启动之前从生成器 下一个时代。它通常应该等于唯一的数量 数据集的样本除以批量大小。

        因此,对于初学者来说,与 fit() 例程相比,听起来您的 fit_generator 正在吸收更多数量的样本。 See here for more details.

        【讨论】:

        • 感谢您的回答!它解决了部分问题。你是对的。我提供了太多样本,因为我错误地理解了steps_per_epoch。如果我将参数除以批量大小(如文档所建议的那样),结果可重现地收敛到 0.6931,但它仍然与 fit 方法不同,并且仍然慢 10 倍......
        • @wehnsdaefflae:我能找到的最好的就是这个,说实话,为什么生成器在 fit() 例程的可比输入上运行时会变慢是没有意义的:github.com/fchollet/keras/issues/2730
        • 另见此,建议降低验证步骤:github.com/fchollet/keras/issues/6406#issuecomment-308248241'
        • 感谢您的研究!很高兴看到至少速度问题似乎不是由于我的代码(不再)。由于其他方面仍处于开放状态,我将把这个问题再开放几天......
        • 除此之外,您还可以在fit_generator 中增加max_queue_size 以在训练时保持批量生产
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-20
        • 1970-01-01
        • 1970-01-01
        • 2018-03-11
        • 1970-01-01
        • 2020-09-25
        相关资源
        最近更新 更多