web-dev-qa-db-de.com

xgboost in R: Wie übergibt xgb.cv die optimalen Parameter an xgb.train

Ich habe das xgboost-Paket in R durchforstet und mehrere Demos sowie Tutorials durchlaufen, aber das verwirrt mich immer noch: Nachdem xgb.cv für die Kreuzvalidierung verwendet wurde, wie werden die optimalen Parameter an xgb.train übergeben? Oder sollte ich die idealen Parameter (wie nround, max.depth) basierend auf der Ausgabe von xgb.cv berechnen?

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)
24
snowneji

Sieht aus, als hätten Sie xgb.cv falsch verstanden, es handelt sich nicht um eine Parametersuchfunktion. Es erfolgt eine k-fache Kreuzvalidierung, mehr nicht.

In Ihrem Code wird der Wert von param nicht geändert. 

Um die besten Parameter in Rs XGBoost zu finden, gibt es einige Methoden. Dies sind 2 Methoden, 

(1) Verwenden Sie das Paket mlr, http://mlr-org.github.io/mlr-tutorial/release/html/

Es gibt ein XGBoost + mlr Beispielcode in der Kaggle-Herausforderung Prudential. 

Dieser Code dient jedoch der Regression, nicht der Klassifizierung. Soweit ich weiß, gibt es noch keine mlogloss-Metrik im mlr-Paket, daher müssen Sie die mlogloss-Messung selbst von Grund auf neu codieren. CMIIW. 

(2) Zweites Verfahren, indem die Parameter manuell eingestellt werden, dann wiederholen, beispielsweise 

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)

Dann finden Sie den besten (minimalen) mlogloss, 

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

min_logloss ist der Mindestwert von mlogloss, während min_logloss_index der Index (round) ist. 

Sie müssen den obigen Vorgang mehrmals wiederholen und jedes Mal die Parameter manuell ändern (mlr führt die Wiederholung für Sie durch). Bis Sie schließlich das beste globale Minimum min_logloss erhalten. 

Hinweis: Sie können dies in einer Schleife von 100 oder 200 Iterationen tun, in denen Sie für jede Iteration den Parameterwert zufällig einstellen. Auf diese Weise müssen Sie den besten [parameters_list, min_logloss, min_logloss_index] in Variablen oder in einer Datei speichern. 

Hinweis: Es ist besser, einen zufälligen Startwert durch set.seed() für reproduzierbare Ergebnisse festzulegen. Unterschiedlicher zufälliger Samen ergibt ein anderes Ergebnis. Sie müssen also [parameters_list, min_logloss, min_logloss_index, seednumber] in den Variablen oder der Datei speichern. 

Sagen Sie, dass Sie schließlich 3 Ergebnisse in 3 Iterationen/Wiederholungen erhalten: 

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

Dann müssen Sie die dritten Parameter verwenden (es hat ein globales Minimum min_logloss von 1.9745). Ihr bester Index (nrounds) ist 780

Sobald Sie die besten Parameter erhalten haben, verwenden Sie es im Training, 

# 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)

Ich denke nicht, dass Sie watchlist im Training brauchen, weil Sie die Kreuzvalidierung durchgeführt haben. Wenn Sie dennoch watchlist verwenden möchten, ist dies in Ordnung. 

Noch besser können Sie in xgb.cv frühes Anhalten verwenden. 

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

Wenn mit diesem Code der mlogloss-Wert in 8 Schritten nicht abnimmt, wird der xgb.cv angehalten. Sie können Zeit sparen. Sie müssen maximize auf FALSE setzen, da Sie ein Minimum an mlogloss erwarten. 

Hier ist ein Beispielcode mit 100 Iterationsschleifen und zufällig ausgewählten Parametern. 

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)

Mit diesem Code führen Sie die Kreuzvalidierung 100 Mal mit zufälligen Parametern durch. Dann erhält man den besten Parametersatz, dh in der Iteration mit minimalem min_logloss

Erhöhen Sie den Wert von early.stop.round, falls Sie feststellen, dass er zu klein ist (zu frühes Anhalten). Sie müssen auch das Limit der Zufallsparameterwerte basierend auf Ihren Datenmerkmalen ändern. 

Und für 100 oder 200 Iterationen möchte ich, dass Sie verbose in FALSE ändern möchten. 

Randnotiz: Dies ist ein Beispiel für eine Zufallsmethode. Sie können sie z. durch Bayes'sche Optimierung für bessere Methode. Wenn Sie über eine Python-Version von XGBoost verfügen, gibt es ein gutes Hyperparameter-Skript für XGBoost, https://github.com/mpearmain/BayesBoost , um nach den besten Parametern zu suchen, die mit der Bayesian-Optimierung festgelegt wurden. 

Edit: Ich möchte die dritte manuelle Methode hinzufügen, die von "Davut Polat", einem Kaggle-Master, im Kaggle-Forum veröffentlicht wurde.

Bearbeiten: Wenn Sie Python und Sklearn kennen, können Sie GridSearchCV zusammen mit xgboost.XGBClassifier oder xgboost.XGBRegressor verwenden

60
silo

Dies ist eine gute Frage und eine großartige Antwort von Silo mit vielen Details! Ich fand es sehr hilfreich für jemanden, der xgboost neu ist, wie ich. Vielen Dank. Die Methode zum Randomisieren und Vergleichen mit der Grenze ist sehr inspirierend. Gut zu bedienen und gut zu wissen. Jetzt im Jahr 2018 sind einige Überarbeitungen erforderlich, beispielsweise sollte early.stop.roundearly_stopping_rounds sein. Die Ausgabe mdcv ist etwas anders organisiert:

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

Und abhängig von der Anwendung (linear, logistisch usw.) müssen die objective, eval_metric und die Parameter entsprechend angepasst werden. 

Für jeden, der eine Regression durchführt, ist hier die leicht angepasste Version des Codes (die meisten sind die gleichen wie oben). 

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))
2
Yang Liu

Ich fand die Antwort von Silo sehr hilfreich. Neben seinem Ansatz der Zufallsforschung möchten Sie möglicherweise die Bayes-Optimierung verwenden, um den Prozess der Hyperparametersuche zu erleichtern, z. rBayesianOptimization-Bibliothek . Das Folgende ist mein Code mit der rbayesianoptimization-Bibliothek.

cv_folds <- KFold(dataFTR$isPreIctalTrain, nfolds = 5, stratified = FALSE, seed = seedNum)
xgb_cv_bayes <- function(nround,max.depth, min_child_weight, subsample,eta,gamma,colsample_bytree,max_delta_step) {
param<-list(booster = "gbtree",
            max_depth = max.depth,
            min_child_weight = min_child_weight,
            eta=eta,gamma=gamma,
            subsample = subsample, colsample_bytree = colsample_bytree,
            max_delta_step=max_delta_step,
            lambda = 1, alpha = 0,
            objective = "binary:logistic",
            eval_metric = "auc")
cv <- xgb.cv(params = param, data = dtrain, folds = cv_folds,nrounds = 1000,early_stopping_rounds = 10, maximize = TRUE, verbose = verbose)

list(Score = cv$evaluation_log$test_auc_mean[cv$best_iteration],
     Pred=cv$best_iteration)
# we don't need cross-validation prediction and we need the number of rounds.
# a workaround is to pass the number of rounds(best_iteration) to the Pred, which is a default parameter in the rbayesianoptimization library.
}
OPT_Res <- BayesianOptimization(xgb_cv_bayes,
                              bounds = list(max.depth =c(3L, 10L),min_child_weight = c(1L, 40L),
                                            subsample = c(0.6, 0.9),
                                            eta=c(0.01,0.3),gamma = c(0.0, 0.2),
                                            colsample_bytree=c(0.5,0.8),max_delta_step=c(1L,10L)),
                              init_grid_dt = NULL, init_points = 10, n_iter = 10,
                              acq = "ucb", kappa = 2.576, eps = 0.0,
                              verbose = verbose)
best_param <- list(
booster = "gbtree",
eval.metric = "auc",
objective = "binary:logistic",
max_depth = OPT_Res$Best_Par["max.depth"],
eta = OPT_Res$Best_Par["eta"],
gamma = OPT_Res$Best_Par["gamma"],
subsample = OPT_Res$Best_Par["subsample"],
colsample_bytree = OPT_Res$Best_Par["colsample_bytree"],
min_child_weight = OPT_Res$Best_Par["min_child_weight"],
max_delta_step = OPT_Res$Best_Par["max_delta_step"])
# number of rounds should be tuned using CV
#https://www.hackerearth.com/practice/machine-learning/machine-learning-algorithms/beginners-tutorial-on-xgboost-parameter-tuning-r/tutorial/
# However, nrounds can not be directly derivied from the bayesianoptimization function
# Here, OPT_Res$Pred, which was supposed to be used for cross-validation, is used to record the number of rounds
nrounds=OPT_Res$Pred[[which.max(OPT_Res$History$Value)]]
xgb_model <- xgb.train (params = best_param, data = dtrain, nrounds = nrounds)
0
Penning Yu