Общие сведения
В бустинге (из ансамбля моделей машинного обучения), алгоритмы реализуют последовательный процесс (в отличие от бэггинга, где он распараллелен), который генерирует слабые обучающие алгоритмы и комбинирует их с сильным (как и во всех методах ансамбля). В бустинге на каждой итерации процесса модель пытается адаптивно исправить ошибки предыдущей итерации, в отличие от бэггинга, в котором слабые обучающие алгоритмы обучаются независимо.
Один из алгоритмов бустинга, градиентный бустинг, использует градиентный спуск для минимизации функции потерь прямо в этих последовательных моделях (в отличие от алгоритма AdaBoost, где обучение происходит посредством изменения весов обучающих экземпляров). Слабые обучающие алгоритмы, созданные при градиентном бустинге во время обучения, обычно реализуются в виде деревьев решений. Самое неэффективное в градиентном бустинге – это последовательный процесс создания деревьев, поскольку в таком случае создается всего одно дерево за раз.Чтобы обойти это ограничение Тяньцзи Ченом и Карлосом Гестрином было предложено улучшение алгоритма градиентного бустинга, которое называется XGBoost, что расшифровывается как Extreme Gradient Boosting или экстремальный градиентный бустинг. Это своего рода градиентный бустинг на стероидах, который используется в основном для классификации, но также порой для регрессии и ранжирования.
По сравнению со стандартным градиентным бустингом, новый метод существенно увеличивает производительность за счет гиперпараметров, поддержки GPU, кроссвалидации и регуляризации алгоритмов. В целом модель получается более эффективной, быстрее обучается и менее подвержена переобучению.
В последнее время XGBoost обрел большую популярность и выиграл множество соревнований по машинному обучению в Kaggle. Считается, что он обладает большой вычислительной мощностью и точностью.
XGBoost и Apache Spark
Во время стандартного workflow в ML используются такие системы как Spark для создания пайплайна машинного обучения, где вы предварительно обрабатываете и чистите данные, а затем результат передается на этап машинного обучения, зачастую с помощью Spark MLlib, если вы уже используете Spark.
В контексте этой статьи важно то, что в XGBoost есть распараллеливание процесса построения дерева, что позволяет производить между узлами распределенное обучение и прогнозирование. То есть если я, как пользователь Apache Spark MLlib, могу использовать его для расширения возможностей обучения XGBoost и работы на продакшене, то, по сути, я могу радоваться высокой производительности XGBoost и мощным механизмам работы Spark для инженерии признаков и построения ML-пайплайнов.
Встречайте XGBoost4J-Spark — проект, который объединяет XGBoost и Apache Spark, добавляя XGBoost к фреймворку Apache Spark MLlib.
XGBoost4J-Spark дает возможность построить пайплайн MLlib, который предварительно обрабатывает данные перед обучением модели XGBoost, обучает ее и может использоваться для параллельного прогнозирования на продкшене. С помощью этой библиотеки каждый воркер XGBoost оборачивается в таск Spark, при этом обучающий датасет из памяти Spark отправляется воркерам XGBoost, которые невидимо существуют в исполнителях Spark.
<dependency> <groupId>ml.dmlc</groupId> <artifactId>xgboost4j-spark</artifactId> <version>0.90</version> </dependency>
Подготовка данных (пример с ирисами)
Как говорилось ранее, XGBoost4J-Spark позволяет «подогнать» данные под интерфейс XGBoost.
Как только мы считаем датасет «Цветы Ириса» в DataFrame, нам нужно будет:
- Преобразовать столбцы из String к Double;
- Объединить столбцы признаков в вектора, чтобы данные соответствовали интерфейсу фреймворка машинного обучения Spark.
import org.apache.spark.ml.feature.StringIndexer import org.apache.spark.ml.feature.VectorAssembler val stringIndexer = new StringIndexer(). setInputCol("class"). setOutputCol("classIndex"). fit(irisDF) val labelTransformed = stringIndexer.transform(irisDF).drop("class") val vectorAssembler = new VectorAssembler(). setInputCols(Array("sepal length", "sepal width", "petal length", "petal width")). setOutputCol("features") val xgbInput = vectorAssembler.transform(labelTransformed).select("features", "classIndex")
В DataFrame выше в результате будут два столбца, “features”: вектор – представляющий признаки ириса и “classIndex”: лейбл типа Double. Такой DataFrame можно спокойно скормить обучающему движку XGBoost4J-Spark.
Распределенное обучение
import ml.dmlc.xgboost4j.scala.spark.XGBoostClassifier val xgbClassifier = new XGBoostClassifier(). setFeaturesCol("features"). setLabelCol("classIndex"). setObjective("multi:softmax") setMaxDepth(2). setNumClass(3). setNumRound(100). setNumWorkers(10).
Полный список параметров XGBoost вы можете найти здесь. Обратите внимание, что в XGBoost4J-Spark вы также можете использовать camelСase, как в примере выше.
Заметки
- multi:softmax означает, что мы делаем многоклассовую классификацию с помощью функции softmax. Для этого нужно задать количество классов с помощью параметра num_class.
- max_depth – это максимальная глубина дерева, созданного на каждой итерации бустинга. Увеличение этого значения сделает модель сложной и склонной к переобучению. При обучении глубоких деревьев XGBoost потребляет много памяти.
- num_rounds – количество раундов бустинга.
- Параметр num_workers определяет сколько параллельных воркеров нам нужно при обучении
XGBoostClassificationModel
. Позже этот параметр станет отложенными тасками в Spark, которые в перспективе будут обрабатываться менеджером кластера (в большинстве случаев YARN).
Ранняя остановка поддерживается с помощью параметров num_early_stopping_rounds и maximize_evaluation_metrics.
Теперь мы можем создать трансформер, обучив классификатор XGBoost на входном DataFrame. В результате процесса обучения мы получаем модель, которую можно использовать для получения прогнозов.
val xgbClassificationModel = xgbClassifier.fit(xgbInput)
Параллельное прогнозирование
XGBoost4j-Spark поддерживает пакетное прогнозирование и точечное прогнозирование.
Для пакетного прогнозирования модель берет DataFrame со столбцом, содержащим векторы признаков, делает прогноз для каждого вектора признаков и выводит новый DataFrame с результатами. В этом процессе XGBoost4J-Spark запускает таск Spark с воркером XGBoost для каждой части входного DataFrame для параллельного пакетного прогнозирования.
val predictionsDf = xgbClassificationModel.transform(inputDF) predictionsDf.show() +----------------+----------+-------------+-------------+----------+ | features |classIndex|rawPrediction| probability |prediction| +----------------+----------+-------------+-------------+----------+ |[5.1,3.5,1.2,.. | 0.0|[3.4556984...|[0.9957963...| 0.0| |[4.7,3.2,1.3,.. | 0.0|[3.4556984...|[0.9961891...| 0.0| |[5.7,4.4,1.5,.. | 0.0|[3.4556984...|[0.9964334...| 0.0| +----------------+----------+-------------+-------------+----------+
Для точечного прогнозирования модель принимает один вектор.
val features = xgbInput.head().getAs[Vector]("features") val result = xgbClassificationModel.predict(features)
Точечное прогнозирование с помощью XGBoost не рекомендуется из-за больших накладных расходов, поскольку они будут сравнимы с единичным прогнозом.
На данный момент последняя версия (0.9) XGBoost4J-Spark требует Spark 2.4.x., в основном потому, что теперь в нем используются средства
org.apache.spark.ml.param.shared
, которые доступны не полностью в более ранних версиях Spark.Также эта версия включает в себя более последовательную обработку пропущенных значений, лучшую производительность для многоядерных процессоров, улучшение управления кэшированием разделенных обучающих данных для сокращения времени обучения и т.д.
Узнать больше вы можете в документации XGBoost.
Источники
XGBoost с CUDA XGBoost в Spark c GPU и RAPIDS XGboost4J-Spark
Узнать о курсе подробнее.