En préparant une intervention pour mardi prochain, j’épluchais les résultats renvoyés pour un exercice, et j’ai eu un résultat assez étrange avec un modèle de classification. J’avais donné la même base cet automne à l’ensae, et j’avais donc près d’une trentaine d’autres modèles, pour comparer (disons plutôt que sur la même base de test, j’ai une trentaine de prévisions). Les observations noires sont celles obtenues cet automne (le trait correspond aux meilleurs AUC sur la base de test), et les observations rouges sont celles que j’ai obtenu pour l’intervention de mardi (là encore, le trait vertical correspond aux meilleurs modèles, au sens du AUC), sur une observation de la base de test,
Ce sont les probabilités prédites (mais j’ai enlevé l’échelle).
Pour presque toutes mes observations, les poids rouges sont bien au dessus des autres… Mais cela n’enlève en rien le fait que l’AUC obtenu (pour les deux modèles rouges) est très bon. C’est effectivement un résultat important (et connu) : le critère AUC (mais plus généralement la courbe ROC en entier) n’indique en rien si la valeur prédite est bonne, ou pas. Il nous dit juste si l’ordre obtenu est correct. Si les valeurs les plus importantes sont effectivement les valeurs pour lesquelles on a un 1, l’AUC sera très bon.
C’est ce qu’on peut observer sur le petit exemple ci-dessous. Considérons un modèle logistique simulé assez simple,
> n=1e3 > set.seed(1) > x1=rnorm(n) > x2=runif(n) > u=-3+x2+x1 > p=exp(u)/(1+exp(u)) > y=rbinom(n,prob=p,size=1) > library(ROCR) > df=data.frame(y,x1,x2) > mean(df$y) [1] 0.116 > reg=glm(y~.,data=df,family=binomial) > p=predict(reg,type="response") > mean(p) [1] 0.116 > pred1=prediction(p, df$y) > L=performance(pred1, "tpr", "fpr")
L’AUC est ici
> auc=performance(pred1, "auc")@y.values[[1]] > auc [1] 0.7681191
et la courbe ROC est la suivante,
> plot(unlist(L@x.values),unlist(L@y.values), + type="s",col="blue")
Supposons que l’on change la constante du modèle logistique,
> reg$coefficients[1]=0
Dans ce cas, notre prévision est assez mauvaise, car la probabilité moyenne prédite est ici
> u=reg$coefficients[1]+reg$coefficients[2]* + df$x1+reg$coefficients[3]*df$x2 > p=exp(u)/(1+exp(u)) > mean(p) [1] 0.6060676
(on est loin des 11,6% de 1 dans la base). Pourtant le AUC est bon
> pred1=prediction(p, df$y) > L=performance(pred1, "tpr", "fpr") > auc=performance(pred1, "auc")@y.values[[1]] > auc [1] 0.7681191
(c’est en fait la même valeur qu’auparavant, ce qui a du sens puisque la courbe ROC est identique)
> lines(unlist(L@x.values),unlist(L@y.values), + type="s",col="red")
Autrement dit, ces outils, classiquement utilisés pour juger la qualité d’un classifieur, ne permettent en aucun cas de dire que la probabilité prédite a du sens. Ces critères permettent juste de dire qu’on identifie assez bien les personnes qui ont le plus de chance d’avoir la réponse 1. Ce qui n’est pas si mal… mais c’est un autre problème que celui d’avoir une probabilité qui soit pertinente.
OpenEdition suggests that you cite this post as follows:
Arthur Charpentier (June 18, 2016). Ce que la courbe ROC (et l’AUC) ne raconte pas. Freakonometrics. Retrieved September 9, 2024 from https://doi.org/10.58079/ov4c
C’est le gros défaut de la courbe ROC et de l’AUC en effet. Ma petite expérience sur les compétitions Kaggle m’ont permis de découvrir une autre métrique d’évaluation qui me semble plus intéressante que l’AUC ou le MSE : la Logloss (ou perte logarithmique) et qui est aussi disponible dans le cadre d’une classification non binaire (Multi Class Logloss) !
(En général, je regarde les trois métriques : AUC, MSE et Logloss)
La Logloss pénalise alors énormément les prédictions faibles et fortes si elles sont erronées et permet de donner un sens aux probabilités prédites, ce qui n’est pas du tout le cas de l’AUC !
La logloss elle est calculée comme étant -1/N fois la somme de i=1 à N de y_i*log(pi) + (1-y_i)*log(1-p_i)
Où :
N est le nombre d’observations;
log est le logarithme népérien;
y_i est la modalité binaire que l’on doit prédire;
p_i est P(y_i = 1).
Conseil : remplacez les prédictions par max[min(p,1-10^{-15}),10^{-15}] car si une prédiction est à 0 ou 1, la log-loss ira à l’infini.
Une explication (en anglais) de la Logloss ainsi que quelques bouts de code R et Python : https://www.kaggle.com/wiki/LogarithmicLoss