Python内存错误:无法分配数组内存

13

我有一个大小为250MB的CSV文件,需要读取其中约7000行和9000列。每一行代表一张图片,每一列是一个像素(灰度值范围在0-255之间)。

我最初尝试了一个简单的np.loadtxt("data/training_nohead.csv",delimiter=",")但是这给我带来了一个内存错误。我认为这很奇怪,因为我正在运行64位Python,并安装了8GB的内存,但它只使用了大约512MB就崩溃了。

此后,我尝试了SEVERAL其他策略,包括:

  1. import fileinput并逐行读取,将它们附加到数组中
  2. 在读入整个文件后使用np.fromstring
  3. np.genfromtext
  4. 手动解析文件(由于所有数据都是整数,所以编码相当容易)

每种方法都给我带来了同样的结果。512MB左右的内存错误。想知道是否有关于512MB的特殊情况,我创建了一个简单的测试程序,直到Python崩溃前填满内存:

str = " " * 511000000 # Start at 511 MB
while 1:
    str = str + " " * 1000 # Add 1 KB at a time

执行这个操作直到1GB左右时才会崩溃。我只是为了好玩,尝试了:str = " " * 2048000000(填充2GB) - 这个操作没有任何问题。内存被填满后也没有抱怨。所以问题不在于我可以分配多少总内存,而是似乎与我可以分配多少次内存有关...

我在谷歌上搜索了很久,直到找到了这篇帖子:Python out of memory on large CSV file (numpy)

我完全复制了答案中的代码:

def iter_loadtxt(filename, delimiter=',', skiprows=0, dtype=float):
    def iter_func():
        with open(filename, 'r') as infile:
            for _ in range(skiprows):
                next(infile)
            for line in infile:
                line = line.rstrip().split(delimiter)
                for item in line:
                    yield dtype(item)
        iter_loadtxt.rowlength = len(line)

    data = np.fromiter(iter_func(), dtype=dtype)
    data = data.reshape((-1, iter_loadtxt.rowlength))
    return data

这次调用 iter_loadtxt("data/training_nohead.csv") 会出现稍微不同的错误:

MemoryError: cannot allocate array memory

搜索此错误时,我只找到了一个不太有用的帖子:在创建布尔NumPy数组(Python)时出现内存错误(MemoryError)

由于我正在运行Python 2.7,所以这不是我的问题。如果有任何帮助,将不胜感激。


3
你尝试过分两步来做吗?第一步:计算数组的维度 nxm 和数据类型。第二步:将数据放入预先分配好的数组中(指定 dtype,对于 np.fromiter() 来说,指定 count 可能就足够了)。 - jfs
我已经知道数组的维度(7049 x 9146),所以我会尝试这个。编辑 - 是9246,而不是9146。虽然这并不重要。 - stevendesu
成功了!请将其发布为答案,以便我可以接受它。额外加分:它只用了大约8秒钟!我感到非常惊讶。 - stevendesu
4
您可以发布自己的答案。您已经做了所有的工作,请添加一个小的代码示例,以避免MemoryError。 - jfs
2个回答

5

在@J.F. Sebastian的帮助下,我得出了以下答案:

train = np.empty([7049,9246])
row = 0
for line in open("data/training_nohead.csv")
    train[row] = np.fromstring(line, sep=",")
    row += 1

当然,这个回答假设你已经知道行和列的数量。如果您事先没有这些信息,则计算行数将需要一些时间,因为您必须读取整个文件并计算\n字符的数量。以下内容可以满足要求:
num_rows = 0
for line in open("data/training_nohead.csv")
    num_rows += 1

如果每行都有相同数量的列,则可以只计算第一行,否则需要跟踪最大值。

num_rows = 0
max_cols = 0
for line in open("data/training_nohead.csv")
    num_rows += 1
    tmp = line.split(",")
    if len(tmp) > max_cols:
        max_cols = len(tmp)

这种解决方案最适用于数值数据,因为包含逗号的字符串可能会使事情变得非常复杂。


2
请注意:您可以在此处使用内置函数 for i, line in enumerate(file)ncols = max(ncols, len(line.split(',')))。通常情况下(不是在这种情况下),CSV 行可能跨越多个物理行,即枚举 CSV 行的正确方法是:for i, row in enumerate(csv.reader(file)) - jfs

0

这是一个旧的讨论,但可能会对现在的人有所帮助。

我认为我知道为什么str = str + " " * 1000str = " " * 2048000000更慢。

运行第一个时,我相信操作系统需要在内存中分配新对象,即str + " " * 1000,只有在将名称str引用到它之后才能进行。在将名称'str'引用到新对象之前,它无法摆脱第一个对象。 这意味着操作系统需要同时分配大约两次“str”对象,使其仅能够处理1 GB而不是2 GB。 我相信使用下一个代码将从您的操作系统中获得与单个分配相同的最大内存:

str = " " * 511000000
while(1):
    l = len(str)
    str = " "
    str = " " * (len + 1000)

如果我说错了,请随意纠正我。


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