【问题标题】:How to slice memmap efficiently?如何有效地对 memmap 进行切片?
【发布时间】:2013-09-04 13:16:23
【问题描述】:

目前我正在研究一个非常庞大的数据集,几乎不适合我的记忆,所以我使用np.memmap。但在某些时候,我必须将我的数据集拆分为训练和测试。 当我想使用一些索引数组对np.memmap 进行切片时,我发现了这种情况: (您可以在下面找到代码和内存分配)

Line #    Mem usage    Increment   Line Contents
================================================
 7    29.340 MB     0.000 MB   def my_func2():
 8    29.340 MB     0.000 MB       ARR_SIZE = (1221508/4,430)
 9    29.379 MB     0.039 MB       big_mmap = np.memmap('big_mem_test.mmap',shape=ARR_SIZE, dtype=np.float64, mode='r')    
10    38.836 MB     9.457 MB       idx = range(ARR_SIZE[0])
11  2042.605 MB  2003.770 MB       sub = big_mmap[idx,:]
12  3046.766 MB  1004.160 MB       sub2 = big_mmap[idx,:]
13  3046.766 MB     0.000 MB       return  type(sub)

但如果我喜欢连续切片,我会使用以下代码:

Line #    Mem usage    Increment   Line Contents
================================================
15    29.336 MB     0.000 MB   def my_func3():
16    29.336 MB     0.000 MB       ARR_SIZE = (1221508/4,430)
17    29.375 MB     0.039 MB       big_mmap = np.memmap('big_mem_test.mmap',shape=ARR_SIZE, dtype=np.float64, mode='r')    
18    29.457 MB     0.082 MB       sub = big_mmap[0:1221508/4,:]
19    29.457 MB     0.000 MB       sub2 = big_mmap[0:1221508/4,:]  

请注意,在第 18,19 行的第二个示例中,没有内存分配,整个操作要快得多。

在第 11 行的第一个示例中,有一个位置,因此在切片期间会读取整个 big_mmap 矩阵。但更令人惊讶的是第 12 行还有另一个位置。做更多这样的操作很容易耗尽内存。

当我拆分数据集时,索引相当随机且不连续,因此我无法使用big_mmap[start:end,:] 表示法。

我的问题是:

有没有其他方法可以让我在不将整个数据读取到内存的情况下对 memmap 进行切片?

为什么在使用索引切片时将整个矩阵读入内存(示例一)?

为什么要再次读取和分配数据(第一个示例第 12 行)?

【问题讨论】:

  • 我想建议通过 h5py 库而不是 memmap 使用 hdf5 文件。它们更灵活(也更强大)并且非常易于使用。
  • 是的,我正在考虑它,实际上我正在考虑在底部使用 hdf5 的PyTables。但我仍然对这种 memmap 行为感兴趣。
  • 我知道这是最简单的出路,但您是否考虑购买更多内存? (这避免了问题而不是解决问题,但是稍后当您的数据增长更多时,您将再次遇到同样的问题)
  • @usethedeathstar 实际上是相反的。如果我购买更多内存,我将再次面临更大数据集的问题。但是如果我用一种磁盘分页来解决它。我将能够处理数据,购买 ram 只会加快整个过程。

标签: python numpy


【解决方案1】:

您在第一个示例中看到的双重分配不是由于 memmap 行为;相反,这是由于 __getitem__ 是如何为 numpy 的 ndarray 类实现的。当使用列表(如第一个示例中)索引 ndarray 时,将从源数组中复制数据。当使用切片对象对其进行索引时,会在源数组中创建一个视图(不复制任何数据)。例如:

In [2]: x = np.arange(16).reshape((4,4))

In [3]: x
Out[3]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

In [4]: y = x[[0, 2], :]

In [5]: y[:, :] = 100

In [6]: x
Out[6]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

y 是来自x 的数据副本,因此更改yx 没有影响。现在通过切片索引数组:

In [7]: z = x[::2, :]

In [8]: z[:, :] = 100

In [9]: x
Out[9]: 
array([[100, 100, 100, 100],
       [  4,   5,   6,   7],
       [100, 100, 100, 100],
       [ 12,  13,  14,  15]])

关于您的第一个问题,我不知道有一种方法可以让您创建包含整个数组的 任意 切片,而无需将整个数组读入内存。您可能会考虑两个选项(除了您已经讨论过的 HDF5/PyTables 之类的东西):

  1. 如果您按顺序访问训练和测试集的元素(而不是将它们作为两个完整数组操作),您可以轻松编写一个小型包装类,其 __getitem__ 方法使用您的索引数组来自 memmap 的适当样本(即 training[i] 返回 big_mmap[training_ids[i]])

  2. 将您的数组拆分为两个单独的文件,其中包含专门的训练或测试值。然后你可以使用两个独立的 memmap 对象。

【讨论】:

  • 其实我没有提到它,但我使用解决方案没有。 2 - 将测试和训练集分成两个memmaps。但实际上我做了不止一次拆分,因此在实际处理开始之前拆分数据集需要一些时间。
猜你喜欢
  • 2021-03-14
  • 2018-08-13
  • 1970-01-01
  • 2018-06-25
  • 1970-01-01
  • 2020-05-22
  • 1970-01-01
  • 2018-05-30
  • 1970-01-01
相关资源
最近更新 更多