多次将 numpy.argpartition() 分配给列表元素时内存泄漏



我在理解代码中的内存泄漏时遇到问题。我想我的错误与 numpy 数组是可变的有关,因为它可以使用.copy()来解决。

不过,我不明白为什么会发生这种情况。下面是内存泄漏代码的最小示例,它使用大约 1600MB 的内存:

import numpy as np
import sys
k_neighbours = 5
np.random.seed(42)
data = np.random.rand(10000)
for _ in range(3):
closest_neighbours = [
# get indices of k closest neighbours
np.argpartition(
np.abs(data-point),
k_neighbours
)[:k_neighbours]
for point in data
]
print('nsize:',sys.getsizeof(closest_neighbours))
print('first 3 entries:',closest_neighbours[:3])

这是相同的代码,但增加了.copy().这似乎解决了问题,正如我所期望的那样,该程序的内存约为 80 MB。

for _ in range(3):
closest_neighbours = [
# get indices of k closest neighbours
np.argpartition(
np.abs(data-point),
k_neighbours
)[:k_neighbours].copy()
for point in data
]
print('nsize:',sys.getsizeof(closest_neighbours))
print('first 3 entries:',closest_neighbours[:3])

两者的最终结果相同:

size: 87624
first 3 entries: [
array([   0, 3612, 2390,  348, 3976]),
array([   1, 6326, 2638, 9978,  412]),
array([5823, 5866,    2, 1003, 9307])
]

不出所料。

我本以为np.argpartition()会创建一个对象,因此,我不明白为什么copy()解决了内存问题。即使不是这种情况,并且np.argpartition()以某种方式更改了data对象本身,为什么会导致内存泄漏?

您的问题可以归结为此示例:

import numpy as np
array = np.empty(10000)
view = array[:5]
copy = array[:5].copy()

在这里,view对象的内存使用量也将远高于copy对象的内存使用量。

解释

如 NumPy手册中所述,"NumPy 切片创建视图而不是副本"。因此,原始数组的底层内存"在从它派生的所有数组都被垃圾回收之前不会被释放"。

在对大型数组进行切片时,Numpy 文档还建议使用copy(): "从大阵列中提取一小部分时必须小心......在这种情况下,建议使用显式 copy((。

测量内存使用情况

sys.getsizeof两个示例中返回相同值的原因是,"只考虑直接归因于对象的内存消耗,而不考虑它引用的对象的内存消耗"。在您的示例中,您在列表对象上调用了sys.getsizeof,因此该对象返回列表的大小,并且不考虑其中 NumPy 数组的大小。

例如,sys.getsizeof([None for _ in data])还将返回87624

numpy 数组的内存使用情况

要获取data数组的大小,您可以使用data作为其参数调用sys.getsizeof

sys.getsizeof(data)

现在,要获取closest_neighbours列表中所有数组的大小,您可以尝试如下操作:

sum(sys.getsizeof(x) for x in closest_neighbours)

请注意,如果列表包含任何views,这将不起作用。正如 Python 文档中所述,sys.getsize"将返回正确的结果[对于内置对象],但这不一定适用于第三方扩展,因为它是特定于实现的。如果是 NumPy 视图,view.__sizeof__()将返回 96。

最新更新