【问题标题】:Using memmap files for batch processing使用 memmap 文件进行批处理
【发布时间】:2015-11-21 10:54:14
【问题描述】:

我有一个庞大的数据集,我希望对其进行 PCA。我受到 RAM 和 PCA 计算效率的限制。 因此,我转而使用迭代 PCA。

数据集大小-(140000,3504)

documentation 声明 This algorithm has constant memory complexity, on the order of batch_size, enabling use of np.memmap files without loading the entire file into memory.

这真的很好,但不确定如何利用它。

我尝试加载一个 memmap,希望它能以块的形式访问它,但我的 RAM 崩溃了。 我下面的代码最终使用了大量 RAM:

ut = np.memmap('my_array.mmap', dtype=np.float16, mode='w+', shape=(140000,3504))
clf=IncrementalPCA(copy=False)
X_train=clf.fit_transform(ut)

当我说“我的 RAM 爆炸”时,我看到的 Traceback 是:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\site-packages\sklearn\base.py", line 433, in fit_transfo
rm
    return self.fit(X, **fit_params).transform(X)
  File "C:\Python27\lib\site-packages\sklearn\decomposition\incremental_pca.py",
 line 171, in fit
    X = check_array(X, dtype=np.float)
  File "C:\Python27\lib\site-packages\sklearn\utils\validation.py", line 347, in
 check_array
    array = np.array(array, dtype=dtype, order=order, copy=copy)
MemoryError

如何在不影响准确性的情况下通过减少批量大小来改进这一点?


我的诊断思路:

我查看了sklearn 源代码,在fit() 函数Source Code 中可以看到以下内容。这对我来说很有意义,但我仍然不确定我的情况出了什么问题。

for batch in gen_batches(n_samples, self.batch_size_):
        self.partial_fit(X[batch])
return self

编辑: 在最坏的情况下,我将不得不为 iterativePCA 编写自己的代码,该代码通过读取和关闭 .npy 文件来进行批处理。但这会破坏利用已经存在的黑客的目的。

编辑2: 如果我能以某种方式删除一批已处理的memmap file。这很有意义。

编辑3: 理想情况下,如果 IncrementalPCA.fit() 只是使用批处理,它不应该使我的 RAM 崩溃。发布整个代码,只是为了确保我之前没有将 memmap 完全刷新到磁盘时出错。

temp_train_data=X_train[1000:]
temp_labels=y[1000:] 
out = np.empty((200001, 3504), np.int64)
for index,row in enumerate(temp_train_data):
    actual_index=index+1000
    data=X_train[actual_index-1000:actual_index+1].ravel()
    __,cd_i=pywt.dwt(data,'haar')
    out[index] = cd_i
out.flush()
pca_obj=IncrementalPCA()
clf = pca_obj.fit(out)

令人惊讶的是,我注意到out.flush 并没有释放我的记忆。有没有办法使用del out 完全释放我的内存,然后有人将文件的指针传递给IncrementalPCA.fit()

【问题讨论】:

  • 相信this answer会给你一些提示
  • 1) 为什么不指定转换后所需的特征数量?我的意思是在转换之后你可能会得到相同的数据集,但是在 RAM 中(因为它是由转换生成的),因此它会消耗大量的 RAM。在构造函数中指定 n_components 参数。 2)即使您指定了 n_components (必须小于或等于数据集中的特征数),它也可能不适合内存,因为您正在尝试一次性计算转换后的数据集。可能需要批量转换,每批次调用transform方法,将转换后的数据保存到HDD。
  • @Olologin 感谢您的评论!我只尝试了clf = IncrementalPCA().fit(X_train_mmap),它使我的 RAM 崩溃了。我热衷于节省 98% 的方差。
  • @AbhishekBhatia,尝试使用 IncrementalPCA(n_components=1).fit(X_train_mmap)。是否成功完成?
  • 您在问题中遗漏了一些非常重要的信息——这使得@Olologin 很难提供帮助。首先-您说“炸毁了我的RAM”。你应该把完整的追溯。说MemoryError 在调用fit 时立即发生也会很有帮助——而不是经过一些繁重的处理。我知道您正在尝试通过节省空间并表明您已经考虑过问题可能出在哪里来提供帮助,但始终要追溯。您能否检查我在您的问题中编辑的 Traceback 是否与您看到的相符?

标签: python numpy scikit-learn pca


【解决方案1】:

单独以下是否会触发崩溃?

X_train_mmap = np.memmap('my_array.mmap', dtype=np.float16,
                         mode='w+', shape=(n_samples, n_features))
clf = IncrementalPCA(n_components=50).fit(X_train_mmap)

如果没有,那么您可以使用该模型通过批量转换(迭代地投影您的数据)为更小的数据:

X_projected_mmap = np.memmap('my_result_array.mmap', dtype=np.float16,
                             mode='w+', shape=(n_samples, clf.n_components))
for batch in gen_batches(n_samples, self.batch_size_):
    X_batch_projected = clf.transform(X_train_mmap[batch])
    X_projected_mmap[batch] = X_batch_projected

我没有测试过那个代码,但我希望你能明白。

【讨论】:

  • 好主意!!这往往效果更好,但为什么要指定number of components。这对记忆有何影响?
  • 有没有办法在 Python 中完全从内存中刷新 memmap 并以某种方式只存储一个指针?我注意到memmap_object.flush()del memmap_object 有不同的效果。如果可能,我只想传递指向 IncrementalPCA 的指针。
  • 不幸的是,即使使用最小的可能(可能是荒谬的)n_components=1,当数据数组(即使使用memmap)是这样时,在IncrementalPCA 中对fit 的调用也可能会失败尺寸。请参阅my answer below 了解原因(也许 OP 可以验证设置 n_components=1 - 在我的机器上它在相同的设置下失败)。希望 OP 可以访问具有大量 RAM 的 64 位计算机。
【解决方案2】:

您在 32 位环境中遇到了sklearn 的问题。我假设您使用的是 np.float16,因为您处于 32 位环境中,并且您需要它来允许您创建 memmap 对象而不会出现 numpy thowing 错误。

在 64 位环境中(在 Windows 上使用 Python3.3 64 位测试),您的代码开箱即用。所以,如果你有 64 位计算机可用 - 安装 python 64 位和numpyscipyscikit-learn 64 位,你就可以开始了。

很遗憾,如果您无法做到这一点,就没有简单的解决办法。我有raised an issue on github here,但是不容易打补丁。根本问题是,在库中,如果您的类型是float16,则会触发将数组复制到内存。详情如下。

所以,我希望您可以访问具有大量 RAM 的 64 位环境。如果没有,您将不得不自己拆分阵列并对其进行批处理,这是一项相当大的任务...

NB 很高兴看到您从源头诊断问题 :) 但是,如果您查看代码失败的行(来自 Traceback ),您将看到您找到的for batch in gen_batches 代码永远无法到达。


详细诊断:

OP代码产生的实际错误:

import numpy as np
from sklearn.decomposition import IncrementalPCA

ut = np.memmap('my_array.mmap', dtype=np.float16, mode='w+', shape=(140000,3504))
clf=IncrementalPCA(copy=False)
X_train=clf.fit_transform(ut)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\site-packages\sklearn\base.py", line 433, in fit_transfo
rm
    return self.fit(X, **fit_params).transform(X)
  File "C:\Python27\lib\site-packages\sklearn\decomposition\incremental_pca.py",
 line 171, in fit
    X = check_array(X, dtype=np.float)
  File "C:\Python27\lib\site-packages\sklearn\utils\validation.py", line 347, in
 check_array
    array = np.array(array, dtype=dtype, order=order, copy=copy)
MemoryError

check_array(code link) 的调用使用dtype=np.float,但原始数组有dtype=np.float16。即使check_array()函数defaults to copy=Falsepasses this to np.array(),也忽略了(as per the docs),以满足dtype不同的要求;因此,np.array 制作了一份副本。

这可以在 IncrementalPCA 代码中解决,方法是确保为具有dtype in (np.float16, np.float32, np.float64) 的数组保留dtype。但是,当我尝试该补丁时,它只会将MemoryError 推到执行链中。

当来自主 scipy 代码的代码 calls linalg.svd() 发生同样的复制问题时,这次错误发生在调用 to gesdd() 时,这是一个来自 lapack 的包装本机函数。因此,我认为没有办法解决这个问题(至少不是一个简单的方法 - 它至少是核心 scipy 中的代码更改)。

【讨论】:

  • 这是一句好话。在这种情况下,最好先存储使用dtype=np.float64 存储的映射数据。或者,也可以使用在旅途中显式加载的数据手动显式展开拟合我的调用partial_fit,而无需考虑内存映射。
  • @ogrisel 是的 - 您可以从 dtype=np.float64 开始,但是 OP(如果在 32 位环境中)无法创建具有这么多成员的 memmap。至少我认为是这样,我在努力理解确切的用例。我认为您建议“手动”分块数据并将其传递给partial_fit 是正确的。
  • @AbhishekBhatia 这能回答问题吗?如果是这样,我将不胜感激。我知道它可能无法让你做你想做的事,但它确实详细解释了它失败的原因。有关任何进展,请参阅链接的 github 问题。请注意,我建议的补丁将允许您使用fit(),但不能使用fit_transform()
猜你喜欢
  • 2016-06-18
  • 2013-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多