Почему GridSearchCV в scikit-learn порождает так много потоков

Вот вывод pstree моего текущего работающего GridSearch, мне любопытно посмотреть, какие процессы происходят, и есть кое-что, что я пока не могу объяснить.

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

Я удалил все, что не имеет отношения к делу. Фигурные скобки означают темы.

  • Появление perl связано с тем, что я использовал parallel -j 20 для запуска своих заданий на python. Как видите, 20* действительно показывает, что процессов 20.
  • Процесс bash перед каждым из процессов python связан с активацией виртуальной среды Anaconda с помощью source activate venv.
  • Внутри каждого процесса Python порождаются еще 5 процессов Python (5*). Это потому, что я указал от n_jobs=5 до GridSearchCV.

На этом мое понимание заканчивается.

Вопрос: кто-нибудь может объяснить, почему существуют еще 11 потоков python (11*[{python}]) вместе с поиском по сетке, и 31 поток python (31*[{python}]) порождаются внутри каждого из 5 заданий поиска по сетке?

Обновление: добавлен код для вызова 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)

Обновление (27 сентября 2017 г.):

Я собрал тестовый код, чтобы вы могли легко воспроизвести его, если интересно.

Я протестировал тот же код на Mac Pro и нескольких машинах с Linux и воспроизвел результат @igrinis, но только на Mac Pro. На машинах с Linux я получаю другие числа, чем раньше, но стабильно. Таким образом, количество созданных потоков может зависеть от конкретного потока данных в GridSearchCV.

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

Обратите внимание, что pstree, установленный homebrew/linuxbrew на компьютерах Mac Pro и Linux, отличается. Здесь я размещаю точные версии, которые я использовал:

Мак:

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

Линукс:

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}]

Линукс parallel здесь не используется. n_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.

person zyxue    schedule 21.09.2017    source источник
comment
Можете ли вы показать вызов функции с точными значениями параметров для GridSearchCV?   -  person Gambit1614    schedule 21.09.2017
comment
@MohammedKashif, пожалуйста, посмотрите мой добавленный код   -  person zyxue    schedule 21.09.2017
comment
ActivityMonitor показывает количество потоков на Mac. У 1 процесса было 4 потока, у других 5 только 1.   -  person igrinis    schedule 27.09.2017
comment
поставил export OMP_NUM_THREADS=1, это может играть роль   -  person zyxue    schedule 12.10.2017


Ответы (1)


Из sklearn.GridSearchCV документа:

n_jobs : int, по умолчанию = 1 Количество заданий для параллельного выполнения.

pre_dispatch : целое число или строка, необязательный параметр Управляет количеством заданий, которые отправляются во время параллельного выполнения. Уменьшение этого числа может быть полезно, чтобы избежать резкого увеличения потребления памяти, когда диспетчеризуется больше заданий, чем может обработать ЦП. Этот параметр может быть: None, и в этом случае все задания создаются и порождаются немедленно. Используйте это для легких и быстро выполняемых заданий, чтобы избежать задержек из-за создания заданий по требованию. Целое число, указывающее точное общее количество созданных заданий. Строка, задающая выражение как функцию от n_jobs, как в ' 2*n_jobs'

Если я правильно понимаю документацию, GridSearchCV порождает кучу потоков в виде количества точек сетки и одновременно запускает только n_jobs. Я считаю, что число 31 является своего рода ограничением ваших 40 возможных значений. Попробуйте поиграться со значением параметра pre_dispatch.

Еще 11 потоков, я считаю, не имеют ничего общего с самим GridSearchCV, так как он показан на том же уровне. Я думаю, что это остатки других команд.

Кстати, я не наблюдаю такого поведения на Mac (вижу только 5 процессов, порожденных GridSearchCV, как и следовало ожидать), так что это может быть из-за несовместимых библиотек. Попробуйте обновить sklearn и numpy вручную.

Вот мой вывод 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

ответ на второй комментарий:

Это ваш код на самом деле. Только что сгенерированная отделимая 1d проблема с двумя классами:

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)) )

100 000 сэмплов было достаточно, чтобы процессор был загружен примерно на минуту.

person igrinis    schedule 23.09.2017
comment
Не могли бы вы показать, как выглядит ваш вывод pstree? - person zyxue; 24.09.2017
comment
Не могли бы вы также показать свой код? попробую воспроизвести ваш эксперимент - person zyxue; 24.09.2017
comment
Я воспроизвел ваши результаты на Mac OS, но точно такой же код, который я тестировал на нескольких машинах с Linux, по-прежнему порождает множество потоков. Я разместил весь свой тестовый код в gist, пожалуйста, смотрите обновления. - person zyxue; 27.09.2017
comment
не могли бы вы попробовать провести эксперимент на Linux-машине и посмотреть, что получится? - person zyxue; 28.09.2017
comment
Привет @igrinis, ты думаешь, ты понял, почему. Я хотел дать вам награду, но не уверен, что ваши результаты можно считать ответом. - person zyxue; 30.09.2017
comment
Я посмотрел исходный код. Из того, что я видел, распараллеливание в LogisticRegression выполняется многопроцессорной обработкой из-за проблем с GIL, если только не выбран решатель 'sag' (тогда более эффективно использовать многопоточность). Итак, я прихожу к выводу, что 31 поток, который вы видите, — это python вспомогательных потока интерпретатора, которые запускаются внутри для целей планирования и передачи данных, поэтому рабочие не будут голодать. Я считаю, что если вы углубитесь в потоки, вы увидите, что большинство из них спят, потому что python действительно отстой в многопоточных вычислениях (материал GIL). - person igrinis; 01.10.2017