Статья выбрана из Medium, составлена сердцем машины,Нажмите здесь, чтобы перейти к исходному тексту.
Несмотря на возрождение и популярность нейронных сетей в последние годы, алгоритм бустинга по-прежнему имеет свои незаменимые преимущества в таких сценариях, как ограниченные обучающие выборки, короткое время обучения и отсутствие знаний о настройке параметров. В этой статье сравниваются три репрезентативных алгоритма повышения, CatBoost, Light GBM и XGBoost, с точки зрения различий в структуре алгоритмов, обработки категориальных переменных каждого алгоритма и реализации алгоритма на наборе данных; хотя выводы этой статьи основанный на конкретном наборе данных, но в целом XGBoost медленнее, чем два других алгоритма.
Недавно я участвовал в конкурсе kaggle WIDS Datathon и попал в топ-10, используя различные алгоритмы бустинга. С тех пор мне очень интересно узнать внутреннюю работу этих алгоритмов, включая настройку параметров, их преимущества и недостатки, поэтому у меня есть эта статья. Несмотря на возрождение и популярность нейронных сетей в последние годы, я уделяю больше внимания алгоритмам бустинга, потому что они по-прежнему имеют абсолютное преимущество в сценариях, где размер обучающей выборки ограничен, требуемое время обучения короткое, а знание настройки параметров невелико. отсутствует.
- В марте 2014 года XGBOOST был впервые предложен Ченом Тяньци в качестве исследовательского проекта.
- В январе 2017 года Microsoft выпустила первую стабильную версию LightGBM.
- В апреле 2017 года крупнейшая российская технологическая компания Яндекс открыла исходный код CatBoost.
Поскольку XGBoost (часто называемый убийцей GBM) уже давно используется в машинном обучении, и сегодня есть так много статей, подробно описывающих его, эта статья будет посвящена CatBoost и LGBM, о которых мы расскажем ниже:
- Различия в алгоритмической структуре
- Обработка категориальных переменных для каждого алгоритма
- Как понять параметры
- Реализация алгоритма на наборе данных
- Производительность каждого алгоритма
Структурные различия между LightGBM и XGBoost
При фильтрации выборок данных для поиска значений сегментации LightGBM использует новую технику: одностороннюю выборку на основе градиента (GOSS), тогда как XGBoost определяет оптимальную сегментацию с помощью алгоритма предварительной классификации и алгоритма гистограммы. Экземпляры здесь представляют собой наблюдения/выборки.
Сначала давайте разберемся, как работает алгоритм предварительной классификации:
- Для каждого узла перебрать все функции
- Для каждой функции классифицируйте образцы в соответствии со значением функции.
- Выполните линейное сканирование, чтобы определить оптимальную сегментацию на основе получения базовой информации о текущей функции.
- Выберите лучший из всех результатов сегментации признаков
Проще говоря, алгоритм гистограммы делит все точки данных на дискретные области объекта и использует эти дискретные области для определения значения разделения гистограммы. Хотя с точки зрения скорости вычислений алгоритм гистограммы более эффективен, чем алгоритм предварительной классификации, который должен пройти все возможные точки сегментации на предварительно классифицированных собственных значениях, он все же медленнее, чем алгоритм GOSS.
Почему метод GOSS настолько эффективен?
В Adaboost вес выборки является хорошим индикатором важности выборки. Однако в дереве принятия решений о повышении градиента (GBDT) нет естественного веса выборки, поэтому метод выборки, используемый Adaboost, здесь нельзя использовать напрямую, и тогда нам нужен метод выборки на основе градиента.
Градиент характеризует наклон касательной к функции потерь, поэтому естественно предположить, что если градиент точки данных в каком-то смысле очень велик, то эти выборки очень важны для нахождения оптимальной точки разделения, поскольку потери выше.
GOSS хранит все примеры с большими градиентами и делает случайную выборку из примеров с небольшими градиентами. Например, если есть 500 000 строк данных, из которых 10 000 строк данных имеют большой градиент, то мой алгоритм выберет (из оставшихся 490 000 строк случайным образом выбираются 10 000 строк данных с большим градиентом + x%). Если x принять за 10%, то окончательный выбранный результат получается путем определения значения разделения, 59 000 строк, извлеченных из 500 000 строк.
Здесь есть основное допущение: если градиент обучающих примеров в обучающей выборке мал, то и ошибка обучения алгоритма на этой обучающей выборке будет мала, потому что обучение уже завершено.
Чтобы использовать одно и то же распределение данных, GOSS вводит постоянный коэффициент для выборок данных с небольшим градиентом при вычислении прироста информации. Таким образом, GOSS обеспечивает хороший баланс между сокращением количества выборок данных и сохранением точности изученного дерева решений.
Как каждая модель обрабатывает атрибутивные категориальные переменные?
CatBoost
CatBoost может назначать индикаторы категориальных переменных, а затем получать результат в виде однократного кодирования через однократное максимальное (однократное максимальное: на всех функциях использовать однократное кодирование для разных чисел, меньших или равных заданному значение параметра).
Если в операторе CatBoost не задан параметр «пропустить», CatBoost будет обрабатывать все столбцы как числовые переменные.
Обратите внимание, что алгоритм CatBoost выдаст ошибку, если столбец данных содержит строковые значения. Кроме того, переменные типа int со значениями по умолчанию также по умолчанию обрабатываются как числовые данные. В CatBoost переменные должны быть объявлены, чтобы алгоритм обрабатывал их как категориальные переменные.
Для категориальных переменных с большим количеством возможных значений, чем однократный максимум, CatBoost использует очень эффективный метод кодирования, который похож на кодирование среднего, но уменьшает переоснащение. Его конкретный метод реализации заключается в следующем:
1. Произвольно отсортируйте входной набор выборок и сгенерируйте несколько наборов случайных перестановок.
2. Преобразуйте токен значения с плавающей запятой или атрибута в целое число.
3. Преобразуйте все категориальные результаты собственных значений в числовые результаты в соответствии со следующей формулой.
Среди них CountInClass указывает, сколько образцов отмечено значением «1» в текущем значении классификационного признака; Prior — начальное значение молекулы, которое определяется по начальным параметрам. TotalCount — это количество выборок во всех выборках (включая текущую выборку), которые имеют то же значение категориального признака, что и текущая выборка.
Его можно представить следующей математической формулой:
LightGBM
Подобно CatBoost, LighGBM также может обрабатывать данные атрибутов, используя ввод имен функций; он не выполняет однократное кодирование данных, поэтому это намного быстрее, чем однократное кодирование. LGBM использует специальный алгоритм для определения значения сегментации атрибутивных признаков.
Обратите внимание, что категориальные переменные необходимо преобразовать в целочисленные переменные перед созданием набора данных, подходящего для LGBM; этот алгоритм не позволяет передавать строковые данные в параметр категориальных переменных.
XGBoost
В отличие от алгоритмов CatBoost и LGBM, сам XGBoost не может обрабатывать категориальные переменные, но, как и случайные леса, принимает только числовые данные. Следовательно, прежде чем передавать категориальные данные в XGBoost, данные должны быть обработаны различными методами кодирования: такими как кодирование маркеров, среднее кодирование или горячее кодирование.
Сходство в гиперпараметрах
Все эти модели требуют настройки большого количества параметров, но мы будем говорить только о самых важных. Ниже приведена таблица, в которой важные параметры различных алгоритмов упорядочены по функциям.
выполнить
Здесь я использую набор данных Kaggle о задержках рейсов в 2015 году, который содержит как категориальные, так и числовые переменные. В этом наборе данных около 5 миллионов записей, поэтому он подходит для оценки скорости обучения и точности трех алгоритмов бустинга одновременно. Я использовал 10% данных: 500 000 строк записей.
Для моделирования используются следующие функции:
- месяц, день, неделя: целые данные
- Маршрут или номер рейса: целочисленные данные
- Аэропорты отправления и прибытия: числовые данные
- Время отправления: данные с плавающей запятой
- Ситуация с задержкой прибытия: эта функция используется в качестве цели прогнозирования и превращается в двоичную переменную: задерживается ли рейс более чем на 10 минут.
- Расстояние и время полета: данные с плавающей запятой
import pandas as pd, numpy as np, time
from sklearn.model_selection import train_test_split
data = pd.read_csv("flights.csv")
data = data.sample(frac = 0.1, random_state=10)
data = data[["MONTH","DAY","DAY_OF_WEEK","AIRLINE","FLIGHT_NUMBER","DESTINATION_AIRPORT",
"ORIGIN_AIRPORT","AIR_TIME", "DEPARTURE_TIME","DISTANCE","ARRIVAL_DELAY"]]
data.dropna(inplace=True)
data["ARRIVAL_DELAY"] = (data["ARRIVAL_DELAY"]>10)*1
cols = ["AIRLINE","FLIGHT_NUMBER","DESTINATION_AIRPORT","ORIGIN_AIRPORT"]
for item in cols:
data[item] = data[item].astype("category").cat.codes +1
train, test, y_train, y_test = train_test_split(data.drop(["ARRIVAL_DELAY"], axis=1), data["ARRIVAL_DELAY"],
random_state=10, test_size=0.25)
XGBoost
import xgboost as xgb
from sklearn import metrics
def auc(m, train, test):
return (metrics.roc_auc_score(y_train,m.predict_proba(train)[:,1]),
metrics.roc_auc_score(y_test,m.predict_proba(test)[:,1]))
# Parameter Tuning
model = xgb.XGBClassifier()
param_dist = {"max_depth": [10,30,50],
"min_child_weight" : [1,3,6],
"n_estimators": [200],
"learning_rate": [0.05, 0.1,0.16],}
grid_search = GridSearchCV(model, param_grid=param_dist, cv = 3,
verbose=10, n_jobs=-1)
grid_search.fit(train, y_train)
grid_search.best_estimator_
model = xgb.XGBClassifier(max_depth=50, min_child_weight=1, n_estimators=200,\
n_jobs=-1 , verbose=1,learning_rate=0.16)
model.fit(train,y_train)
auc(model, train, test)
Light GBM
import lightgbm as lgb
from sklearn import metrics
def auc2(m, train, test):
return (metrics.roc_auc_score(y_train,m.predict(train)),
metrics.roc_auc_score(y_test,m.predict(test)))
lg = lgb.LGBMClassifier(silent=False)
param_dist = {"max_depth": [25,50, 75],
"learning_rate" : [0.01,0.05,0.1],
"num_leaves": [300,900,1200],
"n_estimators": [200]
}
grid_search = GridSearchCV(lg, n_jobs=-1, param_grid=param_dist, cv = 3, scoring="roc_auc", verbose=5)
grid_search.fit(train,y_train)
grid_search.best_estimator_
d_train = lgb.Dataset(train, label=y_train)
params = {"max_depth": 50, "learning_rate" : 0.1, "num_leaves": 900, "n_estimators": 300}
# Without Categorical Features
model2 = lgb.train(params, d_train)
auc2(model2, train, test)
#With Catgeorical Features
cate_features_name = ["MONTH","DAY","DAY_OF_WEEK","AIRLINE","DESTINATION_AIRPORT",
"ORIGIN_AIRPORT"]
model2 = lgb.train(params, d_train, categorical_feature = cate_features_name)
auc2(model2, train, test)
CatBoost
При настройке CatBoost сложно назначить метрики категориальным функциям. Поэтому я также привожу результаты настройки, когда никакие категориальные признаки не передаются, и оцениваю две модели: одну с категориальными признаками и одну без них. Я отрегулировал только один горячий максимум, потому что он не влияет на другие параметры.
import catboost as cb
cat_features_index = [0,1,2,3,4,5,6]
def auc(m, train, test):
return (metrics.roc_auc_score(y_train,m.predict_proba(train)[:,1]),
metrics.roc_auc_score(y_test,m.predict_proba(test)[:,1]))
params = {'depth': [4, 7, 10],
'learning_rate' : [0.03, 0.1, 0.15],
'l2_leaf_reg': [1,4,9],
'iterations': [300]}
cb = cb.CatBoostClassifier()
cb_model = GridSearchCV(cb, params, scoring="roc_auc", cv = 3)
cb_model.fit(train, y_train)
With Categorical features
clf = cb.CatBoostClassifier(eval_metric="AUC", depth=10, iterations= 500, l2_leaf_reg= 9, learning_rate= 0.15)
clf.fit(train,y_train)
auc(clf, train, test)
With Categorical features
clf = cb.CatBoostClassifier(eval_metric="AUC",one_hot_max_size=31, \
depth=10, iterations= 500, l2_leaf_reg= 9, learning_rate= 0.15)
clf.fit(train,y_train, cat_features= cat_features_index)
auc(clf, train, test)
Эпилог
Чтобы оценить модель, мы должны учитывать как скорость, так и точность модели.
Имейте в виду, что CatBoost лучше всего работает на тестовом наборе с самой высокой точностью на тестовом наборе (0,816), наименьшим переоснащением (близкая точность на обучающем и тестовом наборах) и наименьшим временем прогнозирования и отладки. Но эта производительность возникает только при наличии категориальных признаков и корректировке однократного максимума. Без использования преимуществ алгоритма CatBoost в отношении этих функций его производительность становится наихудшей: точность всего 0,752. Поэтому мы считаем, что CatBoost будет работать хорошо, только если мы включим в данные категориальные переменные и правильно настроим эти переменные.
Второй использует XGBoost, который также работает довольно хорошо. Даже без учета того, что набор данных содержит категориальные переменные, которые можно использовать после преобразования в числовые переменные, его точность очень близка к CatBoost. Однако единственная проблема с XGBoost заключается в том, что он слишком медленный. Особенно его настройка очень утомительна (я потратил 6 часов на запуск GridSearchCV - очень плохо). Лучшим вариантом является настройка параметров отдельно вместо использования GridSearchCV.
Последняя модель — LightGBM. Здесь следует отметить, что LightGBM очень плохо работает как по скорости обучения, так и по точности при использовании функций CatBoost. Я думаю, это связано с тем, что он использует некоторое модифицированное среднее кодирование категориальных данных, что, в свою очередь, приводит к переоснащению (точность обучающего набора очень высока: 0,999, особенно по сравнению с точностью тестового набора). Но если мы используем LightGBM обычно, как XGBoost, он достигает аналогичной точности быстрее, чем XGBoost, если не выше (LGBM — 0,785, XGBoost — 0,789).
Наконец, следует отметить, что эти выводы справедливы для данного конкретного набора данных и могут быть или не быть верными для других наборов данных. Но в большинстве случаев XGBoost медленнее двух других алгоритмов.
Итак, какой алгоритм вы предпочитаете?