h5py:使用分块存储将大数据加载到HDF5

问题描述 投票:0回答:2

我有 3072 个大小为 1024x1024 的矩阵,所以我的数据集看起来像 1024x1024x3072。这些数据总计 24 GB,这使得无法加载到内存中,因此我希望使用 HDF5 的分块存储方法来通过可加载到内存(128x128x3072)的块进行操作,以便我可以对它们进行操作。问题是,我的代码似乎效率极低,需要超过 12 个小时才能创建一段数据 (1024x1024x300) 的 HDF5 文件。 这是我到目前为止编写的代码

with h5py.File("FFT_Heights.h5", "w") as f: 
   dset = f.create_dataset( "chunked", (1024, 1024, 300), chunks=(128, 128, 300), dtype='complex128'
   ) 
   for ii in tqdm(range(300)):
       dset[ii] = np.load(f'K field {ii}.npy').astype('complex128')

正如您在我的示例代码中看到的,我只从 3072 个矩阵中取出了 300 个,这是因为我试图在运行整个数据之前确保代码适用于较小的数据集。另外,请记住,我的数据很复杂,并且在创建文件时不得损害虚部,因此我事先设置了 dtype。所以,最重要的是,问题在于写入速度。生成的 HDF5 文件构建正确,我已经检查过,但问题是我需要为 3072 个图像运行此代码,我想知道是否有办法使此文件创建更有效(我已经还尝试了不同的块大小,但在写入速度方面得到了相同的结果)。最后,我正在研究Python。预先感谢!

python file storage hdf5 h5py
2个回答
1
投票

您需要修改块大小。尺寸和形状错误。

  1. 首先,它太大了。建议的块大小范围为 10 KiB 到 1 MiB(数据集越大则越大)。根据我的计算,你的是 77 Mib。
  2. 更重要的是,它的形状不适合加载(和读取)图像数组。每个图像的尺寸为 1024x1024,并且您一次加载 1 个图像。对于块形状为 (128, 128, 300),您将必须写入 64 个块(8x8 块)。

我将块修改为

chunks=(1024, 1024, 1)
。这与 1 个图像的形状匹配,因此每次访问图像时都会写入或读取 1 个块。此外,它将块大小减少到 17 MiB。

我运行了加载 400 个 complex128 npy 文件的测试。它在具有 24 GB RAM 的(非常)旧的 Windows 工作站上运行只需 33 秒。注意:加载时间不是线性的。前 250 个文件加载速度很快(0.7 秒内加载 25 个 npy 文件)。文件 250-400 需要更长的时间(3.6 秒内 25 个 npy 文件)。

注意:我必须修改将 npy 文件加载到数据集的行上的数据集索引。我不确定你的索引是如何/为什么起作用的。也许广播对你有用。请参阅下面的代码:

cnt = 400
with h5py.File("FFT_Heights.h5", "w") as h5f: 
   dset = h5f.create_dataset("chunked", (1024, 1024, cnt), 
                             chunks=(1024, 1024, 1), dtype='complex128')     
   total = time.time()
   for ii in range(cnt):
       dset[:,:,ii] = np.load(f'K field {ii}.npy')    
       
print(f'Total elapsed time={time.time()-total:.2f}')       

0
投票

这是根据您关于需要读取最后一个轴上的 HDF5 数据的评论而更新的过程。正如我的评论中提到的,这造成了设置块形状的困境。对于将数据加载为图像切片和通过图像切片数据,最佳块形状是不同的。因此,“最佳”块形状必须平衡这两个过程。换句话说,如果修改块形状以提高读取性能,则会降低读取步骤中的加载性能(反之亦然)。然而,有一种方法可以通过对加载过程进行小修改来解决 I/O 瓶颈。

您的程序调用

np.load()
来读取每个 .npy 文件并将其直接加载到 HDF5 数据集中。相反,创建一个 NumPy 数组(大小适合第三块轴上的图像数量),读取足够的 .npy 文件来填充数组,最后将其添加到 HDF5 数据集。然后重复。 :-) 我运行了一些测试,这个过程加载和读取数据的速度比使用任一卡盘形状的原始过程更快。读取 300 个 .npy 文件的计时数据:

  1. 块的计时数据=(1024, 1024, 1) [我的第一个答案]

    • 加载数据文件所用时间 = 18.59 秒
    • 读取数据集切片所用时间 = 934.50 秒
  2. 块的计时数据=(256, 256, 15)

    • 加载数据文件所用时间 = 1533.21
    • 读取数据集切片所用时间 = 64.29
  3. 修改加载过程后的块的计时数据=(256,256,15)

    • 加载数据文件所用时间 = 20.73
    • 读取数据集切片所用时间 = 32.06

代码如下。
(注意,它以 15 个为一组读取 300 个 .npy 文件,因此最后一个循环与增量匹配。对于一般情况,需要进行一些调整 - 或者选择一个作为图像数量因素的增量,例如 16第3072章)。

c0, c1, c2 = 256, 256, 15
cnt = 300

with h5py.File("FFT_Heights_2.h5", "w") as h5f: 
    dset = h5f.create_dataset("chunked", (1024, 1024, cnt), 
                              chunks=(c0, c1, c2), dtype='complex128')     
    total = time.time()
    incr = time.time()
    arr_k = 0
    arr = np.empty(shape=(1024, 1024, c2),dtype='complex128')
    for ii in range(cnt):
        if (ii+1) % 25 == 0:
            print(f'working on array {ii}; elapsed time={time.time()-incr:.2f}')
            incr = time.time()
        if arr_k == c2:
            dset[:,:,ii:ii+c2] = arr
            arr_k = 0
            arr = np.empty(shape=(1024, 1024, c2),dtype='complex128')
        arr[:,:,arr_k] = np.load(f'K field {ii}.npy')
        arr_k += 1              
       
print(f'Elapsed time to load data files = {time.time()-total:.2f}')       

total = time.time()
with h5py.File("FFT_Heights_2.h5") as h5f: 
    dset = h5f["chunked"]
    for i in range(4):
        print(f'i={i}; ',end='')
        for j in range(4):
            print(f' j={j}... ',end='')
            cplx_arr = dset[256*i:256*(i+1),256*j:256*(j+1),:]
        print('')    
print(f'Elapsed time to read dataset slices = {time.time()-total:.2f}') 
© www.soinside.com 2019 - 2024. All rights reserved.