2
$\begingroup$

Using R, I got a bunch of confusion matrices from some model fitting. I'm trying to choose the best model by looking at their confusion matrix. Not an easy task.

My current method of comparison is to choose the best model as the one that looks the most like a diagonal matrix. My interpretation is that the diagonal of this matrix would contain the higher values relative to the other matrices.

My method for comparison in R is to take out each diagonal, append it to a new dataframe, one vector column for every confusion matrix, so it would be simpler to decide with just a look.

  • Am I correct in comparing the diagonals in this manner?
    • If no, what would be a better method of comparison?
    • If yes, is there a better algorithm than the one I am using?
$\endgroup$
0

1 Answer 1

0
$\begingroup$

There is no single perfect solution to comparing multi class classification performances on basis of their individual classes - but there are different approaches that might be appropriate for your problem. Your approach of looking at the diagonal of the confusion matrix does conceptually make sense, but likely is more complicated than necessary, as the confusion matrix still holds much details (how classes are confused - which is good to know but complicated to automatically select a best model from). To name some other ways of selecting a model by looking at results over individual classes: you could use multi class ROC curves (see e.g. this answer) and multi class AUC/AUROC (see e.g. multiclass.roc in pROC). The latter gives you a single performance metric value per model which can be used to decide on the single best model if necessary.

I usually end up computing one or more of those: a) all ROC curves for all 1-vs-all problems that are part of my $N$ class problem, and using those to look at b) the distribution of the AUCs over all classes, c) the distribution of EERs over all classes, c) and distribution of the true positive and false positive rates over all classes. The more models I have to evaluate, the more I usually automate this process. For example, for thousands of models I will compute only a single, scalar metric value which allows me to filter bad performing models, which will leave me with a smaller amount of models to look at in detail. Here's a simple example of how this could look like:

At first we create a model that gives some example data and is able to compute class probabilities:

library(caret)
library(pROC)
m <- train(iris[,1:2], iris[,5], method = 'lda', metric = 'Kappa', trControl = trainControl(method = 'repeatedcv', number = 10, repeats = 20, classProbs = T, savePredictions = T))
str(m$pred)

Next we calculate all ROC curves for all 1-vs-all problems part of our $N$ class problem:

rocs <- llply(unique(m$pred$obs), function(cls) {
    roc(response = m$pred$obs==cls, predictor = m$pred[,as.character(cls)])
})
# for curiosity: look at the ROC curves
plot(rocs[[1]])
l_ply(rocs[-1], plot, add=T)

Using those ROC curves we can calculate all AUC for all classes. Those represent the models power per individual class, and its mean, median, or similar could already be seen as metric for the overall power:

aucs <- laply(rocs, function(x) as.numeric(x$auc))
summary(aucs)

The same is true for the EER per class:

eers <- laply(rocs, function(x) x$sensitivities[which.min(abs(x$sensitivities-x$specificities))])
summary(eers)

We can also look at the true positive and true negative rate per class - which is more related to your idea of looking at the diagonal of the confusion matrix than the previous approaches. The mean or median true positive and true negative rate (or the mean or median of those means/medians if you want to have a single value) could be used to derive the overall power of your model as well:

conf <- ldply(unique(m$pred$obs), function(cls) data.frame(tp=sum(m$pred$pred[m$pred$obs==cls]==cls), 
                                                        fn=sum(m$pred$pred[m$pred$obs==cls]!=cls), 
                                                        tn=sum(m$pred$pred[m$pred$obs!=cls]!=cls),
                                                        fp=sum(m$pred$pred[m$pred$obs!=cls]==cls)))
conf$tpr <- with(conf, tp/(tp+fn))
conf$tnr <- with(conf, tn/(tn+fp))
summary(conf[,5:6])

Bottom line: in general I would use such approaches to filter models you for sure don't want to look at - but for the remaining handful of models, looking at the confusion matrix usually is a good idea, as it still holds much information about what is going on for them.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.