Comment accorder les hyperparamètres des arbres xgboost?

69

J'ai une classe de données déséquilibrées et je veux régler les hyperparamètres du tress renforcé à l'aide de xgboost.

Des questions

  1. Existe-t-il un équivalent de gridsearchcv ou randomsearchcv pour xgboost?
  2. Si non, quelle est l'approche recommandée pour ajuster les paramètres de xgboost?
GeorgeOfTheRF
la source
Merci, mais ce lien aborde un problème différent et ne répond pas à ma question.
GeorgeOfTheRF
La dénomination exacte du paramètre xgboost(max.depth)ou xgb.train(max_depth)? Xgboost utilise-t-il de manière incohérente le point et le trait de soulignement pour le paramètre à différents endroits? Ou sont-ils convertis?
smci
1
@smci, check "help (" xgboost-obsolète ")"
Hemant Rupani

Réponses:

82

Depuis l'interface xgboosten careta récemment changé, voici un script qui fournit une procédure pas à pas entièrement commenté l' utilisation caretde syntoniser xgboosthyper-paramètres.

Pour cela, je vais utiliser les données d'entraînement du concours Kaggle "Give Me Some Credit" .

1. Adapter un xgboostmodèle

Dans cette section, nous:

  • adapter un xgboostmodèle avec des hyperparamètres arbitraires
  • évaluer la perte (AUC-ROC) à l'aide d'une validation croisée ( xgb.cv)
  • tracer la métrique d'évaluation de la formation par rapport aux tests

Voici un code pour le faire.

library(caret)
library(xgboost)
library(readr)
library(dplyr)
library(tidyr)

# load in the training data
df_train = read_csv("04-GiveMeSomeCredit/Data/cs-training.csv") %>%
  na.omit() %>%                                                                # listwise deletion 
  select(-`[EMPTY]`) %>%
  mutate(SeriousDlqin2yrs = factor(SeriousDlqin2yrs,                           # factor variable for classification
                                   labels = c("Failure", "Success")))

# xgboost fitting with arbitrary parameters
xgb_params_1 = list(
  objective = "binary:logistic",                                               # binary classification
  eta = 0.01,                                                                  # learning rate
  max.depth = 3,                                                               # max tree depth
  eval_metric = "auc"                                                          # evaluation/loss metric
)

# fit the model with the arbitrary parameters specified above
xgb_1 = xgboost(data = as.matrix(df_train %>%
                                   select(-SeriousDlqin2yrs)),
                label = df_train$SeriousDlqin2yrs,
                params = xgb_params_1,
                nrounds = 100,                                                 # max number of trees to build
                verbose = TRUE,                                         
                print.every.n = 1,
                early.stop.round = 10                                          # stop if no improvement within 10 trees
)

# cross-validate xgboost to get the accurate measure of error
xgb_cv_1 = xgb.cv(params = xgb_params_1,
                  data = as.matrix(df_train %>%
                                     select(-SeriousDlqin2yrs)),
                  label = df_train$SeriousDlqin2yrs,
                  nrounds = 100, 
                  nfold = 5,                                                   # number of folds in K-fold
                  prediction = TRUE,                                           # return the prediction using the final model 
                  showsd = TRUE,                                               # standard deviation of loss across folds
                  stratified = TRUE,                                           # sample is unbalanced; use stratified sampling
                  verbose = TRUE,
                  print.every.n = 1, 
                  early.stop.round = 10
)

# plot the AUC for the training and testing samples
xgb_cv_1$dt %>%
  select(-contains("std")) %>%
  mutate(IterationNum = 1:n()) %>%
  gather(TestOrTrain, AUC, -IterationNum) %>%
  ggplot(aes(x = IterationNum, y = AUC, group = TestOrTrain, color = TestOrTrain)) + 
  geom_line() + 
  theme_bw()

Voici à quoi ressemble la AUC de test versus formation:

entrez la description de l'image ici

2. Recherche hyperparamètre en utilisant train

Pour la recherche d'hyperparamètre, nous effectuons les étapes suivantes:

  • créer une data.framecombinaison unique de paramètres pour laquelle nous voulons des modèles formés.
  • Spécifiez les paramètres de contrôle qui s'appliquent à la formation de chaque modèle, y compris les paramètres de validation croisée, et spécifiez que les probabilités doivent être calculées pour que l'ASC puisse être calculée.
  • valider et former les modèles pour chaque combinaison de paramètres, en enregistrant l’ASC pour chaque modèle.

Voici un code qui montre comment faire cela.

# set up the cross-validated hyper-parameter search
xgb_grid_1 = expand.grid(
  nrounds = 1000,
  eta = c(0.01, 0.001, 0.0001),
  max_depth = c(2, 4, 6, 8, 10),
  gamma = 1
)

# pack the training control parameters
xgb_trcontrol_1 = trainControl(
  method = "cv",
  number = 5,
  verboseIter = TRUE,
  returnData = FALSE,
  returnResamp = "all",                                                        # save losses across all models
  classProbs = TRUE,                                                           # set to TRUE for AUC to be computed
  summaryFunction = twoClassSummary,
  allowParallel = TRUE
)

# train the model for each parameter combination in the grid, 
#   using CV to evaluate
xgb_train_1 = train(
  x = as.matrix(df_train %>%
                  select(-SeriousDlqin2yrs)),
  y = as.factor(df_train$SeriousDlqin2yrs),
  trControl = xgb_trcontrol_1,
  tuneGrid = xgb_grid_1,
  method = "xgbTree"
)

# scatter plot of the AUC against max_depth and eta
ggplot(xgb_train_1$results, aes(x = as.factor(eta), y = max_depth, size = ROC, color = ROC)) + 
  geom_point() + 
  theme_bw() + 
  scale_size_continuous(guide = "none")

Enfin, vous pouvez créer un diagramme à bulles pour l’AUC sur les variations de etaet max_depth:

entrez la description de l'image ici

tchakravarty
la source
Caret ne supporte-t-il toujours que les valeurs de profondeur eta, gamma et maximale pour la recherche sur grille, qu’en est-il du sous-échantillon et des autres paramètres de xgboost?
GeorgeOfTheRF
2
@ML_Pro Le support pour la plupart des xgboostparamètres existe maintenant, en particulier pour le gammanouveau Voici une liste complète des paramètres pris en charge.
tchakravarty
C’est le support de xgboost, non? Ma question concerne la prise en charge de tous les paramètres par
Caret
1
Quels seraient les changements nécessaires pour la classification multiclass. La documentation indique également une utilisation scale_pose_weightpour une classification déséquilibrée. Pouvez-vous fournir des détails sur comment faire? Merci!
discipulus
1
Pour le problème de classe non équilibrée, scale_pos_weightest maintenant documenté dans la documentation sur les paramètres . scale_pos_weightn’est pas un paramètre d’ajustement caret, mais vous pouvez comparer manuellement. Dans mon cas, en utilisant le poids s'est avéré avoir peu d'effet (classification binaire,> 20% de positifs)
geneorama
24

Le paquet Caret a incorporé xgboost.

cv.ctrl <- trainControl(method = "repeatedcv", repeats = 1,number = 3, 
                        #summaryFunction = twoClassSummary,
                        classProbs = TRUE,
                        allowParallel=T)

    xgb.grid <- expand.grid(nrounds = 1000,
                            eta = c(0.01,0.05,0.1),
                            max_depth = c(2,4,6,8,10,14)
    )
    set.seed(45)
    xgb_tune <-train(formula,
                     data=train,
                     method="xgbTree",
                     trControl=cv.ctrl,
                     tuneGrid=xgb.grid,
                     verbose=T,
                     metric="Kappa",
                     nthread =3
    )

Échantillon de sortie

eXtreme Gradient Boosting 

32218 samples
   41 predictor
    2 classes: 'N', 'Y' 

No pre-processing
Resampling: Cross-Validated (3 fold, repeated 1 times) 
Summary of sample sizes: 21479, 21479, 21478 
Resampling results

  Accuracy   Kappa      Accuracy SD   Kappa SD   
  0.9324911  0.1094426  0.0009742774  0.008972911

Un inconvénient que je vois est que d’autres paramètres de xgboost tels que le sous-échantillon, etc. ne sont pas supportés par caret actuellement.

Modifier

Gamma, colsample_bytree, min_child_weight et le sous-échantillon, etc. peuvent maintenant être réglés (juin 2017) directement à l'aide de Caret. Il suffit de les ajouter à la grille du code ci-dessus pour que cela fonctionne. Merci usr11852 pour l'avoir souligné dans le commentaire.

GeorgeOfTheRF
la source
4
Une mise à jour mineure concernant l'inconvénient mentionné. caretmaintenant (février 2017) prend en charge des paramètres supplémentaires pour gamma, colsample_bytree, min_child_weightet subsample. (Donc, effectivement, vous pouvez accorder presque tout - compte tenu du temps)
usεr11852 dit Réintégrer Monic
10

Je sais que c'est une vieille question, mais j'utilise une méthode différente de celle ci-dessus. J'utilise la fonction BayesianOptimization du package d'optimisation bayesienne pour trouver les paramètres optimaux. Pour ce faire, vous devez d’abord créer des plis de validation croisée, puis créer une fonction xgb.cv.bayesayant pour paramètres les paramètres hyper boostants que vous souhaitez modifier. Dans cet exemple, je suis à l'écoute max.depth, min_child_weight, subsample, colsample_bytree, gamma. Vous appelez ensuite xgb.cvcette fonction avec les paramètres hyper définis dans les paramètres d'entrée de xgb.cv.bayes. Ensuite, vous appelez BayesianOptimizationavec xgb.cv.bayeset les plages souhaitées des hyper paramètres de boosting. init_pointsest le nombre de modèles initiaux avec des hyper-paramètres pris au hasard dans les plages spécifiées, etn_iterest le nombre de tours de modèles après les points initiaux. La fonction fournit tous les paramètres de renforcement et l’AUC de test.

cv_folds <- KFold(as.matrix(df.train[,target.var]), nfolds = 5, 
                  stratified = TRUE, seed = 50)
xgb.cv.bayes <- function(max.depth, min_child_weight, subsample, colsample_bytree, gamma){
  cv <- xgv.cv(params = list(booster = 'gbtree', eta = 0.05,
                             max_depth = max.depth,
                             min_child_weight = min_child_weight,
                             subsample = subsample,
                             colsample_bytree = colsample_bytree,
                             gamma = gamma,
                             lambda = 1, alpha = 0,
                             objective = 'binary:logistic',
                             eval_metric = 'auc'),
                 data = data.matrix(df.train[,-target.var]),
                 label = as.matrix(df.train[, target.var]),
                 nround = 500, folds = cv_folds, prediction = TRUE,
                 showsd = TRUE, early.stop.round = 5, maximize = TRUE,
                 verbose = 0
  )
  list(Score = cv$dt[, max(test.auc.mean)],
       Pred = cv$pred)
}

xgb.bayes.model <- BayesianOptimization(
  xgb.cv.bayes,
  bounds = list(max.depth = c(2L, 12L),
                min_child_weight = c(1L, 10L),
                subsample = c(0.5, 1),
                colsample_bytree = c(0.1, 0.4),
                gamma = c(0, 10)
  ),
  init_grid_dt = NULL,
  init_points = 10,  # number of random points to start search
  n_iter = 20, # number of iterations after initial random points are set
  acq = 'ucb', kappa = 2.576, eps = 0.0, verbose = TRUE
)
Bryan Schwimmer
la source
1
C'est une bonne approche, mais il y a une mise en garde : le paquetage rBayesianOptimization, à partir de la dernière version CRAN 1.1.0 (qui n'a pas été mise à jour depuis plus de 2 ans), n'a pas de test et une licence plus restrictive que Python. package par les auteurs originaux de la méthode, qui a des tests. Voir github.com/fmfn/BayesianOptimization .
egnha
8

C'est une question plus ancienne, mais je pensais partager comment j'accordais les paramètres xgboost. Au départ, je pensais que j'utiliserais caret pour cela, mais j'ai récemment découvert un problème concernant tous les paramètres ainsi que les valeurs manquantes. J'envisageais également d'écrire une boucle itérative à travers différentes combinaisons de paramètres, mais je voulais qu'elle s'exécute en parallèle et demanderait trop de temps. L'utilisation de gridSearch à partir du package NMOF fournissait le meilleur des deux mondes (tous les paramètres ainsi que le traitement en parallèle). Voici un exemple de code pour la classification binaire (fonctionne sur Windows et Linux):

# xgboost task parameters
nrounds <- 1000
folds <- 10
obj <- 'binary:logistic'
eval <- 'logloss'

# Parameter grid to search
params <- list(
  eval_metric = eval,
  objective = obj,
  eta = c(0.1,0.01),
  max_depth = c(4,6,8,10),
  max_delta_step = c(0,1),
  subsample = 1,
  scale_pos_weight = 1
)

# Table to track performance from each worker node
res <- data.frame()

# Simple cross validated xgboost training function (returning minimum error for grid search)
xgbCV <- function (params) {
  fit <- xgb.cv(
    data = data.matrix(train), 
    label = trainLabel, 
    param =params, 
    missing = NA, 
    nfold = folds, 
    prediction = FALSE,
    early.stop.round = 50,
    maximize = FALSE,
    nrounds = nrounds
  )
  rounds <- nrow(fit)
  metric = paste('test.',eval,'.mean',sep='')
  idx <- which.min(fit[,fit[[metric]]]) 
  val <- fit[idx,][[metric]]
  res <<- rbind(res,c(idx,val,rounds))
  colnames(res) <<- c('idx','val','rounds')
  return(val)
}

# Find minimal testing error in parallel
cl <- makeCluster(round(detectCores()/2)) 
clusterExport(cl, c("xgb.cv",'train','trainLabel','nrounds','res','eval','folds'))
sol <- gridSearch(
  fun = xgbCV,
  levels = params,
  method = 'snow',
  cl = cl,
  keepNames = TRUE,
  asList = TRUE
)

# Combine all model results
comb=clusterEvalQ(cl,res)
results <- ldply(comb,data.frame)
stopCluster(cl)

# Train model given solution above
params <- c(sol$minlevels,objective = obj, eval_metric = eval)
xgbModel <- xgboost(
  data = xgb.DMatrix(data.matrix(train),missing=NaN, label = trainLabel),
  param = params,
  nrounds = results[which.min(results[,2]),1]
)

print(params)
print(results)
John Richardson
la source