Multiprocessing 无法使用超过 65536 字节的训练数据训练 Scikit Learn 模型

Multiprocessing Fails to Train Scikit Learn Models with More Than 65536 Bytes of Training Data

我正在尝试使用 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 系统生成进程而不是按照建议分叉它们 here. I've investigated suggestions like , and avoiding an iPython environment (executing python code straight from the terminal) to no avail. Changing the number of Pool processes also has no impact. I have also tried switching from multiprocessing.Pool to multiprocessing.Process to avoid daemonic Pool processes from trying to spawn processes from within the KMeans joblib integration, as discussed ,但没有成功。

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

经过反复试验,问题似乎是环境错误,因为 运行 以上代码在新环境中有效。我不确定我的哪个包裹导致了问题。

我有一个类似的问题 - 在没有多处理的情况下,一切都按预期工作,但在多处理的情况下,执行从未完成(我没有检查数据大小是否重要)。

TL;DR: 将所有相关的导入放在函数中(run_kmeans 在你的例子中)解决了这个问题。但仅限于函数内部。

我的假设:当你在“全局进程”(意思是 - 在函数之外)中使用带有导入的多进程时,fork 发生在导入之后,这意味着如果你导入的库(比如 sklearn)用于进程 ID 的某种原因,然后如果在 fork 之后进程 ID 发生变化但库仍然认为进程 ID 是前一个 - 来自 fork 之前的那个,它可能会中断。这就是为什么在函数内部进行导入会产生这样一种情况,即在 确定进程 ID(或者可能在 fork 之后发生的其他事情)之后完成导入。它必须只在函数内部(而不是在“全局进程”和函数中),因为 python 如果库已经导入,则不会再次导入它们。