2 votes

Comment évaluer LightGBM en R en utilisant le kappa de Cohen ?

J'utilise régulièrement XGBoost dans R et je veux commencer à utiliser LightGBM sur les mêmes données. Mon objectif est d'utiliser le kappa de Cohen comme métrique d'évaluation. Cependant, je ne parviens pas à mettre en œuvre correctement LightGBM - il semble qu'aucun apprentissage ne se produise. Comme exemple très simple, je vais utiliser le jeu de données titanic.

library(data.table)
library(dplyr)
library(caret)

titanic <- fread("https://raw.githubusercontent.com/pcsanwald/kaggle-titanic/master/train.csv")

titanic_complete <- titanic %>%
   select(survived, pclass, sex, age, sibsp, parch, fare, embarked) %>% 
   mutate_if(is.character, as.factor) %>%
   mutate(survived = as.factor(survived)) %>% 
   na.omit()

train_class <- titanic_complete %>% 
   select(survived) %>% 
   pull()

train_numeric <- titanic_complete %>% 
   select_if(is.numeric) %>% 
   data.matrix()

ctrl <- trainControl(method = "none", search = "grid")

tune_grid_xgbTree <- expand.grid(
   nrounds = 700,
   eta = 0.1,
   max_depth = 3,
   gamma = 0,
   colsample_bytree = 0,
   min_child_weight = 1,
   subsample = 1)

 set.seed(512)
 fit_xgb <- train(
    x = train_numeric,
    y = train_class,
    tuneGrid = tune_grid_xgbTree,
    trControl = ctrl,
    method = "xgbTree",
    metric = "Kappa",
    verbose = TRUE)

 confusionMatrix(predict(fit_xgb, train_numeric), train_class)

Donne un Kappa de 0,57 évalué sur l'ensemble d'entraînement (qui sert uniquement à montrer mon problème, sinon j'utiliserais la validation croisée).

Pour LightGBM, j'écris Kappa comme une fonction d'évaluation personnalisée :

library(lightgbm)
lgb.kappa <- function(preds, y) {
   label <- getinfo(y, "label")
   k <- unlist(e1071::classAgreement(table(label, preds)))["kappa"]
   return(list(name = "kappa", value = as.numeric(k), higher_better = TRUE))
 }

X_train <- titanic_complete %>% select(-survived) %>% data.matrix()
y_train <- titanic_complete %>% select(survived) %>% data.matrix()
y_train <- y_train - 1

dtrain <- lgb.Dataset(data = X_train, label = y_train)

Ici, j'utilise le même jeu de paramètres que dans XGBoost mais j'ai essayé différentes combinaisons sans succès.

fit_lgbm <- lgb.train(data = dtrain,
                  objective = "binary",
                  learning_rate = 0.1,
                  nrounds = 700,
                  colsample_bytree = 0,
                  eval = lgb.kappa,
                  min_child_weight = 1,
                  max_depth = 3)

Il n'y a pas d'apprentissage et l'algorithme sort "Pas d'autres fractionnements avec un gain positif, meilleur gain : -inf" et Kappa = 0.

Si quelqu'un a réussi à mettre en œuvre LightGBM (peut-être avec une métrique d'évaluation personnalisée), je serais très heureux d'avoir un indice sur la façon de résoudre ce problème.

4voto

James Lamb Points 398

Il n'y a pas d'apprentissage et l'algorithme sort "Pas d'autres divisions avec un gain positif, meilleur gain : -inf".

Cela s'explique par le fait que Valeurs par défaut des paramètres du LightGBM sont configurés pour des ensembles de données plus importants. L'ensemble de données de formation dans votre exemple ci-dessus ne compte que 714 lignes. Pour faire face à ce problème, je recommande de régler les paramètres de LightGBM sur des valeurs qui permettent des nœuds feuilles plus petits, et de limiter le nombre de feuilles plutôt que la profondeur.

list(
    "min_data_in_leaf" = 3
    , "max_depth" = -1
    , "num_leaves" = 8
)

et Kappa = 0.

Je crois que votre implémentation du Kappa de Cohen comporte une erreur. L'entrée de e1071::classAgreement() devrait être une table de comptes (une matrice de confusion), et preds est sous la forme de probabilités prédites. Je pense que cette implémentation est correcte, en me basant sur la description de cette métrique sur Wikipedia .

lgb.kappa <- function(preds, dtrain) {
    label <- getinfo(dtrain, "label")
    threshold <- 0.5
    thresholded_preds <- as.integer(preds > threshold)
    k <- unlist(e1071::classAgreement(table(label, thresholded_preds)))["kappa"]
    return(list(name = "kappa", value = as.numeric(k), higher_better = TRUE))
}

Enfin, je pense que 700 itérations est probablement trop pour un ensemble de données de 700 observations. Vous pouvez voir la valeur des métriques évaluées par rapport aux données de formation à chaque itération en passant les données de formation comme ensemble de validation.

Pris ensemble, je pense que le code ci-dessous accomplit ce que la question originale demandait.

library(data.table)
library(dplyr)
library(caret)
library(lightgbm)

titanic <- fread("https://raw.githubusercontent.com/pcsanwald/kaggle-titanic/master/train.csv")

titanic_complete <- titanic %>%
    select(survived, pclass, sex, age, sibsp, parch, fare, embarked) %>% 
    mutate_if(is.character, as.factor) %>%
    mutate(survived = as.factor(survived)) %>% 
    na.omit()

train_class <- titanic_complete %>% 
    select(survived) %>% 
    pull()

train_numeric <- titanic_complete %>% 
    select_if(is.numeric) %>% 
    data.matrix()

lgb.kappa <- function(preds, dtrain) {
    label <- getinfo(dtrain, "label")
    threshold <- 0.5
    thresholded_preds <- as.integer(preds > threshold)
    k <- unlist(e1071::classAgreement(table(label, thresholded_preds)))["kappa"]
    return(list(name = "kappa", value = as.numeric(k), higher_better = TRUE))
}

X_train <- titanic_complete %>% select(-survived) %>% data.matrix()
y_train <- titanic_complete %>% select(survived) %>% data.matrix()
y_train <- y_train - 1

# train, printing out eval metrics at ever iteration
fit_lgbm <- lgb.train(
    data = lgb.Dataset(
        data = X_train,
        label = y_train
    ),
    params = list(
        "min_data_in_leaf" = 3
        , "max_depth" = -1
        , "num_leaves" = 8
    ),
    objective = "binary",
    learning_rate = 0.1,
    nrounds = 10L,
    verbose = 1L,
    valids = list(
        "train" = lgb.Dataset(
            data = X_train,
            label = y_train
        )
    ),
    eval = lgb.kappa,
)

# evaluate a custom function after training
fit_lgbm$eval_train(
    feval = lgb.kappa
)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X