python列表中的内存泄漏问题

Posted

技术标签:

【中文标题】python列表中的内存泄漏问题【英文标题】:Memory leakage issue in python list 【发布时间】:2021-01-23 04:40:01 【问题描述】:

身份列表包含大约 57000 张图像的大数组。现在,我在itertools.product() 的帮助下创建了一个负面清单。这会将整个列表存储在内存中,这非常昂贵,并且我的系统在 4 分钟后挂起。

如何优化以下代码并避免节省内存?`

for i in range(0, len(idendities) - 1):
    for j in range(i + 1, len(idendities)):
        cross_product = itertools.product(samples_list[i], samples_list[j])
        cross_product = list(cross_product)

        for cross_sample in cross_product:
            negative = []
            negative.append(cross_sample[0])
            negative.append(cross_sample[1])
            negatives.append(negative)
            print(len(negatives))

negatives = pd.DataFrame(negatives, columns=["file_x", "file_y"])
negatives["decision"] = "No"

negatives = negatives.sample(positives.shape[0])

内存9.30会越来越高,有一点系统已经完全挂掉了。

我也实现了下面的答案,并根据他的答案修改了代码。

for i in range(0, len(idendities) - 1):
    for j in range(i + 1, len(idendities)):
        for cross_sample in itertools.product(samples_list[i], samples_list[j]):
            negative = [cross_sample[0], cross_sample[1]]
            negatives.append(negative)
            print(len(negatives))

negatives = pd.DataFrame(negatives, columns=["file_x", "file_y"])
negatives["decision"] = "No"

第三版代码

即使您打开一个文件,这个 CSV 文件也太大,然后它会提示您的程序无法加载所有文件。关于这个过程,十分钟,然后系统将完全挂起。

for i in range(0, len(idendities) - 1):
    for j in range(i + 1, len(idendities)):
        for cross_sample in itertools.product(samples_list[i], samples_list[j]):
            with open('/home/khawar/deepface/tests/results.csv', 'a+') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow([cross_sample[0], cross_sample[1]])
            negative = [cross_sample[0], cross_sample[1]]
            negatives.append(negative)

negatives = pd.DataFrame(negatives, columns=["file_x", "file_y"])
negatives["decision"] = "No"

negatives = negatives.sample(positives.shape[0])

内存截图。

【问题讨论】:

是的;您不需要一次完整的样品清单吗?你需要 ?正如我告诉你的那样,即使你想在上面做一些机器学习方法,你也可以逐行阅读,你不应该一次性加载整个训练数据;您需要对数据进行切片 所以我想现在与这些行无关;也许它与您代码的其他部分有关 但我正在考虑一件可能的事情 python 有抓取收集 somtimes 它并没有清除他完成的任务,变量和......你需要手动清理这些东西检查这个问题并通知我:***.com/questions/1316767/… 其实,为了衡量算法的性能,我需要比较所有的负对和正对。 【参考方案1】:

itertools 中的 product 是 generator,因此它自然不会将整个列表存储在内存中,而是在下一行 cross_product = list(cross_product) 中转换它列出将整个数据存储在内存中的对象。

生成器的想法是您不会像调用list(itertools.product(samples_list[i], samples_list[j])) 那样同时进行所有计算。所以你要做的就是一一生成结果:

试试这样的:

for i in range(len(idendities) - 1):
    for j in range(i + 1, len(idendities)):
        for cross_sample in itertools.product(samples_list[i], samples_list[j]):
            # do something ...

所以我想我发现了你的问题;您首先将所有样本附加到否定列表,因为您的内存会越来越高,您需要实时写入每一行,一次一行;

您的数据是 csv 对吗?所以你可以这样做:

import csv
for i in range(0, len(idendities) - 1):
    for j in range(i + 1, len(idendities)):
        for cross_sample in itertools.product(samples_list[i], samples_list[j]):

            with open('results.csv', 'a+') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow([cross_sample[0], cross_sample[1]])

这个想法是实时写你的行

也请查看此链接how to write the real time data into csv file in python

感谢@9mat、@cybot 以及这些问题How to get Cartesian product in Python using a generator?、how to write the real time data into csv file in python

【讨论】:

有人告诉了这个但从来没有给我一个完整的答案“原因是这种方式一次只有一个来自 cross_product 迭代器的 cross_sample 在内存中实例化。在 RAM 上大量节省”【参考方案2】:

您可以创建一个类来表示多个列表的乘积,其行为类似于列表,但不存储任何组合。这只会按需“组合”项目。

class ProductList:    
    def __init__(self,*data):
        self.data = data
        self.size = 1
        for d in self.data: self.size *= len(d)

    def __len__(self): return self.size
    
    def __getitem__(self,index):
        if isinstance(index,slice):
            return [*map(self.__getitem__,range(len(self))[index])]
        result = tuple()
        for d in reversed(self.data):
            index,i = divmod(index,len(d))
            result = (d[i],) + result
        return result

    def __iter__(self):
        for i in range(len(self)): yield self[i]

    def __contains__(self,value):
        return len(value) == len(self.data) \
               and all(v in d for v,d in zip(value,self.data))
    
    def index(self,value):
        index = 0
        for v,d in zip(value,self.data):
            index = index*len(d)+d.index(v)
        return index

用法:

p = ProductList(range(1234),range(1234,5678),range(5678,9101))

print(*p[:10],sep="\n")

(0, 1234, 5678)
(0, 1234, 5679)
(0, 1234, 5680)
(0, 1234, 5681)
(0, 1234, 5682)
(0, 1234, 5683)
(0, 1234, 5684)
(0, 1234, 5685)
(0, 1234, 5686)
(0, 1234, 5687)


len(p) # 18771376008

p[27]  # (2, 6, 12)

for c in p[103350956:103350960]: print(c)

(6, 4763, 5995)
(6, 4763, 5996)
(6, 4763, 5997)
(6, 4763, 5998)


p.index((6, 4763, 5995)) # 103350956
p[103350956]             # (6, 4763, 5995)

(6, 4763, 5995) in p     # True
(5995, 4763, 6) in p     # False

【讨论】:

【参考方案3】:

实际上,生成的对保存在你的记忆中,这就是为什么你的记忆会越来越高。

您必须更改生成对的代码并立即从内存中释放它们。

以前的代码:

for i in range(0, len(idendities) - 1):
    for j in range(i + 1, len(idendities)):
        cross_product = itertools.product(samples_list[i], samples_list[j])
        cross_product = list(cross_product)

        for cross_sample in cross_product:
            negative = []
            negative.append(cross_sample[0])
            negative.append(cross_sample[1])
            negatives.append(negative)
            print(len(negatives))

negatives = pd.DataFrame(negatives, columns=["file_x", "file_y"])
negatives["decision"] = "No"

内存高效代码将配对保存在列表中,然后第二次无需再次生成。

samples_list = list(identities.values())
negatives = pd.DataFrame()

    if Path("positives_negatives.csv").exists():
        df = pd.read_csv("positives_negatives.csv")
    else:
        for combo in tqdm(itertools.combinations(identities.values(), 2), desc="Negatives"):
            for cross_sample in itertools.product(combo[0], combo[1]):
                negatives = negatives.append(pd.Series("file_x": cross_sample[0], "file_y": cross_sample[1]).T,
                                             ignore_index=True)
        negatives["decision"] = "No"
        negatives = negatives.sample(positives.shape[0])
        df = pd.concat([positives, negatives]).reset_index(drop=True)
        df.to_csv("positives_negatives.csv", index=False)

【讨论】:

以上是关于python列表中的内存泄漏问题的主要内容,如果未能解决你的问题,请参考以下文章

Python C 包装器内存泄漏

Python 2.7 列表理解泄漏变量名 [重复]

为Python中的列表保留内存?

避免实体框架查询返回的列表中的内存泄漏

C - 排序到列表和内存泄漏

使用动态内存从转发列表中泄漏内存