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 predict
mé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.frame
das probabilidades previstas e classificar os casos. Observe que cabe a você construir a coluna K data.frame
de 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 predict
mé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?
car
ou um dos*lab
pacotes) 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.predict
método.Respostas:
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,
glmnet
suporta logit multinomial (regularizado) que pode ser usado como um classificador de várias classes.fonte
glmnet
incluir umamultinomial
função de perda. Gostaria de saber se essa função de perda poderia ser usada em outros algoritmos em R, comoada
ougbm
?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).