xgboost в R: как xgb.cv передает оптимальные параметры в xgb.train

Я xgboost пакет xgboost в R и прошел несколько демонстраций, а также учебники, но это все еще меня смущает: после использования xgb.cv для перекрестной проверки, как оптимальные параметры передаются xgb.train? Или я должен рассчитать идеальные параметры (например, nround, max.depth) на основе вывода xgb.cv?

param <- list("objective" = "multi:softprob",
              "eval_metric" = "mlogloss",
              "num_class" = 12)
cv.nround <- 11
cv.nfold <- 5
mdcv <-xgb.cv(data=dtrain,params = param,nthread=6,nfold = cv.nfold,nrounds = cv.nround,verbose = T)

md <-xgb.train(data=dtrain,params = param,nround = 80,watchlist = list(train=dtrain,test=dtest),nthread=6)

Ответ 1

Похоже, вы неправильно поняли xgb.cv, это не функция поиска параметров. Это делает k-folds cross validation, не более того.

В вашем коде он не изменяет значение param.

Чтобы найти лучшие параметры в R XGBoost, есть некоторые методы. Это 2 метода,

(1) Используйте пакет mlr, http://mlr-org.github.io/mlr-tutorial/release/html/

В запросе Kaggle Prudential есть пример кода XGBoost + mlr,

Но этот код предназначен для регрессии, а не для классификации. Насколько я знаю, нет mlogloss метрики еще в mlr пакета, так что вы должны кодировать mlogloss измерения с нуля самостоятельно. CMIIW.

(2) Второй метод, вручную задав параметры, затем повторите, например,

param <- list(objective = "multi:softprob",
      eval_metric = "mlogloss",
      num_class = 12,
      max_depth = 8,
      eta = 0.05,
      gamma = 0.01, 
      subsample = 0.9,
      colsample_bytree = 0.8, 
      min_child_weight = 4,
      max_delta_step = 1
      )
cv.nround = 1000
cv.nfold = 5
mdcv <- xgb.cv(data=dtrain, params = param, nthread=6, 
                nfold=cv.nfold, nrounds=cv.nround,
                verbose = T)

Затем вы найдете лучший (минимум) mlogloss,

min_logloss = min(mdcv[, test.mlogloss.mean])
min_logloss_index = which.min(mdcv[, test.mlogloss.mean])

min_logloss - минимальное значение mlogloss, а min_logloss_index - индекс (round).

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

Примечание. Вы можете сделать это в цикле из 100 или 200 итераций, в котором для каждой итерации вы произвольно устанавливаете значение параметра. Таким образом, вы должны сохранить лучшие [parameters_list, min_logloss, min_logloss_index] в переменных или в файле.

Примечание: лучше установить случайное семя методом set.seed() для воспроизводимого результата. Различные случайные урожаи дают разные результаты. Таким образом, вы должны сохранить [parameters_list, min_logloss, min_logloss_index, seednumber] в переменных или файле.

Скажем, что в итоге вы получите 3 результата в 3 итерациях/повторах:

min_logloss = 2.1457, min_logloss_index = 840
min_logloss = 2.2293, min_logloss_index = 920
min_logloss = 1.9745, min_logloss_index = 780

Затем вы должны использовать третьи параметры (минимальный минимум min_logloss 1.9745). Ваш лучший индекс (nrounds) - 780.

Как только вы получите лучшие параметры, используйте его в обучении,

# best_param is global best param with minimum min_logloss
# best_min_logloss_index is the global minimum logloss index
nround = 780
md <- xgb.train(data=dtrain, params=best_param, nrounds=nround, nthread=6)

Я не думаю, что вам нужен watchlist в тренинге, потому что вы сделали перекрестное подтверждение. Но если вы все еще хотите использовать watchlist, все в порядке.

Еще лучше вы можете использовать раннюю остановку в xgb.cv

mdcv <- xgb.cv(data=dtrain, params=param, nthread=6, 
                nfold=cv.nfold, nrounds=cv.nround,
                verbose = T, early.stop.round=8, maximize=FALSE)

С помощью этого кода, когда значение mlogloss не уменьшается в 8 шагов, xgb.cv остановится. Вы можете сэкономить время. Вы должны установить maximize в FALSE, потому что вы ожидаете минимальный уровень mlogloss.

Вот пример кода с 100 циклами итераций и случайными выбранными параметрами.

best_param = list()
best_seednumber = 1234
best_logloss = Inf
best_logloss_index = 0

for (iter in 1:100) {
    param <- list(objective = "multi:softprob",
          eval_metric = "mlogloss",
          num_class = 12,
          max_depth = sample(6:10, 1),
          eta = runif(1, .01, .3),
          gamma = runif(1, 0.0, 0.2), 
          subsample = runif(1, .6, .9),
          colsample_bytree = runif(1, .5, .8), 
          min_child_weight = sample(1:40, 1),
          max_delta_step = sample(1:10, 1)
          )
    cv.nround = 1000
    cv.nfold = 5
    seed.number = sample.int(10000, 1)[[1]]
    set.seed(seed.number)
    mdcv <- xgb.cv(data=dtrain, params = param, nthread=6, 
                    nfold=cv.nfold, nrounds=cv.nround,
                    verbose = T, early.stop.round=8, maximize=FALSE)

    min_logloss = min(mdcv[, test.mlogloss.mean])
    min_logloss_index = which.min(mdcv[, test.mlogloss.mean])

    if (min_logloss < best_logloss) {
        best_logloss = min_logloss
        best_logloss_index = min_logloss_index
        best_seednumber = seed.number
        best_param = param
    }
}

nround = best_logloss_index
set.seed(best_seednumber)
md <- xgb.train(data=dtrain, params=best_param, nrounds=nround, nthread=6)

С помощью этого кода вы выполняете кросс-проверку 100 раз, каждый раз со случайными параметрами. Затем вы получаете лучший набор параметров, то есть на итерации с минимальным min_logloss.

Увеличьте значение early.stop.round если вы обнаружите, что он слишком маленький (слишком ранняя остановка). Вам также необходимо изменить предел значений случайных параметров на основе ваших данных.

И, для 100 или 200 итераций, я думаю, вы хотите изменить verbose значение FALSE.

Боковое примечание. Это пример случайного метода, его можно настроить, например, путем оптимизации байесовского метода для лучшего метода. Если у вас есть версия XGBoost на Python, есть хороший гиперпараметрический скрипт для XGBoost, https://github.com/mpearmain/BayesBoost для поиска лучших параметров, заданных с помощью байесовской оптимизации.

Изменение: я хочу добавить третий ручной метод, размещенный "Давут Полат" мастером Kaggle на форуме Kaggle.

Изменение: если вы знаете Python и sklearn, вы также можете использовать GridSearchCV вместе с xgboost.XGBClassifier или xgboost.XGBRegressor

Ответ 2

Это хороший вопрос и отличный ответ от силоса с большим количеством деталей! Я нашел его очень полезным для кого-то нового для xgboost подобного мне. Спасибо. Метод рандомизации и сравнения с границей очень вдохновляет. Хорошо использовать и хорошо знать. Теперь в 2018 году необходим небольшой пересмотр, например, early.stop.round должен быть early_stopping_rounds. Вывод mdcv организован несколько иначе:

  min_rmse_index  <-  mdcv$best_iteration
  min_rmse <-  mdcv$evaluation_log[min_rmse_index]$test_rmse_mean

И зависит от приложения (линейный, логистический и т.д.), objective, eval_metric и параметры должны быть соответствующим образом скорректированы.

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

library(xgboost)
# Matrix for xgb: dtrain and dtest, "label" is the dependent variable
dtrain <- xgb.DMatrix(X_train, label = Y_train)
dtest <- xgb.DMatrix(X_test, label = Y_test)

best_param <- list()
best_seednumber <- 1234
best_rmse <- Inf
best_rmse_index <- 0

set.seed(123)
for (iter in 1:100) {
  param <- list(objective = "reg:linear",
                eval_metric = "rmse",
                max_depth = sample(6:10, 1),
                eta = runif(1, .01, .3), # Learning rate, default: 0.3
                subsample = runif(1, .6, .9),
                colsample_bytree = runif(1, .5, .8), 
                min_child_weight = sample(1:40, 1),
                max_delta_step = sample(1:10, 1)
  )
  cv.nround <-  1000
  cv.nfold <-  5 # 5-fold cross-validation
  seed.number  <-  sample.int(10000, 1) # set seed for the cv
  set.seed(seed.number)
  mdcv <- xgb.cv(data = dtrain, params = param,  
                 nfold = cv.nfold, nrounds = cv.nround,
                 verbose = F, early_stopping_rounds = 8, maximize = FALSE)

  min_rmse_index  <-  mdcv$best_iteration
  min_rmse <-  mdcv$evaluation_log[min_rmse_index]$test_rmse_mean

  if (min_rmse < best_rmse) {
    best_rmse <- min_rmse
    best_rmse_index <- min_rmse_index
    best_seednumber <- seed.number
    best_param <- param
  }
}

# The best index (min_rmse_index) is the best "nround" in the model
nround = best_rmse_index
set.seed(best_seednumber)
xg_mod <- xgboost(data = dtest, params = best_param, nround = nround, verbose = F)

# Check error in testing data
yhat_xg <- predict(xg_mod, dtest)
(MSE_xgb <- mean((yhat_xg - Y_test)^2))