Master 2 MIMSE Apprentissage automatique 2015-2016 TP2 : analyse discriminante et régression logistique 1 Données synthétiques Récupérer les jeux de données synth_train.txt et synth_test.txt. On a Y ∈ {1, 2} et X ∈ R2 . On dispose de 100 données d’apprentissage et 200 données test. 1. Charger le jeu de données d’apprentissage dans R (soit avec la commande read.table, soit avec l’outil d’importation de Rstudio : Tools > Import Dataset). Afficher les données d’apprentissage. 2. Représenter graphiquement les observations à l’aide de la fonction plot. On pourra colorier les points en fonction de leur classe à l’aide du paramètre col et modifier le symbole avec le paramètre pch (=point character) et rajouter une légende à l’aide de la fonction legend. 3. On s’intéresse d’abord au classifieur QDA (quadratic discriminant analysis) (a) Constuire le classifieur QDA à partir des données d’apprentissage et de la fonction qda du package MASS . (b) Prédire les classes et les probabilités à posteriori des deux points de coordonnées (0,0) et (-2,2). newdata <- data.frame(x1=c(0,-2),x2=c(0,2)) (c) Représenter graphiquement la frontière de décision : commencer par construire une grille de points, prédire ces points, puis ajouter ces points sur le graphique en les coloriant en fonction de leur prédiction. (d) Charger le jeu de données test dans R. Prédire les données de l’ensemble test avec ce classifieur et comparer avec les vraies classes. Calculer le taux d’erreur empirique (nombre de fausses prédictions sur l’échantillon d’apprentissage divisé par taille de cet échantillon). 4. On s’intéresse maintenant au classifieur LDA (linear discriminant analysis). (a) Reprendre les questions précédentes (contruction du classifieur, prédiction des deux points, frontière de décision et taux d’erreur test) avec la fonction lda du package MASS. (b) Dans le cas particulier de la classification binaire, on peut écrire que la probabilité à posteriori P(Y = 2|X = x) est une fonction logistique d’un score linéaire : P(Y = 2|X = x) = exp(α0 + xT α) 1 + exp(α0 + xT α) et que ce score linéaire est une fonction logit de la probabilité à posteriori : log P(Y = 2|X = x) = α0 + xT α. P(Y = 1|X = x) Le score linéaire α0 + xT α est le logarithme de l’odd ratio P(Y = 2|X = x) , aussi appelé logP(Y = 1|X = x) odds ou encore la probabilité à posteriori sur l’échelle logit. — Trouver l’expression de α0 et α dans le cours (p. 40 des diapos du chapitre 2). 1 — A quelle valeur se compare α0 + xT α pour classer x ? — Calculer les log-odds (probabilités à posteriori sur l’échelle logit) des deux points de coordonnées (0,0) et (-2,2). 5. On s’intéresse maintenant à la régression logistique qui fournit un fonction de classification binaire et estime directement les probabilités à posteriori. (a) Reprendre les questions précédentes (contruction du classifieur, prédiction des deux points, frontière de décision et taux d’erreur test) avec la fonction glm et en vous aidant du code suivant : #regression logistique sur donnees d'apprentissage ?glm g <- glm(as.factor(y)~x1+x2,data=train,family=binomial) #proba a posteriori P(Y=2|X=x) pour x=(0,0),x=(-2,2) predict(g, newdata, type="response") ## 1 2 ## 9.999998e-01 1.061813e-06 #P(Y=2|X=(0,0))=0.99 #P(Y=2|X=(-2,2))=0.00000106 #fontieres de decision prob_grille <- predict(g,data.frame(grille),type="response") pred_grille <- 2*I(prob_grille>0.5)+1*I(prob_grille<=0.5) (b) En régression logistique, on définit le modèle suivant : P(Y = 2|X = x) = exp(β0 + xT β) 1 + exp(β0 + xT β) et les paramètres β0 et β sont estimés par maximum de vraisemblance sur les données d’apprentissage. On a donc comme en LDA un score linéaire qui est une fonction logit de la probabilité à posteriori : P(Y = 2|X = x) log = β0 + xT β, P(Y = 1|X = x) aussi appellé log-odds ou encore la probabilité à posteriori sur l’échelle logit. — Quelles sont les valeurs des paramètres β0 et β estimés sur les données d’apprentissage ? — Quel test est utilisé pour tester la nullité de ces coefficients ? — Calculer le log-odd (probabilités à posteriori sur l’échelle logit) du points de coordonnées (-2,2) de 3 manières différents. 6. Pour les trois méthodes considérées ici, un seuil a été fixé pour constuire les fonctions de classification g. Ce seuil vaut 0.5 si on regarde les probabilités à posteriori p = P(Y = 2|X = x) et 0 si p on regarde les log-odds . On va faire varier ce seuil en LDA et regarder comment le taux de 1−p bons classements varie. (a) Faire varier le seuil de 0 à 1 par pas de 0.1 et représenter sur un graphique le taux de bons classement en fonction du seuil. (b) Recommencer mais en faisant varier cette fois le seuil des log-odds par pas de 0.1. (c) Calculer les taux de vrais positis (T V P ) et de vrais négatifs (T V N ) lorsque le seuil varie sur l’échelle des log-odds par pas de 0.1. Ici positif = 2. Tracer ensuite la courbes ROC des points (1 − T V N (s); T V P (s)). 7. On peut retrouver ces graphiques avec le package ROCR et le code suivant : library(ROCR) # 3 fonctions : prediction, performance, plot #label.ordering pour le label de la classe negative puis positive pred <- prediction(prob_test, Ytest,label.ordering=c("1","2")) perf <- performance(pred, "acc") plot(perf) 2 0.9 0.8 0.7 0.6 0.3 0.4 0.5 Accuracy 0.0 0.2 0.4 0.6 0.8 1.0 Cutoff 0.6 0.3 0.4 0.5 Accuracy 0.7 0.8 0.9 pred <- prediction(odds_test, Ytest,label.ordering=c("1","2")) perf <- performance(pred, "acc") plot(perf) −10 −5 0 5 Cutoff pred <- prediction(prob_test, Ytest,label.ordering=c("1","2")) perf <- performance(pred, "tpr", "fpr") plot(perf,colorize=TRUE) #courbe ROC avec couleur selon les seuils 3 10 1.01 0.2 0.4 0.6 0.8 1.0 0.8 0.6 0.4 0 0.0 0.2 True positive rate 0.0 0.2 0.4 0.6 0.8 1.0 0.8 1.0 False positive rate auc <- performance(pred, "auc")@y.values[[1]] auc ## [1] 0.9893642 3.87 −0.61 0.6 −5.08 0.4 0.0 −9.56 0.2 True positive rate 0.8 8.35 1.0 12.83 pred <- prediction(odds_test, Ytest,label.ordering=c("1","2")) perf <- performance(pred, "tpr", "fpr") plot(perf,colorize=TRUE) #courbe ROC avec couleur selon les seuils 0.0 0.2 0.4 0.6 False positive rate 8. On souhaite maintenant associer un coût 3 fois plus important aux faux positifs. 4 Le coût moyen est mesuré par la fonction average_cost. average_cost <- function(Y_pred, Y_test) { FP = (Y_pred == "2") & (Y_test == "1") FN = (Y_pred == "1") & (Y_test == "2") return(sum(3*FP+1*FN)/length(Y_test)) } Proposez un seuil pour le classifieur LDA qui minimise ce coût moyen sur les données d’apprentissage. Calculer alors le T V P et le T V N sur les données tests. g <- lda(y~x1+x2,data=train) prob_test <- predict(g,Xtest)$posterior[,2] odds_test <- log(prob_test/(1-prob_test)) s <- seq(min(odds_test),max(odds_test),by=0.1) cout_moy <- rep(NA,length(s)) for (i in 1:length(s)) { pred_test <- 2*I(odds_test>s[i])+1*I(odds_test<=s[i]) cout_moy[i] <- average_cost(pred_test,Ytest) } plot(s,cout_moy,type="l", col=2,xlab="seuil", ylab="Cout moyen", main="Seuil sur les log-odds") 0.2 0.4 Cout moyen 0.6 0.8 Seuil sur les log−odds −10 −5 0 5 seuil #seuil optimal s[which.min(cout_moy)] ## [1] 2.04147 s <- seq(0,1,by=0.01) cout_moy <- rep(NA,length(s)) for (i in 1:length(s)) { pred_test <- 2*I(prob_test>s[i])+1*I(prob_test<=s[i]) cout_moy[i] <- average_cost(pred_test,Ytest) } 5 10 plot(s,cout_moy,type="l", col=2,xlab="seuil", ylab="Cout moyen", main="Seuil sur les proba") 0.6 0.2 0.4 Cout moyen 0.8 Seuil sur les proba 0.0 0.2 0.4 0.6 seuil #seuil optimal s[which.min(cout_moy)] ## [1] 0.88 log(0.885/(1-0.885)) ## [1] 2.040656 hist(prob_test) 6 0.8 1.0 60 0 20 40 Frequency 80 100 120 Histogram of prob_test 0.0 0.2 0.4 0.6 0.8 1.0 prob_test hist(odds_test) 20 15 0 5 10 Frequency 25 30 35 Histogram of odds_test −10 −5 0 5 10 odds_test 2 Données réelles Récuperer le jeu de données Desbois_complet.rda. Les données concernent n = 1260 exploitations agricoles réparties en K = 2 groupes : le groupe des exploitations saines et le groupe des exploitations 7 défaillantes. On veut construire un score de détection du risque financier applicable aux exploitations agricoles. Pour chaque exploitation agricole on a mesuré une batterie de p = 30 crières économiques et financiers pour construire le score. La variable qualitative à expliquer est donc la variable difficulté de paiement (0=sain et 1=défaillant). Une étude de cas de ces données est disponible à l’adresse suivante : http://publications-sfds.fr/index.php/csbigs/article/view/351/331 1. Charger le jeu de données complet dans R. 2. On veut dans un premier temps comparer les performances de différents classifieur. (a) Quelles stratégies peuvent être envisagées pour comparer correctement les taux d’erreur des classifieurs obtenus avec les méthodes knn, lda, qda et régression logistique ? (b) Retrouver le graphique ci-dessous obtenue avec B = 50 découpages aléatoire des données en 945 observations d’apprentissage/validation et 315 observations test. 0.08 0.10 0.12 0.14 0.16 0.18 0.20 0.22 Erreurs test pour 50 decoupages knn lda qda glm 3. On veut maintenant définir une fonction de classification (un classifieur) avec moins de variables explicatives et donc effectuer une sélection de variables. Une première approche consiste à choisir un critère de choix de modèle et un algorithme de sélection de variables. (a) Créer un jeu de données de données d’apprentissage de taille 945 (75% des données) et un jeu de données test de taille 315 (25% des données) avec le code suivant. set.seed(30) tr <- sample(1:nrow(data),945) train <- data[tr,] test <- data[-tr,] (b) Quels sont les taux d’erreurs sur les données tests des fonctions de classification obtenues avec toutes les variables en lda et regression logistique. error_rate <function(y_pred, y_test) { return(sum(y_pred!=y_test)/length(y_test)) } 8 (c) La fonction greedy.wilks du package klaR effectue une sélection de variable pas à pas ascendante en lda. Testez cette méthode avec le code ci-dessous. Combien de variables sont sélectionnées, quel est le taux d’erreur de ce classifieur sur les données test ? library(klaR) ?greedy.wilks # lda avec selection de variables pas à pas ascendante g <- greedy.wilks(DIFF~.,data=train,niveau=0.1) g$formula ## DIFF ~ R1 + R32 + R14 + R17 + R2 + R3 + R36 + R21 + R4 ## <environment: 0x3b5e728> #prediction des donnees test pred_wilks <- predict(lda(g$formula,data=train),test)$class (d) La fonction step effectue une sélection de variable pas à pas ascendante, descendante ou ascendante/descendante en régression logistique. Testez cette méthode avec le code ci-dessous. Combien de variables sont sélectionnées, quel est le taux d’erreur sur les données test de ce classifieur ? ?step library(MASS) ?stepAIC # logistique avec selection de variables pas a pas ascendante et descendante modelenul <- glm(DIFF~1,data=train) modelecomplet <- glm(DIFF~.,data=train) g <- step(modelenul, scope=list(upper=modelecomplet), direction="both",trace=FALSE) g$formula ## DIFF ~ R1 + R32 + R14 + R17 + R2 + R3 + R36 + R21 + R4 + R19 + ## R37 # logistique + selection pas à pas descendante # g <- step(modelecomplet,direction="backward",trace=FALSE) # logistique + selection pas à pas ascendante # g <- step(modelenul, scope=list(lower=modelenul,upper=modelecomplet), # direction="forward",trace=FALSE) # g$formula #prediction des donnees test prob <- predict(g,newdata=test,type="response") pred_step <- 1*I(prob>0.5)+0*I(prob<=0.5) 4. Pour sélectionner des variables, il est également possible d’utiliser des approches de type regression pénalisée (régularisée). Les deux méthodes classiques de régression pénalisées sont la régression ridge et la regression LASSO. La régression ridge est utilisée pour gérer la question de la multicolinéarité, et la régression LASSO est utilisée pour gérer la question de la sélection de variable. De manière très simplifiée, on écrit le modèle de régression sous la forme Y = Xβ + ε et on peut dire qu’en régression ridge l’estimateur de β est défini par : β̂ = arg minp ||Y − Xβ||2 + λ||β||22 β∈R et qu’en régression LASSO il est défini par : β̂ = arg minp ||Y − Xβ||2 + λ||β||1 β∈R où dans les deux cas λ est un paramètre positif à choisir. La régression ridge correspond à la minimisation d’un critère des moindres carrés avec une pénalité de type `2 et la régression LASSO à la minimisation d’un critère des moindres carrés avec une 9 pénalité de type `1 . Pour la méthode LASSO, on voit bien que : - si λ = 0 on retrouve l’estimateur des moindres carrés de la régression linéaire multiple. - si λ tend vers l’infini, on annulle tous les β̂j pour j = 1, . . . , p. En pratique, le paramètre λ est souvent fixé par validation croisée. Des méthodes de type LASSO peuvent également être utilisées en classification : - la fonction PenalizedLDA effectue une lda régularisée avec pénalité de type `1 . - la fonction glmnet effectue une régression logistique régularisée avec pénalité de type `1 .. Pour effectuer une régression logistique régulariséee de type LASSO, appliquez le R code ci-dessous et en dédruie : - la stratégie du choix du paramètre λ implémentée par défaut, — la liste des variables sélectionnées, — le taux d’erreur test. library(glmnet) ?glmnet #choix de lambda par validation croisée 10 fold ?cv.glmnet g <- cv.glmnet(as.matrix(train[,-1]),train$DIFF,family="binomial", type.measure="class") #quel autre type de mesure est possible ? plot(g) 8 7 6 5 4 4 4 4 0.3 0.1 0.2 Misclassification Error 0.4 0.5 20 19 18 15 15 12 11 11 12 −8 −6 −4 log(Lambda) g$lambda.min # a bien comprendre ## [1] 0.003399611 g$lambda.1se # a bien comprendre ## [1] 0.0554052 coef(g) ## 23 x 1 sparse Matrix of class "dgCMatrix" ## 1 10 −2 3 0 ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## (Intercept) -1.825295 R1 2.431740 R2 . R3 . R4 . R5 . R6 . R7 . R8 . R11 . R12 . R14 1.204252 R17 . R18 6.138417 R19 . R21 . R22 . R24 . R28 . R30 . R32 -3.237207 R36 . R37 . #prediction avec les variables selectionnées prob <- predict(g,as.matrix(test[,-1]),type="response") pred_glmnet <- 1*I(prob>0.5)+0*I(prob<=0.5) 5. Proposez une stratégie pour évaluer le taux d’erreur du classifieur obtenu avec la fonction glmnet. 6. Constuire le classifieur qui sera finalement utilisé pour prédire si de nouvelles exploitations sont saines ou défaillantes. 11