Plusieurs méthodes de machine learning qui ont toutes un même objectif de prédiction. Ces techniques requièrent typiquement le calibrage d’un ou plusieurs paramètres à partir des données. Aussi, la première fiche présente une stratégie pour calibrer un jeu de paramètres pour n’importe quelle méthode. Les parties suivantes présentent diverses méthodes comme les forêts aléatoires, la régression sous contraintes.

Enfin, pour clore notre étude, nous proposons une stratégie permettant de comparer les qualités prédictives de différentes méthodes.

La grande majorité des méthodes de machine learning nécessite d’optimiser de nombreux paramètres. Ainsi, les packages associés à une méthode proposent le plus souvent des fonctions d’optimisation pour choisir les paramètres par validation croisée par exemple (cf. fiche suivante). Il est donc possible de traiter toutes les données et de choisir les paramètres avec le jeu de données initial. La méthode est alors calibrée pour prédire de nouveaux individus en utilisant une fonction predict comme nous l’avons fait en régression par exemple.

Dans notre étude, nous présentons les fonctions qui permettent de calibrer une méthode puis de prédire de nouvelles données. Mais nous avons choisi d’insister sur l’évaluation de la qualité de prévision du modèle. Pour ce faire, nous avons décidé de découper initialement les données en une partie d’apprentissage et une partie de validation. Cette dernière permet d’évaluer les qualités prédictives du modèle optimisé sur l’échantillon d’apprentissage. On a également identifié parmi ces ourriels ceux qui sont des spams. Le but est de proposer une règle pour prédire si un nouveau message est un spam ou non à partir des mots utilisés dans le courriel. Le jeu de données est disponible dans le package kernlab, on peut le charger par :

library(kernlab)
 data(spam)

On présente seulement le résumé des cinq dernières variables :

summary(spam[,54:58])
##     charHash          capitalAve        capitalLong       capitalTotal    
##  Min.   : 0.00000   Min.   :   1.000   Min.   :   1.00   Min.   :    1.0  
##  1st Qu.: 0.00000   1st Qu.:   1.588   1st Qu.:   6.00   1st Qu.:   35.0  
##  Median : 0.00000   Median :   2.276   Median :  15.00   Median :   95.0  
##  Mean   : 0.04424   Mean   :   5.191   Mean   :  52.17   Mean   :  283.3  
##  3rd Qu.: 0.00000   3rd Qu.:   3.706   3rd Qu.:  43.00   3rd Qu.:  266.0  
##  Max.   :19.82900   Max.   :1102.500   Max.   :9989.00   Max.   :15841.0  
##       type     
##  nonspam:2788  
##  spam   :1813  
##                
##                
##                
## 

Les 48 premières variables contiennent la fréquence dans le courriel du nom de la variable (par exemple money). La variable 54 indique la fréquence du caractère dans le message. Les variables 55 à 57 contiennent la longueur moyenne, la longueur la plus grande et la longueur totale des lettres majuscules. Sur les 4 601 courriels, 1 813 ont été identifiés comme des spams.

Calibration d’un algorithme avec caret

Object

Un des problèmes les plus courants de l’apprentissage supervisé consiste à prédire une sortie \(y ∈ Y\) à partir d’entrées \(x = (x1, . . . , xd) ∈ R^d\) .Il s’agit donc de chercher un algorithme de prévision \(f : R^d→ Y\) qui pour une entrée x renverra la prévision \(y = f(x)\). Pour trouver la « meilleure » fonction f, le statisticien dispose d’un échantillon \((X1, Y1), . . . ,(Xn, Yn)\) composé de n couples indépendants et identiquement distribués à valeurs dans$ R^d × Y$. La plupart des méthodes statistiques fournissent à l’utilisateur une famille d’algorithmes \({fθ, θ ∈ Θ}\), où \(θ\) désigne le paramètre à calibrer et Θ l’ensemble des valeurs possibles de ce(s) paramètre(s). Le paramètre \(θ\) peut par exemple représenter le nombre de plus proches voisins pour l’algorithme des plus proches voisins, le nombre d’itérations en boosting, la profondeur de l’arbre pour les arbres CART, etc. Ce paramètre permet le plus souvent de mesurer la complexité du modèle. Une trop faible complexité ne permettra pas d’ajuster suffisamment bien les données et engendrera un mauvais pouvoir prédictif. À l’inverse, un modèle trop complexe ajustera presque parfaitement les données d’apprentissage mais aura du mal à bien prédire de nouveaux individus : on parle alors de sur-ajustement ou sur-apprentissage.

Ainsi, le choix de θ se révèle crucial pour la performance de l’algorithme de prévision et l’utilisateur doit trouver une procédure permettant de sélectionner automatiquement ce paramètre. Ce processus de sélection dépend de plusieurs points : – l’algorithme d’apprentissage (arbres, forêts, boosting, etc.) ; – la grille des paramètres candidats \(Θ\) ; – la fonction de perte (AUC, taux de mal classés, erreur quadratique moyenne, etc.) ; – la méthode d’estimation du critère (apprentissage/validation, validation croisée, bootstrap, estimation out of bag, etc.).

Le package caret (Classification And REgression Training) propose une démarche générale permettant de calibrer les paramètres d’un très grand nombre d’algorithmes statistiques (plus de 230 algorithmes en 2018).

Ce package propose un très grand nombre de fonctionnalités. Nous considérerons ici uniquement la sélection automatique de paramètres à l’aide de la fonction train.

Forêts aléatoires

Objet

Tout comme les arbres, les forêts aléatoires permettent de prédire une variable quantitative ou qualitative à partir de variables explicatives quantitatives et qualitatives. Cette famille d’algorithmes a été introduite par Breiman (2001) et permet de pallier, dans une certaine mesure, le manque de stabilité des arbres. La méthode est simple à mettre en œuvre et possède souvent de bonnes performances en terme de qualité de prédiction sur des jeux de données complexes, notamment en présence d’un grand nombre de variables explicatives.

Comme son nom l’indique, une forêt aléatoire consiste à agréger un grand nombre d’arbres de classification ou de régression (selon la nature de la variable à expliquer). Le caractère aléatoire du processus vient tout d’abord du fait que les arbres sont construits sur des échantillons bootstrap. Les échantillons bootstrap s’obtiennent généralement par tirage de n observations avec remise parmi \(n\) dans l’échantillon initial et les arbres sont construits selon une légère variante de l’algorithme CART présenté. En particulier, un second niveau d’aléatoire est introduit à chaque étape de la construction des arbres : à chaque nœud, seul un sous-ensemble de variables est sélectionné aléatoirement pour choisir la coupure. L’estimateur des forêts aléatoires en régression s’écrit:\[F(x)=\frac{1}{B}\sum_{k=1}^B T_ {k}(x)\] où Tk(x) représente l’arbre de décision construit sur le k-ème échantillon bootstrap

1. Importer les données

Le jeu de données se trouve dans le package kernlab, on peut le charger par :

library(kernlab)
data(spam)

On sépare l’échantillon en un échantillon d’apprentissage de taille 3 000 et un échantillon de validation de taille 1 601. L’échantillon d’apprentissage sera utilisé pour construire la forêt et sélectionner ses paramètres, celui de validation pour estimer les performances de la forêt et vérifier que cette dernière ne fait pas de sur-ajustement.

set.seed(5678)
perm <- sample(4601,3000)
app <- spam[perm,]
valid <- spam[-perm,]

2. Construire et analyser une forêt aléatoire

Plusieurs packages permettent de construire des forêts aléatoires, nous utiliserons le package randomForest et la fonction randomForest. La syntaxe de cette fonction est similaire aux autres fonctions de régression ou de discrimination, une formule indique la variable à expliquer et les variables explicatives.

library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
set.seed(1234)
foret <- randomForest(type~.,data=app)
foret
## 
## Call:
##  randomForest(formula = type ~ ., data = app) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 7
## 
##         OOB estimate of  error rate: 5.03%
## Confusion matrix:
##         nonspam spam class.error
## nonspam    1767   59  0.03231106
## spam         92 1082  0.07836457

La sortie renseigne sur : – le type de forêt aléatoire ajustée : classification, puisque la variable à expliquer est ici qualitative ; – le nombre d’arbres agrégés : 500, la valeur par défaut ; – le paramètre mtry, qui correspond au nombre de variables sélectionnées à chaque étape de construction des arbres : 7 (par défaut ce paramètre est égal à la partie entière de la racine carrée du nombre de variables explicatives en classification et au nombre de variables divisé par 3 en régression) ; – le taux d’erreur et la matrice de confusion estimés par la méthode Out Of Bag.

La méthode Out Of Bag consiste à utiliser les observations qui ne sont pas dans les échantillons bootstrap comme un échantillon de validation pour estimer la performance de la forêt. Cette astuce permet d’éviter de faire de la validation croisée pour avoir une idée précise de la performance de la forêt aléatoire en terme de prévision. Le taux d’erreur estimé par cette technique est ici de 4.73 %.

3. Sélectionner les paramètres de la forêt

Plusieurs paramètres peuvent être calibrés pour construire une forêt aléatoire, notamment le nombre d’arbres ntree et le nombre de variables mtry sélectionnées aléatoirement à chaque étape de construction des arbres. Un des avantages des forêts aléatoires est la stabilité des performances en fonction de ces paramètres. Il faut néanmoins s’assurer de les choisir convenablement. Concernant le nombre d’arbres, il est recommandé de le prendre assez grand. La théorie garantit que la forêt aléatoire est convergente lorsque le nombre d’arbres tend vers l’infini. On choisit généralement le nombre d’arbres de manière à se situer aussi près que possible de la limite. Le nombre d’itérations (arbres) pour atteindre le régime limite dépend bien entendu des données, le plus souvent on utilise la fonction plot sur la forêt et on regarde si les taux d’erreur représentés dans la figure construitesont stables :

plot(foret)

La courbe noire correspond à l’erreur de classification totale, les courbes verte et rouge aux erreurs de classification pour les spams et les non-spams. On peut retrouver les valeurs dans foret$err.rate :

tail(foret$err.rate)
##               OOB    nonspam       spam
## [495,] 0.05000000 0.03231106 0.07751278
## [496,] 0.05000000 0.03231106 0.07751278
## [497,] 0.05033333 0.03231106 0.07836457
## [498,] 0.05033333 0.03231106 0.07836457
## [499,] 0.05066667 0.03285871 0.07836457
## [500,] 0.05033333 0.03231106 0.07836457

Les taux d’erreur sont stables, on pourra donc conserver une forêt avec 500 arbres. Si le nombre d’arbres n’est pas suffisant, il suffit de modifier le paramètre ntree dans la fonction randomForest.

Concernant le paramètre mtry, nous proposons ici de le sélectionner à l’aide du package caret . On considère la grille suivante de candidats :

grille.mtry <- data.frame(mtry=seq(1,57,by=3))

On estime ensuite le taux de bien classés (Accuracy) en utilisant la technique Out Of Bag. Nous privilégions cette technique car elle est bien adaptée aux forêts aléatoires, elle est notamment plus rapide qu’une validation croisée. Avec cette méthode de sélection, nous choisirons mtry=10.

library(caret)
## Loading required package: ggplot2
## 
## Attaching package: 'ggplot2'
## The following object is masked from 'package:randomForest':
## 
##     margin
## The following object is masked from 'package:kernlab':
## 
##     alpha
## Loading required package: lattice
ctrl <- trainControl(method="oob")
library(doParallel) ## pour paralléliser
## Warning: package 'doParallel' was built under R version 4.2.3
## Loading required package: foreach
## Loading required package: iterators
## Loading required package: parallel
cl <- makePSOCKcluster(4)
registerDoParallel(cl)
set.seed(12345)
sel.mtry <- train(type~.,data=app,method="rf",trControl=ctrl,
tuneGrid=grille.mtry)
#stopCluster(cl) # fermeture des clusters
 sel.mtry
## Random Forest 
## 
## 3000 samples
##   57 predictor
##    2 classes: 'nonspam', 'spam' 
## 
## No pre-processing
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##    1    0.9160000  0.8191048
##    4    0.9513333  0.8972161
##    7    0.9510000  0.8965912
##   10    0.9503333  0.8952804
##   13    0.9493333  0.8931229
##   16    0.9503333  0.8951522
##   19    0.9480000  0.8902768
##   22    0.9463333  0.8867774
##   25    0.9473333  0.8887681
##   28    0.9456667  0.8852657
##   31    0.9466667  0.8873256
##   34    0.9446667  0.8830646
##   37    0.9433333  0.8803201
##   40    0.9440000  0.8816557
##   43    0.9443333  0.8823421
##   46    0.9413333  0.8760203
##   49    0.9423333  0.8779654
##   52    0.9443333  0.8823061
##   55    0.9413333  0.8759823
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 4.

4. Faire de la prévision

La fonction predict.randomForest, que l’on peut appeler en utilisant le raccourci predict, permet de prédire le label d’un nouveau courriel. Pour la forêt construite avec le paramètre mtry sélectionné dans la partie précédente, on obtient les prévisions pour les individus de l’échantillon de validation avec :

set.seed(5432)
foret1 <- randomForest(type~.,data=app,mtry=10)
prev.valid <- predict(foret1,newdata=valid)
prev.valid[1:10]
##       1       5       6      13      19      21      23      24      25      26 
##    spam    spam    spam    spam    spam nonspam    spam    spam    spam    spam 
## Levels: nonspam spam

Parmi les dix premiers individus de l’échantillon de validation, 9 sont considérés comme spam. On peut également estimer la probabilité qu’un nouveau courriel soit un spam en ajoutant l’argument type=“prob” :

prob.valid <- predict(foret1,newdata=valid,type="prob")
prob.valid[1:10,]
##    nonspam  spam
## 1    0.008 0.992
## 5    0.048 0.952
## 6    0.324 0.676
## 13   0.002 0.998
## 19   0.020 0.980
## 21   0.616 0.384
## 23   0.082 0.918
## 24   0.208 0.792
## 25   0.012 0.988
## 26   0.012 0.988

La probabilité d’être un spam pour le premier courriel de l’échantillon de validation est estimée à 1.000, celle pour le second à 0.694, etc. Par défaut, quand les probabilités sont supérieures à 0.5, le courriel est prévu comme spam.

5. Estimer les performances de la forêt

On souhaite estimer la performance de la forêt sélectionnée (avec mtry=10) enestimant son taux de mal classés et sa courbe ROC. Nous allons également comparer ces performances à la forêt utilisant la valeur p par défaut de mtry, c’est-à-dire mtry=7. Les taux de mal classés peuvent être estimés en utilisant l’échantillon de validation ou par la méthode Out Of Bag. On les obtient directement avec la fonction randomForest :

set.seed(5432)
foret2 <- randomForest(type~.,data=app,
xtest=valid[,-58],ytest=valid[,58],keep.forest=TRUE)
set.seed(891)
foret3 <- randomForest(type~.,data=app,mtry=10,
xtest=valid[,-58],ytest=valid[,58],keep.forest=TRUE)
foret2
## 
## Call:
##  randomForest(formula = type ~ ., data = app, xtest = valid[,      -58], ytest = valid[, 58], keep.forest = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 7
## 
##         OOB estimate of  error rate: 4.8%
## Confusion matrix:
##         nonspam spam class.error
## nonspam    1768   58  0.03176342
## spam         86 1088  0.07325383
##                 Test set error rate: 5%
## Confusion matrix:
##         nonspam spam class.error
## nonspam     935   27  0.02806653
## spam         53  586  0.08294210

Les taux d’erreur sont relativement proches, ce qui confirme bien que la méthode est relativement peu sensible aux choix des paramètres. De plus, ces taux d’erreur sont du même ordre que ceux obtenus sur les données d’apprentissage dans la section 3. On peut donc considérer qu’il n’y a pas de sur-ajustement pour ces deux forêts.

Le package pROC permet de tracer rapidement les courbes ROC et de calculer les aires sous les courbes ROC (AUC) :

library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
prev2 <- predict(foret2,newdata=valid,type="prob")[,2]
roc2 <- roc(valid$type,prev2)
## Setting levels: control = nonspam, case = spam
## Setting direction: controls < cases
prev3 <- predict(foret3,newdata=valid,type="prob")[,2]
roc3 <- roc(valid$type,prev3)
## Setting levels: control = nonspam, case = spam
## Setting direction: controls < cases
plot(roc2,print.auc=TRUE,print.auc.x=0.4,print.auc.y=0.3)
plot(roc3,add=TRUE,col="red",print.auc=TRUE,print.auc.col="red",
print.auc.x=0.4,print.auc.y=0.2)

6. Interpréter la forêt aléatoire

On reproche souvent aux forêts aléatoires l’aspect « boîte noire » et le manque d’interprétabilité. Il existe néanmoins des indicateurs qui permettent de mesurer Courbes ROC et AUC des forêts aléatoires avec mtry=7 (noir), mtry=10 (rouge) et d’un arbre de classification (bleu).

L’importance des variables explicatives dans la construction de la forêt. La sortie importance de la fonction randomForest renvoie une note (ou un score) pour chaque variable : plus la note est élevée, plus la variable est importante dans la forêt. Cette note est construite à partir de l’erreur OOB de la forêt. Pour chaque variable, on compare l’erreur OOB de la forêt à l’erreur calculée sur l’échantillon OOB où les valeurs de la variable en question ont été permutées aléatoirement. Si l’écart entre ces deux erreurs est petit, cela signifie que les valeurs de la variable n’ont pas grande importance vis-à-vis de la performance de la forêt. A contrario, plus l’écart est grand, plus la variable sera importante. Les commandes suivantes permettent de représenter, à l’aide d’un diagramme en bâtons, les 10 variables les plus importantes de la forêt foret2 selon ce critère :

var.imp <- foret2$importance
ord <- order(var.imp,decreasing=TRUE)
barplot(sort(var.imp,decreasing = TRUE)[1:10],
names.arg=rownames(var.imp)[ord][1:10],cex.names=0.6)

Le point d’exclamation est le caractère le plus influent pour identifier les spam. La valeur de la mesure d’importance n’a pas vraiment d’interprétation, on l’utilisera généralement pour ordonner les variables par ordre décroissant d’importance. Pour un grand nombre de variables, on pourra aussi utiliser cette mesure d’importance pour faire de la sélection de variables à l’aide d’une procédure en 2 étapes : – on construit la forêt et on calcule l’importance de chaque variable ; – on reconstruit une forêt en ne conservant que les K variables les plus importantes de la forêt précédente, le paramètre \(K\) devant être choisir par l’utilisateur.