8.3. Параллелизм, управление ресурсами и конфигурация

8.3.1. Параллелизм

Некоторые оценщики и утилиты scikit-learn могут распараллеливать дорогостоящие операции с использованием нескольких ядер ЦП благодаря следующим компонентам:

  • через библиотеку joblib . В этом случае количество потоков или процессов можно контролировать с помощью n_jobs параметра.
  • через OpenMP, используемый в коде C или Cython.

Кроме того, некоторые из подпрограмм numpy, которые используются внутри scikit-learn, также могут быть распараллелены, если numpy установлен с определенными числовыми библиотеками, такими как MKL, OpenBLAS или BLIS.

Мы описываем эти 3 сценария в следующих подразделах.

8.3.1.1. Параллелизм на основе Joblib

Когда базовая реализация использует joblib, количество рабочих (потоков или процессов), которые создаются параллельно, можно контролировать с помощью n_jobs параметра.

Примечание

Где (и как) происходит распараллеливание в оценщиках, в настоящее время плохо задокументировано. Пожалуйста, помогите нам, улучшив нашу документацию и решив проблему 14228 !

Joblib может поддерживать как многопроцессорность, так и многопоточность. Выбирает ли joblib поток или процесс, зависит от используемой серверной части.

Scikit-learn обычно полагается на loky бэкэнд, который является бэкэндом по умолчанию для joblib. Loky — это серверная часть с несколькими процессорами. При выполнении многопроцессорной обработки, чтобы избежать дублирования памяти в каждом процессе (что нецелесообразно для больших наборов данных), joblib создаст мем-карту, которую могут совместно использовать все процессы, если размер данных превышает 1 МБ.

В некоторых конкретных случаях (когда код, который выполняется параллельно, освобождает GIL), scikit-learn укажет, joblib что предпочтительнее использовать многопоточный бэкэнд.

Как пользователь, вы можете контролировать бэкэнд, который будет использовать joblib (независимо от того, что рекомендует scikit-learn), используя диспетчер контекста:

from joblib import parallel_backend

with parallel_backend('threading', n_jobs=2):
    # Your scikit-learn code here

Пожалуйста, обратитесь к документации joblib для получения более подробной информации.

На практике, помогает ли параллелизм улучшить время выполнения, зависит от многих факторов. Обычно лучше экспериментировать, чем предполагать, что увеличение числа рабочих — это всегда хорошо. В некоторых случаях параллельное выполнение нескольких копий некоторых оценщиков или функций может нанести серьезный ущерб производительности (см. Избыточную подписку ниже).

8.3.1.2. Параллелизм на основе OpenMP

OpenMP используется для распараллеливания кода, написанного на Cython или C, полагаясь исключительно на многопоточность. По умолчанию (и если joblib не пытается избежать превышения лимита подписки), реализация будет использовать как можно больше потоков.

Вы можете контролировать точное количество используемых потоков с помощью OMP_NUM_THREADS переменной среды:

OMP_NUM_THREADS=4 python my_script.py

8.3.1.3. Параллельные подпрограммы Numpy из числовых библиотек

Scikit-learn в значительной степени полагается на NumPy и SciPy, которые внутренне вызывают многопоточные процедуры линейной алгебры, реализованные в таких библиотеках, как MKL, OpenBLAS или BLIS.

Количество потоков , используемых OpenBLAS, библиотеки MKL или BLIS могут быть установлены с помощью MKL_NUM_THREADSOPENBLAS_NUM_THREADS и BLIS_NUM_THREADS переменные окружения.

Обратите внимание, что scikit-learn не имеет прямого контроля над этими реализациями. Scikit-learn полагается исключительно на Numpy и Scipy.

Примечание

На момент написания (2019 г.) пакеты NumPy и SciPy, распространяемые на pypi.org (используется pip) и на канале conda-forge, связаны с OpenBLAS, а пакеты conda, отправленные по каналу «defaults» с anaconda.org, связаны по умолчанию с МКЛ.

8.3.1.4. Превышение подписки: слишком много потоков

Обычно рекомендуется избегать использования значительно большего количества процессов или потоков, чем количество процессоров на машине. Избыточная подписка происходит, когда программа запускает слишком много потоков одновременно.

Предположим, у вас есть машина с 8 процессорами. Рассмотрим случай, когда вы запускаете GridSearchCV (распараллеленный с помощью joblib) с n_jobs=8 более чем HistGradientBoostingClassifier (распараллеленный с помощью OpenMP). Каждый экземпляр HistGradientBoostingClassifier будет порождать 8 потоков (поскольку у вас 8 процессоров). Это общее количество 8 * 8 = 64 потоков, что приводит к превышению лимита физических ресурсов ЦП и накладным расходам при планировании.

Избыточная подписка может возникнуть точно так же, как и в случае параллельных подпрограмм из MKL, OpenBLAS или BLIS, вложенных в вызовы joblib.

Начиная с того момента joblib >= 0.14, когда loky используется бэкэнд (что по умолчанию), joblib сообщит своим дочерним процессам, что нужно ограничить количество потоков, которые они могут использовать, чтобы избежать переподписки. На практике эвристика, которую использует joblib, заключается в том, чтобы указать процессам использовать max_threads = n_cpus // n_jobs их через соответствующую переменную среды. Вернемся к нашему примеру сверху, так как joblib бэкэнда является GridSearchCV loky, каждый процесс будет только в состоянии использовать 1 нить вместо 8, минимизируя таким образом вопрос сверхподписка

Обратите внимание, что:

  • Установка одной из переменных окружения вручную ( OMP_NUM_THREADSMKL_NUM_THREADSOPENBLAS_NUM_THREADSили BLIS_NUM_THREADS) будет иметь приоритет над тем, что joblib пытается сделать. Общее количество потоков будет n_jobs * <LIB>_NUM_THREADS. Обратите внимание, что установка этого ограничения также повлияет на ваши вычисления в основном процессе, который будет использовать только <LIB>_NUM_THREADS. Joblib предоставляет диспетчер контекста для более точного контроля над количеством потоков в его рабочих процессах (см. Документы joblib, ссылки на которые приведены ниже)
  • Joblib в настоящее время не может избежать переподписки в многопоточном контексте. Это возможно только с loky серверной частью (которая порождает процессы).

Вы найдете дополнительные сведения о предотвращении переподписки с помощью joblib в документации.

8.3.2. Переключатели конфигурации

8.3.2.1. Среда выполнения Python

sklearn.set_config управляет следующим поведением:

accept_finiteиспользуется для пропуска проверки, что позволяет ускорить вычисления, но может привести к ошибкам сегментации, если данные содержат NaN.
рабочая памятьоптимальный размер временных массивов, используемых некоторыми алгоритмами.

8.3.2.2. Переменные среды

Эти переменные среды следует установить перед импортом scikit-learn.

SKLEARN_SITE_JOBLIBКогда для этой переменной среды задано ненулевое значение, scikit-learn использует библиотеку вакансий сайта, а не ее версию, поставляемую производителем. 
Следовательно, для запуска scikit-learn необходимо установить joblib. 
Обратите внимание, что вы используете сайт joblib на свой страх и риск: версии scikit-learn и joblib должны быть совместимы. 
В настоящее время поддерживается joblib 0.11+. 
Кроме того, дампы из joblib.Memory могут быть несовместимы, и вы можете потерять некоторые кеши и вам придется повторно загрузить некоторые наборы данных.
Не рекомендуется, начиная с версии 0.21: Начиная с версии 0.21 этот параметр не действует, библиотека заданий была удалена, а библиотека заданий сайта всегда используется.
SKLEARN_ASSUME_FINITEУстанавливает значение по умолчанию для assume_finite аргумента sklearn.set_config.
SKLEARN_WORKING_MEMORYУстанавливает значение по умолчанию для working_memory аргумента sklearn.set_config.
SKLEARN_SEEDУстанавливает начальное число глобального генератора случайных чисел при запуске тестов для воспроизводимости.
SKLEARN_SKIP_NETWORK_TESTSЕсли для этой переменной среды задано ненулевое значение, тесты, требующие доступа к сети, пропускаются. Если эта переменная среды не задана, сетевые тесты пропускаются.