#!/usr/bin/env python
# coding: utf-8
#
#
# ## Открытый курс по машинному обучению. Сессия № 2
# Автор материала: программист-исследователь Mail.ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ Юрий Кашницкий. Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала.
# # Тема 10. Бустинг
# ## Часть 2. Сравнение Xgboost и градиентного бустинга Sklearn
# ## XGBoost
#
# Выделяют три группы параметров:
# - Общие параметры, отвечающие за базовый алгоритм для бустинга и распараллеливание.
# - Параметры выбранного базового алгоритма.
# - Параметры обучения, отвечающие за функцию потерь и метрику качества на валидации.
#
# **1. Общие параметры:**
# - booster [default=gbtree] - тип базового алгоритма для бустинга: дерево решений gbtree или линейная модель gblinear.
# - silent [default=0] - выдавать (silent=0) или нет (silent=1) сообщения по ходу работы алгоритма.
# - nthread [default to maximum number of threads available if not set] - число нитей доступных для параллельной работы xgboost.
#
# **2. Параметры базового алгоритма:**
#
# **2.1. Дерево решений:**
# - eta [default=0.3] - темп обучения, перед добавлением дерева в композицию оно умножается на eta. Используется для предотвращения переобучения за счёт "сокращения" весов базовых алгоритмов, делая модель более консервативной. Чем меньше eta, тем больше нужно итераций num_boost_round для обучения модели с хорошим качеством. Диапазон: [0, 1]
# - gamma [default=0] - минимальное снижение значения функции потерь, необходимое для дальнейшего разбиения вершины дерева. Большие значения gamma > 0 приводят к более консервативным моделям. Диапазон: [0, $\infty$).
# - max_depth [default=6] - максимальная глубина дерева. Диапазон: [1, $\infty$).
# - min_child_weight [default=1] - минимальное необходимое (взвешенное) число примеров в каждой вершине. Чем больше, тем более консервативна итоговая модель. Диапазон: [0, $\infty$).
# - max_delta_step [default=0] - обычно равен нулю. Положительные значения используются при несбалансированных классах для ускорения сходимости. Диапазон [0, $\infty$).
# - subsample [default=1] - доля выборки, используемая для обучения каждого дерева. Если subsample < 1, то выбирается случайная подвыборка, что помогает в борьбе с переобучением. Диапазон: (0, 1]
# - colsample_bytree [default=1] - доля признаков, используемая для обучения каждого дерева. Диапазон: (0, 1]
# - lambda [default=1] - коэффициент перед $L_2$-регуляризатором в функции потерь.
# - alpha [default=0] - коэффициент перед $L_1$-регуляризатором в функции потерь.
#
# **2.2. Линейная модель:**
# - lambda [default=0] - коэффициент перед $L_2$-регуляризатором вектора весов в функции потерь.
# - alpha [default=0] - коэффициент перед $L_1$-регуляризатором вектора весов в функции потерь.
# - lambda_bias [default=0] - коэффициент перед $L_2$-регуляризатором смещения (свободного члена) в функции потерь.
#
# **3. Параметры задачи обучения:**
# - objective [default=reg:linear] - используемая при обучении функция потерь:
# - "reg:linear" – линейная регрессия.
# - "reg:logistic" – логистическая регрессия.
# - "binary:logistic" – логистическая регрессия для бинарной классификации, на выходе - вероятность.
# - "binary:logitraw" – то же самое, но на выходе - значение до его преобразования логистической функцией.
# - "count:poisson" – регрессия Пуассона (используется для оценки числа каких-то событий, счётный признак), на выходе - матожидания распределения Пуассона. В этом случае max_delta_step автоматически устанавливается равным 0.7.
# - "multi:softmax" – обобщение логистической регрессии на многоклассовый случай. При этом нужно задать параметр num_class.
# - "multi:softprob" – то же самое, но на выходе - вектор размера ndata * nclass, который можно преобразовать в матрицу, содержащую вероятности отнесения данного объекта к данному классу.
# - "rank:pairwise" – используется для задач ранжирования.
# - base_score [default=0.5] - инициализация значения модели для всех примеров, глобальное смещение.
# - eval_metric [default according to objective] - метрика качества на валидационной выборке (по умолчанию соответствует функции потерь: rmse - для регрессии, error - для классификации, mean average precision - для ранжирования). Выбрать можно одну из следующих метрик:
# - "rmse": root mean square error.
# - "logloss": минус логарифм правдоподобия.
# - "error": доля ошибок для бинарной классификации.
# - "merror": то же самое для многоклассовой классификации.
# - "mlogloss": logloss для многоклассовой классификации.
# - "auc": AUC.
# - "ndcg": Normalized Discounted Cumulative Gain.
# - "map": Mean average precision.
# - "ndcg@n",”map@n”: здесь n - целое число, первые n позиций в списке не учитываются.
# - "ndcg-",”map-”,”ndcg@n-”,”map@n-”: списку из всех положительных примеров будет присвоено значение 0 (вместо 1).
# - seed [default=0] - для воспроизводимости "случайности".
#
# **Параметры в xgboost.train**:
# - params (dict) – параметры, описанные выше.
# - dtrain (DMatrix) – обучающая выборка.
# - num_boost_round (int) – число итераций бустинга.
# - evals (list) – список для оценки качества во время обучения.
# - obj (function) – собственная функция потерь.
# - feval (function) – собственная функция для оценки качества.
# - maximize (bool) – нужно ли максимизировать feval.
# - early_stopping_rounds (int) – активирует early stopping. Ошибка на валидации должна уменьшаться каждые early_stopping_rounds итераций для продолжения обучения. Список evals должен быть не пуст. Возвращается модель с последней итерации. Если произошел ранний останов, то модель будет содержать поля: bst.best_score и bst.best_iteration.
# - evals_result (dict) – результаты оценки качества.
# - verbose_eval (bool) – вывод значения метрики качества на каждой итерации бустинга.
# - learning_rates (list or function) – коэффициент скорости обучения для каждой итерации - list l: eta = l[boosting round] - function f: eta = f(boosting round, num_boost_round).
# - xgb_model (file name of stored xgb model or ‘Booster’ instance) – возможность продолжить обучения имеющейся модели XGB.
#
#
# ## sklearn.ensemble.GradientBoostingClassifier
# - loss [default="deviance"] - оптимизируемая функция потерь. Одна из {"deviance", "exponential"}. Первая соответствует логистической регрессии и возвращает вероятности, вторая - AdaBoost.
# - learning_rate [default=0.1] - темп обучения, аналогично eta для XGBoost.
# - n_estimators [default=100] - число итераций градиентного бустинга.
# - max_depth [default=3] - аналогично max_depth для XGBoost.
# - min_samples_split [default=2] - минимальное число примеров, необходимое для разветвления в данной вершине, аналогично min_child_weight для XGBoost.
# - min_samples_leaf [default=1] - минимальное число примеров в листе.
# - min_weight_fraction_leaf [default=0.0] - минимальное взвешенное число примеров в листе.
# - subsample [default=1.0] - аналогично subsample для XGBoost.
# - max_features (int, float, string or None) [default=None] - число (или доля) признаков, используемых при разбиении вершины.
# - "auto", тогда max_features=sqrt(n_features).
# - "sqrt", тогда max_features=sqrt(n_features).
# - "log2", тогда max_features=log2(n_features).
# - None, тогда max_features=n_features.
# - max_leaf_nodes [default=None]
# - init (BaseEstimator or None) [default=None] - алгоритм для начальных предсказаний.
# - verbose [default=0] - аналогично silent для XGBoost.
# - warm_start [default=False] - если True, используется ансамбль с предыдущего вызова fit, новые алгоритмы добавляются к нему, иначе строится новый алгоритм.
# ## Сравнение алгоритмов по времени работы
#
# Посмотрим на время обучения классификаторов XGBooster и GradientBoostingClassifier. Для этого будем генерировать выборку из 1000 объектов и 50 признаков с помощью sklearn.datasets.make_classification и замерять время обучения.
# In[ ]:
import time
import xgboost as xgb
from sklearn.datasets import make_classification
from sklearn.ensemble import GradientBoostingClassifier
from tqdm import tqdm_notebook
xgb_params1 = {
"booster": "gbtree",
"max_depth": 3,
"eta": 0.1,
"silent": 1,
"objective": "binary:logistic",
"nthread": 1,
}
xgb_params2 = {
"booster": "gbtree",
"max_depth": 3,
"eta": 0.1,
"silent": 1,
"objective": "binary:logistic",
"nthread": 4,
}
sklearn_params = {"n_estimators": 100, "max_depth": 3}
xgb_time1 = list()
xgb_time2 = list()
sklearn_time = list()
n_runs = 50
for i in tqdm_notebook(range(n_runs)):
# Generating dataset
X, y = make_classification(n_samples=1000, n_features=50, n_informative=20)
# Training XGBooster (nthread=1)
t = time.time()
bst = xgb.train(xgb_params1, xgb.DMatrix(X, label=y), num_boost_round=100)
elapsed = time.time() - t
xgb_time1.append(elapsed)
# Training XGBooster (nthread=4)
t = time.time()
bst = xgb.train(xgb_params2, xgb.DMatrix(X, label=y), num_boost_round=100)
elapsed = time.time() - t
xgb_time2.append(elapsed)
# Training GradientBoostingClassifier
t = time.time()
clf = GradientBoostingClassifier(**sklearn_params).fit(X, y)
elapsed = time.time() - t
sklearn_time.append(elapsed)
# In[ ]:
get_ipython().run_line_magic('matplotlib', ' inline')
import matplotlib.pyplot as plt
xgb_mean1 = sum(xgb_time1) / n_runs
xgb_mean2 = sum(xgb_time2) / n_runs
sklearn_mean = sum(sklearn_time) / n_runs
plt.figure(figsize=(12, 8))
plt.plot(xgb_time1, label="XGBooster (nthread = 1)", lw=2)
plt.plot(xgb_time2, label="XGBooster (nthread = 4)", lw=2)
plt.plot(sklearn_time, label="GradientBoostingClassifier", lw=2)
plt.legend(loc="best")
plt.text(
1,
(xgb_mean1 + sklearn_mean) / 2,
"XGBoost (nthread = 1) mean time = %.2f" % xgb_mean1
+ "\n\nXGBoost (nthread = 4) mean time = %.2f" % xgb_mean2
+ "\n\nScikit-learn mean time = %.2f" % sklearn_mean,
fontsize=15,
)
plt.xlabel("Iteration number")
plt.ylabel("Running time, sec")
plt.title("XGBoost vs. GradientBoostingClassifier comparison")
plt.show()
# ## Выводы
#
# Основные преимущества XGBoost по сравнению с sklearn.ensembles.GradientBoostingClassifier:
# - Помимо деревьев возможно использование линейных моделей в качестве базовых классификаторов.
# - Скорость работы.
# - Возможность распараллеливания.
# - Значительно больший выбор стандартных функций потерь, а также возможность задавать свою функцию потерь.
# - Наличие регуляризаторов в итоговой функции потерь и возможность задавать их коэффициенты, что даёт еще один метод борьбы с переобучением, помимо использования случайности (subsample, colsample_bytree) и основных параметров дерева решений.
# - Встроенная обработка missing values.
# ## Полезные ссылки
# - [XGBoost](https://xgboost.readthedocs.org/en/latest/parameter.html)
# - [GradientBoostingClassifier](http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html)
# - [Сравнение](https://github.com/szilard/benchm-ml) различных библиотек для машинного обучения, в том числе sklearn и xgboost