Введение

В предыдущих статьях мы представили Дерево решений, сравнили дерево решений с Случайным лесом, сравнили случайный лес с AdaBoost, и сравнил AdaBoost с Усиление градиента. Это было настоящее путешествие. К сожалению, всему есть конец.

Эта статья завершит серию алгоритмов дерева. В частности, сначала мы рассмотрим XGBoost, что означает Экстремальное повышение градиента. Как следует из названия, XGBoost — это экстремальная и продвинутая версия повышения градиента, более гибкая (т. е. параметры регуляризации) и более эффективная (т. е. параллельные вычисления). Кроме того, мы будем использовать набор данных Титаник в качестве примера для иллюстрации.

⚡ Код будет предоставлен в последнем разделе этой статьи.

XGBoost

Прежде всего, XGBoost можно использовать в регрессии, бинарной классификации и многоклассовой классификации (один против всех). В этой статье мы сосредоточимся на двоичной классификации.

Во-вторых, у XGBoost и повышения градиента много общего, включая логарифмические шансы и логистические функции. Если вы не знакомы с этими понятиями, посмотрите эту статью или это видео (StatQuest).

Итак, без лишних слов, давайте погрузимся в XGBoost! Предположим, мы хотим предсказать, страдает ли человек ожирением (бинарная классификация), используя его/ее пол и уровень холестерина. Конкретно у нас в обучающей выборке 10 человек — 3 из них с ожирением, остальные 7 человек без ожирения.

💦 Дружеское напоминание — процесс может показаться утомительно долгим, но на самом деле концепция проста и понятна!

1. Присваивает одинаковые начальные прогнозы (0,5) образцам.

Для начала мы назначаем начальные прогнозы каждой выборке. Поскольку мы не обучали никакую модель и ничего не извлекали из данных, мы можем просто установить 0,5 в качестве начального прогноза для каждой выборки. А именно, у человека есть 50% шанс быть тучным.

0,5 — значение по умолчанию в модуле XGBoost. Мы можем изменить его на любое значение, которое нам нравится, если оно находится в диапазоне [0, 1].

2. Рассчитайте псевдоостаток для каждого образца.

Псевдо-остатки — это не что иное, как промежуточный член ошибки, указывающий на то, что прогнозируемые значения являются временными/промежуточными. Следовательно, псевдоостаток рассчитывается как (фактическое значение — прогнозируемое значение).

В этом примере 3 человека страдают ожирением (обозначено 1), поэтому их псевдоостатки составляют (1–0,5) : 0,5. Остальные 7 человек не страдают ожирением (обозначены 0), поэтому их псевдоостатки равны (0–0,5): -0,5.

⚡ Прогнозируемые значения всех выборок одинаковы, поскольку это наше ПЕРВОЕ дерево. После первой итерации прогнозируемые значения, вероятно, будут другими.

3. Построить дерево решений на основе псевдоостатков

Теперь мы можем построить наше первое дерево. Как и в обычном дереве решений, мы разбиваем атрибуты с наилучшим качеством. В отличие от обычного дерева решений, качество НЕ измеряется среднеквадратичной ошибкой псевдоостатков, XGBoost имеет уникальный метод выбора атрибута.

Чтобы быть более конкретным, мы рассчитываем оценку сходства для каждого узла. Тогда Прибыль узла представляет собой показатель сходства его левого дочернего узла + показатель сходства его правого дочернего узла — показатель сходства себя. Наконец, мы выбираем разбиение с наибольшей прибылью.

Я знаю, это звучит довольно абстрактно, давайте посмотрим на формулы, чтобы получить более конкретное представление.

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

⚡ Суммируем остатки перед тем, как возьмем квадрат. Таким образом, положительные остатки и отрицательные остатки будут устранены!

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

4. Обрежьте дерево — и листья, и узлы

Чтобы уменьшить проблему переобучения, мы можем установить собственные пороги обрезки. Начнем с сокращения узлов. γ (gamma) в XGBoost – это минимально допустимое усиление узла. Если узел имеет более низкий коэффициент усиления, чем γ, этот узел будет обрезан, а дерево станет более мелким. Следовательно, если мы установим высокое γ или добавим большое λ при вычислении показателей сходства, дерево будет более ограниченным по глубине.

Более того, XGBoost может обрезать листья с помощью Cover. Мы можем думать о покрытии как о минимально допустимом весе дочернего узла. Формула покрытия — это просто знаменатель показателя сходства без λ — Σ [Вероятность * (1-Вероятность)]. Если только 1 дочерний узел имеет больший вес, чем покрытие, мы все равно удалим оба листа, так как разделение допустимо только тогда, когда 2 листа удовлетворяют ограничению покрытия.

⚡ Обрезаем дерево снизу вверх. Если нижний узел имеет более высокий коэффициент усиления, чем γ, мы останавливаемся. Даже если верхний узел имеет более низкий коэффициент усиления, чем γ, мы не удаляем его.

5. Вычислите выходное значение каждого узла в логарифмических коэффициентах.

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

⚡ Формула почти такая же, как и в бустинге градиента, за исключением того, что теперь у нас есть дополнительный параметр регуляризации — λ.

Как и прежде, λ здесь снижает чувствительность прогноза к изолированным наблюдениям(см. StatQuest)и снижает вероятность переобучения. Скажем, λ равно 1, и у нас есть лист, содержащий 2 человека с ожирением и 1 человека без ожирения. Остаток тучности человека 0,5; остаток не страдающего ожирением человека составляет -0,5 (шаг 2). Предыдущие вероятности для всех равны 0,5, поскольку мы назначаем одинаковые прогнозы на шаге 1. Следовательно, выходное значение этого листа равно (0,5 + 0,5 + (-0,5)) / [(0,5 * (1–0,5)) + (0,5 * (1–0,5)) + (0,5 * (1–0,5)) + 1], что составляет 0,286.

⚡ Предыдущие вероятности для разных выборок должны быть разными. Они должны быть одинаковыми только в дереве First.

6. Обновляет предыдущие предсказанные вероятности

Выходные значения, рассчитанные на шаге 5, используются для обновления предсказанных вероятностей (шаг 1). Формула выглядит следующим образом.

Как и при повышении градиента, мы можем назначить скорость обучения. Что ж, в XGBoost скорость обучения называется eta.

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

Прежде чем мы подставим формулу, нам нужно сделать еще одну вещь. На шаге 1 мы предсказали, что каждый человек имеет 50%-й шанс страдать ожирением, но это не логарифмическая вероятность. Нам нужно преобразовать предсказанную вероятность в логарифм шансов, и ее можно рассчитать по формуле log(p/(1-p)).

В частности, логарифм нечетной вероятности 0,5 равен log(0,5/(1–0,5)), что равно 0. Следовательно, учитывая эта 0,3, новый логарифм нечетный для человека, принадлежащего листу примера на шаге 5, будет 0 (предыдущее нечетное логарифмическое) + 0,3 * 0,286 (выходное значение дерева 1), что равно 0,086.

⚡ Мы делаем маленький шаг за один раз, устанавливая низкое ета. Даже если направление изменений не прямо сейчас (т. е. люди с ожирением имеют более низкие логарифмические шансы ожирения), мы сможем исправить это по мере увеличения числа итераций.

7. Повторите шаги с 2 по 6.

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

После этого мы повторяем шаги со 2 по 6 до тех пор, пока не будет построено необходимое количество деревьев или остатки не станут малыми (прогнозируемые значения очень близки к фактическим значениям).

Прежде чем мы продолжим, как именно XGBoost выполняет классификацию?

Во-первых, мы используем формулу из шага 6 для расчета логарифмической нечетности для новой выборки. Затем мы преобразуем логарифмическую нечетность обратно в вероятность, используя формулу из шага 7, и сравниваем эту вероятность с нашим порогом!

Если логарифм нечетного для человека равен 0,6, прогнозируемая вероятность того, что этот человек страдает ожирением, равна 0,646. При пороговом значении 0,6 этот человек будет прогнозироваться как «Ожирение», поскольку 0,646 выше, чем 0,6.

Вот и все! Вы могли заметить, что я копирую и вставляю контент из повышения градиента, и да, я это сделал. На самом деле, повышение градиента и XGBoost имеют много общего, за исключением того, что XGBoost более гибче и эффективнее.

Теперь мы изучили рабочий процесс XGBoost и можем использовать xgboost в Python. Кроме того, существует МНОЖЕСТВО параметров, которые можно настроить (мы поговорим только о некоторых из них).

  • max_depth (по умолчанию — 6): максимальная глубина дерева.
  • n_estimators (по умолчанию — 100): количество деревьев в XGBoost, по умолчанию 100.
  • eta (по умолчанию — 0,3): скорость обучения; определяет уменьшение вклада дерева на каждой итерации повышения. Когда скорость обучения высока, XGBoost имеет тенденцию к переоснащению данных; когда скорость обучения низка, нам нужно больше итераций, чтобы повысить точность. Таким образом, n_estimators и learning_rate можно рассматривать как компромисс.
  • gamma (по умолчанию — 0): минимальное усиление, необходимое для разделения узла.
  • reg_lambda (по умолчанию — 0): параметр регуляризации, используемый для снижения чувствительности прогноза к изолированным наблюдениям.

xgboost НЕ входит в библиотеку sklearn, это независимая библиотека. Прежде чем импортировать библиотеку, ее необходимо установить.

# Install
!pip install xgboost
# Import
import xgboost as xgb

Пример

Перейдем к примеру Титаника. Подводя итог, мы хотим предсказать, выживет ли пассажир в событии Титаник. Мы предварительно обработали набор данных до и выбрали нужные нам независимые переменные XPclass, Sex, Age, SibSp, Parch, Fare, а также зависимую переменную ySurvived. Набор данных выглядит так —

Далее мы разделяем данные на обучающую и тестовую выборку в соотношении 80/20. А именно, мы используем 80% данных для обучения модели, 20% данных для оценки модели.

x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=65)

Теперь мы хотим узнать, как работает XGBoost. Во-первых, мы создаем переменную learning_rate_range для хранения скорости обучения, которую мы хотим попробовать — от 0,01 до 1 с интервалом 0,05. Затем мы сохраняем результаты обучения и тестирования XGBoost в списке train_XG и test_XG.

# XGBoost (different learning rate)
learning_rate_range = np.arange(0.01, 1, 0.05)
test_XG = [] 
train_XG = []
for lr in learning_rate_range:
    xgb_classifier = xgb.XGBClassifier(eta = lr)
    xgb_classifier.fit(x_train, y_train)
    train_XG.append(xgb_classifier.score(x_train, y_train))
    test_XG.append(xgb_classifier.score(x_test, y_test))

Затем мы можем нарисовать линейный график, чтобы увидеть, как работает XGBoost.

fig = plt.figure(figsize=(10, 7))
plt.plot(learning_rate_range, train_XG, c='orange', label='Train')
plt.plot(learning_rate_range, test_XG, c='m', label='Test')
plt.xlabel('Learning rate')
plt.xticks(learning_rate_range)
plt.ylabel('Accuracy score')
plt.ylim(0.6, 1)
plt.legend(prop={'size': 12}, loc=3)
plt.title('Accuracy score vs. Learning rate of XGBoost', size=14)
plt.show()

XGBoost справился со своей задачей. Он имеет точность 81,1% при скорости обучения 0,11. Однако проблема переобучения весьма заметна. Проблема переобучения заключается в том, что модель получает слишком много нерелевантных деталей из обучающих данных, и ее производительность на невидимых данных будет нестабильной. Другими словами, даже если мы обнаружим, что модель хорошо работает на этих конкретных данных тестирования, мы не можем быть уверены, что она будет продолжать работать так же.

Таким образом, нам нужно решить эту проблему путем регуляризации модели. Когда мы навязываем модели регуляризацию, мы ограничиваем ее возможности.

Вообще говоря, мы можем уменьшить количество итераций (n_estimators) и скорость обучения (learning_rate) или увеличить минимальное усиление, необходимое в узле (gamma) и параметр регуляризации (reg_lambda), чтобы модель не могла изучить функцию тренировочный наборслишком хорошо.

В этом примере мы только настроим reg_lambda на 1 и выполним поиск по сетке оптимального min_child_weight, чтобы уменьшить проблему переобучения. В реальном мире мы можем использовать поиск по сетке и K-кратную перекрестную проверку, чтобы найти наилучшее сочетание параметров (см. эту статью).

Кроме того, мы можем видеть, что оценка обучения очень близка к 1, когда скорость обучения выше 0,5, в то время как оценка тестирования продолжает падать, что подразумевает проблему переобучения. Таким образом, мы меняем learning_rate_range на —от 0,01 до 0,5 с интервалом 0,05.

# new learning rate range
learning_rate_range = np.arange(0.01, 0.5, 0.05)
fig = plt.figure(figsize=(19, 17))
idx = 1
# grid search for min_child_weight
for weight in np.arange(0, 4.5, 0.5):
    train = []
    test = []
    for lr in learning_rate_range:
        xgb_classifier = xgb.XGBClassifier(eta = lr, reg_lambda=1, min_child_weight=weight)
        xgb_classifier.fit(x_train, y_train)
        train.append(xgb_classifier.score(x_train, y_train))
        test.append(xgb_classifier.score(x_test, y_test))
    fig.add_subplot(3, 3, idx)
    idx += 1
    plt.plot(learning_rate_range, train, c='orange', label='Training')
    plt.plot(learning_rate_range, test, c='m', label='Testing')
    plt.xlabel('Learning rate')
    plt.xticks(learning_rate_range)
    plt.ylabel('Accuracy score')
    plt.ylim(0.6, 1)
    plt.legend(prop={'size': 12}, loc=3)
    title = "Min child weight:" + str(weight)
    plt.title(title, size=16)
plt.show()

Оно работает! Как видите, проблема переобучения серьезна, когда min_child_weight равно 0, в то время как оценка обучения и тестирования обычно становится ближе по мере увеличения min_child_weight. В частности, проблема переобучения кажется незначительной, когда min_child_weight равно 3,5, поэтому давайте увеличим этот график.

Прохладный! Хотя мы не полностью устранили переобучение, мы уменьшили разрыв между результатами обучения и результатами тестирования. В частности, XGBoost достигает максимальной точности 0,846 при скорости обучения 0,16.

Разве не удивительно, что мы достигаем лучшей производительности после того, как наложили некоторые параметры регуляризации?

Да, такое случается время от времени. Регуляризация ограничивает способность модели изучать конкретную тенденцию тренировочного набора, а НЕ общую тенденцию. Иногда повышающая модель может лучше учиться, когда она находится в процессе регуляризации.

В этом примере показаны возможности XGBoost и его гибкость с точки зрения настройки параметров. Кроме того, если вы запустите алгоритм на своем компьютере, вы обнаружите, что он на самом деле быстрый благодаря своей природе параллельных вычислений.

Однако мы всегда должны уделять особое внимание проблеме переобучения. Алгоритм бустинга, включая XGBoost, может слишком хорошо и подробно изучить обучающие данные и не уловить общую тенденцию. К счастью, мы всегда можем настроить параметры, чтобы ограничить его способность к обучению, пока не найдем приемлемую степень переобучения.

💛 Если вам понравилась эта статья, обязательно подпишитесь на меня! Это действительно воодушевляет меня и мотивирует продолжать делиться. Большое спасибо.

Рекомендации

  1. https://xgboost.readthedocs.io/en/stable/parameter.html
  2. https://analyticsindiamag.com/top-xgboost-interview-questions-for-data-scientists/

Кодирование