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

11

这是我的当前运行的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):

我在 gist 上整理了一个测试代码,如果您感兴趣可以轻松地复现。

我在 Mac Pro 和多台 linux 机器上测试了相同的代码,并重现了 @igrinis 的结果,但仅在 Mac Pro 上。在 Linux 机器上,我得到的数字与之前不同,但一致。因此,生成的线程数可能取决于传递给 GridSearchCV 的特定数据源。

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

请注意,Homebrew/Linuxbrew在Mac Pro和Linux机器上安装的pstree版本是不同的。这里我发布我使用的确切版本:

Mac:

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函数调用吗? - Gambit1614
@MohammedKashif,请查看我添加的代码。 - zyxue
ActivityMonitor显示了Mac上的线程数。1个进程有4个线程,其他进程只有5个中的1个。 - igrinis
设置 export OMP_NUM_THREADS=1,这可能会起到作用。 - zyxue
1个回答

4

来自sklearn.GridSearchCV文档:

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

pre_dispatch:int或string,可选 控制在并行执行期间分派的作业数量。减少此数字可以避免当分派的作业多于CPU可以处理的作业时内存消耗的爆炸。此参数可以是: None,在这种情况下,所有作业都会立即创建和生成。对于轻量级和快速运行的作业,请使用此选项,以避免由于按需生成作业而导致的延迟 一个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)) )

使用10万个样本足以让CPU忙碌约1分钟。


请问您能否展示一下您的 pstree 输出是什么样子? - zyxue
你能否把你的代码也展示一下呢?我会尝试复现你的实验。 - zyxue
我在 Mac OS 上重现了你的结果,但是我在多台 Linux 机器上测试了完全相同的代码仍然会产生许多线程。我已经在 gist 上发布了我的整个测试代码,请查看更新。 - zyxue
请您在 Linux 机器上尝试一下这个实验,看看效果如何? - zyxue
嗨@igrinis,你认为你已经弄清楚原因了吗?我倾向于给你悬赏,但不确定你的结果是否符合答案。 - zyxue
我已经查看了源代码。从我所看到的,LogisticRegression中的并行化是通过多进程实现的,因为由于GIL问题,除非选择的求解器是sag(那么使用多线程更有效率)。因此,我得出结论,你看到的31个线程是python解释器的辅助线程,它们在内部运行以进行调度和数据传输,以便工作线程不会饿死。我相信,如果你深入研究线程,你会发现大多数线程都处于睡眠状态,因为python在多线程计算方面真的很差(GIL问题)。 - igrinis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接