INTRODUCTION

Pour faire suite à mon article sur le Machine Learning, je vous propose de rentrer dans le vif du sujet. A travers ce petit rapport, j’aborde ,via des exemples, quelques problèmes de Classification, de Régression et de Clustering (Je préfère employer le mot clustering mais il faut le percevoir comme un groupement d’objets).

Le problème de Classification sera abordé avec le célèbre jeu de données titanic que vous pouvez télécharger ici. J’insisterai sur la Matrice de confusion pour discuter de la performance du modèle. On étudiera la régression avec les données air. A ce niveau on calculera le RMSE et on étudiera son évolution en fonction d’ajout de complexité dans le modèle. Pour terminer, nous étudierons le Clustering à travers le jeu de données seed. Je vous montrerai comment vous pouvez évaluer votre modèle selon la compacité de chaque groupe (cluster) et de leur séparation.

Par ailleurs, je souligne que pour ne pas alourdir le document, j’ai pris une partie propre de chaque jeu de donnée. Vous pouvez télécharger les vraies données utilisées dans ce document en suivant les liens ci-dessous. Cela vous permettra de refaire toute la procédure aec votre propre code et de comparer les résultats :).

Classification

# Importation des données. Notons que dans R, il y a un package "titanic" qui contient un jeu de données Titanic.

titanic <- read.csv("titanic.csv")

# Strucure des données

str(titanic)
## 'data.frame':    714 obs. of  4 variables:
##  $ Survived: int  0 1 1 1 0 0 0 1 1 1 ...
##  $ Pclass  : int  3 1 3 1 3 1 3 3 2 3 ...
##  $ Sex     : Factor w/ 2 levels "female","male": 2 1 1 1 2 2 2 1 1 1 ...
##  $ Age     : num  22 38 26 35 35 54 2 27 14 4 ...
# Résumé statistique des variables de type numérique

summary(titanic)
##     Survived          Pclass          Sex           Age       
##  Min.   :0.0000   Min.   :1.000   female:261   Min.   : 0.42  
##  1st Qu.:0.0000   1st Qu.:1.000   male  :453   1st Qu.:20.12  
##  Median :0.0000   Median :2.000                Median :28.00  
##  Mean   :0.4062   Mean   :2.237                Mean   :29.70  
##  3rd Qu.:1.0000   3rd Qu.:3.000                3rd Qu.:38.00  
##  Max.   :1.0000   Max.   :3.000                Max.   :80.00
Notre jeu de données *titanic* fournit les informations sur le sort (a survécu ou a péri) de 714 passagers selon leur âge ( variable *Age*), leur sex (*Sex*) et leur classe économique (*Pclass*). Il est aussi intéressant de représenter la distribution des variables.
# titanic$Survived as factor : yes pour 1 (les survivants) et no pour 0 (ceux qui ont péri)

titanic$Survived <- as.factor(titanic$Survived)

#titanic$Pclass as factor. Il y a en fait 3 niveaux de classe économique : 1ère, 2ème et 3ème.

titanic$Pclass <- as.factor(titanic$Pclass)

# Représentation graphique des variables catégorielles

library(ggplot2)

# titanic en format long
titanic_factor <- titanic %>%
  select_if(is.factor) %>%
  gather(key = "variable", value = "valeur")

ggplot(titanic_factor, aes(valeur)) +
  geom_bar() +
  facet_wrap(~ variable, scales = "free_x")

Il y a plus de personnes de troisième classe économique, plus d’hommes que de femmes et plus de morts que de survivants.

Formation d’un arbre de classification

N’oubliez pas que pour les algorithmes ayant un aspect aléatoire en leur sein, il faut toujours s’assurer de la reproductibilité du code.

library(rpart)

# Reproductibilité

set.seed(123)

# Formation du modèle

arbre <- rpart(Survived ~ ., data = titanic, method = "class")

# Prédiction. La prédiction est faite sur le même jeu de données titanic pour voir si le modèle rend bien compte de la réalité.  

pred1 <- predict(arbre, type = "class")

# Visualisation du modèle

library(rpart.plot)

rpart.plot(arbre, yesno = 2)

Normalement, on fait la prédiction sur un jeu de données étiquetées que le modèle n’a jamais vu afin de s’assurer vraiment s’il peut être opérationnel ou pas. Mais le modèle n’est t-il pas censé bien représenter les données qui ont servi à sa formation ? C’est ce que nous allons vérifié avec la matrice de confusion.

Evaluation du modèle e classification par la Matrice de Confusion

La matrice de confusion donne 04 valeurs :

  • TP (True Positive) : Les survivants correctement prédit par le modèle comme ayant survécu ;

  • FP (False Positive) : Les personnes décédées qui ont été prédit à tort par le modèle comme étant des survivants au naufrage.

  • FN (False Negative) : Les survivants ayant été prédit à tort par le modèle comme ayant péris.

  • TN (True Negative) : Les personnes décédées correctement prédit par le modèle comme personnes ayant péri dasn le Naufrage.

#Matrice de confusion

conf <- table(titanic$Survived, pred1)
conf
##    pred1
##       0   1
##   0 371  53
##   1  78 212
#Composants de la matrice de confusion. TP : True Positive, FP : False Positive, FN : False Negative, TN : True Negative

TN <- conf[1, 1]
FN <- conf[2, 1]
FP <- conf[1, 2]
TP <- conf[2, 2]

# Précision globale : c'est la diagonale de la matrice

precision_globale <- (TP+TN)/(TP+FP+FN+TN)
precision_globale
## [1] 0.8165266
# Les réels survivants du jeu de données titanic sont au nombre de :

TP+FP # Personnes ayant réellement survécu au naufrage
## [1] 265
# Les personnes réellement décédées du jeu de données titanic sont au nombre de : 

FN+TN # Personnes réellement décédées
## [1] 449

Avec une précision globale d’environ 82%, le modèle n’a prédit à tort que 18% du destin des passagers. Globalement, on peut dire que le modèle est acceptable mais en réalité il n’est pas vraiment satisfaisant.

Sachez que le choix du paramètre d’évaluation d’un modèle doit être fait au cas par cas selon le contexte de votre étude et l’objectif que vous visez. Le paramètre précision globale qui découle de la matrice de confusion peut dans certains cas ne pas être adéquat pour mesurer la performance d’un modèle de classification. Dans un prochain article, on étudiera un jeu de données médicales (classification pour l’aide au diagnostique de cancer) et je vous montrerai que même si la précision globale est élevée,cela ne signifie pas forcément que le modèle est performant et qu’il peut être déployé.

Dans la pratique, on étudie plusieurs paramètres pour évaluer la performance d’un modèle.

Régression

# Importation des données

air <- read.csv("air.csv")

# Structure des données

str(air)
## 'data.frame':    1503 obs. of  6 variables:
##  $ freq     : int  800 1000 1250 1600 2000 2500 3150 4000 5000 6300 ...
##  $ angle    : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ ch_length: num  0.305 0.305 0.305 0.305 0.305 ...
##  $ velocity : num  71.3 71.3 71.3 71.3 71.3 71.3 71.3 71.3 71.3 71.3 ...
##  $ thickness: num  0.00266 0.00266 0.00266 0.00266 0.00266 ...
##  $ dec      : num  126 125 126 128 127 ...
# Résumé statistique des variables numériques

summary(air)
##       freq           angle          ch_length         velocity    
##  Min.   :  200   Min.   : 0.000   Min.   :0.0254   Min.   :31.70  
##  1st Qu.:  800   1st Qu.: 2.000   1st Qu.:0.0508   1st Qu.:39.60  
##  Median : 1600   Median : 5.400   Median :0.1016   Median :39.60  
##  Mean   : 2886   Mean   : 6.782   Mean   :0.1365   Mean   :50.86  
##  3rd Qu.: 4000   3rd Qu.: 9.900   3rd Qu.:0.2286   3rd Qu.:71.30  
##  Max.   :20000   Max.   :22.200   Max.   :0.3048   Max.   :71.30  
##    thickness              dec       
##  Min.   :0.0004007   Min.   :103.4  
##  1st Qu.:0.0025351   1st Qu.:120.2  
##  Median :0.0049574   Median :125.7  
##  Mean   :0.0111399   Mean   :124.8  
##  3rd Qu.:0.0155759   3rd Qu.:130.0  
##  Max.   :0.0584113   Max.   :141.0

Le jeu de données “air” donne la pression acoustique dec produite par l’aile d’un avion selon différents paramètres comme la fréquence du vent (fred), l’angle de vol (angle), la vitesse de vol (velocity), l’épaisseur de l’aile () et sa longueur (ch_length).

Formation du modèle

Nous alons construire dans un premier temps un modèle de régression multivariable qui prédit la pression acoustique de l’aile en fonction de la fréquence du vent, de l’angle de vol et de la longueur de l’aile. Ensuite, nous ajouterons plus de complexité au modèle en ajoutant les autres variables.

# Les Deux modèles 

reg1 <- lm(dec ~ freq + angle + ch_length, data = air)

reg2 <- lm(dec ~ freq + angle + ch_length + velocity + thickness, data = air)

# Paramètres des modèles

summary(reg1)
## 
## Call:
## lm(formula = dec ~ freq + angle + ch_length, data = air)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -17.212  -3.251   0.054   3.310  20.143 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.381e+02  4.427e-01  312.03   <2e-16 ***
## freq        -1.200e-03  4.509e-05  -26.62   <2e-16 ***
## angle       -6.664e-01  2.783e-02  -23.95   <2e-16 ***
## ch_length   -3.885e+01  1.694e+00  -22.94   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 5.223 on 1499 degrees of freedom
## Multiple R-squared:  0.428,  Adjusted R-squared:  0.4269 
## F-statistic: 373.9 on 3 and 1499 DF,  p-value: < 2.2e-16
summary(reg2)
## 
## Call:
## lm(formula = dec ~ freq + angle + ch_length + velocity + thickness, 
##     data = air)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -17.480  -2.882  -0.209   3.152  16.064 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.328e+02  5.447e-01  243.87   <2e-16 ***
## freq        -1.282e-03  4.211e-05  -30.45   <2e-16 ***
## angle       -4.219e-01  3.890e-02  -10.85   <2e-16 ***
## ch_length   -3.569e+01  1.630e+00  -21.89   <2e-16 ***
## velocity     9.985e-02  8.132e-03   12.28   <2e-16 ***
## thickness   -1.473e+02  1.501e+01   -9.81   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 4.809 on 1497 degrees of freedom
## Multiple R-squared:  0.5157, Adjusted R-squared:  0.5141 
## F-statistic: 318.8 on 5 and 1497 DF,  p-value: < 2.2e-16
# Prédiction

pred_reg1 <- predict(reg1)

pred_reg2 <- predict(reg2)

Evaluation des modèles par calcul du RMSE.

Si vous avez une data frame (df) avec une variable-cible y, alors le rmse se calcule par la formule :

rmse = sqrt((1/nrow(df))*sum((df$y-pred)^2)) où pred est le vecteur de valeurs prédites par le modèle pour la variable y.

On peut facilement se perdre dans toutes ses parenthèses. Heureusement qu’il y a une fonction rmse dans R pour calculer le rmse du modèle.

library(Metrics)

rmse1 <- rmse(actual = air$dec, predicted = pred_reg1)

rmse1 # rmse du 1er modèle
## [1] 5.215778
rmse2 <- rmse(actual = air$dec, predicted = pred_reg2)

rmse2 # rmse du 2ème modèle
## [1] 4.799244

Le rmse du premier modèles est égal à 5,215 décibels (unité demesure de la pression acoustique). Mais ce chiffre en lui-même ne nous aide pas beaucoup. Qu’est-ce que nous pouvons en tirer quant à la performance du modèle ?

Le rmse du deuxième model (plus complexe que le premier à cause de l’ajout d’autres variables prédictrices) est égal à environ 4,8 donc inférieur à celui du premier modèle.

Il faut comprendre qu’une valeur de rmse égal à 0 (pratiquement jamais atteinte dans la pratique) indiquerait un ajustement parfait aux données. En général, un RMSE inférieur est préférable à un plus élevé.

Donc, par rapport au paramètre rmse, nous choisirons plutôt le deuxième modèle pred_reg2. Notre choix est confirmé par le R² (c’est la variance expliquée par le modèle) de chacun des modèles. 42,8% de la variance est expliquée par le modèle pred_reg1 alors qu’environ 51,6% de la variance est expliquée par le modèle 2 : pred_reg2. S’il est vrai que selon ces paramètres (RMSE et R²), le deuxième modèle est plus performant que le premier, il n’en demeurre pas moins que leurs performances ne sont pas satisfaisantes (R² faible).

Clustering

# Importation des données

seeds <- read.csv("seeds.csv")

# Structure des données

str(seeds)
## 'data.frame':    210 obs. of  7 variables:
##  $ area         : num  15.3 14.9 14.3 13.8 16.1 ...
##  $ perimeter    : num  14.8 14.6 14.1 13.9 15 ...
##  $ compactness  : num  0.871 0.881 0.905 0.895 0.903 ...
##  $ length       : num  5.76 5.55 5.29 5.32 5.66 ...
##  $ width        : num  3.31 3.33 3.34 3.38 3.56 ...
##  $ asymmetry    : num  2.22 1.02 2.7 2.26 1.35 ...
##  $ groove_length: num  5.22 4.96 4.83 4.8 5.17 ...
# Résumé statistique

summary(seeds)
##       area         perimeter      compactness         length     
##  Min.   :10.59   Min.   :12.41   Min.   :0.8081   Min.   :4.899  
##  1st Qu.:12.27   1st Qu.:13.45   1st Qu.:0.8569   1st Qu.:5.262  
##  Median :14.36   Median :14.32   Median :0.8734   Median :5.524  
##  Mean   :14.85   Mean   :14.56   Mean   :0.8710   Mean   :5.629  
##  3rd Qu.:17.30   3rd Qu.:15.71   3rd Qu.:0.8878   3rd Qu.:5.980  
##  Max.   :21.18   Max.   :17.25   Max.   :0.9183   Max.   :6.675  
##      width         asymmetry      groove_length  
##  Min.   :2.630   Min.   :0.7651   Min.   :4.519  
##  1st Qu.:2.944   1st Qu.:2.5615   1st Qu.:5.045  
##  Median :3.237   Median :3.5990   Median :5.223  
##  Mean   :3.259   Mean   :3.7002   Mean   :5.408  
##  3rd Qu.:3.562   3rd Qu.:4.7687   3rd Qu.:5.877  
##  Max.   :4.033   Max.   :8.4560   Max.   :6.550

Le jeu de données seeds donne les mesures de différents paramètres de 210 semences (graines). Le problème est qu’on ne connait pas quelles mesures pour quel type de graine (données non étiquetées). On choisit de grouper les graines en 3 types avec l’algorithme K-means pour le clustering.

Formation du modèle

# Reproductibilité

set.seed(123)

# Modèle

clust <- kmeans(seeds, centers = 3)

summary(clust)
##              Length Class  Mode   
## cluster      210    -none- numeric
## centers       21    -none- numeric
## totss          1    -none- numeric
## withinss       3    -none- numeric
## tot.withinss   1    -none- numeric
## betweenss      1    -none- numeric
## size           3    -none- numeric
## iter           1    -none- numeric
## ifault         1    -none- numeric
# Graphique montrant les clusters
# Représentons par exemple les observations avec les variables compactness ~ length colorées par les clusters.

plot(length ~ compactness, data = seeds, col = clust$cluster)

Evaluation du modèle

L’évaluation d’un modèle de clustering consiste, entres autres méthodes, à vérifier si les groupes (les clusters) sont bien séparés entre eux et si chaque cluster est bien compact.

# Vérifier si les groupes sont bien séparés

clust$betweenss
## [1] 2132.534
# Vérifier si les clusters sont compacts

clust$tot.withinss
## [1] 587.3186
# Ratio

clust$tot.withinss/clust$betweenss
## [1] 0.2754088

On constate que clust\(tot.withinss* est largement inférieur à *clust\)betweenss. Le ratio est de 0,27. Donc les groupes sont bien compactes. Le graphique aussi le montre bien. Il est donc probable que ces trois clusters (groupes) représentent bien les trois types de semences (graines), même s’il n’existe pas de données étiquetées pour vérifier cela.

Ce type de technique de Machine Learning est tr-s utilisé en Marketing où on effectue le clustering des clients afin d’avoir des groupes distincts de clients de même caractéristiques pour leur adresser des publicités ciblées.

CONCLUSION

Dans ce rapport, nous avons étudié de manière simplifiée Trois différents problèmes conernant respectivement la Classification, la Régression et le Clustering.

Pour le modèle de Classification, nous avons utilisé la matrice de Confusion pour évaluer la qualité du modèle.

En ce qui concerne le modèle de Régression, nous avons calculé le RMSE pour évaluer les deux modèles construits. Notre choix final a été confirmé par le R², paramètre donnant la variance expliquée par le modèle.

Pour finir, nous avons appliqué l’algorithme de clustering kmeans afin de grouper les semences de même caractéristique. Soulignons que dans la pratique, le nombre de cluster est déterminé soit par connaissance du phénomène (l’expérience joue à ce niveau) ou soit on choisit le nombre de cluster optimal. Dans un autre document, j’expliquerai cela en détail.

Merci pour la lecture de ce petit document et à bientôt pour d’autres documents sur le Machine Learning.