Python列表的内存泄漏问题

4

身份列表包含大约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])

内存使用率将越来越高,某一点系统会完全挂起。

我还根据下面的答案实现了以下代码修改。

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])

内存截图。

输入图像描述


所以我猜现在与这些行无关,也许是关于你代码的其他部分。 - DRPK
Python有垃圾回收机制,但有时它无法清除已完成的任务、变量等,需要手动清理这些内容。请查看此问题并通知我:https://dev59.com/BXM_5IYBdhLWcg3wlEPO - DRPK
实际上,为了衡量算法性能,我需要比较所有的负对和正对。 - Khawar Islam
样本列表是不同的身份吗?您能否稍微澄清一下? - DhakkanCoder
不同。实际上,它们都是图片。 - Khawar Islam
显示剩余3条评论
3个回答

5
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]])

这个想法是实时写入你的行(数据)

查看此链接也可以:如何在Python中将实时数据写入CSV文件

感谢@9mat@cybot以及以下问题:如何使用生成器在Python中获得笛卡尔积?如何在Python中将实时数据写入CSV文件


有人告诉过我这个问题,但从未给出完整的答案:“原因是这种方式只会在内存中一次实例化一个交叉样本,可以大大节省RAM。” - Khawar Islam

0

实际上,生成的配对数据会保存在您的内存中,这就是为什么您的内存会越来越高。

您需要更改代码,以便在生成配对数据后立即将其释放出内存。

先前的代码:

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)

0
你可以创建一个类来表示多个列表的乘积,它的行为类似于一个列表,但不存储任何组合。这样只有在需要时才会“组合”项目。
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接