scikit-learn joblib 错误:多处理池 self.value 超出“i”格式代码的范围,仅适用于大型 numpy 数组

Posted

技术标签:

【中文标题】scikit-learn joblib 错误:多处理池 self.value 超出“i”格式代码的范围,仅适用于大型 numpy 数组【英文标题】:scikit-learn joblib bug: multiprocessing pool self.value out of range for 'i' format code, only with large numpy arrays 【发布时间】:2014-08-15 21:56:27 【问题描述】:

我的代码在较小的测试样本上运行良好,例如 X_trainy_train 中的 10000 行数据。当我为数百万行调用它时,我得到了结果错误。包中的错误,还是我可以做一些不同的事情?我正在使用 Anaconda 2.0.1 中的 Python 2.7.7,并将 Anaconda 的多处理包中的 pool.py 和 scikit-learn 的外部包中的 parallel.py 放在我的 Dropbox 上。

测试脚本是:

import numpy as np
import sklearn
from sklearn.linear_model import SGDClassifier
from sklearn import grid_search
import multiprocessing as mp


def main():
    print("Started.")

    print("numpy:", np.__version__)
    print("sklearn:", sklearn.__version__)

    n_samples = 1000000
    n_features = 1000

    X_train = np.random.randn(n_samples, n_features)
    y_train = np.random.randint(0, 2, size=n_samples)

    print("input data size: %.3fMB" % (X_train.nbytes / 1e6))

    model = SGDClassifier(penalty='elasticnet', n_iter=10, shuffle=True)
    param_grid = [
        'alpha' : 10.0 ** -np.arange(1,7),
        'l1_ratio': [.05, .15, .5, .7, .9, .95, .99, 1],
    ]
    gs = grid_search.GridSearchCV(model, param_grid, n_jobs=8, verbose=100)
    gs.fit(X_train, y_train)
    print(gs.grid_scores_)

if __name__=='__main__':
    mp.freeze_support()
    main()

这导致输出:

Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Started.
('numpy:', '1.8.1')
('sklearn:', '0.15.0b1')
input data size: 8000.000MB
Fitting 3 folds for each of 48 candidates, totalling 144 fits
Memmaping (shape=(1000000L, 1000L), dtype=float64) to new file c:\users\laszlos\appdata\local\temp\4\joblib_memmaping_pool_6172_78765976\6172-284752304-75223296-0.pkl
Failed to save <type 'numpy.ndarray'> to .npy file:
Traceback (most recent call last):
  File "C:\Anaconda\lib\site-packages\sklearn\externals\joblib\numpy_pickle.py", line 240, in save
    obj, filename = self._write_array(obj, filename)
  File "C:\Anaconda\lib\site-packages\sklearn\externals\joblib\numpy_pickle.py", line 203, in _write_array
    self.np.save(filename, array)
  File "C:\Anaconda\lib\site-packages\numpy\lib\npyio.py", line 453, in save
    format.write_array(fid, arr)
  File "C:\Anaconda\lib\site-packages\numpy\lib\format.py", line 406, in write_array
    array.tofile(fp)
ValueError: 1000000000 requested and 268435456 written

Memmaping (shape=(1000000L, 1000L), dtype=float64) to old file c:\users\laszlos\appdata\local\temp\4\joblib_memmaping_pool_6172_78765976\6172-284752304-75223296-0.pkl
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Vendor:  Continuum Analytics, Inc.
Package: mkl
Message: trial mode expires in 28 days
Traceback (most recent call last):
  File "S:\laszlo\gridsearch_largearray.py", line 33, in <module>
    main()
  File "S:\laszlo\gridsearch_largearray.py", line 28, in main
    gs.fit(X_train, y_train)
  File "C:\Anaconda\lib\site-packages\sklearn\grid_search.py", line 597, in fit
    return self._fit(X, y, ParameterGrid(self.param_grid))
  File "C:\Anaconda\lib\site-packages\sklearn\grid_search.py", line 379, in _fit
    for parameters in parameter_iterable
  File "C:\Anaconda\lib\site-packages\sklearn\externals\joblib\parallel.py", line 651, in __call__
    self.retrieve()
  File "C:\Anaconda\lib\site-packages\sklearn\externals\joblib\parallel.py", line 503, in retrieve
    self._output.append(job.get())
  File "C:\Anaconda\lib\multiprocessing\pool.py", line 558, in get
    raise self._value
struct.error: integer out of range for 'i' format code

编辑:ogrisel 的答案确实适用于 scikit-learn-0.15.0b1 的手动内存映射。不要忘记一次只运行一个脚本,否则你仍然会耗尽内存并且线程过多。 (我的 CSV 文件大小约为 12.5 GB 的数据,使用 8 个线程,运行大约 60 GB。)

【问题讨论】:

你能发布你正在使用的 Anaconda 版本吗?如果可能,请发布 parallel.py 和 pool.py。可能是您创建了太多并行进程。反正这种东西更适合做bug报告。 你能发布你正在使用的python和scikit-learn的版本吗?在官方存储库中查看该特定版本的代码会很有帮助。那时你就不需要发布文件了。 这听起来像是 joblib 中的一个严重错误,但是我无法通过运行以下脚本来重现它:gist.github.com/ogrisel/dab225fc9d0a365119b6 您能否提供一个带有随机数据的独立脚本,可以触发您机器上的错误?你有哪个 numpy 版本? 当你在 Windows 上时,不要忘记放置一个 if __name__ == '__main__': 块来保护调用 scikit-learn 的部分涉及 n_jobs=8 仅供参考,我可以在一个大的 windows 盒子上运行我以前的 gist 中的代码而不会崩溃。 【参考方案1】:

作为一种解决方法,您可以尝试将数据显式地手动映射为explained in the joblib documentation。

编辑#1:这是重要的部分:

from sklearn.externals import joblib

joblib.dump(X_train, some_filename)
X_train = joblib.load(some_filename, mmap_mode='r+')

然后将这个 memmap 的数据传递给 scikit-learn 0.15+ 下的GridSearchCV

编辑#2: 此外:如果您使用 32 位版本的 Anaconda,每个 python 进程将被限制为 2GB,这也会限制内存。

我刚刚在 Python 3.4 下为 numpy.save 找到了一个 bug,但即使已修复,随后对 mmap 的调用也会失败:

OSError: [WinError 8] Not enough storage is available to process this command

所以请使用 64 位版本的 Python(Anaconda 作为 AFAIK,目前没有其他 64 位软件包用于 numpy / scipy / scikit-learn==0.15.0b1)。

编辑 #3: 我发现了另一个可能导致 Windows 下内存使用过多的问题:当前 joblib.Parallel 内存默认将输入数据映射到 mmap_mode='c':此写入时复制设置似乎会导致 Windows 耗尽页面文件,有时会触发“[错误 1455] 页面文件太小,无法完成此操作”错误。设置mmap_mode='r'mmap_mode='r+' 不会触发该问题。我将运行测试,看看我是否可以在下一个版本的 joblib 中更改默认模式。

【讨论】:

@ogrisel 这解决了原始崩溃被规避的问题。然而,系统停了下来,当我终于可以启动资源监视器时,我看到 RAM 的使用量很小,但 HDD 的使用非常愉快。在那之后不久,我得到了一个新的内存错误,我将其添加到问题中,尽管也许它值得另一个线程。对此有什么想法吗?感谢您的宝贵时间! 我编辑了我的答案,指出您不应该使用 32 位版本的 Anaconda。 @ogrisel 快速说明一下,问题出现在其他对象上,不仅是 GridSearchCV,即 ElasticNetCV,而且解决方法也适用于此。 我做了一些进一步的基准测试,似乎 Windows 处理 mmap 由于某种原因正在大量使用分页文件(这是在 Rackspace VM 中测试的,因为我没有大型 Windows 服务器在眼前)。对于类似硬件上的大型 mmap 数据,Linux 似乎效率更高。 windows下的问题源于mmap_mode='c'的使用(copy on write)。使用mmap_mode='r' 效果更好(无需在后台分页)。我更改了 joblib master 中的默认值。

以上是关于scikit-learn joblib 错误:多处理池 self.value 超出“i”格式代码的范围,仅适用于大型 numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章

使用 joblib 加载腌制 scikit-learn 模型时出现 KeyError

加载训练有素的 scikit-learn/imblearn 管道模型时出现问题

线程 QueueManagerThread 中的异常 - scikit-learn

这个joblib是什么并行语法呢?这么多括号

使用 scikit-learn 和 Flask 对随机森林回归器建模

如何在 scikit-learn 中保存随机森林?