Узнайте, как обучить одну из самых мощных древовидных моделей (XGBoost) с помощью языка R.

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

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

XGBoost, или Extreme Gradient Boosting, можно использовать для регрессии или классификации — в этом посте мы будем использовать пример регрессии.

Как и в других моих сериях учебных пособий по науке о данных, мы собираемся использовать Набор данных по совместному использованию велосипедов в Лондоне — этот набор данных содержит информацию о спросе на совместно используемые велосипеды для инициативы по совместному использованию велосипедов в Лондоне — у нас есть данные, собранные за день и час:

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

В нашем случае использования мы хотим предсказать количество поездок на велосипеде, используя как атмосферные данные, так и метаданные о дне — например, если конкретный день был праздником или выходным. Для простоты мы не будем выполнять какие-либо функции во время поста.

Следующая команда позволяет нам читать CSV-файлы с помощью R:

london_bike <- read.csv(‘./london_merged.csv’)

Кроме того, мы загрузим все библиотеки, которые нам нужны для этого урока:

library(dplyr)
library(xgboost) 
library(Metrics)
library(ggplot2)

Разделение на обучение и тестирование

Давайте воспользуемся простой функцией, чтобы разделить данные для обучения и тестирования, оставив 20% данных в качестве резервного набора для оценки производительности:

# Splitting into train and test
train_test_split <- function(data, percentage) {
 
 data_with_row_id <- data %>% 
 mutate(id = row_number())
 
 set.seed(1234)
 training_data <- data_with_row_id %>%
 sample_frac(percentage)
 test_data <- anti_join(
 data_with_row_id,
 training_data,
 by=’id’
 )
 
 training_data$id <- NULL
 test_data$id <- NULL
 
 return (list(training_data, test_data))
}
# Keeping 80% for the training set
training_data <- train_test_split(london_bike, 0.8)[[1]]
test_data <- train_test_split(london_bike, 0.8)[[2]]

У нас остается 13 931 временная позиция (как мы видели в предварительном просмотре, каждая строка представляет данные в определенный час) для обучения.

Если вы хотите, вы также можете использовать набор задержек вне времени — вместо случайного разделения данных используйте непрерывный период времени (последние дни набора данных) в качестве тестового набора.

Для тестирования (оценки количественной производительности) нашего алгоритма мы будем использовать 3,483 временных позиции. Имейте в виду, что мы будем оценивать наши алгоритмы, используя среднеквадратичную ошибку.

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

# Defining the features in X_train and X_test
X_train <- training_data[,c('t1','t2','hum',
                                  'wind_speed','weather_code',
                                  'is_holiday','is_weekend',
                                  'season')]
X_test <- test_data[,c('t1','t2','hum',
                          'wind_speed','weather_code',
                          'is_holiday','is_weekend',
                          'season')]

Как указано в постановке задачи, мы хотим предсказать, сколько велосипедов будет использоваться в течение определенного часа и дня — это означает, что нашей целью будет столбец «cnt», содержащий то же значение:

# Defining the Target
y_train <- training_data$cnt
y_test <- test_data$cnt

Когда все настроено, давайте обучим нашу модель XGBoost с помощью R!

Обучение XGBoost

Мы будем обучать модель с помощью библиотеки xgboost в R. В этом примере мы будем использовать 10 раундов в нашей модели (этот параметр немного «похож» на количество деревьев в модели случайного леса):

set.seed(1234)
xgb <- xgboost(data = as.matrix(X_train),
               label = y_train,
               nround = 10)

Обратите внимание на кое-что интересное — хотя в большинстве библиотек R используется понятие формулы в формате y ~ x1 + x2 + … + xn, это неверно для библиотеки xgboost . В аргументе данные мы указываем функции (X), а в метке указываем цель (y) — это основная причина, по которой мы создали четыре разных объекта. , аналогично тому, что делается в Python sklearn. Также XGBoost ожидает матрицу на метке данных, поэтому нам нужно явно преобразовать наш объект — мы можем сделать это в конвейере данных или в самой функции.

С аргументами по умолчанию функция xgboost выводит историю показателя RMSE (среднеквадратичная ошибка) (для регрессии) для каждой итерации:

Обратите внимание, что после 10 итераций наша среднеквадратическая ошибка по-прежнему снижалась — может быть хорошей идеей использовать более высокий параметр nround , но мы сделаем это через минуту.

Давайте также зафиксируем время выполнения нашей модели XGBoost:

# Timing the execution
system.time(
            xgboost(data = as.matrix(X_train),
            label = y_train,
            nround = 10)
)

Эта модель работала примерно за 0,41 секунды — намного быстрее, чем большинство моделей мешков (таких как Random Forests). Также общеизвестно, что бустинговые модели, как правило, быстрее обучаются, чем бэггинговые.

Мы знаем, что наш RMSE составляет ~ 845 велосипедов на тренировочном наборе. А как насчет удерживающего набора? Мы можем оценить наш XGBoost на тестовом наборе с небольшой помощью функции rmse из пакета Metrics:

# Assessing performance on the test set
rmse(
 y_test, 
 predict(xgb, as.matrix(X_test))
)

Этот код будет вычислять RMSE между y_test —реальным количеством арендованных велосипедов в примерах тестового набора — и predict(xgb, as.matrix(X_test)) —прогнозы, которые мы будем генерировать на основе нашей модели XGBoost.

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

Наш RMSE для тестового набора составляет ~891 велосипед, как и для тренировочного набора. Можем ли мы улучшить это, изменив некоторые параметры xgboost? Может быть! Давайте проверим!

Расширение гиперпараметров XGBoost

В настоящее время мое дерево спускается только на 6 уровней — это параметр по умолчанию для функции xgboost. Давайте немного подправим это, а также добавим больше итераций.

Чтобы добавить новый гиперпараметр в модель XGBoost, вам просто нужно указать новый аргумент в функции, например:

# Adding more hyperparameters to the XGBoost model
xgb_ext <- xgboost(data = as.matrix(X_train),
               label = y_train,
               nround = 50,
               max_depth=20)

Супер просто! Мы только что добавили новый параметр max_depth в нашу модель повышения. Для этой модели можно настроить множество параметров — с помощью команды ?xgboost в R вы сможете увидеть их все, включая их описание.Настройка этих параметров является ключом к повышение производительности и стабильности.

Как мы видели в первой модели xgboost, у нас все еще есть возможности для улучшения нашей производительности — это похоже подтверждается выходными данными нашего xgb_ext модель:

Обратите внимание, что RMSE для этих последних 8 итераций намного ниже, чем для первой xgb_model. Еще лучше, давайте визуализируем RMSE на каждой итерации:

После 20го выполнения наша среднеквадратическая ошибка выравнивается — эта стабилизация является нормальным поведением бустинг-моделей и может означать, что вы нашли глобальный минимум ошибки для этого конкретного набора гиперпараметров.

Если мы увеличили количество итераций, как это повлияет на время выполнения? Давайте посмотрим.

system.time(xgboost(data = as.matrix(X_train),
                    label = y_train,
                    nround = 50,
                    max_depth=20))

Этот xgboost выполнялся немного дольше, чем первый — около 8,42 секунды. Это нормально, так как мы строим более сложные деревья (более глубокие), а также делаем больше итераций.

Хорошо, среднеквадратичное отклонение у нас ниже, но… хороший результат в обучающей выборке — это, возможно, «плацебо для науки о данных» — как мы работаем с невидимыми данными?

В качестве финального теста давайте посмотрим на нашу производительность на контрольном наборе:

# Assessing Performance of Extended XGBoost
rmse(
 y_test, 
 predict(xgb_ext, as.matrix(X_test))
)

СКО для тестового набора составляет 1008 велосипедов! Как и в экономике, в науке о данных не бывает бесплатных обедов — нам удалось улучшить наш алгоритм в обучающем наборе, но в итоге мы вошли подгонять территорию, создавая модель с высокой дисперсией.

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

Вероятно, теперь нам следует попробовать уменьшить параметр максимальной глубины и оставить 50 итераций, чтобы попытаться найти лучший баланс между обучением и ошибкой теста — попробуйте сами, как испытание!

Спасибо, что нашли время, чтобы прочитать этот пост! Я также написал учебник о том, как обучать модели мешков (случайный лес) в R, если вы хотите взглянуть:



Я также организовал курс Udemy, чтобы с нуля изучить концепции науки о данных, и мне бы очень хотелось, чтобы вы были рядом!

Вот небольшая суть, которую вы можете использовать для своих проектов, просто изменив входные данные и функции:

Набор данных, используемый в этом посте, соответствует положениям и условиям лицензии открытого правительства, доступным по адресу https://www.kaggle.com/hmavrodiev/london-bike-sharing-dataset.