Параллельные вычисления с R

Язык R предлагает выгодные средства, позволяющие создавать статистические модели, методы обработки и визуализации данных, но масштабирование может быть затруднено с увеличением объема данных.
По умолчанию R ограничивается запуском только в одном потоке на ЦП . Если мы хотим получить более быстрые результаты или выполнять сложные задачи, нам нужно использовать некоторые пакеты, которые могут использовать преимущества нескольких ядер ЦП с нашей машины, чтобы сократить время обработки.

H2O

H2O - это полностью открытая распределенная платформа машинного обучения в памяти с линейной масштабируемостью. H2O поддерживает наиболее широко используемые алгоритмы статистического и машинного обучения, включая машины с градиентным усилением, обобщенные линейные модели, глубокое обучение и многое другое. H2O также имеет ведущую в отрасли функциональность AutoML, которая автоматически обрабатывает все алгоритмы и их гиперпараметры для создания таблицы лидеров из лучших моделей. Платформа H2O используется более чем 18 000 организаций по всему миру и чрезвычайно популярна в сообществах разработчиков R & Python.
https://www.h2o.ai/products/h2o/

H2O, помимо того, что является пакетом, способным моделировать данные, также может быть инструментом, используемым в R для использования ресурсов машины, поскольку он имеет программное обеспечение на основе Java в качестве бэкэнда, и его основная цель - быть распределенным, параллельный механизм обработки в памяти с использованием многопоточности и многоузлов.
Используя библиотеку H2O в R, можно определить количество потоков в пуле потоков, которое очень близко зависит от количества используемых процессоров . По умолчанию он использует все доступные ЦП на хосте (nthreads = -1); для ручного определения мы должны использовать положительное целое число, которое напрямую указывает количество процессоров (например, nthreads = 2).

Параллельное построение модели

Мы можем использовать возможности H2O вместе с пакетами R для параллельных вычислений разными способами для более быстрого решения сложных проблем.
Чтобы проиллюстрировать эту комбинацию, давайте рассмотрим два сценария, которые различаются решаемой проблемой и используемый параллельный метод.
В первом сценарии план состоит в том, чтобы построить несколько моделей для одной и той же задачи с разными гиперпараметрами, используемыми при обучении, чтобы выбрать лучшую модель с точки зрения производительности теста. Во втором сценарии план состоит в том, чтобы создать несколько разных моделей для разных результатов, но в которых все они используют одни и те же данные знаний, только ответ будет отличаться.
Следует отметить, что только основные и необходимые шаги предварительной обработки будут приняты для создания моделей без специальной обработки для достижения наилучшего возможного решения. Основное внимание будет уделено демонстрации некоторых идей, которые могут служить основой для разработки более сложных проектов.

doParallel

Пакет doParallel - это параллельный бэкэнд для пакета foreach. Он предоставляет механизм, необходимый для параллельного выполнения циклов foreach. Пакет foreach должен использоваться вместе с таким пакетом, как doParallel, для параллельного выполнения кода. Пользователь должен зарегистрировать параллельный бэкэнд для использования, иначе foreach будет выполнять задачи последовательно, даже если используется оператор% dopar%.
https://cran.r-project.org/web/packages/doParallel/vignettes/gettingstartedParallel.pdf

Очень просто запустить быстрый пример, сравнивающий прошедшее время последовательного цикла с двухъядерным параллельным циклом. Просто замените % do% на % dopar%.

user   system elapsed
55.838  1.049  52.408
user   system elapsed
5.187   0.231  27.896

Теперь перейдем к более сложным сценариям. В первом сценарии хорошо известный набор данных Titanic (https://www.kaggle.com/c/titanic/data) будет служить примером, где мы будем использовать параллельный цикл, зарегистрированный пакетом doParallel, и возможности H2O для одновременного создания разных моделей. В этом случае это будет проблема контролируемого обучения с ответом на биномиальную классификацию (выжил: true или false), и мы будем использовать машину повышения градиента в качестве алгоритма техники машинного обучения.
Все создано модели будут отличаться значением гиперпараметра (max_depth). Последним шагом будет оценка всех (20) моделей, созданных в тестовом наборе данных, в которых значение показателя производительности (AUC - площадь под кривой) для каждого модель будет возвращена. Начальное значение одинаково как для разделения данных, так и для начального условия модели, поэтому результаты при разных подходах будут точно такими же.

Скрипт выполняет следующее:

  • Получает 2 аргумента (порт H2O и максимальная глубина);
  • Запускает экземпляр H2O, используя 1 Thread на указанном порту в качестве аргумента.
  • Импортировать набор данных titanic для запущенного кластера H2O;
  • Разделите набор данных на обучающие, действительные и тестовые наборы данных;
  • Создайте единую Модель GBM с определенными гиперпараметрами и с max_depth, указанным в качестве аргумента;
  • Оценивает модель, созданную с использованием тестового набора данных, и собирает рассчитанную метрику AUC;
  • Очистить все объекты в памяти в экземпляре H2O;
  • Закрывает экземпляр H2O;
  • Возвращает значение AUC или ноль в случае любой ошибки или предупреждения.

На основе этого сценария мы можем создать несколько способов его выполнения с помощью вызова system. Некоторые общие проблемы при использовании и понимании пакета doParallel возникают из-за объектов, соединений или пакетов, которых нет в среде, в которой выполняется цикл foreach. Этот системный метод вызова - это способ гарантировать, что у нас не будет таких проблем, поскольку скрипт загружает все необходимые ресурсы для выполнения. Хотя это работает, это не обязательно означает, что это лучшее решение для всех случаев. Просто подумайте о совместной требуемой операции, такой как загрузка большого набора данных, которая будет повторяться при каждом выполнении, становясь неэффективным.

Последовательный цикл

Параллельный цикл с doParallel

Поиск по сетке H2O

Для тех, кто более знаком с возможностями H2O, на этом этапе вам должно быть интересно, почему бы просто не использовать поиск по сетке для решения этой проблемы. Это та же концепция: построение моделей с помощью набора гиперпараметров и, следовательно, выбор лучшей модели.
Хотя в предыдущем примере мы можем иметь больший контроль в сочетании параметров для одной модели, в отличие от стратегию «RandomDiscrete» в поиске по сетке или чтобы избежать некоторых комбинаций, которые, как мы знаем, могут не работать, выбирая стратегию «декартова». И помните, это всего лишь простой пример, демонстрирующий другой подход, цель которого состоит в том, чтобы инициировать более сложные идеи и решения.
В любом случае, давайте добавим и сравним поиск по сетке как тип выполнения, хотя время выполнения будет меньше. ожидаемые. В последних версиях H2O пользователи могут указывать параметр «параллелизм» при выполнении поиска по сетке. Значение 1 указывает на последовательное построение (по умолчанию); значение 0 используется для адаптивного параллелизма; а любое значение больше 1 устанавливает точное количество моделей, построенных параллельно.

Параллельный цикл с уникальным экземпляром H2O

Поскольку поиск по сетке не повторяет некоторые операции, такие как загрузка набора данных, на всех итерациях мы определяли другое решение, чтобы попытаться воспроизвести его режим обработки. Обычно запускается только один экземпляр H2O с теми же настройками, которые использовались ранее при поиске по сетке. Набор данных загружается один раз, и только после этого он вызывается сценарием создания модели (адаптированным).
Этот адаптированный сценарий должен иметь инструкцию для запуска экземпляра в H2O, но путем размещения того же порта, что и запущенный экземпляр, и опции startH2O = ЛОЖЬ для подключения, а не для запуска.

Выполненные итерации:

Все шаги и разные экземпляры H2O
(каждая итерация загружает набор данных и запускает экземпляр H2O)

  • Последовательный (1 поток на каждую итерацию)
  • Параллельный (2 ядра - 1 поток на каждую итерацию)
  • Параллельный (5 ядер - 1 поток на каждую итерацию)

Оптимизированные шаги и уникальный экземпляр H2O
(набор данных, который он загружает один раз, и только один экземпляр, который он инициирует)

  • Сетка (1 поток для всех итераций - уровень параллелизма 1)
  • Сетка (2 потока для всех итераций - уровень параллелизма 2)
  • Сетка (5 потоков для всех итераций - уровень параллелизма 5)
  • Последовательный (1 ядро ​​- 1 поток для всех итераций)
  • Параллельный (2 ядра - 2 потока для всех итераций)
  • Параллельный (5 ядер - 5 потоков для всех итераций)

Как и ожидалось, поиск по сетке H2O уже достаточно оптимизирован, и это очень хорошее решение. Хотя, как уже упоминалось, мы можем реализовать более конкретные процессы создания моделей с аналогичным временем или даже создать решение, которое объединяет лучшее из обоих миров: параллельный цикл с использованием поиска по сетке H2O для создания нескольких моделей. Существует большой набор возможностей для более эффективного использования ресурсов нашей машины, ускорения некоторых анализов или даже сокращения затрат, например, в службах Azure и AWS.

mclapply
Другой способ (более простой и прямой) включить параллельную обработку - использовать функцию mclapply из пакета parallel. Функция mclapply по сути является распараллеленной версией функции lapply.

Первые два аргумента mclapply () точно такие же, как и для lapply (). Однако у mclapply () есть дополнительные аргументы (которые должны быть названы), наиболее важным из которых является аргумент mc.cores, который вы можете использовать для указания количества процессоров / ядер, между которыми вы хотите разделить вычисление. Например, если на вашем компьютере 4 ядра, вы можете указать mc.cores = 4, чтобы разбить распараллеливание вашей операции на 4 ядра (хотя это может быть не лучшей идеей, если вы выполняете другие операции в фоновом режиме, кроме R)
https://bookdown.org/rdpeng/rprogdatascience/parallel-computation.html

В качестве примера для демонстрации mclapply мы будем использовать набор данных распознавания цифр, представленный в конкурсе Kaggle (https://www.kaggle.com/c/digit-recognizer).
В этой демонстрации мы преобразуем ответ полиномиальной классификации (label: [0–9]) в несколько биномиальных классификаций (isX: True или False), где вместо создания уникальной модели для идентификации десять цифр, мы собираемся создать 10 моделей (по одной модели для каждой цифры) с вероятностями True или False. используя функциональные возможности H2O AutoML.
В этом случае разделение может быть не лучшим решением, но в сценарии, когда вам действительно нужно создать отдельные модели классификации на основе тех же данных знаний, одинаковая обработка данных и моделирование для разных выходных данных, это может быть хорошим и более быстрым вариантом.

Мы будем использовать 10 ядер, что означает, что он будет обрабатывать H2O AutoML для всех цифр одновременно. Как вы можете заметить в приведенном выше скрипте, для каждой итерации экземпляр H2O запускается с 1 потоком, каждый со своим портом. Каждая итерация будет создавать как можно больше моделей за максимальное время в 300 секунд, а затем использовать лучшую модель (автоматически классифицируемую H2O по наивысшему значению AUC при перекрестной проверке) для прогнозирования меток тестового набора данных.

user    system   elapsed
87.369   9.970   408.359

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

Не важно для демонстрации, но конечным результатом была таблица с вероятностями каждой строки для каждой цифры. С помощью этой таблицы вы можете построить последний столбец с решениями, поддерживаемыми всеми моделями.

Заключительные замечания
И это небольшая демонстрация во многих других возможностях использования параллельной обработки в R с функциями H2O. Как было продемонстрировано, H2O уже предоставляет очень эффективный способ создания нескольких моделей машинного обучения, минуя ограничения R. дополнительный пакет R, чтобы сделать это быстрее. Но если ваша проблема требует построения нескольких разных моделей с аналогичными данными и подготовкой, автоматически построенными, оцененными и развернутыми, стоит изучить и реализовать эти параллельные пакеты R. В зависимости от объема данных вам придется выбирать между использованием одного экземпляра в H2O с большим количеством ресурсов или нескольких независимых экземпляров с меньшим количеством ресурсов в зависимости от его плюсов и минусов (время, конфликт заданий, управление памятью, стабильность,…). < br /> Если проблема еще более сложная, вы можете закрепить свое решение и, например, использовать службы Azure или AWS Batch. Пакет doAzureParallel из Azure очень похож на doParallel, где мы можем распределять нашу обработку на несколько машин одновременно. Возможно тема для следующего поста!

Ссылки
https://cran.r-project.org/web/packages/doParallel/vignettes/gettingstartedParallel.pdf
https: //privefl.github. io / blog / a-guide-to-parallelism-in-r /
https://bookdown.org/rdpeng/rprogdatascience/parallel-computation.html