如何从大型 .h5 数据集中批量读取数据,使用 ImageDataGenerator 和 model.fit 进行预处理,所有这些都不会耗尽内存?

Posted

技术标签:

【中文标题】如何从大型 .h5 数据集中批量读取数据,使用 ImageDataGenerator 和 model.fit 进行预处理,所有这些都不会耗尽内存?【英文标题】:How do I read data from large .h5 dataset in batches, preprocess with ImageDataGenerator & model.fit, all without running out of memory? 【发布时间】:2020-10-05 17:26:55 【问题描述】:

总结:尝试使用 TF/Keras 处理大型数据集时内存不足。我知道批处理是解决方案的主要组成部分......只是不明白如何实现。

问题:我如何从一个非常大的 .h5 数据集中批量读取数据,标准化/去除平均值,然后拆分数据,所有这些都不会耗尽内存?

背景:构建一个工作流来研究自然发生的地震信号的无监督深度嵌入聚类 (DEC)。这个问题特别是在数据的预处理中,即为训练/验证自动编码器/解码器准备数据。

数据:~6e6 个来自阵列的地震探测频谱图。 维度:(m,n,o) = (6e6, 66, 301) = (samples, freq_bins, time_bins)。 数据存储在一个数据集下的 .h5 文件中。 .h5 文件在磁​​盘上占用约 1 TB。

硬件:双 Intel Xeon E5-2683 v4 2.1 GHz,40MB 缓存,16 核,2 Titan GPU,528GB RAM

当前的预处理程序: 1. 通过选择 M 个随机索引、按升序排序并迭代地切片 .h5 数据集来组装一个由 M 个频谱图组成的 numpy 数组 X。 (此外:这里最快的方法是保存 .h5 数据集,其中包含为以后读取而优化的块,然后使用简单的“for”循环来访问数据。花式索引和“read_direct”需要更长的时间来遍历数据集。 ) 2. 从 X 中修剪不必要的数据(频率和时间 bin 值,以及最后的 46 个时间 bin 数据)并添加第 4 轴“p”作为“幅度 bin”。最终形状:(m,n,o,p) = (M,64,256,1)。 3. 去除均值并对数据进行标准化。 4. 将 X 拆分为训练/验证集。

# Define sample size:
M = int(1e6)
# Load spectrograms into X:
with h5py.File(train_dataname, 'r') as f:
    DataSpec = '/30sec/Spectrogram'
    dset = f[DataSpec]
    m, n, o = dset.shape
    index = sorted(np.random.choice(m, size=M, replace=False))
    X = np.empty([M, n, o])
    for i in range(M):
        X[i,:,:] = dset[index[i],:,:]

# Remove the frequency and time vectors from the data, trim time bins to len=256:
X = X[:,1:-1,1:256]

# Add amplitude dimension:
X = X[..., np.newaxis]
m, n, o, p = X.shape

# Remove mean & standardize data:
datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    samplewise_center=True,
    samplewise_std_normalization=True)
datagen.fit(X)
X = datagen.standardize(X)

# Split data into training/validation:
X_train, X_val = train_test_split(X, test_size=0.2, shuffle=True, random_state=812)

# Free up memory:
del X

详细问题: 当 M ~ 1e6 时,X 占用大约 30% 的 RAM(总 RAM 为 528GB)。运行上面的代码会产生下面的内存错误。考虑到该操作正在复制整个数组,内存不足也就不足为奇了...

---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-10-fb00ad200706> in <module>
----> 1 datagen.fit(X)

~/Anaconda/anaconda3/envs/AEC-DEC/lib/python3.6/site-packages/keras_preprocessing/image/image_data_generator.py in fit(self, x, augment, rounds, seed)
    943             np.random.seed(seed)
    944 
--> 945         x = np.copy(x)
    946         if augment:
    947             ax = np.zeros(

~/Anaconda/anaconda3/envs/AEC-DEC/lib/python3.6/site-packages/numpy/lib/function_base.py in copy(a, order)
    790 
    791     """
--> 792     return array(a, order=order, copy=True)
    793 
    794 # Basic operations

MemoryError:

我正在尝试做什么(需要你的帮助!): 我知道我的解决方案在于批处理,但我不确定如何实现它,以及如何将它与读取 .h5 的有效方法配对,而无需将 M 频谱图读入数组,然后进行批处理。我已经确定了 model.fit_generator 方法,现在似乎不赞成使用 model.fit;我已经阅读了 hdf5matrix 实用程序。 在一个问题中陈述:我如何从一个非常大的 .h5 数据集中批量读取数据,标准化/去除平均值,然后拆分数据,所有这些都不会耗尽内存?

尽管我花了很多时间试图弄清楚这一点,但我不清楚如何将所有部分放在一起,这就是为什么我正在寻找一些经过深思熟虑的指导来推动我朝着正确的方向前进。提前感谢您的帮助!

【问题讨论】:

你安装 h5.py 了吗? 是的; h5py、numpy 和所需的 keras/tf 模块已导入但未显示。 【参考方案1】:

您必须避免使X 的内存占用翻倍的进程。 (我知道,这是显而易见的)。这是一个 BIG 数组,使用 X = X[:,1:-1,1:256](可能使用 X = X[..., np.newaxis])所需的内存增加一倍。 他们的关键是以最终所需的大小/形状分配X(以避免复制)。然后修改你的逻辑,将数据从dsetf['/30sec/Spectrogram'])加载到一个中间数组(下面的ds_arr),根据需要修改,然后加载到X

我整理了一个替代程序。这可能不是计算效率最高的,但避免了X 的副本。

# Define sample size:
M = int(1e6)
# Load spectrograms into X:
with h5py.File(train_dataname, 'r') as f:
    DataSpec = '/30sec/Spectrogram'
    dset = f[DataSpec]
    m, n, o = dset.shape        
    index = sorted(np.random.choice(m, size=M, replace=False))

# new code:     
    X = np.empty([M, n-1, o-46, 1])
    for i in range(M):
        ds_arr=dset[index[i],1:,1:256]   
        ds_arr=ds_arr[..., np.newaxis]
        X[i,:,:,:] = ds_arr

# Remove mean & standardize data:

仔细检查我的切片符号。我不完全确定要从第二个索引中删除哪个值(第一个还是最后一个值?)。使用ds_arr=dset[index[i],1:-1,1:256] 时出现广播错误。错误信息是:

ValueError: could not broadcast input array from shape (63,255,1) into shape (64,255,1) 

【讨论】:

感谢您的回复,这有助于清理我的代码并减少内存占用,但遗憾的是还不够。您的代码运行良好 - 实际上我给出了错误的初始尺寸,n = 66,而不是 65。已更新原始帖子。 您能否按照我的代码中的尺寸成功分配X?如果是这样,您需要查看其他领域。如果不是,则需要减小M的大小。 是的,一旦我更正了尺寸,我就能让它工作。我最终决定减少Mo。我试图不妥协,因为接收到的信号类型有很多差异......但最后我没有看到我的 AEC 性能有明显的影响。再次感谢您的建议!

以上是关于如何从大型 .h5 数据集中批量读取数据,使用 ImageDataGenerator 和 model.fit 进行预处理,所有这些都不会耗尽内存?的主要内容,如果未能解决你的问题,请参考以下文章

根据标准从大型数据集中读取特定数据,以避免将整个文件读入内存

如何从 BigQuery 中存储的大型线串数据集中查找所有道路交叉口

从大型数据集中采样

对从大型数据集中聚合的数据使用 Altair

从大型数据集中提取最小/最大/平均数据的宏

如何从 Synthia 数据集中读取标签(注释)文件?