【问题标题】:How to handle the last batch using keras fit_generator如何使用 keras fit_generator 处理最后一批
【发布时间】:2019-09-17 06:50:15
【问题描述】:

我正在使用定制的批处理生成器来尝试解决在使用标准 model.fit() 函数时形状不兼容的问题(BroadcastGradientArgs 错误),因为训练数据中最后一个批处理的大小很小。我使用提到的批处理生成器here 和 model.fit_generator() 函数:

class Generator(Sequence):
    # Class is a dataset wrapper for better training performance
    def __init__(self, x_set, y_set, batch_size=256):
        self.x, self.y = x_set, y_set
        self.batch_size = batch_size
        self.indices = np.arange(self.x.shape[0])

    def __len__(self):
        return math.floor(self.x.shape[0] / self.batch_size) 

    def __getitem__(self, idx):
        inds = self.indices[idx * self.batch_size:(idx + 1) * self.batch_size] #Line A
        batch_x = self.x[inds]
        batch_y = self.y[inds]
        return batch_x, batch_y

    def on_epoch_end(self):
        np.random.shuffle(self.indices)

但如果最后一批的大小小于提供的批量大小,它似乎会丢弃最后一批。如何更新它以包含最后一批并(例如)用一些重复的样本扩展它?

另外,不知何故,我不明白“A 线”的工作原理!

更新: 这是我在模型中使用生成器的方式:

# dummy model
input_1 = Input(shape=(None,))
...
dense_1 = Dense(10, activation='relu')(input_1)
output_1 = Dense(1, activation='sigmoid')(dense_1)

model = Model(input_1, output_1)
print(model.summary())

#Compile and fit_generator
model.compile(optimizer='adam', loss='binary_crossentropy')

train_data_gen = Generator(x1_train, y_train, batch_size)
test_data_gen = Generator(x1_test, y_test, batch_size)

model.fit_generator(generator=train_data_gen, validation_data = test_data_gen, epochs=epochs, shuffle=False, verbose=1)

 loss, accuracy = model.evaluate_generator(generator=test_data_gen)
print('Test Loss: %0.5f Accuracy: %0.5f' % (loss, accuracy))

【问题讨论】:

    标签: python keras deep-learning generator


    【解决方案1】:

    我认为罪魁祸首是这条线

        return math.floor(self.x.shape[0] / self.batch_size)
    

    用这个替换它可能会起作用

        return math.ceil(self.x.shape[0] / self.batch_size) 
    

    想象一下,如果您有 100 个样本和 32 个批次。它应该分为 3.125 个批次。但是如果你使用math.floor,它会变成3和discord 0.125。

    对于A行,如果batch size为32,当index为1时[idx * self.batch_size:(idx + 1) * self.batch_size]会变成[32:64],也就是说,选择self.indices的第33到64个元素

    **更新 2,将输入更改为无形状并使用 LSTM 并添加评估

    import os
    os.environ['CUDA_VISIBLE_DEVICES'] = ""
    import math
    import numpy as np
    from keras.models import Model
    from keras.utils import Sequence
    from keras.layers import Input, Dense, LSTM
    
    
    class Generator(Sequence):
        # Class is a dataset wrapper for better training performance
        def __init__(self, x_set, y_set, batch_size=256):
            self.x, self.y = x_set, y_set
            self.batch_size = batch_size
            self.indices = np.arange(self.x.shape[0])
    
        def __len__(self):
            return math.ceil(self.x.shape[0] / self.batch_size)
    
        def __getitem__(self, idx):
            inds = self.indices[idx * self.batch_size:(idx + 1) * self.batch_size]  # Line A
            batch_x = self.x[inds]
            batch_y = self.y[inds]
            return batch_x, batch_y
    
        def on_epoch_end(self):
            np.random.shuffle(self.indices)
    
    
    # dummy model
    input_1 = Input(shape=(None, 10))
    x = LSTM(90)(input_1)
    x = Dense(10)(x)
    x = Dense(1, activation='sigmoid')(x)
    
    model = Model(input_1, x)
    print(model.summary())
    
    # Compile and fit_generator
    model.compile(optimizer='adam', loss='binary_crossentropy')
    
    x1_train = np.random.rand(1590, 20, 10)
    x1_test = np.random.rand(90, 20, 10)
    y_train = np.random.rand(1590, 1)
    y_test = np.random.rand(90, 1)
    
    train_data_gen = Generator(x1_train, y_train, 256)
    test_data_gen = Generator(x1_test, y_test, 256)
    
    model.fit_generator(generator=train_data_gen,
                        validation_data=test_data_gen,
                        epochs=5,
                        shuffle=False,
                        verbose=1)
    
    loss = model.evaluate_generator(generator=test_data_gen)
    print('Test Loss: %0.5f' % loss)
    
    

    这个运行没有任何问题。

    【讨论】:

    • 但它会给出与素数形状相同的不兼容错误。
    • 例如?即使形状是 7 并且批量大小是 32 也应该没有问题?
    • 假设我的训练样本的长度是 1590 并且我的批量大小是 32(我知道批量 30 会很好,但是为了当前的例子让我们保持这样)然后最后一批的大小为22。这22个样本除了丢弃之外如何处理?
    • 你不需要做任何事情,1590/32 是 49.6875,所以你可以使用 50 步,像 x[1568:1600] 这样的东西即使 x 只有 1590 个元素也不会出错。跨度>
    • 它在我的机器上运行没有问题,只需将输入形状更改为固定数字,因为密集层需要它是特定的。我更新了我在主要答案中使用的代码。
    【解决方案2】:

    除了其他答案中的策略,可以根据您的范围(意图)以不同的方式解决此类问题。

    如果您希望按照问题中的建议在最后一批中重复一些样本(直到最后一批的大小等于 batch_size),您可以(例如)检查数据集中的最后一个样本是否为达到,如果是,做点什么。例如:

    N_batches = int(np.ceil(len(dataset) / batch_size))
    batch_size = 32
    batch_counter = 0
    while True:
      current_batch = []
      idx_start = batch_size * batches_counter
      idx_end = batch_size * (batches_counter + 1)
      for idx in range(idx_start, idx_end):
          ## Next line sets idx to the index of the last sample in the dataset:
          idx = len(dataset)-1 if (idx > len(data_set)-1) else idx
          current_batch.append(dataset[idx])
          .
          .
          .
      batch_counter += 1
      if (batch_counter == N_batches):
         batch_counter = 0
    

    显然,它不需要是最后一个样本,它可以(例如)是数据集中的随机样本:

    idx = random.randint(0,len(dataset) if (idx > len(data_set)-1) else idx
    

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2019-10-07
      • 1970-01-01
      • 2016-12-20
      • 2019-01-05
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      • 2018-07-01
      相关资源
      最近更新 更多