Erweiterung von 2-Klassen-Modellen auf Mehrklassenprobleme

11

Dieses Dokument zu Adaboost enthält einige Vorschläge und Codes (Seite 17) für die Erweiterung von 2-Klassen-Modellen auf Probleme der K-Klasse. Ich möchte diesen Code so verallgemeinern, dass ich problemlos verschiedene 2-Klassen-Modelle anschließen und die Ergebnisse vergleichen kann. Da die meisten Klassifizierungsmodelle über eine Formelschnittstelle und eine predictMethode verfügen, sollte ein Teil davon relativ einfach sein. Leider habe ich keine Standardmethode zum Extrahieren von Klassenwahrscheinlichkeiten aus 2-Klassen-Modellen gefunden, sodass für jedes Modell ein benutzerdefinierter Code erforderlich ist.

Hier ist eine Funktion, die ich geschrieben habe, um ein Problem der K-Klasse in Probleme der 2-Klasse aufzuteilen und K-Modelle zurückzugeben:

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

Hier ist eine Vorhersagemethode, die ich geschrieben habe, um jedes Modell zu durchlaufen und Vorhersagen zu treffen:

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

Und schließlich ist hier eine Funktion, um eine data.frameder vorhergesagten Wahrscheinlichkeiten zu normalisieren und die Fälle zu klassifizieren. Beachten Sie, dass es an Ihnen liegt, die K-Spalte data.frameder Wahrscheinlichkeiten aus jedem Modell zu erstellen, da es keine einheitliche Möglichkeit gibt, Klassenwahrscheinlichkeiten aus einem 2-Klassen-Modell zu extrahieren:

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

Hier ist ein Beispiel mit 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

Hier ist ein Beispiel mit lda(ich weiß, dass lda mehrere Klassen verarbeiten kann, aber dies ist nur ein Beispiel):

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

Diese Funktionen sollten für jedes 2-Klassen-Modell mit einer Formelschnittstelle und einer predictMethode funktionieren . Beachten Sie, dass Sie die X- und Y-Komponenten manuell aufteilen müssen, was etwas hässlich ist, aber das Schreiben einer Formelschnittstelle ist mir im Moment ein Rätsel.

Ist dieser Ansatz für alle sinnvoll? Gibt es eine Möglichkeit, es zu verbessern, oder gibt es ein vorhandenes Paket, um dieses Problem zu lösen?

Zach
quelle
2
Wow, bis Sie gefragt haben und ich nachgesehen habe, wäre ich mir sicher gewesen, dass ein Paket (wie caroder eines der *labPakete) eine Funktion wie Ihre bereitgestellt hätte. Entschuldigung, ich kann nicht helfen. Ich habe ein bisschen darüber gelesen, wie K-Way-SVM funktioniert, und es scheint komplizierter zu sein, als ich gedacht hätte.
Wayne
1
@ Wayne: Ich auch! Ich war mir sicher, dass es eine allgemeine Funktion geben würde, die dies tun würde, vorausgesetzt, das Modell verfügt über eine predictMethode.
Zach

Antworten:

1

Eine Möglichkeit zur Verbesserung besteht darin, den Ansatz "Alle Paare gewichtet" zu verwenden, der angeblich besser als "Eins gegen alle" ist, während er noch skalierbar ist.

glmnetUnterstützt bei vorhandenen Paketen (reguliertes) multinomiales Logit, das als Klassifikator für mehrere Klassen verwendet werden kann.

Jewgeni
quelle
Mir sind die vielen Pakete in R bekannt, die die Klassifizierung mehrerer Klassen unterstützen (z. B. glmnet, zufällige Gesamtstrukturen, Kernlab, rpart, nnet usw.). Ich bin eher neugierig darauf, binäre Klassifizierungspakete (z. B. gbm) auf Probleme mit mehreren Klassen auszudehnen. Ich werde mich mit "Alle Paare gewichtet" befassen.
Zach
Interessant ist glmnetauch eine multinomialVerlustfunktion. Ich frage mich, ob diese Verlustfunktion in anderen Algorithmen in R verwendet werden könnte, wie adaoder gbm?
Zach
Ja, einige Methoden können erweitert werden, um die multinomiale Verlustfunktion zu unterstützen. Zum Beispiel wird die logistische Kernel-Regression hier auf diese Weise erweitert: books.nips.cc/papers/files/nips14/AA13.pdf Soweit bekannt, adaist sie für eine bestimmte (exponentielle) Verlustfunktion "reserviert", aber man könnte eine weitere Erhöhung erweitern -basierte Methode zur Unterstützung der multinomialen Verlustfunktion - siehe z. B. Seite 360 ​​der Elemente des statistischen Lernens für Details zu GBM mit mehreren Klassen - K-Binärbäume werden für jede Boosting-Iteration erstellt, wobei K die Anzahl der Klassen ist (nur ein Baum pro Iteration) wird im binären Fall benötigt).
Jewgeni