Python: numpy.corrcoef 内存错误

3 投票
1 回答
4327 浏览
提问于 2025-04-18 13:05

我在尝试计算从文本中读取的一大堆数据之间的相关性。可是当数据量特别大的时候,程序就会出现内存错误。有没有人能告诉我该怎么解决这个问题?谢谢!

下面是我的代码:

enter code here

import numpy
from numpy  import *
from array import *
from decimal import *
import sys

Threshold = 0.8;
TopMostData = 10;

FileName = sys.argv[1]

File = open(FileName,'r')

SignalData = numpy.empty((1, 128));
SignalData[:][:] = 0;

for line in File:

    TempLine = line.split();
    TempInt = [float(i) for i in TempLine]
    SignalData = vstack((SignalData,TempInt))

del TempLine;
del TempInt;

File.close();

TempData = SignalData;
SignalData = SignalData[1:,:]
SignalData = SignalData[:,65:128]

print "File Read | Data Stored" + " | Total Lines: " + str(len(SignalData))

CorrelationData = numpy.corrcoef(SignalData)

下面是出现的错误:

Traceback (most recent call last):
  File "Corelation.py", line 36, in <module>
    CorrelationData = numpy.corrcoef(SignalData)
  File "/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py", line 1824, in corrcoef
    return c/sqrt(multiply.outer(d, d))
MemoryError

1 个回答

8

根据评论的内容,你可能遇到了内存不足的问题。如果你使用的是32位的Python,这种情况会更糟,下面的方法也可能无法解决。不过,如果你使用的是64位的Python,并且内存不是特别紧张,我们可以采取一些措施,因为计算相关性可以分块进行,这样你只需要同时在内存中保留两行数据。

你可以把输入数据分成,比如说1000行一块,然后生成的1000 x 1000的矩阵就很容易放进内存里。接着,你可以把结果组合成一个大的输出矩阵,这个矩阵不一定要在内存中。我建议即使你有很多内存,也采用这种方法,因为这样更节省内存。计算相关系数并不需要快速随机访问,如果输入数据能放在内存里,速度提升也不会太大。

可惜的是,numpy.corrcoef并不会自动处理这些,所以我们需要自己来计算相关系数。幸运的是,这并没有听起来那么复杂。

可以参考以下思路:

import numpy as np

# number of rows in one chunk
SPLITROWS = 1000

# the big table, which is usually bigger
bigdata = numpy.random.random((27000, 128))

numrows = bigdata.shape[0]

# subtract means form the input data
bigdata -= np.mean(bigdata, axis=1)[:,None]

# normalize the data
bigdata /= np.sqrt(np.sum(bigdata*bigdata, axis=1))[:,None]

# reserve the resulting table onto HDD
res = np.memmap("/tmp/mydata.dat", 'float64', mode='w+', shape=(numrows, numrows))

for r in range(0, numrows, SPLITROWS):
    for c in range(0, numrows, SPLITROWS):
        r1 = r + SPLITROWS
        c1 = c + SPLITROWS
        chunk1 = bigdata[r:r1]
        chunk2 = bigdata[c:c1]
        res[r:r1, c:c1] = np.dot(chunk1, chunk2.T)

一些注意事项:

  • 上面的代码是在np.corrcoef(bigdata)上测试过的
  • 如果你有复杂的值,你需要创建一个复杂的输出数组res,并对chunk2.T取共轭复数
  • 为了提高性能并减少内存使用,代码会对bigdata进行一些处理;如果你需要保留原数据,记得先复制一份

在我的机器上,上面的代码运行大约需要85秒,但数据大部分可以放进内存里,而且我有一个SSD硬盘。这个算法的编写顺序是为了避免对硬盘的随机访问,也就是说,访问是相对顺序的。相比之下,即使你有很多内存,标准版本的速度也不会快太多。(实际上,在我的情况下,它花了更多时间,我怀疑是因为我的16 GiB内存不够用了,导致了大量的交换。)

你可以通过省略矩阵的一半来加快实际计算,因为res.T == res。实际上,你可以省略所有c > r的块,然后再进行镜像处理。另一方面,性能可能主要受限于硬盘的性能,因此其他优化不一定能带来更快的速度。

当然,这种方法很容易进行并行处理,因为每个块的计算是完全独立的。此外,内存映射数组可以很容易地在多个线程之间共享。

撰写回答