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

Но, как и любой алгоритм машинного обучения, XGBoost также требует настройки гиперпараметров. Чтобы сделать это простым и эффективным способом, мы объединим его с GridSearchCV Scikit-Learn.

Гиперпараметры XGBoost

Есть несколько параметров, которые мы можем использовать при определении классификатора или регрессора XGBoost. Если вы хотите увидеть их все, посмотрите официальную документацию здесь. В этой статье мы рассмотрим только самые распространенные. Такие как:

  • Learning_rate: скорость обучения. На каждом этапе повышения эти значения уменьшают вес новых функций, предотвращая переоснащение или локальный минимум. Это значение должно быть от 0 до 1. Значение по умолчанию - 0,3.
  • max_depth: максимальная глубина дерева. Будьте осторожны, чем больше глубина, тем сложнее модель и ее будет легче переоснастить. Это значение должно быть целым числом больше 0 и иметь по умолчанию 6.
  • n_estimators: количество деревьев в нашем ансамбле.
  • гамма: термин регуляризации, связанный со сложностью модели. Это минимальная потеря, необходимая для раскола листа. Это может быть любое значение больше нуля, а значение по умолчанию - 0.
  • colsample_bytree: представляет долю столбцов для подвыборки. Это связано со скоростью алгоритма и предотвращением переобучения. Значение по умолчанию - 1, но может быть любым числом от 0 до 1.
  • лямбда: L2 регуляризация весов. Это способствует меньшему весу. По умолчанию 1, но это может быть любое значение.
  • альфа: регуляризация L1 весов. Как и лямбда, это также способствует уменьшению веса. По умолчанию 1, но также может быть любое значение.

Оптимизация гиперпараметров

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

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

Самый простой способ найти оптимальные гиперпараметры - проверить каждую комбинацию гиперпараметров. Это называется поиском по сетке. Количество итераций - это произведение количества каждого гиперпараметра. Например: Предположим, мы хотим протестировать модель с 5 значениями гиперпараметра альфа, 10 для бета и 2 для гаммы. Поиск по сетке будет выполняться 5 * 10 * 2 = 100 итераций.

Случайный поиск

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

Реализация

Чтобы прояснить ситуацию, давайте рассмотрим пример использования XGBoost с scikit-learn. Данные и процесс очистки, которые я использую здесь, такие же, как в предыдущей статье о сборке и вставке.

В этом примере мы будем использовать набор данных переписи 1994 года о доходах в США. Он содержит такую ​​информацию, как семейное положение, возраст, вид работы и многое другое. В качестве целевого столбца у нас есть категориальный тип данных, который сообщает, если зарплата меньше или равна 50 тысячам в год (0) или нет (1). Давайте изучим DataFrame с помощью метода info Pandas:

RangeIndex: 32561 entries, 0 to 32560
Data columns (total 15 columns):age               32561 non-null int64
workclass         32561 non-null object
fnlwgt            32561 non-null int64
education         32561 non-null object
education_num     32561 non-null int64
marital_status    32561 non-null object
occupation        32561 non-null object
relationship      32561 non-null object
race              32561 non-null object
sex               32561 non-null object
capital_gain      32561 non-null int64
capital_loss      32561 non-null int64
hours_per_week    32561 non-null int64
native_country    32561 non-null object
high_income       32561 non-null int8
dtypes: int64(6), int8(1), object(8)

Как мы видим, есть числовые (int64 и int8) и категориальные (объектные) типы данных. Мы должны обрабатывать каждый тип отдельно, чтобы отправить его предиктору.

Загрузка и очистка данных

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

import numpy as np
import pandas as pd
# Load CSV
df = pd.read_csv('data/income.csv')
# Convert target to categorical
col = pd.Categorical(df.high_income)
df["high_income"] = col.codes

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

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.preprocessing import MinMaxScaler
class PreprocessTransformer(BaseEstimator, TransformerMixin):
  def __init__(self, cat_features, num_features):
    self.cat_features = cat_features
    self.num_features = num_features
  def fit(self, X, y=None):
    return self
  def transform(self, X, y=None):
    df = X.copy()
    # Treat ? workclass as unknown
    df.loc[df['workclass'] == '?', 'workclass'] = 'Unknown'
    # Too many categories, just convert to US and Non-US
    df.loc[df['native_country']!='United-States','native_country']='non_usa'
    # Convert columns to categorical
    for name in self.cat_features:
      col = pd.Categorical(df[name])
      df[name] = col.codes
    # Normalize numerical features
    scaler = MinMaxScaler()
    df[self.num_features] = scaler.fit_transform(df[num_features])
    return df

Затем данные разделяются на обучающие и тестовые, чтобы позже мы могли увидеть, обобщена ли наша модель на невидимые данные.

from sklearn.model_selection import train_test_split
# Split the dataset into training and testing
X_train, X_test, y_train, y_test = train_test_split(
  df.drop('high_income', axis=1),
  df['high_income'],
  test_size=0.2,
  random_state=42,
  shuffle=True,
  stratify=df['high_income']
)

Строительство трубопровода

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

from sklearn.pipeline import Pipeline
from sklearn.feature_selection import SelectKBest, chi2
import xgboost as xgb
# Get columns list for categorical and numerical
categorical_features = df.select_dtypes('object').columns.tolist()
numerical_features = df.select_dtypes('int64').columns.tolist()
# Create a pipeline
pipe = Pipeline([
  ('preproc', PreprocTransformer(categorical_features, numerical_features)),
  ('fs', SelectKBest()),
  ('clf', xgb.XGBClassifier(objective='binary:logistic'))
])

Теперь мы определим наше пространство поиска для поиска по сетке. Здесь будут настраиваться гиперпараметры: n_estimators, learning_rate, max_depth, colsample_bytree и gamma . Всего получается 288 комбинаций. Мы также используем KFold с 10 разделениями в качестве перекрестной проверки и AUC и точность в качестве оценки.

from sklearn.model_selection import KFold, GridSearchCV
from sklearn.metrics import accuracy_score, make_scorer
# Define our search space for grid search
search_space = [
  {
    'clf__n_estimators': [50, 100, 150, 200],
    'clf__learning_rate': [0.01, 0.1, 0.2, 0.3],
    'clf__max_depth': range(3, 10),
    'clf__colsample_bytree': [i/10.0 for i in range(1, 3)],
    'clf__gamma': [i/10.0 for i in range(3)],
    'fs__score_func': [chi2],
    'fs__k': [10],
  }
]
# Define cross validation
kfold = KFold(n_splits=10, random_state=42)
# AUC and accuracy as score
scoring = {'AUC':'roc_auc', 'Accuracy':make_scorer(accuracy_score)}
# Define grid search
grid = GridSearchCV(
  pipe,
  param_grid=search_space,
  cv=kfold,
  scoring=scoring,
  refit='AUC',
  verbose=1,
  n_jobs=-1
)
# Fit grid search
model = grid.fit(X_train, y_train)

Оценки модели и матрицу неточностей можно получить следующим образом:

predict = model.predict(X_test)
print('Best AUC Score: {}'.format(model.best_score_))
print('Accuracy: {}'.format(accuracy_score(y_test, predict)))
print(confusion_matrix(y_test,predict))

Выход:

Best AUC Score: 0.9116424235417477
Accuracy: 0.8615077537233226
[[4621  324]
 [ 578  990]]

А лучшие параметры можно получить:

print(model.best_params_)

Которые:

  • colsample_bytree: 0,2
  • гамма: 0,2
  • скорость обучения: 0,1
  • max_depth: 3
  • n_estimators: 200

Заключение

XGBoost - это гибкий и мощный алгоритм машинного обучения. Поиск оптимальных гиперпараметров имеет важное значение для получения максимальной отдачи. Одна из альтернатив - использовать поиск по сетке в других библиотеках, таких как scikit-learn.

Увидимся в следующий раз :)