Estendendo modelos de 2 classes a problemas de várias classes


11

Este documento no Adaboost fornece algumas sugestões e códigos (página 17) para estender os modelos de 2 classes para problemas da classe K. Gostaria de generalizar esse código, para que eu possa conectar facilmente diferentes modelos de 2 classes e comparar os resultados. Como a maioria dos modelos de classificação possui uma interface de fórmula e um predictmétodo, parte disso deve ser relativamente fácil. Infelizmente, não encontrei uma maneira padrão de extrair probabilidades de classe de modelos de 2 classes, portanto cada modelo exigirá algum código personalizado.

Aqui está uma função que escrevi para dividir um problema da classe K em problemas de 2 classes e retornar os modelos 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)
}

Aqui está um método de previsão que escrevi para iterar sobre cada modelo e fazer previsões:

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

E finalmente, aqui está uma função para normalizar uma data.framedas probabilidades previstas e classificar os casos. Observe que cabe a você construir a coluna K data.framede probabilidades de cada modelo, pois não há uma maneira unificada de extrair probabilidades de classe de um modelo de 2 classes:

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

Aqui está um exemplo 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

Aqui está um exemplo usando lda(eu sei que o lda pode lidar com várias classes, mas este é apenas um exemplo):

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

Essas funções devem funcionar para qualquer modelo de 2 classes com uma interface de fórmula e um predictmétodo. Observe que você precisa dividir manualmente os componentes X e Y, o que é um pouco feio, mas escrever uma interface de fórmula está além de mim no momento.

Essa abordagem faz sentido para todos? Existe alguma maneira de melhorá-lo ou existe um pacote existente para resolver esse problema?


2
Uau, até que você perguntasse e eu olhasse, eu teria certeza de que algum pacote (como carou um dos *labpacotes) teria fornecido uma função como a sua. Desculpe, não posso ajudar. Eu li um pouco sobre como o k-way SVM funciona e parece que foi mais complicado do que eu pensava.
— Wayne

1
@Wayne: Eu também! Eu tinha certeza de que haveria alguma função geral que faria isso, desde que o modelo tivesse um predictmétodo.
— Zach

Respostas:


1

Uma maneira de melhorar é usar a abordagem "todos os pares ponderados", que é supostamente melhor do que "um contra todos" enquanto ainda é escalável.

Quanto aos pacotes existentes, glmnetsuporta logit multinomial (regularizado) que pode ser usado como um classificador de várias classes.


Estou ciente dos muitos pacotes no R que oferecem suporte à classificação de várias classes (como glmnet, florestas aleatórias, kernlab, rpart, nnet etc.). Estou mais curioso em estender os pacotes de classificação binária (por exemplo, gbm) a problemas de várias classes. Vou analisar "ponderado todos os pares".
— Zach

Além disso, é interessante glmnetincluir uma multinomialfunção de perda. Gostaria de saber se essa função de perda poderia ser usada em outros algoritmos em R, como adaou gbm?
— Zach

Sim, alguns métodos podem ser estendidos para oferecer suporte à função de perda multinomial. Por exemplo, a regressão logística do kernel é estendida dessa maneira aqui: books.nips.cc/papers/files/nips14/AA13.pdf Até onde se sabe, adaé "reservado" para uma função de perda específica (exponencial), mas pode-se estender outro aumento baseado em método para suportar a função de perda multinomial - por exemplo, consulte a página 360 de Os elementos do aprendizado estatístico para obter detalhes sobre GBM-K multi-classes de árvores binárias são construídas para cada iteração dinâmica em que K é o número de classes (apenas uma árvore por iteração é necessário em caso binário).
— Yevgeny
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.