【问题标题】:Memory error when using Keras ImageDataGenerator使用 Keras ImageDataGenerator 时出现内存错误
【发布时间】:2018-09-02 16:13:43
【问题描述】:

我正在尝试使用带有 TensorFlow 后端的 keras 来预测图像中的特征。具体来说,我正在尝试使用 keras ImageDataGenerator。该模型设置为运行 4 个 epoch,并且运行良好,直到第 4 个 epoch 失败并出现 MemoryError。

我在运行 Ubuntu Server 16.04 LTS (HVM)、SSD 卷类型的 AWS g2.2xlarge 实例上运行此模型。

训练图像是 256x256 RGB 像素瓦片(8 位无符号),训练掩码是 256x256 单波段(8 位无符号)瓦片数据,其中 255 == 感兴趣的特征,0 == 其他所有特征。

以下 3 个函数与此错误有关。

如何解决这个 MemoryError?


def train_model():
        batch_size = 1
        training_imgs = np.lib.format.open_memmap(filename=os.path.join(data_path, 'data.npy'),mode='r+')
        training_masks = np.lib.format.open_memmap(filename=os.path.join(data_path, 'mask.npy'),mode='r+')
        dl_model = create_model()
        print(dl_model.summary())
        model_checkpoint = ModelCheckpoint(os.path.join(data_path,'mod_weight.hdf5'), monitor='loss',verbose=1, save_best_only=True)
        dl_model.fit_generator(generator(training_imgs, training_masks, batch_size), steps_per_epoch=(len(training_imgs)/batch_size), epochs=4,verbose=1,callbacks=[model_checkpoint])

def generator(train_imgs, train_masks=None, batch_size=None):

# Create empty arrays to contain batch of features and labels#

        if train_masks is not None:
                train_imgs_batch = np.zeros((batch_size,y_to_res,x_to_res,bands))
                train_masks_batch = np.zeros((batch_size,y_to_res,x_to_res,1))

                while True:
                        for i in range(batch_size):
                                # choose random index in features
                                index= random.choice(range(len(train_imgs)))
                                train_imgs_batch[i] = train_imgs[index]
                                train_masks_batch[i] = train_masks[index]
                        yield train_imgs_batch, train_masks_batch
        else:
                rec_imgs_batch = np.zeros((batch_size,y_to_res,x_to_res,bands))
                while True:
                        for i in range(batch_size):
                                # choose random index in features
                                index= random.choice(range(len(train_imgs)))
                                rec_imgs_batch[i] = train_imgs[index]
                        yield rec_imgs_batch

def train_generator(train_images,train_masks,batch_size):
        data_gen_args=dict(rotation_range=90.,horizontal_flip=True,vertical_flip=True,rescale=1./255)
        image_datagen = ImageDataGenerator()
        mask_datagen = ImageDataGenerator()
# # Provide the same seed and keyword arguments to the fit and flow methods
        seed = 1
        image_datagen.fit(train_images, augment=True, seed=seed)
        mask_datagen.fit(train_masks, augment=True, seed=seed)
        image_generator = image_datagen.flow(train_images,batch_size=batch_size)
        mask_generator = mask_datagen.flow(train_masks,batch_size=batch_size)
        return zip(image_generator, mask_generator)

以下是模型的输出,详细说明了时期和错误消息:

Epoch 00001: loss improved from inf to 0.01683, saving model to /home/ubuntu/deep_learn/client_data/mod_weight.hdf5
Epoch 2/4
7569/7569 [==============================] - 3394s 448ms/step - loss: 0.0049 - binary_crossentropy: 0.0027 - jaccard_coef_int: 0.9983  

Epoch 00002: loss improved from 0.01683 to 0.00492, saving model to /home/ubuntu/deep_learn/client_data/mod_weight.hdf5
Epoch 3/4
7569/7569 [==============================] - 3394s 448ms/step - loss: 0.0049 - binary_crossentropy: 0.0026 - jaccard_coef_int: 0.9982  

Epoch 00003: loss improved from 0.00492 to 0.00488, saving model to /home/ubuntu/deep_learn/client_data/mod_weight.hdf5
Epoch 4/4
7569/7569 [==============================] - 3394s 448ms/step - loss: 0.0074 - binary_crossentropy: 0.0042 - jaccard_coef_int: 0.9975  

Epoch 00004: loss did not improve
Traceback (most recent call last):
  File "image_rec.py", line 291, in <module>
    train_model()
  File "image_rec.py", line 208, in train_model
    dl_model.fit_generator(train_generator(training_imgs,training_masks,batch_size),steps_per_epoch=1,epochs=1,workers=1)
  File "image_rec.py", line 274, in train_generator
    image_datagen.fit(train_images, augment=True, seed=seed)
  File "/home/ubuntu/pyvirt_test/local/lib/python2.7/site-packages/keras/preprocessing/image.py", line 753, in fit
    x = np.copy(x)
  File "/home/ubuntu/pyvirt_test/local/lib/python2.7/site-packages/numpy/lib/function_base.py", line 1505, in copy
    return array(a, order=order, copy=True)
MemoryError

【问题讨论】:

  • 你能偶然清理你的代码吗?您有部分代码被注释掉,并且错误似乎引用了不在您包含的 sn-p 中的代码(我在您的代码中的任何地方都没有看到对 train_generator 的调用,但这是错误位置)。使用您提供的内容可能很难进行调试。
  • 即使您更改随机种子,它是否总是在第 4 个 epoch 发生?我看到你的批量大小是 1,所以每次它只适合 1 张图像进行训练,你的一张图像尺寸非常大并且 np.copy() 会导致内存错误吗?
  • @BinhNguyenLe 它仍然有一个内存错误,纪元较少。我还没有尝试更改随机种子。
  • 你考虑过更新 tensorflow 和 keras 吗?

标签: python tensorflow deep-learning keras


【解决方案1】:

看来您的问题是由于数据太大。我可以看到两种解决方案。第一个是通过spark在分布式系统中运行你的代码,我猜你没有这个支持,所以让我们继续另一个。

第二个是我认为可行的。我会对数据进行切片,然后尝试逐步提供模型。我们可以用 Dask 做到这一点。该库可以对数据进行切片并保存在对象中,然后您可以从磁盘中检索读取的数据,仅在您想要的部分中。

如果您有一个大小为 100x100 矩阵的图像,我们可以检索每个数组,而无需将 100 个数组加载到内存中。我们可以在内存中逐个数组加载(释放前一个),这将是您神经网络中的输入。

为此,您可以将 np.array 转换为 dask 数组并分配分区。例如:

>>> k = np.random.randn(10,10) # Matrix 10x10
>>> import dask.array as da
>>> k2 = da.from_array(k,chunks = 3)
dask.array<array, shape=(10, 10), dtype=float64, chunksize=(3, 3)>
>>> k2.to_delayed()
array([[Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 0, 0)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 0, 1)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 0, 2)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 0, 3))],
   [Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 1, 0)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 1, 1)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 1, 2)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 1, 3))],
   [Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 2, 0)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 2, 1)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 2, 2)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 2, 3))],
   [Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 3, 0)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 3, 1)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 3, 2)),
    Delayed(('array-a08c1d25b900d497cdcd233a7c5aa108', 3, 3))]],
  dtype=object)

在这里,您可以看到数据是如何保存在对象中的,然后您可以分段检索以供模型使用。

要实施此解决方案,您必须在函数中引入一个循环,该循环调用每个分区并提供给 NN 以获得增量训练。

有关详细信息,请参阅 Dask 文档

【讨论】:

  • @Borealis 试试看!这种方法对于分割部分或训练大量数据非常有用
  • 我喜欢这个想法,但如何部分检索它们。我正在阅读文档,但得到的疑问比任何事情都多。
  • @MarlonTeixeira 然后,你可以设置一个迭代器(在你的函数中使用yield而不是return)。获取chunk的方法是:da.from_delayed("你想获取的chunk", shape...),然后你要在内存中进行.compute()或者persist()来获取。
  • 我可以运行您的想法,但不幸的是,我仍然遇到内存错误。我已经尝试了很多东西......也许我必须拆分文件,我认为这是失败......
  • @MarlonTeixeira 您可以尝试将块做得更小。您可以从块大小较小的 dask-dataframe 或应用 ddf.repartition(npartitions=...)
【解决方案2】:

您提供了相当混乱的代码(在我看来),即。看不到对train_generator 的调用。我不确定这是由于大数据导致内存不足的问题,因为您为此使用了 memmap,但现在假设它是。

  • 如果数据非常大,并且无论如何您都是从目录加载图像,那么可能值得考虑使用ImageDataGeneratorflow_from_directory 方法。 不过,这可能需要对设计稍作改动,这可能不是您想要的。

您可以通过以下方式加载它:

train_datagen = ImageDataGenerator()
train_generator = train_datagen.flow_from_directory(
        'data/train',
        target_size=(256, 256),
        batch_size=batch_size,
        ...  # other configurations)

更多关于 in the Keras documentation.

  • 另请注意,如果您有 32 位,memmap 不允许超过 2GB。

  • 你会使用tensorflow-gpu吗?可能你的gpu不够用,你可以试试tensorflow这个包。

我强烈建议尝试一些memory profiling 看看哪里会发生更大的内存分配。


如果不是内存不足的情况,可能是模型中的数据处理错误,因为你的损失函数根本没有改善,例如可能是接线错误。


最后,这里的最后一个注释.. 将训练数据的 memmap 加载为read-only 是一个很好的做法,因为您不想不小心弄乱数据。

更新:我可以看到您已经更新了帖子并提供了 train_generator 方法的代码,但在您的调用中仍然没有调用该方法。

如果我假设您在调用中有拼写错误 - train_generator 而不是您的 d1_model.fit_generator 方法中的 generator 方法,则 fit_generator 方法可能无法处理一批数据,但实际上在整个training_imgs 上,它会在np.copy(x) 调用中复制整个集合。

另外,如前所述,在使用fitfit_generator 方法时,确实存在一些与Keras 内存泄漏有关的问题(您可以找到其中一些,fe.here is an open one)。

【讨论】:

    【解决方案3】:

    如果浮点精度太高,这在运行 32 位时很常见。你是运行32位的吗?您也可以考虑对数组进行强制转换或舍入。

    【讨论】:

    • 我正在运行 8 位无符号图像和训练数据。
    • 您是否检查过数据以确保其保持 8 位精度?我最近遇到了这个问题,数据有时会比指定的精度高得多。更新权重后,我必须明确地对数组进行舍入。
    【解决方案4】:

    一般来说,Keras/Tensorflow 对资源的使用非常好,但是过去有一个已知的内存泄漏导致了问题。为确保这不是导致您出现问题的原因,请尝试将这两行代码添加到您的训练脚本中:

    # load the backend
    from keras import backend as K
    
    # prevent Tensorflow memory leakage
    K.clear_session()
    

    【讨论】:

      【解决方案5】:

      我最近遇到了同样的问题。不知何故,FCN-8 代码可以在我的 tensorflow1.2+keras2.0.9+8G RAM +1060 计算机上成功运行,但是在我的 tf1.4+keras2.1.5+16g ram +1080ti 计算机上使用 modelcheckpoint 时发生内存错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-09-09
        • 2022-06-15
        • 2018-02-06
        • 2020-12-28
        • 1970-01-01
        • 2020-05-28
        • 1970-01-01
        相关资源
        最近更新 更多