3  Partie 3 : Régression Logistique LASSO

4 Régression Logistique Régularisée (Elastic Net)

4.1 Principe du modèle

NoteLASSO et Elastic Net

Le modèle LASSO minimise :

\[\mathcal{L}(\beta) = -\frac{1}{n} \sum_{i=1}^{n} \log P(y_i \mid x_i, \beta) + \lambda \|\beta\|_1\]

\(\lambda\) est le paramètre de régularisation. La pénalité \(\ell_1\) force certains coefficients à exactement zéro, réalisant ainsi une sélection de variables automatique. Nous utilisons l’Elastic Net (Zou & Hastie, 2005) qui combine les pénalités \(\ell_1\) (sélection) et \(\ell_2\) (rétrécissement), contrôlées par le paramètre \(\alpha \in [0, 1]\).

4.2 Entraînement

La grille d’hyperparamètres explore l’ensemble du spectre Elastic Net : \(\alpha \in \{0, 0.25, 0.5, 0.75, 1\}\) (de Ridge pur à LASSO pur) et 10 valeurs de \(\lambda \in [10^{-4}, 10^{-1}]\) en échelle logarithmique. La validation croisée 5-fold est un compromis classique entre biais et variance de l’estimation (Kohavi, 1995) : avec ~17 000 observations, chaque fold contient ~3 400 observations, assurant une estimation stable de l’AUC.

Code
model_file <- here::here("output", "model_logistic.rds")
cm_file    <- here::here("output", "cm_logistic.rds")
roc_file   <- here::here("output", "roc_logistic.rds")

if (file.exists(model_file) && file.exists(cm_file) && file.exists(roc_file)) {
  model_logistic <- readRDS(model_file)
  cm_logistic    <- readRDS(cm_file)
  roc_logistic   <- readRDS(roc_file)
} else {
  grid_glmnet <- expand.grid(
    alpha  = c(0, 0.25, 0.5, 0.75, 1),
    lambda = 10^seq(-4, -1, length.out = 10)
  )
  set.seed(42)
  model_logistic <- train(
    Satisfaction ~ ., data = train_data, method = "glmnet",
    trControl = CV_CONTROL, tuneGrid = grid_glmnet,
    metric = "ROC", preProcess = c("center", "scale")
  )
  pred_logistic <- predict(model_logistic, test_data)
  prob_logistic <- predict(model_logistic, test_data, type = "prob")
  cm_logistic   <- confusionMatrix(pred_logistic, test_data$Satisfaction, positive = "Oui")
  roc_logistic  <- roc(test_data$Satisfaction, prob_logistic$Oui, quiet = TRUE)
  saveRDS(model_logistic, model_file)
  saveRDS(cm_logistic,    cm_file)
  saveRDS(roc_logistic,   roc_file)
}

4.2.1 Sélection des hyperparamètres

Code
plot(model_logistic)
Figure 4.1: AUC en validation croisée selon alpha et lambda

Le meilleur modèle correspond à \(\alpha = 1\) et \(\lambda = 0.002154\). Le fait que \(\alpha = 1\) soit optimal signifie que le modèle retenu est un LASSO pur : la sélection de variables (mise à zéro de coefficients non-informatifs) est plus bénéfique que le simple rétrécissement Ridge pour nos données à nombreuses variables binaires peu prédictives.

4.2.2 Performance sur le jeu test

Code
kable(data.frame(
  Metrique = c("alpha", "lambda", "AUC (CV)", "AUC (test)", "Accuracy",
               "Sensibilité", "Spécificité", "F1"),
  Valeur = c(
    model_logistic$bestTune$alpha,
    round(model_logistic$bestTune$lambda, 6),
    round(max(model_logistic$results$ROC), 4),
    round(auc(roc_logistic), 4),
    round(cm_logistic$overall["Accuracy"], 4),
    round(cm_logistic$byClass["Sensitivity"], 4),
    round(cm_logistic$byClass["Specificity"], 4),
    round(cm_logistic$byClass["F1"], 4)
  )
))
Table 4.1: Métriques de la régression logistique
Metrique Valeur
alpha 1.000000
lambda 0.002154
AUC (CV) 0.667000
AUC (test) 0.669300
Accuracy 0.625000
Sensibilité 0.635400
Spécificité 0.614200
F1 0.632300

La proximité entre AUC CV et AUC test confirme l’absence de surapprentissage : le modèle généralise correctement.

4.2.3 Matrice de confusion

Code
cm_table <- as.data.frame(cm_logistic$table)
ggplot(cm_table, aes(x = Reference, y = Prediction, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), size = 6, fontface = "bold") +
  scale_fill_gradient(low = "white", high = COL_PRIMARY) +
  labs(x = "Réel", y = "Prédit", title = "Matrice de confusion — Rég. Logistique") +
  THEME_REPORT + theme(legend.position = "none")
Figure 4.2: Matrice de confusion — Régression logistique

Le classifieur est légèrement biaisé vers la classe positive. La sensibilité est supérieure à la spécificité, indiquant que le modèle détecte mieux les parfums satisfaisants que les non-satisfaisants.

4.2.4 Interprétation des coefficients

Code
best_model  <- model_logistic$finalModel
best_lambda <- model_logistic$bestTune$lambda
coefs <- as.matrix(coef(best_model, s = best_lambda))
coef_df <- data.frame(Variable = rownames(coefs), Coefficient = coefs[, 1]) %>%
  filter(Variable != "(Intercept)", Coefficient != 0) %>%
  arrange(desc(Coefficient))
n_nonzero <- nrow(coef_df)
top_pos <- head(coef_df, 10)
top_neg <- tail(coef_df, 5)
coef_show <- bind_rows(top_pos, top_neg)
ggplot(coef_show, aes(x = reorder(Variable, Coefficient), y = Coefficient,
                      fill = ifelse(Coefficient > 0, "Positif", "Négatif"))) +
  geom_bar(stat = "identity") + coord_flip() +
  scale_fill_manual(values = c("Positif" = COL_POSITIVE, "Négatif" = COL_NEGATIVE)) +
  labs(x = NULL, y = "Coefficient", fill = NULL,
       title = paste0("Coefficients LASSO (", n_nonzero, " non-nuls sur ",
                      length(feature_cols), ")")) +
  THEME_REPORT
Figure 4.3: Coefficients non-nuls de la régression logistique (LASSO)

Le LASSO retient 41 variables sur 53, mettant les autres à zéro. Rating_Count conserve un coefficient positif, confirmant le biais de popularité : à caractéristiques olfactives égales, un parfum avec plus d’évaluations a une probabilité plus élevée d’être satisfaisant. Les variables de pays et d’année présentent également des coefficients élevés, reflétant l’hétérogénéité géographique du marché et l’effet temporel. Parmi les familles olfactives, notamment fam_fruity et fam_fresh reçoivent des coefficients négatifs, indiquant qu’à popularité égale ces compositions sont associées à une moindre satisfaction — un résultat cohérent avec leurs taux observés dans l’analyse exploratoire.

Bilan — LASSO retenu, AUC test = 0.669, 41/53 variables actives. Baseline linéaire interprétable.