目前我正在处理相当大的数据集,这几乎不适合我的记忆,因此我使用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行)?
答案 0 :(得分:3)
您在第一个示例中看到的双重分配不是由于memmap行为;相反,这是由于numpy的ndarray类如何实现__getitem__
。当使用列表索引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
的数据副本,因此更改y
对x
没有影响。现在通过切片索引数组:
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之外):
如果您正在访问训练和训练的元素;顺序测试集(而不是将它们作为两个整个数组运行),您可以轻松编写一个小包装类,其__getitem__
方法使用您的索引数组从memmap中提取相应的样本(即,training [i]返回big_mmap [training_ids [I]])
将数组拆分为两个单独的文件,其中仅包含训练值或测试值。然后你可以使用两个单独的memmap对象。