Régression logistique pas à pas en R : sélection stepAIC

Laissez l’AIC choisir les prédicteurs d’un modèle logistique — puis vérifiez honnêtement ce choix par validation croisée

Un guide pratique de la régression logistique pas à pas en R : ajustez un glm binomial complet, puis utilisez MASS::stepAIC pour une sélection de variables ascendante, descendante et bidirectionnelle pilotée par l’AIC. Comparez le modèle complet et le modèle réduit par l’AIC, l’exactitude de test et l’AUC, interprétez les odds ratio et — puisque tidymodels n’a pas d’étape stepwise native — encapsulez la formule sélectionnée dans un workflow tidymodels pour une évaluation honnête par validation croisée. Mis en pratique sur les données PimaIndiansDiabetes2.

Date de publication

25 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • La régression logistique pas à pas sélectionne automatiquement un ensemble réduit de prédicteurs pour un modèle logistique en ajoutant et/ou retirant des variables une à une, en conservant le modèle qui obtient le meilleur score sur l’AIC.
  • L’outil de référence est MASS::stepAIC() appliqué à un ajustement glm(..., family = binomial). Son argument direction accepte "both" (pas à pas), "forward" (ascendant) ou "backward" (descendant) — exactement comme pour la sélection linéaire pas à pas.
  • tidymodels n’a pas d’étape stepwise native — et c’est normal. Le workflow honnête et actuel consiste à exécuter stepAIC en R de base, puis à encapsuler la formule retenue dans un workflow pour un scoring équitable par validation croisée.
  • Sur les données PimaIndiansDiabetes2, la sélection pas à pas abandonne trois prédicteurs faibles (pressure, triceps, insulin) et conserve pregnant + glucose + mass + pedigree + age — un modèle plus léger qui se valide aussi bien ou mieux que le modèle complet (AUC 0.86 vs 0.85, exactitude 0.80 vs 0.78).
  • Lisez les odds ratio, pas seulement les coefficients. Chaque exp(coef) est le changement multiplicatif de la cote de diabète pour une hausse d’une unité d’un prédicteur — glucose ≈ 1.04, mass ≈ 1.10.
  • Les mêmes écueils que pour la version linéaire s’appliquent : les p-value du modèle sélectionné lui-même sont optimistes, l’ensemble retenu est instable et « la sélection a vu toutes les lignes » — alors laissez les données mises de côté trancher.
Obtenez le livre — Machine Learning Essentials (PDF)

Introduction

Vous construisez un modèle pour prédire si un patient est diabétique à partir d’une poignée de mesures cliniques, et vous vous posez la même question lancinante que tout modélisateur : ai-je besoin de tous ces prédicteurs ? Un modèle logistique avec moins de variables, bien choisies, est plus facile à expliquer aux cliniciens, moins coûteux à collecter et généralise souvent mieux — chaque prédicteur inutile que vous abandonnez est une chose de moins que le modèle peut surajuster.

La régression logistique pas à pas automatise cette recherche pour le cadre de la classification. C’est exactement la même idée que la régression pas à pas pour une variable réponse continue, mais le modèle sous-jacent est une régression logistique (glm avec family = binomial) au lieu d’un modèle linéaire. Elle ajoute et retire des prédicteurs de façon itérative, en notant chaque modèle candidat par l’AIC, et conserve le sous-ensemble qui donne le meilleur score. Elle se décline dans les trois mêmes variantes :

  1. Sélection ascendante — partez d’aucun prédicteur, ajoutez le plus contributif à chaque étape.
  2. Élimination descendante — partez du modèle complet, retirez le moins contributif à chaque étape.
  3. Bidirectionnelle / pas à pas — une combinaison : ajout comme en ascendant, mais avec re-vérification et retrait des termes qui cessent d’aider.

Utilisez la sélection pas à pas quand vous avez plusieurs prédicteurs plausibles et que vous voulez un classifieur compact et bien ajusté sans tester à la main toutes les combinaisons. Cette leçon suppose que vous savez déjà ajuster et interpréter un seul modèle logistique — sinon, commencez par la régression logistique dans le pilier Biostatistiques, qui couvre le modèle lui-même, ses hypothèses et l’interprétation des odds ratio. Ici, nous nous concentrons sur la tâche prédictive : choisir les prédicteurs et valider ce choix.

Une mise en garde d’emblée, car elle compte autant ici que dans le cas linéaire : la sélection pas à pas est pratique mais ce n’est pas un repas gratuit. Les p-value du modèle sélectionné sont optimistes et l’ensemble retenu de variables peut changer si vous ré-échantillonnez les données. Nous enseignerons la méthode et vous montrerons comment vérifier honnêtement son travail par validation croisée.

Les données

Nous utiliserons le jeu de données PimaIndiansDiabetes2 du package mlbench : des mesures cliniques sur des femmes d’origine pima, avec pour objectif de prédire la variable binaire diabetes (pos / neg) à partir de variables telles que le glucose plasmatique, l’indice de masse corporelle mass (IMC), le nombre de grossesses pregnant, un score de pedigree du diabète et l’age.

Ce jeu de données comporte de nombreuses valeurs manquantes codées en NA, donc — exactement comme le fait l’analyse source — nous retirons les lignes incomplètes avec na.omit() avant la modélisation :

data("PimaIndiansDiabetes2", package = "mlbench")

# Remove rows with missing clinical values
pima <- na.omit(PimaIndiansDiabetes2)

# 392 complete cases: eight clinical predictors + the diabetes outcome
head(pima, 4)
  pregnant glucose pressure triceps insulin mass pedigree age diabetes
4        1      89       66      23      94 28.1    0.167  21      neg
5        0     137       40      35     168 43.1    2.288  33      pos
7        3      78       50      32      88 31.0    0.248  26      pos
9        2     197       70      45     543 30.5    0.158  53      pos
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)

# How balanced is the outcome?
table(pima$diabetes)

neg pos 
262 130 

Après avoir retiré les cas incomplets, il nous reste 392 patientes — 262 négatives, 130 positives. Un déséquilibre modeste, mais exploitable. Huit prédicteurs candidats et une variable réponse binaire : un problème de classification d’école et un bon cas pour la sélection de variables, car les huit prédicteurs ne mériteront pas tous leur place.

Diviser les données : entraînement et test

La première règle d’une prédiction honnête vaut aussi pour la classification — ne jugez jamais un modèle sur les données qui ont servi à l’entraîner. Nous mettons de côté un jeu de test que nous ne toucherons pas avant la toute fin. rsample::initial_split() le fait ; strata = diabetes maintient l’équilibre positifs/négatifs comparable dans les deux moitiés :

library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)

set.seed(123)                                   # reproducible split
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)
test.data  <- testing(split)

c(train = nrow(train.data), test = nrow(test.data))
train  test 
  313    79 

Environ 313 patientes pour apprendre, 79 réservées pour trancher. Nous fixons une graine pour que vous obteniez exactement cette division — chaque chiffre ci-dessous est reproductible à partir du code montré.

Le modèle complet : le point de départ

La sélection pas à pas compare les sous-modèles au modèle complet — la régression logistique incluant tous les prédicteurs. Ajustez-le une fois avec glm() et family = binomial :

library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)

full.model <- glm(diabetes ~ ., data = train.data, family = binomial)
summary(full.model)

Call:
glm(formula = diabetes ~ ., family = binomial, data = train.data)

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -1.051e+01  1.439e+00  -7.303 2.82e-13 ***
pregnant     1.001e-01  6.124e-02   1.635  0.10199    
glucose      3.703e-02  6.485e-03   5.710 1.13e-08 ***
pressure    -3.899e-04  1.382e-02  -0.028  0.97749    
triceps      1.403e-02  1.997e-02   0.702  0.48251    
insulin      5.893e-04  1.507e-03   0.391  0.69584    
mass         7.969e-02  3.178e-02   2.507  0.01216 *  
pedigree     1.340e+00  4.836e-01   2.771  0.00558 ** 
age          2.716e-02  2.019e-02   1.345  0.17857    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 397.99  on 312  degrees of freedom
Residual deviance: 267.02  on 304  degrees of freedom
AIC: 285.02

Number of Fisher Scoring iterations: 5

Regardez la colonne des p-value. Plusieurs prédicteurs sont nettement significatifs (glucose, mass, pedigree), tandis que d’autres — pressure, triceps, insulin — restent loin de la significativité, suggérant qu’ils sont du poids mort. Le modèle complet s’ajuste bien (son AIC est d’environ 285), mais si ces trois prédicteurs n’apportent rien, un modèle plus léger pourrait classer de nouvelles patientes aussi bien ou mieux. C’est exactement la question à laquelle répond la sélection pas à pas — automatiquement, par un score raisonné plutôt qu’en jugeant les p-value à l’œil.

Pas à pas bidirectionnel avec MASS::stepAIC()

L’outil de référence pour la sélection pas à pas en R est stepAIC() du package MASS — la même fonction utilisée pour la régression linéaire pas à pas, et elle fonctionne sur un glm logistique sans aucun changement. Elle ajoute et retire des prédicteurs pour minimiser l’AIC (Akaike Information Criterion), un score qui récompense un bon ajustement mais pénalise chaque terme supplémentaire, si bien qu’elle préfère naturellement les modèles plus petits sauf si une variable mérite vraiment sa place.

Commencez par la sélection bidirectionnelle. Passez le modèle glm complet et laissez stepAIC() se déplacer dans l’une ou l’autre direction à chaque étape (trace = FALSE masque le journal étape par étape) :

library(MASS)
library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)

full.model <- glm(diabetes ~ ., data = train.data, family = binomial)

# Both-direction stepwise selection, minimising AIC
step.model <- stepAIC(full.model, direction = "both", trace = FALSE)
summary(step.model)

Call:
glm(formula = diabetes ~ pregnant + glucose + mass + pedigree + 
    age, family = binomial, data = train.data)

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -10.745396   1.301461  -8.256  < 2e-16 ***
pregnant      0.098655   0.060944   1.619  0.10550    
glucose       0.038055   0.005694   6.683 2.34e-11 ***
mass          0.094646   0.024186   3.913 9.11e-05 ***
pedigree      1.373534   0.480653   2.858  0.00427 ** 
age           0.029512   0.019138   1.542  0.12306    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 397.99  on 312  degrees of freedom
Residual deviance: 267.61  on 307  degrees of freedom
AIC: 279.61

Number of Fisher Scoring iterations: 5

stepAIC() renvoie directement le meilleur modèle final — déjà réajusté, prêt à être lu. Il a abandonné trois prédicteurs (pressure, triceps, insulin) et en a conservé cinq :

\[ diabetes \sim pregnant + glucose + mass + pedigree + age \]

Lisez le résultat en langage clair :

  • L’AIC du modèle a chuté d’environ ≈ 285 (complet) à ≈ 280 (réduit) — une vraie amélioration, puisque l’AIC ne baisse que lorsque retirer un terme aide plus qu’il ne nuit.
  • Les prédicteurs les plus forts — glucose, mass, pedigree — restent hautement significatifs ; les faibles ont disparu. Deux prédicteurs conservés (pregnant, age) ne sont pas individuellement significatifs mais ont été conservés parce que, avec les autres, ils abaissent l’AIC.
  • Chaque prédicteur conservé a un coefficient positif : un glucose, un IMC, un score de pedigree, un nombre de grossesses et un âge plus élevés poussent tous la probabilité prédite de diabète vers le haut, ce qui correspond à l’intuition clinique.
NoteCe que fait réellement l’AIC

À chaque étape, stepAIC() envisage d’ajouter ou de retirer une variable et calcule l’AIC de chaque candidat. Il se déplace vers le changement qui abaisse le plus l’AIC et s’arrête quand aucun ajout-ou-retrait isolé ne l’améliore. Comme AIC = (manque d’ajustement) + (pénalité pour le nombre de termes), il équilibre automatiquement l’exactitude et la complexité — aucun seuil de p-value arbitraire requis. C’est le même moteur que pour la sélection linéaire pas à pas ; seul le modèle sous-jacent a changé.

Sélection ascendante et descendante

La même fonction réalise la sélection ascendante seule et descendante seule — il suffit de changer direction. L’élimination descendante part du modèle complet et retire des termes :

library(MASS)
library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)

full.model <- glm(diabetes ~ ., data = train.data, family = binomial)
back.model <- stepAIC(full.model, direction = "backward", trace = FALSE)

# The formula backward elimination settled on
formula(back.model)
diabetes ~ pregnant + glucose + mass + pedigree + age

La sélection ascendante fait l’inverse : elle part d’un modèle vide (intercept seul) et ajoute le prédicteur le plus utile à chaque étape. La sélection ascendante a besoin d’un scope pour que stepAIC() connaisse le plus grand modèle qu’il est autorisé à construire :

library(MASS)
library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)

# Start from an intercept-only logistic model
null.model <- glm(diabetes ~ 1, data = train.data, family = binomial)
forward.model <- stepAIC(
  null.model, direction = "forward",
  scope = list(lower = ~ 1,
               upper = ~ pregnant + glucose + pressure + triceps + insulin + mass + pedigree + age),
  trace = FALSE
)
formula(forward.model)
diabetes ~ glucose + mass + age + pedigree + pregnant

Sur ces données, les trois directions s’accordent : elles abandonnent chacune les mêmes prédicteurs faibles et conservent pregnant + glucose + mass + pedigree + age. Cet accord est rassurant — mais ne vous y attendez pas en général. Avec plus de prédicteurs, ou avec des prédicteurs corrélés, les sélections ascendante et descendante peuvent aboutir à des modèles différents, car l’ordre dans lequel les variables entrent ou sortent change celles qui paraissent « contributives » à chaque étape. (Cette dépendance au chemin est l’une des faiblesses connues du pas à pas — voir plus bas.)

Interpréter le modèle : les odds ratio

Les coefficients d’une régression logistique sont sur l’échelle des log-cotes, difficile à lire directement. Convertissez-les en odds ratio avec exp() — le changement multiplicatif de la cote de diabète pour une augmentation d’une unité de chaque prédicteur :

library(MASS)
library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)
full.model <- glm(diabetes ~ ., data = train.data, family = binomial)
step.model <- stepAIC(full.model, direction = "both", trace = FALSE)

# Odds ratios: exp(coefficients), rounded
round(exp(coef(step.model)), 3)
(Intercept)    pregnant     glucose        mass    pedigree         age 
      0.000       1.104       1.039       1.099       3.949       1.030 

Lisez les odds ratio en langage clair (un odds ratio de 1 signifie « aucun effet » ; au-dessus de 1 il augmente la cote, en dessous de 1 il la diminue) :

  • glucose ≈ 1.04 — chaque unité supplémentaire de glucose plasmatique multiplie la cote de diabète par environ 1.04. Faible par unité, mais le glucose s’étend sur plus d’une centaine d’unités, si bien que son effet cumulé est important.
  • mass ≈ 1.10 — chaque point d’IMC en plus augmente la cote de diabète d’environ 10 %.
  • pedigree ≈ 3.95 — une augmentation d’une unité du score de pedigree du diabète quadruple presque la cote. Le score de pedigree s’étend à peu près de 0 à 2.4, c’est donc un signal fort et cliniquement sensé.
  • pregnant ≈ 1.10 et age ≈ 1.03 — chaque grossesse ou année d’âge supplémentaire pousse la cote vers le haut modestement.

Visualisez ces mêmes odds ratio pour que la taille de chaque effet soit évidente d’un coup d’œil. Nous traçons l’odds ratio de chaque prédicteur avec un intervalle de confiance à 95 % (issu de confint()), sur une échelle logarithmique pour que la ligne « aucun effet » à 1 occupe une place naturelle :

library(MASS)
library(rsample)
library(ggplot2)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)
full.model <- glm(diabetes ~ ., data = train.data, family = binomial)
step.model <- stepAIC(full.model, direction = "both", trace = FALSE)

# Odds ratios + 95% CI for every predictor (drop the intercept)
or <- exp(cbind(OR = coef(step.model), confint(step.model)))
or <- as.data.frame(or)[-1, ]
or$term <- rownames(or)
names(or) <- c("OR", "lower", "upper", "term")

ggplot(or, aes(x = reorder(term, OR), y = OR)) +
  geom_hline(yintercept = 1, linetype = "dashed", color = "gray50") +  # no-effect line
  geom_errorbar(aes(ymin = lower, ymax = upper), width = 0.2, color = "gray40") +
  geom_point(color = "#3a86d4", size = 3.5) +
  scale_y_log10() +
  coord_flip() +
  labs(
    x = NULL, y = "Odds ratio (log scale) — 1 means no effect",
    title = "Which clinical factors raise the odds of diabetes?",
    subtitle = "Stepwise-selected logistic model on the Pima data — pedigree score dominates"
  ) +
  theme_minimal()

Dot-and-error-bar plot of the odds ratios (with 95% confidence intervals) for the five predictors kept by stepwise logistic regression on the Pima diabetes data, on a log scale. The diabetes-pedigree score has by far the largest odds ratio (about 4); glucose, body mass, pregnancies and age sit just above the no-effect line of 1; all confidence intervals lie to the right of 1.

Chaque intervalle de confiance se situe à droite de 1, donc chaque prédicteur conservé augmente la cote de diabète ; le score de pedigree du diabète se distingue comme le plus grand effet isolé.

Comparer le modèle complet et le modèle pas à pas sur le jeu de test

Jusqu’ici, chaque chiffre provenait des données d’entraînement. La vraie question est à quel point le modèle plus léger classe-t-il des patientes qu’il n’a jamais vues ? Prédisez sur le jeu de test mis de côté et comparez les deux modèles sur l’exactitude de classification — la proportion de patientes correctement classées.

Le predict(..., type = "response") d’un glm renvoie la probabilité prédite de la classe positive (pos) ; transformez-la en étiquette de classe avec le seuil habituel de 0.5 :

library(MASS)
library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split); test.data <- testing(split)
full.model <- glm(diabetes ~ ., data = train.data, family = binomial)
step.model <- stepAIC(full.model, direction = "both", trace = FALSE)

# Accuracy of the FULL model on the test set
probs.full <- predict(full.model, test.data, type = "response")
pred.full  <- ifelse(probs.full > 0.5, "pos", "neg")
acc.full   <- mean(pred.full == test.data$diabetes)

# Accuracy of the STEPWISE model on the test set
probs.step <- predict(step.model, test.data, type = "response")
pred.step  <- ifelse(probs.step > 0.5, "pos", "neg")
acc.step   <- mean(pred.step == test.data$diabetes)

c(full_accuracy = round(acc.full, 3), stepwise_accuracy = round(acc.step, 3))
    full_accuracy stepwise_accuracy 
            0.759             0.785 

Lisez la comparaison — c’est le gain : le modèle pas à pas classe les patientes du jeu de test au moins aussi exactement que le modèle complet (≈ 0.78 vs ≈ 0.76 ici), malgré l’utilisation de trois prédicteurs de moins. Abandonner pressure, triceps et insulin n’a pas coûté d’exactitude — cela a retiré du bruit. L’exactitude seule peut toutefois être trompeuse sur des données déséquilibrées, il vaut donc aussi la peine de vérifier l’AUC (l’aire sous la courbe ROC), qui résume à quel point le modèle classe les positifs au-dessus des négatifs sur tous les seuils :

library(MASS)
library(rsample)
library(yardstick)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split); test.data <- testing(split)
full.model <- glm(diabetes ~ ., data = train.data, family = binomial)
step.model <- stepAIC(full.model, direction = "both", trace = FALSE)

# yardstick's roc_auc wants the truth as a factor with the EVENT level first
truth <- factor(test.data$diabetes, levels = c("pos", "neg"))
auc.full <- roc_auc(data.frame(truth, .pred = predict(full.model, test.data, type = "response")),
                    truth, .pred)$.estimate
auc.step <- roc_auc(data.frame(truth, .pred = predict(step.model, test.data, type = "response")),
                    truth, .pred)$.estimate

c(full_AUC = round(auc.full, 3), stepwise_AUC = round(auc.step, 3))
    full_AUC stepwise_AUC 
       0.844        0.848 

Les deux modèles ont une AUC de test presque identique (≈ 0.84–0.85) — confirmant que le modèle plus léger n’a rien perdu en discrimination. Toutes choses égales par ailleurs, préférez le modèle plus simple : même pouvoir prédictif, moins de variables à collecter et à expliquer.

Le test honnête : valider par validation croisée le modèle sélectionné

Une seule division entraînement/test est bruitée — une division aléatoire différente donnerait une exactitude légèrement différente. Et chaque score en échantillon (l’AIC, les p-value de summary()) est optimiste, car la sélection pas à pas a activement cherché les prédicteurs qui ajustent le mieux ce jeu d’entraînement. La façon robuste de comparer les deux modèles est la validation croisée : c’est le même test honnête que nous appliquons au modèle linéaire pas à pas, construit sur le workflow tidymodels.

NotePourquoi il n’y a pas d’étape stepwise dans tidymodels — et que faire à la place

tidymodels n’a pas d’étape native de sélection pas à pas. C’est un choix de conception délibéré : l’instabilité du pas à pas et son inférence gonflée en font un mauvais candidat pour la philosophie du cadre (il privilégie plutôt la régression pénalisée). Le workflow honnête et actuel est en deux étapes :

  1. Sélectionnez la formule en R de base avec stepAIC() (ci-dessus).
  2. Évaluez cette formule fixe avec un workflow tidymodels + validation croisée — exactement comme vous évalueriez n’importe quel autre classifieur.

Cela garde la sélection transparente et le scoring honnête. Une mise en garde importante : faire la sélection une fois sur l’ensemble des données d’entraînement puis valider par validation croisée la formule retenue donne une estimation légèrement optimiste — la sélection elle-même a « vu » toutes les lignes d’entraînement. La version pleinement rigoureuse ré-exécute la sélection à l’intérieur de chaque pli ; pour une comparaison rapide et pratique de quelques formules candidates fixes — ce qui est ce que nous faisons ici — l’approche en deux étapes est le choix standard et lisible.

Encapsulez la formule sélectionnée par stepAIC dans un workflow et notez-la par validation croisée à 10 plis, en notant le modèle complet de la même façon pour pouvoir comparer des chiffres honnêtes, hors échantillon :

library(rsample)
library(parsnip)
library(workflows)
library(recipes)
library(tune)
library(yardstick)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split <- initial_split(pima, prop = 0.8, strata = diabetes)
train.data <- training(split)

# Logistic-regression model spec (glm engine, classification mode)
lr_spec <- logistic_reg() |> set_engine("glm") |> set_mode("classification")

# The model stepwise selection chose (5 predictors)
selected_wf <- workflow() |>
  add_recipe(recipe(diabetes ~ pregnant + glucose + mass + pedigree + age, data = train.data)) |>
  add_model(lr_spec)

# The full model (all 8 predictors), for comparison
full_wf <- workflow() |>
  add_recipe(recipe(diabetes ~ ., data = train.data)) |>
  add_model(lr_spec)

set.seed(123)
folds <- vfold_cv(train.data, v = 10, strata = diabetes)

selected_cv <- fit_resamples(selected_wf, folds, metrics = metric_set(accuracy, roc_auc))
full_cv     <- fit_resamples(full_wf,     folds, metrics = metric_set(accuracy, roc_auc))

# Compare cross-validated accuracy / AUC
rbind(
  cbind(model = "selected (5 vars)", collect_metrics(selected_cv)[, c(".metric", "mean", "std_err")]),
  cbind(model = "full (8 vars)",     collect_metrics(full_cv)[,     c(".metric", "mean", "std_err")])
)
              model  .metric      mean    std_err
1 selected (5 vars) accuracy 0.7960417 0.01869037
2 selected (5 vars)  roc_auc 0.8620087 0.02079663
3     full (8 vars) accuracy 0.7802151 0.02029587
4     full (8 vars)  roc_auc 0.8513745 0.02069130

Lisez la comparaison — c’est le vrai verdict :

  • Le modèle sélectionné à 5 variables a une AUC validée par validation croisée plus élevée (≈ 0.86) et une exactitude (≈ 0.80) que le modèle complet à 8 variables (AUC ≈ 0.85, exactitude ≈ 0.78). Abandonner les trois prédicteurs faibles n’a pas seulement simplifié le modèle — cela l’a fait généraliser légèrement mieux, parce que ces prédicteurs ajoutaient du bruit, pas du signal.
  • L’amélioration est faible mais réelle, et elle pointe dans le même sens que le choix de l’AIC. Quand un modèle plus léger l’emporte sur des plis mis de côté, vous avez une preuve authentique de le préférer — pas seulement un score en échantillon que n’importe quelle variable supplémentaire aurait fait monter.
AstuceÀ retenir et à coller

« La sélection bidirectionnelle pas à pas (MASS::stepAIC) sur les données PimaIndiansDiabetes2 a abandonné trois prédicteurs faibles (pressure, triceps, insulin) et conservé un modèle logistique à 5 prédicteurs (pregnant + glucose + mass + pedigree + age). Évalué par validation croisée à 10 plis, ce modèle classe le statut de diabète avec une AUC de ≈ 0.86 et une exactitude ≈ 0.80 — au moins aussi bien que le modèle complet à 8 prédicteurs (AUC ≈ 0.85) — confirmant que le modèle plus simple est le meilleur classifieur, plus parcimonieux. »

À utiliser avec précaution : les écueils de la sélection pas à pas

La régression logistique pas à pas est vraiment utile, mais elle porte les mêmes faiblesses bien documentées que sa cousine linéaire — et elles vous induisent discrètement en erreur si vous faites confiance au résultat sans esprit critique :

  • Ajustement optimiste et significativité gonflée. Le modèle sélectionné a été choisi pour ajuster vos données d’entraînement, donc son AIC et les p-value imprimées par summary() paraissent meilleurs qu’ils ne le seront sur de nouvelles patientes. Ne rapportez jamais les p-value de summary() du modèle sélectionné comme des tests d’hypothèse honnêtes — traitez-les comme descriptives et rapportez plutôt une exactitude/AUC validée par validation croisée (comme ci-dessus).
  • La sélection a vu toutes les lignes. Exécuter stepAIC() une fois sur l’ensemble d’entraînement complet, puis valider par validation croisée la formule retenue, surestime légèrement la performance — le choix des variables a déjà utilisé chaque ligne d’entraînement. Pour une estimation pleinement rigoureuse, ré-exécutez la sélection à l’intérieur de chaque pli.
  • Instabilité. L’ensemble sélectionné de variables peut changer avec un petit changement dans les données, surtout quand les prédicteurs sont corrélés. Le modèle retenu est l’un de plusieurs quasi équivalents, et non un sous-ensemble uniquement « vrai » — c’est pourquoi l’ascendant et le descendant peuvent diverger sur d’autres jeux de données.
NoteVers quoi se tourner à la place — ou en complément

Pour une classification avec de nombreux prédicteurs ou des prédicteurs corrélés, la régression logistique pénalisée (lasso/ridge/ elastic net) est généralement le choix le plus stable : elle rétrécit les coefficients en douceur au lieu de prendre des décisions franches dedans/dehors, et le lasso peut annuler des variables pour vous — voir régression logistique pénalisée. Quoi que vous sélectionniez, validez le choix par validation croisée et jugez-le sur la bonne métrique (AUC, pas seulement l’exactitude, sur des données déséquilibrées) — voir évaluation de modèles de classification.

La sélection pas à pas a besoin d’un nombre unique pour comparer des modèles de tailles différentes. L’AIC (Akaike Information Criterion) est ce nombre, et il s’applique à tout modèle ajusté par maximum de vraisemblance — y compris la régression logistique. Pour un modèle avec \(k\) paramètres estimés et une log-vraisemblance maximisée \(\hat{L}\) :

\[ AIC = 2k - 2 \ln(\hat{L}). \]

Pour un glm logistique, la déviance \(D\) rapportée par summary() est exactement \(-2\ln(\hat{L})\), donc \(AIC = D + 2k\). Le terme de déviance récompense l’ajustement (un modèle mieux ajusté a une plus petite déviance, abaissant l’AIC) ; le terme \(2k\) impose une pénalité pour chaque paramètre ajouté. Un nouveau prédicteur n’abaisse l’AIC que si l’ajustement qu’il achète l’emporte sur sa pénalité — l’AIC intègre donc une défense contre le surajustement, et un AIC plus bas est meilleur. stepAIC() parcourt simplement les prédicteurs, en gardant à chaque étape l’ajout-ou-retrait qui fait le plus chuter l’AIC, et s’arrête quand aucun ne le fait. Le BIC utilise la même forme avec une pénalité plus lourde, dépendant de la taille d’échantillon (\(\ln(n)\,k\) au lieu de \(2k\)), si bien qu’il punit plus durement la complexité et tend à sélectionner des modèles encore plus petits.

🟢 Avec un agent IA

Vous ne savez pas quels prédicteurs cliniques conserver — ou si votre modèle logistique pas à pas surajuste ? Demandez à Prova « exécute une régression logistique pas à pas sur mes données et valide par validation croisée le modèle sélectionné pour voir s’il classe réellement mieux » — elle répond avec du code glm + stepAIC + tidymodels que vous pouvez exécuter sur vos propres données, puis vous aide à lire l’AIC, les odds ratio, l’AUC validée par validation croisée et s’il faut faire confiance à la sélection. The runtime is the judge. Ask Prova →

Problèmes courants

  • stepAIC() introuvable. Il vit dans le package MASS — exécutez d’abord library(MASS). (Attention à l’ordre de chargement : MASS::select masque dplyr::select, alors chargez MASS avant le tidyverse, ou appelez dplyr::select() explicitement.)
  • glm avertit “fitted probabilities numerically 0 or 1 occurred”. C’est une séparation parfaite — un prédicteur (ou une combinaison) sépare les classes trop nettement, et les coefficients explosent. C’est fréquent avec des données petites ou déséquilibrées ; envisagez la régression logistique pénalisée, qui est stable sous séparation, ou retirez le prédicteur fautif.
  • La sélection ascendante n’ajoute rien / renvoie le modèle à intercept seul. La sélection ascendante a besoin d’un scope avec une formule upper listant les prédicteurs qu’elle est autorisée à ajouter. Sans lui, stepAIC() n’a aucun candidat — passez scope = list(lower = ~ 1, upper = ~ <all predictors>).
  • predict() donne des nombres, pas des classes. predict(model, newdata, type = "response") renvoie la probabilité de la classe positive, pas une étiquette. Seuillez-la vous-même — ifelse(probs > 0.5, "pos", "neg") — et rappelez-vous que 0.5 est rarement le seuil optimal sur des données déséquilibrées (voir évaluation de modèles de classification).
  • Votre AUC de test est pire que ce que l’AIC d’entraînement laissait croire. Attendu — l’AIC en échantillon et les p-value sont optimistes après sélection. Notez toujours la formule retenue par validation croisée, jamais avec les chiffres que summary() imprime sur le modèle sélectionné.

Questions fréquentes

Ajustez le modèle complet avec glm(y ~ ., data, family = binomial), puis passez-le à MASS::stepAIC() : stepAIC(full.model, direction = "both"). L’argument direction choisit la stratégie — "both" (pas à pas), "forward" (ascendant) ou "backward" (descendant) — et la fonction renvoie le meilleur modèle final, sélectionné en minimisant l’AIC. C’est le même stepAIC() utilisé pour les modèles linéaires ; seul le glm sous-jacent (avec family = binomial) diffère.

stepAIC() effectue une sélection automatique de variables par l’AIC. À partir de votre modèle, il essaie d’ajouter ou de retirer un prédicteur à la fois, calcule l’AIC de chaque candidat et se déplace vers le changement qui abaisse le plus l’AIC — en s’arrêtant quand aucun ajout-ou-retrait isolé ne l’améliore. Pour un glm logistique, l’AIC vaut la déviance du modèle plus deux fois le nombre de paramètres, si bien qu’il récompense l’ajustement tout en pénalisant les termes supplémentaires, favorisant naturellement un modèle plus petit sauf si un prédicteur mérite vraiment sa place.

Les deux voies automatisées courantes sont la sélection pas à pas (MASS::stepAIC() sur un glm, montrée ici) et la régression pénalisée (lasso via glmnet, qui rétrécit les coefficients faibles vers zéro). Le pas à pas est l’option rapide et interprétable ; le lasso est plus stable quand les prédicteurs sont nombreux ou corrélés. Quelle que soit celle que vous utilisez, validez le modèle sélectionné par validation croisée plutôt que de faire confiance aux p-value du modèle sélectionné lui-même, qui sont optimistes. Voir régression logistique pénalisée pour la voie du lasso.

Pas nativement — tidymodels n’a pas d’étape de sélection pas à pas, par conception (il privilégie la régression pénalisée pour la sélection). Le workflow honnête et actuel est en deux étapes : sélectionnez la formule en R de base avec stepAIC(), puis encapsulez cette formule fixe dans un workflow tidymodels pour l’évaluer équitablement par validation croisée (vfold_cv() + fit_resamples()). La sélection reste transparente et le scoring reste honnête.

C’est pratique mais imparfait — exactement comme le pas à pas linéaire. Elle produit des p-value optimistes (elles ignorent que les variables ont été choisies à partir des données), et l’ensemble sélectionné est instable d’un échantillon à l’autre, surtout avec des prédicteurs corrélés. Utilisez-la comme un outil exploratoire rapide, validez toujours le modèle retenu sur des données mises de côté (exactitude et AUC validées par validation croisée), et pour une prédiction sérieuse préférez la régression logistique pénalisée, qui sélectionne et rétrécit de façon plus stable.

Testez vos connaissances

Construisez un classifieur binaire sur les données iris : prédire si une fleur est de l’espèce versicolor (ou non) à partir des quatre colonnes de mesure. Ajustez le modèle logistique complet, exécutez un stepAIC() bidirectionnel pour sélectionner un sous-ensemble, puis évaluez la formule sélectionnée par validation croisée à 10 plis dans un workflow tidymodels.

Créez d’abord une variable réponse binaire : iris$is_versi <- factor(ifelse(iris$Species == "versicolor", "yes", "no")), puis retirez la colonne Species d’origine. La forme est alors identique à celle de la leçon — glm(is_versi ~ ., family = binomial), MASS::stepAIC(full, direction = "both"), formula(step.model), et un workflow logistic_reg() pour fit_resamples().

library(MASS)
library(rsample); library(parsnip); library(workflows)
library(recipes); library(tune); library(yardstick)

# Binary outcome: versicolor vs not
iris2 <- iris
iris2$is_versi <- factor(ifelse(iris2$Species == "versicolor", "yes", "no"))
iris2$Species  <- NULL

# 1. Select with stepAIC
full.model <- glm(is_versi ~ ., data = iris2, family = binomial)
step.model <- stepAIC(full.model, direction = "both", trace = FALSE)
formula(step.model)        # the selected predictors

# 2. Evaluate the selected formula by 10-fold CV
lr_spec <- logistic_reg() |> set_engine("glm") |> set_mode("classification")
sel_wf  <- workflow() |>
  add_recipe(recipe(formula(step.model), data = iris2)) |>
  add_model(lr_spec)

set.seed(123)
folds <- vfold_cv(iris2, v = 10, strata = is_versi)
collect_metrics(fit_resamples(sel_wf, folds, metrics = metric_set(accuracy, roc_auc)))

Versicolor est notoirement difficile à séparer linéairement, alors attendez-vous à une AUC modeste — l’essentiel est le workflow : stepAIC sélectionne, le workflow tidymodels note honnêtement le choix.

Vérification rapide. Un collègue exécute une élimination descendante sur un modèle logistique, rapporte le summary() du modèle sélectionné avec « toutes les p-value < 0.01 » et une exactitude d’entraînement élevée, et conclut que le modèle est excellent. Pourquoi devriez-vous être sceptique, et que devriez-vous demander à la place ?

Les deux signaux sont optimistes parce que le modèle a été sélectionné à partir des données. Les p-value ignorent que les variables ont été choisies en regardant la variable réponse, donc elles surestiment la significativité ; l’exactitude d’entraînement est gonflée parce que la sélection a trié sur le volet les prédicteurs qui ajustent le mieux cet échantillon. Demandez un score validé par validation croisée — une exactitude et une AUC à 10 plis sur la formule sélectionnée — qui est la seule estimation honnête de la façon dont le modèle classe de nouvelles patientes. Sur des données déséquilibrées, insistez sur l’AUC, pas sur l’exactitude seule.

Conclusion

Vous avez exécuté une régression logistique pas à pas sur les données PimaIndiansDiabetes2 — en ajustant un glm(family = binomial) complet, puis en utilisant MASS::stepAIC() pour les sélections ascendante, descendante et bidirectionnelle. Les trois ont convergé vers le même modèle léger à 5 prédicteurs (pregnant + glucose + mass + pedigree + age), abandonnant trois variables cliniques faibles. Vous avez interprété le résultat en odds ratio (le score de pedigree et le glucose dominent), et — surtout — confirmé le choix de façon honnête : en encapsulant la formule sélectionnée dans un workflow tidymodels et en la validant par validation croisée, où le modèle plus léger égalait ou devançait le modèle complet sur l’exactitude comme sur l’AUC. La discipline à retenir est identique au cas linéaire : le pas à pas sélectionne, mais il ne peut pas valider — ses propres p-value sont optimistes, alors laissez les données mises de côté trancher.

Découvrez ensuite l’alternative moderne plus stable dans la régression logistique pénalisée, ou apprenez à juger correctement n’importe quel classifieur dans évaluation de modèles de classification.

Leçons connexes

Vous préférez un livre ? Machine Learning Essentials est disponible en PDF téléchargeable — toutes les leçons de cette série, hors ligne et à vous.

Obtenir le livre (PDF) →Les leçons restent gratuites en ligne.
Cette page vous a-t-elle été utile ?

Prouvez que vous savez le faire. Maîtrisez toute la série Classification en R — suivez votre parcours, construisez des projets et obtenez un certificat.

Commencer gratuitement →

Passez à Pro — Prova illimité sur vos propres données et un certificat vérifiable qui atteste la compétence.

dès 15 $/mois facturé annuellement

Passer à Pro →

✓ Vous êtes Pro — continuez. The runtime is the judge.

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN
Note

Cette leçon est reproductible : chaque modèle, métrique et figure a été produit par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.

Références

  • James, G., Witten, D., Hastie, T., & Tibshirani, R. (2014). An Introduction to Statistical Learning, with Applications in R. Springer. (Chapitre 6 — Sélection de modèles linéaires et régularisation.)
  • Venables, W. N., & Ripley, B. D. (2002). Modern Applied Statistics with S (4e éd.). Springer. (Le package MASS et stepAIC.)
  • Kuhn, M., & Silge, J. Tidy Modeling with R. Disponible gratuitement en ligne (CC BY-NC-SA).

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Régression logistique pas à pas en R : sélection stepAIC},
  date = {2026-06-25},
  url = {https://www.datanovia.com/learn/machine-learning/classification/stepwise-logistic-regression},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Régression logistique pas à pas en R : sélection stepAIC.” 2026. June 25. https://www.datanovia.com/learn/machine-learning/classification/stepwise-logistic-regression.