大数据上的增量PCA

14

我刚刚尝试使用sklearn.decomposition的IncrementalPCA,但它像PCA和RandomizedPCA一样抛出了MemoryError。我的问题是,我尝试加载的矩阵太大无法放入RAM中。现在它作为形状为~(1000000,1000)的数据集存储在hdf5数据库中,因此我有10亿个float32值。我以为IncrementalPCA会批处理加载数据,但显然它试图加载整个数据集,这并没有帮助。这个库应该如何使用?hdf5格式是问题所在吗?

from sklearn.decomposition import IncrementalPCA
import h5py

db = h5py.File("db.h5","r")
data = db["data"]
IncrementalPCA(n_components=10, batch_size=1).fit(data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/software/anaconda/2.3.0/lib/python2.7/site-packages/sklearn/decomposition/incremental_pca.py", line 165, in fit
    X = check_array(X, dtype=np.float)
  File "/software/anaconda/2.3.0/lib/python2.7/site-packages/sklearn/utils/validation.py", line 337, in check_array
    array = np.atleast_2d(array)
  File "/software/anaconda/2.3.0/lib/python2.7/site-packages/numpy/core/shape_base.py", line 99, in atleast_2d
    ary = asanyarray(ary)
  File "/software/anaconda/2.3.0/lib/python2.7/site-packages/numpy/core/numeric.py", line 514, in asanyarray
    return array(a, dtype, copy=False, order=order, subok=True)
  File "h5py/_objects.pyx", line 54, in h5py._objects.with_phil.wrapper (-------src-dir-------/h5py/_objects.c:2458)
  File "h5py/_objects.pyx", line 55, in h5py._objects.with_phil.wrapper (-------src-dir-------/h5py/_objects.c:2415)
  File "/software/anaconda/2.3.0/lib/python2.7/site-packages/h5py/_hl/dataset.py", line 640, in __array__
    arr = numpy.empty(self.shape, dtype=self.dtype if dtype is None else dtype)
MemoryError

感谢您的帮助

2个回答

25

你的程序可能因为试图将整个数据集加载到内存中而失败。每个float32有32位,1,000,000 × 1000个float32需要3.7 GiB的内存。这可能是在只有4 GiB RAM的计算机上出现问题。要检查这是否真的是问题,请尝试仅创建这个大小的数组:

>>> import numpy as np
>>> np.zeros((1000000, 1000), dtype=np.float32)

如果你看到一个MemoryError错误,要么需要更多的RAM,要么需要将数据集分块处理。

对于h5py数据集,我们应该避免将整个数据集传递给方法,而是传递数据集的切片。一次只处理一个。

由于我没有您的数据,让我从创建一个相同大小的随机数据集开始:

import h5py
import numpy as np
h5 = h5py.File('rand-1Mx1K.h5', 'w')
h5.create_dataset('data', shape=(1000000,1000), dtype=np.float32)
for i in range(1000):
    h5['data'][i*1000:(i+1)*1000] = np.random.rand(1000, 1000)
h5.close()

它创建了一个不错的3.8 GiB文件。

现在,如果我们在Linux中,我们可以限制可用于我们程序的内存大小:

$ bash
$ ulimit -m $((1024*1024*2))
$ ulimit -m
2097152

现在如果我们尝试运行你的代码,会出现MemoryError错误。(按Ctrl-D退出新的bash会话并稍后重置限制)

让我们尝试解决这个问题。我们将创建一个IncrementalPCA对象,并多次调用它的.partial_fit()方法,每次提供数据集的不同部分。

import h5py
import numpy as np
from sklearn.decomposition import IncrementalPCA

h5 = h5py.File('rand-1Mx1K.h5', 'r')
data = h5['data'] # it's ok, the dataset is not fetched to memory yet

n = data.shape[0] # how many rows we have in the dataset
chunk_size = 1000 # how many rows we feed to IPCA at a time, the divisor of n
ipca = IncrementalPCA(n_components=10, batch_size=16)

for i in range(0, n//chunk_size):
    ipca.partial_fit(data[i*chunk_size : (i+1)*chunk_size])

对我来说似乎可以工作,如果我查看 top 报告的内容,内存分配保持在200M以下。


好的,基本上我不应该多次调用fit而是应该使用partial_fit。我没有看到这个方法,因为教程使用的是fit。你知道为什么fit有batch_size参数吗?如果它一次性加载整个数据集,那么这个参数有什么作用呢? - KrawallKurt
不将所有数据加载到内存中的技巧可能由h5py库处理。它的数据集对象(h5['data'])似乎像常规的numpy数组一样运作,但实际上并非如此。 IncrementalPCA 不知道它是一个磁盘上的数据结构,并且在某个时刻读取所有行(MemoryError!)。计算仍然以 batch_size 批次执行。 - sastanin
1
这发生在fit()中,它调用了check_array(),该函数应将数据转换为常规的numpy数组(https://github.com/scikit-learn/scikit-learn/blob/0.16.1/sklearn/utils/validation.py#L268)。调用partial_fit()会绕过此转换。 - sastanin
3
@sastanin 我注意到每次迭代的解释方差似乎在减少。这正常吗?我期望它会遵循一个凸曲线,并在某一点接近100%。但我也不确定部分拟合是否需要批量大小和特征数量之间的某种关系。 - aarslan
1
@MehmedB 这个例子的重点不是找出解决方案,而是展示如何处理较小的数据块。根据你的数据集,你可能需要进行随机抽样或进行更多的迭代。在这种情况下,我们正在对白噪声数据进行PCA分析。在足够大的数据集上,所有组件应该是相等的。这个IPCA不应该收敛(因此,解释方差会减少)。 - sastanin
显示剩余2条评论

1
一个人可以使用NumPy的memmap类,它允许像完全在内存中一样操作存储在磁盘上的大型数组;该类仅在需要时将其需要的数据加载到内存中。由于incrementalPCA同时使用批处理,因此内存使用保持在可控范围内。这是一个示例代码。
from sklearn.decomposition import IncrementalPCA
import numpy as np

X_mm = np.memmap(filename, dtype="float32", mode="readonly", shape=(m, n))
batch_size = m // n_batches
inc_pca = IncrementalPCA(n_components=10, batch_size=batch_size)
inc_pca.fit(X_mm)

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