Extension des modèles à 2 classes aux problèmes multi-classes

11

Cet article sur Adaboost donne quelques suggestions et code (page 17) pour étendre les modèles à 2 classes aux problèmes de classe K. Je voudrais généraliser ce code, de sorte que je puisse facilement brancher différents modèles à 2 classes et comparer les résultats. Étant donné que la plupart des modèles de classification ont une interface de formule et une predictméthode, certains de ces éléments devraient être relativement faciles. Malheureusement, je n'ai pas trouvé de moyen standard d'extraire les probabilités de classe des modèles à 2 classes, donc chaque modèle nécessitera un code personnalisé.

Voici une fonction que j'ai écrite pour décomposer un problème de classe K en problèmes de 2 classes et renvoyer des modèles K:

oneVsAll <- function(X,Y,FUN,...) {
    models <- lapply(unique(Y), function(x) {
        name <- as.character(x)
        .Target <- factor(ifelse(Y==name,name,'other'), levels=c(name, 'other'))
        dat <- data.frame(.Target, X)
        model <- FUN(.Target~., data=dat, ...)
        return(model)
    })
    names(models) <- unique(Y)
    info <- list(X=X, Y=Y, classes=unique(Y))
    out <- list(models=models, info=info)
    class(out) <- 'oneVsAll'
    return(out)
}

Voici une méthode de prédiction que j'ai écrite pour itérer sur chaque modèle et faire des prédictions:

predict.oneVsAll <- function(object, newX=object$info$X, ...) {
    stopifnot(class(object)=='oneVsAll')
    lapply(object$models, function(x) {
        predict(x, newX, ...)
    })
}

Et enfin, voici une fonction permettant de normaliser une data.framedes probabilités prédites et de classer les cas. Notez qu'il vous appartient de construire la colonne K data.framedes probabilités à partir de chaque modèle, car il n'existe pas de méthode unifiée pour extraire les probabilités de classe d'un modèle à 2 classes:

classify <- function(dat) {
    out <- dat/rowSums(dat)
    out$Class <- apply(dat, 1, function(x) names(dat)[which.max(x)])
    out
}

Voici un exemple d'utilisation adaboost:

library(ada)
library(caret) 
X <- iris[,-5]
Y <- iris[,5]
myModels <- oneVsAll(X, Y, ada)
preds <- predict(myModels, X, type='probs')
preds <- data.frame(lapply(preds, function(x) x[,2])) #Make a data.frame of probs
preds <- classify(preds)
>confusionMatrix(preds$Class, Y)
Confusion Matrix and Statistics

            Reference
Prediction   setosa versicolor virginica
  setosa         50          0         0
  versicolor      0         47         2
  virginica       0          3        48

Voici un exemple d'utilisation lda(je sais que lda peut gérer plusieurs classes, mais ce n'est qu'un exemple):

library(MASS)
myModels <- oneVsAll(X, Y, lda)
preds <- predict(myModels, X)
preds <- data.frame(lapply(preds, function(x) x[[2]][,1])) #Make a data.frame of probs
preds <- classify(preds)
>confusionMatrix(preds$Class, Y)
Confusion Matrix and Statistics

            Reference
Prediction   setosa versicolor virginica
  setosa         50          0         0
  versicolor      0         39         5
  virginica       0         11        45

Ces fonctions devraient fonctionner pour tout modèle à 2 classes avec une interface de formule et une predictméthode. Notez que vous devez séparer manuellement les composants X et Y, ce qui est un peu moche, mais écrire une interface de formule me dépasse pour le moment.

Cette approche a-t-elle un sens pour tout le monde? Existe-t-il un moyen de l'améliorer ou existe-t-il un package existant pour résoudre ce problème?

Zach
la source
2
Wow, jusqu'à ce que vous demandiez et que je regarde, j'aurais été sûr qu'un paquet (comme car, ou l'un des *labpaquets) aurait fourni une fonction comme la vôtre. Désolé, je ne peux pas aider. J'ai lu un peu sur le fonctionnement de k-way SVM et il semble que c'était plus compliqué que je ne l'aurais pensé.
Wayne
1
@Wayne: Moi aussi! J'étais certain qu'il y aurait une fonction générale qui ferait cela, à condition que le modèle ait une predictméthode.
Zach

Réponses:

1

Une façon de s'améliorer consiste à utiliser l' approche «pondérée toutes les paires» qui est censée être meilleure que «une contre toutes» tout en étant évolutive.

Comme pour les packages existants, glmnetprend en charge le logit multinomial (régularisé) qui peut être utilisé comme classificateur multi-classe.

Yevgeny
la source
Je connais les nombreux packages de R qui prennent en charge la classification multi-classes (tels que glmnet, les forêts aléatoires, kernlab, rpart, nnet, etc.). Je suis plus curieux d'étendre les packages de classification binaire (par exemple gbm) aux problèmes multiclasses. Je vais examiner "pondéré toutes les paires".
Zach
De plus, c'est intéressant qui glmnetinclut une multinomialfonction de perte. Je me demande si cette fonction de perte pourrait être utilisée dans d'autres algorithmes en R, tels que adaou gbm?
Zach
Oui, certaines méthodes peuvent être étendues pour prendre en charge la fonction de perte multinomiale. Par exemple, la régression logistique du noyau est étendue de cette façon ici: books.nips.cc/papers/files/nips14/AA13.pdf Pour autant que le savoir adasoit "réservé" à une fonction de perte spécifique (exponentielle), mais on pourrait étendre un autre boosting méthode basée sur la prise en charge de la fonction de perte multinomiale - par exemple, voir la page 360 ​​des éléments de l'apprentissage statistique pour plus de détails sur le GBM multi-classes - K arbres binaires sont construits pour chaque itération de boosting où K est le nombre de classes (un seul arbre par itération) est nécessaire dans le cas binaire).
Yevgeny