多处理器无法训练训练数据超过65536字节的Scikit学习模型



我正试图使用Python的mulitprocessing库在单独的过程中训练Scikit-Learn中的一系列KMeans集群模型。当我尝试使用multiprocess.Pool来训练模型时,代码运行时不会引发任何运行时错误,但执行永远不会完成。

进一步的研究表明,当训练数据(下面代码片段中的X(的内存大小超过2^16=65536字节时,仅的代码无法终止。小于此值,代码的行为与预期一致。

import sys
import numpy as np
from multiprocessing import Pool
from sklearn.cluster import KMeans
# The Code Below Executes and Completes with MULTIPLIER = 227 but not when MULTIPLIER = 228
MULTIPLIER = 227
# Some Random Training Data
X = np.array(
[[ 0.19276125, -0.05182922, -0.06014779, 0.06234482, -0.00727767, -0.05975948],
[ 0.3541313,  -0.29502648,  0.3088767, 0.02438405, -0.01978588, -0.00060496],
[ 0.22324295, -0.04291656, -0.0991894, 0.04455933, -0.00290042, 0.0316047 ],
[ 0.30497936, -0.03115212, -0.26681659, -0.00742825,  0.00978793, 0.00555566],
[ 0.1584528,  -0.01984878, -0.03908984, -0.03246589, -0.01520335, -0.02516451],
[ 0.16888249, -0.04196552, -0.02432088, -0.02362059,  0.0353778, 0.02663082]]
* MULTIPLIER)
# Prints 65488 when MULTIPLIER = 227 and 65776 when MULTIPLIER = 228
print("Input Data Size: ", sys.getsizeof(X)) 
# Training without Multiprocessing Always Works Regardless of the Size of X
no_multiprocessing = KMeans(n_clusters=2, n_jobs=1).fit(X)
print("Training without multiprocessing complete!") # Always prints
# Training with Mulitprocessing Fails when X is too Large
def run_kmeans(X):
return KMeans(n_clusters=2, n_jobs=1).fit(X)
with Pool(processes=1) as p:
yes_multiprocessing = p.map(run_kmeans, [X])
print("Training with multiprocessing complete!") # Doesn't print when MULTIPLIER = 228

我总是非常小心地将n_jobs参数设置为1None,这样我的进程就不会产生自己的进程。

奇怪的是,这个内存限制似乎并不是multiprocessing.Pool内置的"内存限制";每个元素";内存限制,因为我可以传入一个很长的字符串(消耗超过65536个字节(,并且代码会毫无怨言地终止。

import sys
from multiprocessing import Pool
my_string = "This sure is a silly string" * 2500
print("String size:", sys.getsizeof(y)) # Prints 79554
def add_exclamation(x):
return x + "!"
with Pool(processes=1) as p:
my_string = p.map(add_exclamation, [my_string])
print("Multiprocessing Completed!") # Prints Just Fine

当第一个代码段挂起时终止执行总是会导致以下错误消息:

File "/path/to/my/code", line 29, in <module>
yes_multiprocessing  = p.map(run_kmeans, [X])
File "/.../anaconda3/envs/Main36Env/lib/python3.6/multiprocessing/pool.py", line 266, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/.../anaconda3/envs/Main36Env/lib/python3.6/multiprocessing/pool.py", line 638, in get
self.wait(timeout)
File "/.../anaconda3/envs/Main36Env/lib/python3.6/multiprocessing/pool.py", line 635, in wait
self._event.wait(timeout)
File "/.../anaconda3/envs/Main36Env/lib/python3.6/threading.py", line 551, in wait
signaled = self._cond.wait(timeout)
File "/.../anaconda3/envs/Main36Env/lib/python3.6/threading.py", line 295, in wait
waiter.acquire()
KeyboardInterrupt

我已经尝试过强制我的MacOS系统生成进程,而不是像这里建议的那样分叉它们。我研究了一些建议,比如确保所有相关代码都存在于with块中,以及避免使用iPython环境(直接从终端执行python代码(,但都无济于事。更改Pool进程的数量也没有影响。我还尝试过从multiprocessing.Pool切换到multiprocessing.Process,以避免守护进程Pool尝试从KMeansjoblib集成中派生进程,如本文所述,但没有成功。

如何使用超过65536字节的训练数据在单独的进程上训练多个KMean模型?

经过更多的尝试和错误,这个问题似乎是一个环境错误,因为在新的环境中运行上述代码是有效的。我不完全确定是我的哪个包裹出了问题。

我也遇到了类似的问题——在不进行多处理的情况下工作时,一切都如预期一样,但在进行多处理时,执行从未完成(我没有检查数据大小是否重要(。

TL;DR:将所有相关导入放入函数中(在您的情况下为run_kmeans(解决了问题。但仅限于函数内部。

我的假设是:当您在";全局过程";(意思是-在函数之外(,fork发生在导入之后,这意味着如果您导入的库(比如sklearn(出于某种原因使用了进程id,那么如果在fork之后进程id发生了更改,但库仍然认为进程id是前一个进程id,则可能会中断。这就是为什么在函数内部执行导入会造成这样一种情况,即在确定进程id(或者可能在fork之后发生的其他事情(之后执行导入。它必须只在函数内部(而不是在"全局进程"和函数中(,因为如果库已经导入,python就不会再次导入库。

最新更新