代码之家  ›  专栏  ›  技术社区  ›  Basj

在磁盘上保存的numpy数组中随机访问

  •  0
  • Basj  · 技术社区  · 3 年前

    我有一个很大的numpy数组 A 的形状 (2_000_000, 2000) 属于 dtype float64 ,需要32 GB。

    (或者,将相同的数据拆分为10个形状的阵列(200_0002000),序列化可能更容易?)。

    我们如何将其序列化到磁盘,以便对数据的任何部分进行快速随机读取访问?

    更准确地说,我需要能够阅读来自 A. 随机起始索引 i :

    L = []
    for i in range(10_000):
        i = random.randint(0, 2_000_000 - 16):
        window = A[i:i+16, :]         # window of A of shape (16, 2000) starting at a random index i
        L.append(window)
    WINS = np.concatenate(L)   # shape (10_000, 16, 2000) of float64, ie: ~ 2.4 GB
    

    假设我只有8GB的RAM可用于此任务;完全不可能加载整个32 GB的 A. 在RAM中。

    我们如何在磁盘上序列化的numpy数组中读取这样的窗口? (.h5格式或任何其他格式)

    注:阅读是在随机起始索引处完成的,这一事实很重要。

    0 回复  |  直到 3 年前
        1
  •  1
  •   kcw78    3 年前

    此示例显示如何将HDF5文件用于所描述的过程。

    首先,创建一个HDF5文件,其数据集为 shape(2_000_000, 2000) dtype=float64 价值观我在尺寸上使用了变量,所以你可以修改它。

    import numpy as np
    import h5py
    import random
    
    h5_a0, h5_a1 = 2_000_000, 2_000
    
    with h5py.File('SO_68206763.h5','w') as h5f:
        dset = h5f.create_dataset('test',shape=(h5_a0, h5_a1))
        
        incr = 1_000
        a0 = h5_a0//incr
        for i in range(incr):
            arr = np.random.random(a0*h5_a1).reshape(a0,h5_a1)
            dset[i*a0:i*a0+a0, :] = arr       
        print(dset[-1,0:10])  # quick dataset check of values in last row
    

    接下来,以读取模式打开文件,读取10_000个形状的随机阵列切片 (16,2_000) 并附加到列表中 L 。最后,将列表转换为数组 WINS 。注意,默认情况下,数组将有2个轴——您需要使用 .reshape() 如果您希望每个注释有3个轴(还显示了整形)。

    with h5py.File('SO_68206763.h5','r') as h5f:
        dset = h5f['test']
        L = []
        ds0, ds1 = dset.shape[0], dset.shape[1]
        for i in range(10_000):
            ir = random.randint(0, ds0 - 16)
            window = dset[ir:ir+16, :]  # window from dset of shape (16, 2000) starting at a random index i
            L.append(window)
        WINS = np.concatenate(L)   # shape (160_000, 2_000) of float64,
        print(WINS.shape, WINS.dtype)
        WINS = np.concatenate(L).reshape(10_0000,16,ds1)   # reshaped to (10_000, 16, 2_000) of float64
        print(WINS.shape, WINS.dtype)
    

    上面的过程没有内存效率。您最终得到两个随机切片数据的副本:在列表L和数组WINS中。如果内存有限,这可能是个问题。要避免中间副本,请直接将随机幻灯片中的数据读取到数组中。这样做简化了代码,减少了内存占用。该方法如下所示(WINS2是2轴数组,WINS3是3轴数组)。

    with h5py.File('SO_68206763.h5','r') as h5f:
        dset = h5f['test']
        ds0, ds1 = dset.shape[0], dset.shape[1]
        WINS2 = np.empty((10_000*16,ds1))
        WINS3 = np.empty((10_000,16,ds1))
        for i in range(10_000):
            ir = random.randint(0, ds0 - 16)
            WINS2[i*16:(i+1)*16,:] = dset[ir:ir+16, :]
            WINS3[i,:,:] = dset[ir:ir+16, :]
    
        2
  •  1
  •   Basj    3 年前

    的替代解决方案 h5py 我尝试过并且有效的数据集是使用 memmap ,正如@RyanPepper的评论中所建议的那样。

    将数据写入二进制

    import numpy as np
    with open('a.bin', 'wb') as A:
        for f in range(1000):
            x =  np.random.randn(10*2000).astype('float32').reshape(10, 2000)
            A.write(x.tobytes())
            A.flush()
    

    稍后打开为 memmap

    A = np.memmap('a.bin', dtype='float32', mode='r').reshape((-1, 2000))
    print(A.shape)  # (10000, 2000)
    print(A[1234:1234+16, :])  # window