# install.packages("tidymodels")
# install.packages("skimr")
#install.packages("glmnet")
library(tidymodels)
library(skimr)
library(glmnet)Tuto Machine learning sous R
En complément du corrigé des exercices, voici le code R sous-jacent à Asta, afin que vous puissiez produire ou appliquer les méthodes de machine learning directement sous R.
C’est le package tidymodels qui est utilisé dans ASTA pour faire du machine learning. Il regroupe et intègre les packages r suivants :rsample, recipes, tune, parnsnip, yardstick… Chacun de ces packages étant utile pour le machine learning. Par ailleurs, on utilise le package skimr pour explorer les fichiers.
Les données de l’exercice sont intégrées au package ASTA. On distingue les deux grands types d’apprentissage automatique :
la classification supervisée
la régression supervisée.
L’application ASTA ne permet pas de paramétrer les différents modèles : ces paramètres sont définis par défaut dans l’application. Elle ne permet pas non plus de faire de la validation croisée (qui est d’usage en machine learning) ni d’optimiser chaque modèle pour trouver les meilleurs hyper-paramètres. Ces points ne seront donc pas abordés dans ce tutoriel, mais il est possible de faire ça avec tidymodels.
1 Classification supervisée
Les deux fichiers utilisés pour faire de la classification dans asta sont le fichier vin (le vin est-il bon ou mauvais ?) et le fichier grandile (les ménages sont-ils pauvres ?). On fait quelques modifications sur ces fichiers pour faire en sorte que la variable à expliquer ait le même nom dans les deux fichiers (target).
library(asta)
data <- asta::vins %>% rename(target = quality)
# data <- asta::grandile %>% rename(target = PAUVRE) %>%
# select(-starts_with("LIB")) %>%
# select(-IDENT) %>%
# mutate(target = as.factor(target))1.1 Exploration de la base brute
skim(data)| Name | data |
| Number of rows | 4898 |
| Number of columns | 12 |
| _______________________ | |
| Column type frequency: | |
| factor | 1 |
| numeric | 11 |
| ________________________ | |
| Group variables | None |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| target | 0 | 1 | FALSE | 2 | bon: 3258, mau: 1640 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| fixed.acidity | 0 | 1 | 6.85 | 0.84 | 3.80 | 6.30 | 6.80 | 7.30 | 14.20 | ▁▇▁▁▁ |
| volatile.acidity | 0 | 1 | 0.28 | 0.10 | 0.08 | 0.21 | 0.26 | 0.32 | 1.10 | ▇▅▁▁▁ |
| citric.acid | 0 | 1 | 0.33 | 0.12 | 0.00 | 0.27 | 0.32 | 0.39 | 1.66 | ▇▆▁▁▁ |
| residual.sugar | 0 | 1 | 6.39 | 5.07 | 0.60 | 1.70 | 5.20 | 9.90 | 65.80 | ▇▁▁▁▁ |
| chlorides | 0 | 1 | 0.05 | 0.02 | 0.01 | 0.04 | 0.04 | 0.05 | 0.35 | ▇▁▁▁▁ |
| free.sulfur.dioxide | 0 | 1 | 35.31 | 17.01 | 2.00 | 23.00 | 34.00 | 46.00 | 289.00 | ▇▁▁▁▁ |
| total.sulfur.dioxide | 0 | 1 | 138.36 | 42.50 | 9.00 | 108.00 | 134.00 | 167.00 | 440.00 | ▂▇▂▁▁ |
| density | 0 | 1 | 0.99 | 0.00 | 0.99 | 0.99 | 0.99 | 1.00 | 1.04 | ▇▂▁▁▁ |
| pH | 0 | 1 | 3.19 | 0.15 | 2.72 | 3.09 | 3.18 | 3.28 | 3.82 | ▁▇▇▂▁ |
| sulphates | 0 | 1 | 0.49 | 0.11 | 0.22 | 0.41 | 0.47 | 0.55 | 1.08 | ▃▇▂▁▁ |
| alcohol | 0 | 1 | 10.51 | 1.23 | 8.00 | 9.50 | 10.40 | 11.40 | 14.20 | ▃▇▆▃▁ |
1.2 Partitionnement de la base brute
La base intiale est découpée en trois bases :
base d’entraînement
base de validation
base de test
Il est possible d’utiliser une variable de stratification (souvent la variable à expliquer) pour que les 3 bases aient suffisament d’individus du même type. Les fonctions utilisées sont issues du package rsample.
#Paramétrage des parts pour chaque base.
part_training <- 0.7 #70% pour l'entraînement
part_validation <- (1-part_training)/2 #15% pour la validation
#on contrôle l'aléatoire pour avoir toujours la même base.
set.seed(1234)
#le paramètre strata permet de garder la même proportion de la variable dans les trois bases.
data_split <- initial_validation_split(data,
strata = target,
prop = c(part_training,part_validation)
)
#La dernière base est la base d'entraînement et la base de validation.
train_data <- training(data_split)
test_data <- testing(data_split)
valid_data <- validation(data_split)
train_valid_data <- train_data %>% bind_rows(valid_data)1.3 Modifications de la base
C’est le package recipes qui est utilisé ici pour faire des modifications sur les bases (du preprocessing) qui peuvent être de nombreux types :
ajout/suppresion de variables
transformation de variable (log, exponentielle..)
imputation des données manquantes (plusieurs méthodes)
filtrage d’individus
encodage (one hot par exemple..)
Ces modifications sont parfois nécessaires pour que les modèles puissent marcher (par exemple, la forêt aléatoire ne peut pas s’ajuster sur une base avec des données manquantes). Mais elles peuvent aussi permettre d’améliorer la qualité du modèle.
Il y a certaines transformations qui sont automatiquement appliquées aux bases de données par les modèles.
Le package recipes permet ainsi de tester plusieurs versions de la base (plusieurs recettes appliquées à la base) sur lesquelles on ajustera un même modèle.
https://recipes.tidymodels.org/reference/index.html#basic-functions
#choisir les recettes qu'on applique à la base, avec des step
rec <-
recipe(target ~ ., data = train_data) %>%
step_normalize(all_numeric_predictors()) #%>% Cette base modifiée peut-être visualisée à l’aide des fonctions contenues dans le package recipes. Dans cet exemple, on a choisi de centrer-réduire toutes les variables numériques avec step_normalize.
recette <- prep(rec) #Je prépare la recette
data_modifie <- bake(recette,new_data = NULL) #Je l'appplique par défaut sur la base définie dans la fonction recipe
skim(data_modifie)| Name | data_modifie |
| Number of rows | 3428 |
| Number of columns | 12 |
| _______________________ | |
| Column type frequency: | |
| factor | 1 |
| numeric | 11 |
| ________________________ | |
| Group variables | None |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| target | 0 | 1 | FALSE | 2 | bon: 2280, mau: 1148 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| fixed.acidity | 0 | 1 | 0 | 1 | -3.63 | -0.67 | -0.08 | 0.51 | 8.67 | ▁▇▁▁▁ |
| volatile.acidity | 0 | 1 | 0 | 1 | -1.97 | -0.69 | -0.20 | 0.40 | 8.08 | ▇▅▁▁▁ |
| citric.acid | 0 | 1 | 0 | 1 | -2.71 | -0.52 | -0.12 | 0.45 | 10.73 | ▇▆▁▁▁ |
| residual.sugar | 0 | 1 | 0 | 1 | -1.13 | -0.92 | -0.25 | 0.68 | 11.71 | ▇▁▁▁▁ |
| chlorides | 0 | 1 | 0 | 1 | -1.59 | -0.43 | -0.13 | 0.17 | 12.85 | ▇▁▁▁▁ |
| free.sulfur.dioxide | 0 | 1 | 0 | 1 | -1.96 | -0.71 | -0.12 | 0.60 | 15.13 | ▇▁▁▁▁ |
| total.sulfur.dioxide | 0 | 1 | 0 | 1 | -2.84 | -0.70 | -0.09 | 0.67 | 7.16 | ▂▇▂▁▁ |
| density | 0 | 1 | 0 | 1 | -2.30 | -0.66 | -0.01 | 0.65 | 14.75 | ▇▂▁▁▁ |
| pH | 0 | 1 | 0 | 1 | -3.10 | -0.71 | -0.04 | 0.62 | 4.21 | ▁▇▇▂▁ |
| sulphates | 0 | 1 | 0 | 1 | -2.35 | -0.70 | -0.17 | 0.52 | 5.13 | ▃▇▂▁▁ |
| alcohol | 0 | 1 | 0 | 1 | -2.05 | -0.84 | -0.11 | 0.70 | 2.85 | ▅▇▆▃▁ |
1.4 Choix d’un modèle
Il s’agit maintenant de tester un certain nombre de modèles qui sont des cadres d’apprentissage, et qui seront entraînés dans la phase suivante. Il en existe un grand nombre.
Les modèles utilisés viennent du package parnsnip. Il faut préciser le nom du modèle, puis le moteur qui va servir à estimer les paramètres (set_engine) et les éventuels paramètres et hyper-paramètres à régler.
Pour certains modèles, il faut préciser qu’il s’agit d’un problème de classification (set_mode)
- La régression logistique
mod_lr <-
logistic_reg() %>%
set_engine("glm")- L’arbre de classification
mod_tree <-
decision_tree(
cost_complexity = 0.001, #hyper-paramètre mesurant la complexité de l'arbre
tree_depth = 7, #pour paramétrer la profondeur de l'arbre
min_n = NULL #nombre minimum d'individus dans un noeud pour être divisé
) %>%
set_engine("rpart") %>% #méthode d'estimation
set_mode("classification")La forêt aléatoire
mod_rf <- rand_forest(trees = 1000, #nombre d'arbres mtry = 3, #nombre de variables min_n = NULL) %>% #nombre minimum d'individus dans un noeud pour être divisé set_engine("ranger") %>% set_mode("classification")Le KNN : plus proches voisins
mod_knn <- nearest_neighbor( neighbors = 3 #nombre de voisins ) %>% set_engine("kknn") %>% set_mode("classification")Le SVM (support vector machine)1
mod_svm <- svm_rbf(mode = "classification", cost = 10, rbf_sigma = 0.1, margin = 1) %>% set_engine("kernlab")La régression ridge2
mod_ridge <- logistic_reg(penalty = 0.01, #hyper-paramètre mixture = 0) %>% set_engine("glmnet")la régression lasso3
mod_lasso <- logistic_reg(penalty = 0.001, #hyper-paramètre mixture = 1) %>% set_engine("glmnet")
1.5 Création d’un workflow
Un workflow est une recette appliquée à une base et à un modèle. C’est le package workflow qui permet de créer un workflow.
mod <- mod_rf
wflow <- workflow() %>%
add_model(mod) %>% #ajout du modèle
add_recipe(rec) #ajout de la recette (transfo de la base initiale)
wflow══ Workflow ════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: rand_forest()
── Preprocessor ────────────────────────────────────────────────────────────────
1 Recipe Step
• step_normalize()
── Model ───────────────────────────────────────────────────────────────────────
Random Forest Model Specification (classification)
Main Arguments:
mtry = 3
trees = 1000
Computational engine: ranger
1.6 Ajustement du modèle
C’est à cette étape que le modèle s’ajuste sur les données d’entraînement, c’est à dire que l’algorithme trouve le modèle qui estime le mieux. La fonction fit génère une instance du modèle qui a été entraîné sur la base d’entraînement.
fit <- wflow %>% fit(data=train_data)
extract_fit_parsnip(fit)parsnip model object
Ranger result
Call:
ranger::ranger(x = maybe_data_frame(x), y = y, mtry = min_cols(~3, x), num.trees = ~1000, num.threads = 1, verbose = FALSE, seed = sample.int(10^5, 1), probability = TRUE)
Type: Probability estimation
Number of trees: 1000
Sample size: 3428
Number of independent variables: 11
Mtry: 3
Target node size: 10
Variable importance mode: none
Splitrule: gini
OOB prediction error (Brier s.): 0.1250959
On peut visualiser les estimations de ce modèle sur la base d’entraînement. A chaque classe de la variable target est associée une probabilité : si elle est supérieure à 0,5, alors c’est cette modalité qui est estimée par le modèle.
augment(fit,train_data) %>% select(target,starts_with(".pred")) %>% head(10)1.7 Performance du modèle
L’instance du modèle généré à l’étape précédente est appliquée à la base de validation, qui n’a pas servi à ajuster le modèle. Autrement dit, ce sont des données que le modèle n’a jamais vu et qui vont permettre d’évaluer les capacités prédictives de notre modèle.
En pratique, on ne fait pas de la validation simple comme ici mais de la validation croisée, qui permet de réduire le biais d’échantillonage au moment de la partition de la base brute.
C’est la fonction vfold_cv du package rsample qui permet de paramétrer la validation croisée. Et la fonction fit_resamples du package tune permet de faire les estimations en utilisant la validation croisée.
pred_valid <- augment(fit, valid_data) %>% select(target,starts_with(".pred"))
pred_validA partir de ce tableau de prédiction sur la base de validation, je peux construire un tableau de confusion, qui classe chaque individu de la base de validation dans une des 4 cases du tableau : VP (vrai positif), FP (faux positif), VN (vrai négatif), FN (faux négatif).
pred_valid %>%
conf_mat(target, .pred_class) %>%
autoplot(type="heatmap")A partir de ce tableau de confusion, on peut définir des indicateurs de performance et la courbe ROC. Les fonctions permettant de calculer les performances sont dans le package yardstick.
accuracy <- pred_valid %>%
accuracy(truth = target, .pred_class) %>%
select(.estimate) %>%
as.numeric() %>%
round(2)
specificity <- pred_valid %>%
specificity(truth = target, .pred_class) %>%
select(.estimate) %>%
as.numeric() %>%
round(2)
sensitivity <- pred_valid %>%
sensitivity(truth = target, .pred_class) %>%
select(.estimate) %>%
as.numeric() %>%
round(2)| Modèle | Recette | Accuracy | Spécificité | Sensibilité |
|---|---|---|---|---|
| rand_forest | step_normalize, step | 0.84 | 0.7 | 0.9 |
On peut aussi calculer l’AUC et dessiner la courbe ROC.
pred1 <- names(pred_valid)[3]
roc_plot_valid <- pred_valid %>%
roc_curve(truth = target,all_of(pred1)) %>%
autoplot()
roc_plot_valid1.8 Ajustement du modèle final
Le modèle final (celui qui a la meilleure performance) est ajusté sur la base d’entraînement et la base de validation. C’est cette instance de modèle qui sera utilisé pour faire des prédictions sur des individus qui n’ont jamais été vus par le modèle.
fit_final <-
wflow %>% fit(train_valid_data)
pred_final <- augment(fit_final,test_data)
accuracy <- pred_final %>%
accuracy(truth = target, .pred_class) %>%
select(.estimate) %>%
as.numeric() %>%
round(2)
specificity <- pred_final %>%
specificity(truth = target, .pred_class) %>%
select(.estimate) %>%
as.numeric() %>%
round(2)
sensitivity <- pred_final %>%
sensitivity(truth = target, .pred_class) %>%
select(.estimate) %>%
as.numeric() %>%
round(2)| Modèle | Recette | Accuracy | Spécificité | Sensibilité |
|---|---|---|---|---|
| rand_forest | step_normalize, step | 0.83 | 0.67 | 0.91 |
2 Régression supervisée
Les deux fichiers utilisés sont :
le fichier ozone (on cherche à prédire le taux d’ozone)
le fichier Grandile (on cherche à prédir le taux de pauvreté)
data <- ozone %>%
mutate(target = maxO3) %>%
select(-maxO3)
# data <- grandile %>%
# select(-starts_with("LIB")) %>%
# mutate(target = REV_DISPONIBLE) %>%
# select(-IDENT,-REV_DISPONIBLE)2.1 Exploration de la base brute
skim(data)| Name | data |
| Number of rows | 1366 |
| Number of columns | 13 |
| _______________________ | |
| Column type frequency: | |
| numeric | 13 |
| ________________________ | |
| Group variables | None |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| T6 | 0 | 1 | 12.37 | 3.94 | -1.60 | 10.10 | 12.70 | 15.20 | 21.70 | ▁▂▆▇▂ |
| T9 | 0 | 1 | 16.42 | 4.22 | 4.50 | 13.83 | 16.70 | 19.10 | 28.60 | ▁▃▇▃▁ |
| T12 | 0 | 1 | 19.43 | 5.08 | 5.70 | 16.20 | 19.45 | 22.70 | 33.50 | ▁▅▇▅▁ |
| T15 | 0 | 1 | 20.33 | 5.41 | 5.70 | 16.72 | 20.35 | 24.00 | 35.50 | ▁▅▇▅▁ |
| T18 | 0 | 1 | 18.77 | 5.21 | 4.40 | 15.40 | 18.70 | 22.10 | 34.10 | ▁▅▇▃▁ |
| Ne6 | 0 | 1 | 5.05 | 2.88 | 0.00 | 2.00 | 6.00 | 8.00 | 9.00 | ▆▃▂▇▇ |
| Ne9 | 0 | 1 | 5.20 | 2.55 | 0.00 | 3.00 | 6.00 | 7.00 | 9.00 | ▃▂▃▇▅ |
| Ne12 | 0 | 1 | 5.35 | 2.33 | 0.00 | 4.00 | 6.00 | 7.00 | 8.00 | ▂▂▂▆▇ |
| Ne15 | 0 | 1 | 5.14 | 2.35 | 0.00 | 3.00 | 6.00 | 7.00 | 8.00 | ▂▃▂▆▇ |
| Ne18 | 2 | 1 | 4.73 | 2.63 | 0.00 | 2.00 | 6.00 | 7.00 | 8.00 | ▅▃▁▅▇ |
| Vx | 0 | 1 | -0.80 | 3.41 | -10.83 | -3.21 | -1.03 | 1.53 | 8.66 | ▁▅▇▅▁ |
| maxO3v | 0 | 1 | 79.31 | 27.30 | 15.00 | 63.00 | 76.70 | 94.00 | 173.00 | ▂▇▆▂▁ |
| target | 0 | 1 | 79.28 | 27.09 | 15.00 | 63.00 | 76.80 | 94.00 | 166.00 | ▂▇▇▂▁ |
Dans la base de données brute d’ozone, on constate qu’il y a deux valeurs manquantes sur la variable Ne18. Il faudra en tenir compte dans les phases suivantes.
2.2 Partitionnement de la base brute
La base intiale est découpée en trois bases :
base d’entraînement
base de validation
base de test
Les fonctions permettant de partitionner la base de départ sont issues de rsample. La fonction initial_validation_split divise la base de départ en 3.
part_training <- 0.7
part_validation <- (1-part_training)/2
set.seed(1234) #pour permettre la reproductibilité des résultats
data_split <- initial_validation_split(data,prop = c(part_training,part_validation))
train_data <- training(data_split) #le fichier d'entraînement
test_data <- testing(data_split) #le fichier de test
valid_data <- validation(data_split)
train_valid_data <- train_data %>% bind_rows(valid_data)2.3 Modifications de la base
Il est rare que la base brute puisse directement être traitée par les modèles. Elle nécessite une phase (souvent longue) de nettoyage, de recodage, de discrétisation…
Cette première phase permet de préparer la modélisation. Il est important de noter que les nouvelles données qu’on cherchera à prédire avec le modèle auront ce même format brut, et qu’elle subiront donc les mêmes modifications avant de rentrer dans le modèle.
Le package recipes permet d’appliquer des recettes sur la base brute, c’est à dire des suites de traitement à appliquer à cette base. Cette étape s’appelle aussi le preprocessing, ou feature eigeneering.
Avec la base de données ozone, on choisit de faire de l’imputation de valeurs manquantes par la moyenne (step_impute_mean). On aurait pu aussi filtrer les valeurs manquantes ou utiliser une autre techique d’imputation (médiane, plus proches voisins…).
rec <- recipe(target ~ ., data = train_data) %>%
step_impute_mean(all_numeric_predictors()) #imputation valeurs manquantesUne fois que la recette est enregistrée (liste des traitements à appliquer à la base brute), on peut afficher la base de données avec les verbes prep et bake.
recette <- prep(rec) #Je prépare la recette
data_modifie <- bake(recette,new_data = NULL) #Je l'appplique par défaut sur la base définie dans la fonction recipe
skim(data_modifie)| Name | data_modifie |
| Number of rows | 956 |
| Number of columns | 13 |
| _______________________ | |
| Column type frequency: | |
| numeric | 13 |
| ________________________ | |
| Group variables | None |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| T6 | 0 | 1 | 12.45 | 3.93 | -0.50 | 10.00 | 12.8 | 15.22 | 21.70 | ▁▂▇▇▂ |
| T9 | 0 | 1 | 16.42 | 4.23 | 4.50 | 13.80 | 16.7 | 19.12 | 28.60 | ▁▃▇▃▁ |
| T12 | 0 | 1 | 19.40 | 5.06 | 5.80 | 16.10 | 19.3 | 22.70 | 33.20 | ▁▅▇▅▁ |
| T15 | 0 | 1 | 20.33 | 5.34 | 5.70 | 16.70 | 20.2 | 24.00 | 34.90 | ▁▅▇▅▁ |
| T18 | 0 | 1 | 18.77 | 5.14 | 4.40 | 15.30 | 18.6 | 22.22 | 34.10 | ▁▅▇▃▁ |
| Ne6 | 0 | 1 | 5.06 | 2.89 | 0.00 | 2.00 | 6.0 | 8.00 | 9.00 | ▆▃▃▇▇ |
| Ne9 | 0 | 1 | 5.21 | 2.53 | 0.00 | 3.00 | 6.0 | 7.00 | 9.00 | ▃▂▃▇▃ |
| Ne12 | 0 | 1 | 5.32 | 2.34 | 0.00 | 4.00 | 6.0 | 7.00 | 8.00 | ▂▂▂▅▇ |
| Ne15 | 0 | 1 | 5.12 | 2.35 | 0.00 | 3.00 | 6.0 | 7.00 | 8.00 | ▂▃▂▆▇ |
| Ne18 | 0 | 1 | 4.69 | 2.64 | 0.00 | 2.00 | 6.0 | 7.00 | 8.00 | ▅▃▁▅▇ |
| Vx | 0 | 1 | -0.77 | 3.40 | -10.83 | -3.08 | -1.0 | 1.50 | 8.66 | ▁▅▇▅▂ |
| maxO3v | 0 | 1 | 78.91 | 26.56 | 15.80 | 62.95 | 75.9 | 93.85 | 173.00 | ▂▇▆▂▁ |
| target | 0 | 1 | 79.55 | 26.66 | 15.00 | 63.60 | 77.2 | 94.00 | 166.00 | ▂▇▇▂▁ |
2.4 Choix d’un modèle
Les modèles utilisés viennent du package parnsnip. Il faut préciser le nom du modèle, puis le moteur qui va servir à estimer les paramètres (set_engine) et les éventuels paramètres et hyper-paramètres à régler.
- La régression linéaire
mod_lr <- linear_reg() %>%
set_engine("lm") - L’arbre de décision
mod_tree <-
mod_tree <-
decision_tree(
cost_complexity = 0.001,
tree_depth = 7,
min_n = NULL
) %>%
set_engine("rpart") %>%
set_mode("regression")- La forêt aléatoire
mod_rf <- rand_forest() %>%
set_engine("ranger") %>%
set_mode("regression")- Le SVM (support vector machine)
mod_svm <- svm_linear() %>%
set_mode("regression") %>%
set_engine("LiblineaR")- Le KNN
#Modèle KNN
mod_knn <-
nearest_neighbor(
neighbors = 3
) %>%
set_engine("kknn") %>%
set_mode("regression")2.5 Création d’un workflow
Un workflow est une recette appliquée à une base et à un modèle. C’est le package workflow qui permet de créer un workflow.
mod <- mod_lr
wflow <- workflow() %>%
add_recipe(rec) %>%
add_model(mod_lr)
wflow══ Workflow ════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()
── Preprocessor ────────────────────────────────────────────────────────────────
1 Recipe Step
• step_impute_mean()
── Model ───────────────────────────────────────────────────────────────────────
Linear Regression Model Specification (regression)
Computational engine: lm
2.6 Ajustement du modèle
On peut maintenant faire tourner l’alogorithme choisi pour ajuster le modèle sur les données d’entraînement auxquelles on a appliqué une recette (le workflow).
fit <- wflow %>% fit(data=train_data)
extract_fit_parsnip(fit)parsnip model object
Call:
stats::lm(formula = ..y ~ ., data = data)
Coefficients:
(Intercept) T6 T9 T12 T15 T18
24.41410 -1.48770 -0.15355 1.43674 0.55114 -0.00462
Ne6 Ne9 Ne12 Ne15 Ne18 Vx
-0.20165 -0.62492 -0.56435 -0.50517 0.27717 0.59367
maxO3v
0.58560
On peut visualiser les estimations de ce modèle sur la base d’entraînement. Moins les écarts entre les estimations et les vraies valeurs sont élevés, meilleur est le modèle.
pred_train <- augment(fit,train_data) %>% select(target,.pred) %>% round(1)
pred_train %>% head(10)2.7 Performance du modèle
L’instance du modèle généré à l’étape précédente est appliquée à la base de validation, qui n’a pas servi à ajuster le modèle. Autrement dit, ce sont des données que le modèle n’a jamais vu et qui vont permettre d’évaluer les capacités prédictives de notre modèle. Cette étape permet de s’assurer qu’il n’y a pas de surraprentissage.
pred_valid <- augment(fit,valid_data) %>% select(target,.pred) %>% round(1)
pred_valid %>% head(10)A partir de ce tableau de prédiction sur la base de validation, je peux calculer des indicateurs qui mesurent la distance entre la valeur réelle et la valeur prédite par une instance du modèle retenu.
Le package yardstick calcule automatiquement les indicateurs de performances, avec des fonctions pré-enregistrées.
#risque quadratique
rss <- sum((pred_valid$.pred - pred_valid$target)^2) %>% round(1)
mse <- rss/nrow(pred_valid) %>% round(1)
rmse <- sqrt(mse) %>% round(1)
# rmse(pred_valid,target,.pred) %>% select(.estimate) %>% as.numeric() %>% round(2)
#coefficient de détermination
rsq <- rsq(pred_valid,target,.pred) %>% select(.estimate) %>% as.numeric() %>% round(4)| Modèle | Recette | MSE | RMSE | R² |
|---|---|---|---|---|
| linear_reg | step_impute_mean, step | 221.4931707 | 14.9 | 0.676 |
Dans cet exemple, on a testé les performances de la régression linéaire sur toutes les variables. Il faut bien sûr comparer ces performances avec d’autres modèles pour prendre le meilleur.
2.8 Ajustement du modèle final
Le modèle final (celui qui a été retenu à la phase précédente) est ajusté sur la base d’entraînement et la base de validation.
fit_final <-
wflow %>%
fit(train_valid_data)On utilise cette nouvelle instance pour prédire sur la base de test, c’est à dire sur la base qui n’a pas été utilisée pour choisir le modèle et les hyper-paramètres.
pred_testing <- augment(fit_final, test_data) %>% select(target,.pred)
rss <- sum((pred_testing$.pred - pred_testing$target)^2) %>% round(1)
mse <- rss/nrow(pred_testing) %>% round(1)
rmse <- sqrt(mse) %>% round(1)
# rmse(pred_valid,target,.pred) %>% select(.estimate) %>% as.numeric() %>% round(2)
#coefficient de détermination
rsq <- rsq(pred_testing,target,.pred) %>% select(.estimate) %>% as.numeric() %>% round(4)| Modèle | Recette | MSE | RMSE | R² |
|---|---|---|---|---|
| linear_reg | step_impute_mean, step | 257.6121951 | 16.1 | 0.7258 |