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.
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.
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
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,]
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 %.
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.
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.
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)
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.