为啥 scikit-learn 中的 GridSearchCV 会产生这么多线程

Posted

技术标签:

【中文标题】为啥 scikit-learn 中的 GridSearchCV 会产生这么多线程【英文标题】:Why GridSearchCV in scikit-learn spawn so many threads为什么 scikit-learn 中的 GridSearchCV 会产生这么多线程 【发布时间】:2018-03-03 05:35:09 【问题描述】:

这是我当前运行的 GridSearch 的 pstree 输出,我很想知道正在发生什么进程,还有一些我无法解释的东西。

 ├─bash─┬─perl───20*[bash───python─┬─5*[python───31*[python]]]
 │      │                          └─11*[python]]
 │      └─tee
 └─bash───pstree

我删除了不相关的内容。花括号表示线程。

perl 的出现是因为我使用parallel -j 20 来启动我的python 作业。如您所见,20* 确实显示有 20 个进程。 每个 python 进程之前的 bash 进程是由于使用 source activate venv 激活 Anaconda 虚拟环境所致。 在每个 python 进程中,还会产生另外 5 个 python 进程 (5*)。这是因为我将n_jobs=5 指定为GridSearchCV

我的理解到此结束。

问题:谁能解释为什么在网格搜索的同时还有另外 11 个 Python 线程 (11*[python]),并且在 5 个网格搜索作业中的每一个中都产生了 31 个 Python 线程 (31*[python]) ?

更新:添加调用GridSearchCV的代码

Cs = 10 ** np.arange(-2, 2, 0.1)
skf = StratifiedKFold(n_splits=10, shuffle=True, random_state=0)
clf = LogisticRegression()
gs = GridSearchCV(
    clf,
    param_grid='C': Cs, 'penalty': ['l1'],
                'tol': [1e-10], 'solver': ['liblinear'],
    cv=skf,
    scoring='neg_log_loss',
    n_jobs=5,
    verbose=1,
    refit=True)
gs.fit(Xs, ys)

更新(2017-09-27)

我总结了一个test code 的要点,如果您有兴趣,可以轻松复制。

我在 Mac Pro 和多台 linux 机器上测试了相同的代码,并重现了 @igrinis 的结果,但仅在 Mac Pro 上。在 linux 机器上,我得到的数字与以前不同,但始终如一。因此产生的线程数可能取决于向 GridSearchCV 提供的特定数据。

python─┬─5*[python───31*[python]]
       └─3*[python]

注意,homebrew/linuxbrew 在 Mac Pro 和 linux 机器上安装的 pstree 是不同的。在这里,我发布了我使用的确切版本:

苹果机:

pstree $Revision: 2.39 $ by Fred Hucht (C) 1993-2015
EMail: fred AT thp.uni-due.de

Linux:

pstree (PSmisc) 22.20
Copyright (C) 1993-2009 Werner Almesberger and Craig Small

Mac 版本似乎没有显示线程的选项,我认为这可能是结果中看不到它们的原因。我还没有找到一种在 Mac Pro 上轻松检查线程的方法。如果您碰巧知道方法,请发表评论。

更新 (2017-10-12)

在另一组实验中,我确认设置环境变量OMP_NUM_THREADS 会产生影响。

export OMP_NUM_THREADS=1 之前,如上所述产生了许多(在本例中为 63 个)用途不明的线程:

bash───python─┬─23*[python───63*[python]]
              └─3*[python]

这里没有使用 linux paralleln_jobs=23.

export OMP_NUM_THREADS=1 之后,没有产生线程,但是 3 个 Python 进程仍然存在,我仍然不知道它们的用途。

bash───python─┬─23*[python]
              └─3*[python]

我最初遇到OMP_NUM_THREADS,因为它在我的一些 GridSearchCV 作业中导致错误,错误消息是这样的

OMP: Error #34: System unable to allocate necessary resources for OMP thread:
OMP: System error #11: Resource temporarily unavailable
OMP: Hint: Try decreasing the value of OMP_NUM_THREADS.

【问题讨论】:

你能用 GridSearchCV 的确切参数值显示函数调用吗? @MohammedKashif,请看我添加的代码 ActivityMonitor 显示 Mac 上的线程数。 1 个进程有 4 个线程,其他 5 个只有 1 个。 设置export OMP_NUM_THREADS=1,这可能会起作用 【参考方案1】:

来自sklearn.GridSearchCV文档:

n_jobs : int, 默认=1 并行运行的作业数。

pre_dispatch : int 或 string,可选 控制在并行执行期间分派的作业数。当调度的作业多于 CPU 可以处理的作业时,减少此数字有助于避免内存消耗的爆炸式增长。这个参数可以是: 无,在这种情况下,所有作业都会立即创建和生成。将此用于轻量级和快速运行的作业,以避免由于按需生成作业而导致的延迟 一个 int,给出产生的总作业的确切数量 一个字符串,将表达式作为 n_jobs 的函数,如“2*n_jobs”

如果我正确理解文档,GridSearchCV 会生成一堆线程作为网格点的数量,并且只会同时运行 n_jobs。我相信第 31 号是您的 40 个可能值的某种上限。尝试使用pre_dispatch 参数的值。

我认为另外 11 个线程与 GridSearchCV 本身无关,因为它显示在同一级别。我认为这是其他命令的剩余部分。

顺便说一句,我在 Mac 上没有观察到这种行为(只看到GridSearchCV 产生了 5 个进程,正如人们所期望的那样),因此它可能来自不兼容的库。尝试手动更新sklearnnumpy

这是我的pstree 输出(为保护隐私而删除了部分路径):

 └─┬= 00396 *** -fish
   └─┬= 21743 *** python /Users/***/scratch_5.py
     ├─── 21775 *** python /Users/***/scratch_5.py
     ├─── 21776 *** python /Users/***/scratch_5.py
     ├─── 21777 *** python /Users/***/scratch_5.py
     ├─── 21778 *** python /Users/***/scratch_5.py
     └─── 21779 *** python /Users/***/scratch_5.py

回答第二条评论:

这实际上是您的代码。刚刚生成了可分离的一维二分类问题:

N = 50000
Xs = np.concatenate( (np.random.random(N) , 3+np.random.random(N)) ).reshape(-1, 1)
ys = np.concatenate( (np.zeros(N), np.ones(N)) )

100k 样本足以让 CPU 忙碌大约一分钟。

【讨论】:

您能否展示一下您的pstree 输出是什么样的? 能否请您也出示您的代码?我会尝试重现你的实验 我在 Mac OS 上重现了您的结果,但我在多台 Linux 机器上测试的完全相同的代码仍然产生了许多线程。我已经在 gist 上发布了我的整个测试代码,请查看更新。 能否请您在 linux 机器上进行实验,看看效果如何? 嗨@igrinis,你认为你已经找到原因了吗?我倾向于给你赏金,但不确定你的结果是否符合答案。

以上是关于为啥 scikit-learn 中的 GridSearchCV 会产生这么多线程的主要内容,如果未能解决你的问题,请参考以下文章

为啥带有铰链损失的 SGDClassifier 比 scikit-learn 中的 SVC 实现更快

scikit-learn 和 scipy 库之间的确定系数不同。为啥?

scikit-learn ExtraTreesClassifier - 为啥泡菜文件这么大? (几 GB)

为啥 scikit-learn 对不同的回归器要求不同的数据形状?

为啥我不能从 Spyder 导入 scikit-learn,但我可以从命令行导入?

为啥 pip 在安装 scikit-learn 时会出现权限被拒绝的错误?