Estensione dei modelli di classe 2 a problemi di classe multipla


11

Questo documento su Adaboost fornisce alcuni suggerimenti e codice (pagina 17) per estendere i modelli di classe 2 ai problemi di classe K. Vorrei generalizzare questo codice, in modo da poter collegare facilmente diversi modelli di 2 classi e confrontare i risultati. Poiché la maggior parte dei modelli di classificazione ha un'interfaccia formula e un predictmetodo, alcuni di questi dovrebbero essere relativamente facili. Sfortunatamente, non ho trovato un modo standard per estrarre le probabilità di classe da modelli di 2 classi, quindi ogni modello richiederà un codice personalizzato.

Ecco una funzione che ho scritto per suddividere un problema di classe K in problemi di classe 2 e restituire modelli 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)
}

Ecco un metodo di previsione che ho scritto per scorrere su ogni modello e fare previsioni:

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

E infine, ecco una funzione per trasformare una normalizzazione data.framedelle probabilità previste e classificare i casi. Nota che spetta a te costruire la colonna K data.framedi probabilità da ciascun modello, poiché non esiste un modo unificato per estrarre le probabilità di classe da un modello di 2 classi:

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

Ecco un esempio usando 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

Ecco un esempio usando lda(so che lda può gestire più classi, ma questo è solo un esempio):

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

Queste funzioni dovrebbero funzionare per qualsiasi modello di 2 classi con un'interfaccia formula e un predictmetodo. Nota che devi dividere manualmente i componenti X e Y, il che è un po 'brutto, ma la scrittura di un'interfaccia formula è al di là di me al momento.

Questo approccio ha senso per tutti? Esiste un modo per migliorarlo o esiste un pacchetto esistente per risolvere questo problema?


2
Wow, fino a quando me lo avessi chiesto e non avrei guardato, sarei stato sicuro che alcuni pacchetti (come car, o uno dei *labpacchetti) avrebbero fornito una funzione come la tua. Scusa, non posso aiutarti. Ho letto un po 'su come funziona SV-k-way e sembra che sia stato più complicato di quanto avrei pensato.
Wayne,

1
@Wayne: anche a me! Ero certo che ci sarebbe stata una funzione generale che lo avrebbe fatto, a condizione che il modello avesse un predictmetodo.
Zach,

Risposte:


1

Un modo per migliorare è utilizzare l' approccio "ponderato per tutte le coppie" che è presumibilmente migliore di "uno contro tutti" mentre è ancora scalabile.

Per quanto riguarda i pacchetti esistenti, glmnetsupporta il log multinomiale (regolarizzato) che può essere utilizzato come classificatore multi-classe.


Sono a conoscenza dei numerosi pacchetti in R che supportano la classificazione multi-classe (come glmnet, foreste casuali, kernlab, rpart, nnet, ecc.). Sono più curioso di estendere i pacchetti di classificazione binaria (es. Gbm) a problemi multiclasse. Esaminerò "ponderato tutte le coppie".
Zach,

Inoltre, è interessante che glmnetinclude una multinomialfunzione di perdita. Mi chiedo se questa funzione di perdita potrebbe essere utilizzata in altri algoritmi in R, come adao gbm?
Zach,

Sì, alcuni metodi possono essere estesi per supportare la funzione di perdita multinomiale. Ad esempio, la regressione logistica del kernel è estesa in questo modo qui: books.nips.cc/papers/files/nips14/AA13.pdf Per quanto si sa adaè "riservato" per una specifica funzione di perdita (esponenziale), ma si potrebbe estendere un altro potenziamento metodo basato su supporto per supportare la funzione di perdita multinomiale - ad es. vedi pagina 360 di The Elements of Statistical Learning per dettagli su GBM multi-classe - Gli alberi binari K sono costruiti per ogni iterazione di potenziamento in cui K è il numero di classi (solo un albero per iterazione è necessario nel caso binario).
Evgenij
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.