Validation croisée en R : ensemble de validation, LOOCV et k-fold

Estimez une erreur honnête hors échantillon — et cessez de faire confiance à une seule division aléatoire chanceuse

Un guide pratique de la validation croisée en R avec tidymodels : pourquoi une seule division train/test est instable, et comment l’approche par ensemble de validation, le leave-one-out (LOOCV), la validation croisée k-fold et k-fold répétée donnent une estimation honnête de l’erreur de prédiction. Chaque méthode est branchée sur fit_resamples() appliqué à un workflow, et le RMSE/R²/MAE rééchantillonné se lit en langage clair, avec une correspondance caret → tidymodels pour ceux qui migrent. Démontré sur les données intégrées swiss.

Date de publication

25 juin 2026

Modifié

7 juillet 2026

AstuceL’essentiel à retenir
  • La validation croisée estime la capacité d’un modèle à prédire sur des données nouvelles, jamais vues en l’ajustant sur plusieurs divisions différentes des données et en moyennant l’erreur — l’alternative honnête au fait d’évaluer un modèle sur les données qui ont servi à l’entraîner.
  • Une seule division train/test est instable : une division aléatoire différente donne un nombre différent. La validation croisée moyenne sur de nombreuses divisions afin que l’estimation ne dépende pas d’un tirage chanceux (ou malchanceux).
  • Quatre stratégies, de plus en plus robustes : ensemble de validation (une division) → LOOCV (laisser un point de côté, n fois) → k-fold (vfold_cv(v = 10)) → k-fold répété (repeats = 5).
  • Dans tidymodels, chaque stratégie a la même forme : créez les rééchantillons avec rsample, ajustez le modèle sur chacun avec fit_resamples(), puis moyennez avec collect_metrics() — vous obtenez une mean et une std_err pour chaque métrique.
  • Lisez les deux nombres : la moyenne est votre meilleure estimation de l’erreur hors échantillon ; la std_err vous indique de combien elle a fluctué d’un pli à l’autre (sa stabilité).
  • Utilisez 5 ou 10 plis par défaut. C’est le compromis idéal — bien moins coûteux que le LOOCV et généralement une meilleure estimation de l’erreur de test. Tournez-vous vers le k-fold répété lorsque vous avez besoin d’un nombre encore plus stable.
Obtenez le livre — Machine Learning Essentials (PDF)

Introduction

Vous avez ajusté un modèle de régression et il a obtenu un excellent R² — sur les données mêmes qui ont servi à l’entraîner. Ce nombre est optimiste : le modèle a déjà vu ces points, il aura donc toujours meilleure allure sur eux que sur des données qu’il n’a pas vues. La question qui compte vraiment est : à quel point prédira-t-il les données de demain ?

La validation croisée y répond. C’est une famille de méthodes de rééchantillonnage qui estiment l’erreur de prédiction hors échantillon d’un modèle en l’ajustant de manière répétée sur une partie des données puis en le testant sur une partie réservée qu’il n’a pas vue. Moyennez ces erreurs sur les parties réservées et vous obtenez une estimation honnête de la manière dont le modèle se comportera en production — le nombre que vous devriez rapporter et celui sur lequel vous comparez les modèles.

Cette leçon fait partie du parcours Construire et valider des modèles. Elle approfondit l’étape de validation croisée présentée dans construire des modèles avec tidymodels : nous couvrons les quatre stratégies classiques — ensemble de validation, leave-one-out (LOOCV), k-fold et k-fold répété — chacune branchée sur le même workflow tidymodels. Si vous avez appris cela avec le trainControl() de caret, une correspondance côte à côte figure plus bas. Pour les métriques elles-mêmes (R², RMSE, MAE), voir métriques de précision d’un modèle de régression ; pour le bootstrap, l’ autre grande méthode de rééchantillonnage, voir rééchantillonnage bootstrap.

Les données

Nous utiliserons le jeu de données intégré swiss : des indicateurs standardisés de fécondité et socio-économiques pour 47 provinces francophones de Suisse vers 1888. L’objectif est une régression qui prédit Fertility à partir des cinq autres indicateurs (Agriculture, Examination, Education, Catholic, Infant.Mortality). Il est petit — 47 lignes — ce qui est précisément pourquoi la validation croisée importe ici : avec si peu d’observations, une seule division train/test est bien trop fragile pour qu’on lui fasse confiance.

data("swiss")

# 47 Swiss provinces: predict Fertility from five socio-economic indicators
head(swiss, 4)
             Fertility Agriculture Examination Education Catholic
Courtelary        80.2        17.0          15        12     9.96
Delemont          83.1        45.1           6         9    84.84
Franches-Mnt      92.5        39.7           5         5    93.40
Moutier           85.8        36.5          12         7    33.77
             Infant.Mortality
Courtelary               22.2
Delemont                 22.2
Franches-Mnt             20.2
Moutier                  20.3
data("swiss")

# How much data do we have to spend?
dim(swiss)
[1] 47  6

Quarante-sept lignes, six colonnes (une variable cible + cinq prédicteurs). Tout du long, le modèle est la même régression linéaire ordinaire — Fertility ~ . — afin que chaque nombre que vous voyez reflète la méthode de validation, et non un changement de modèle.

Le problème d’une seule division

Faisons d’abord ce que la leçon Fondations a enseigné : diviser les données une fois, entraîner sur 80 %, et évaluer sur les 20 % réservés. C’est l’approche par ensemble de validation — la forme la plus simple de validation.

rsample::initial_split() réalise la division ; training() et testing() extraient les deux ensembles. Nous ajustons un lm sur l’ensemble d’entraînement et l’évaluons sur l’ensemble de test avec yardstick :

library(rsample)
library(parsnip)
library(yardstick)
data("swiss")

set.seed(123)                                  # reproducible split
split <- initial_split(swiss, prop = 0.80)
train_data <- training(split)
test_data  <- testing(split)

lm_fit <- lm(Fertility ~ ., data = train_data) # fit on the training set
preds  <- predict(lm_fit, test_data)           # predict the held-out 20%

# Score on the test set
res <- data.frame(obs = test_data$Fertility, pred = preds)
data.frame(
  R2   = rsq_vec(res$obs, res$pred),
  RMSE = rmse_vec(res$obs, res$pred),
  MAE  = mae_vec(res$obs, res$pred)
)
         R2     RMSE     MAE
1 0.5927546 9.657747 7.89639

Un RMSE d’environ 9.7 — une prédiction typique se trompe d’environ 9.7 points de fécondité. Mais voici le hic. Ce nombre provient d’une division particulière. Changez la graine — choisissez 20 % différents à réserver — et observez ce qui se passe :

library(rsample)
library(yardstick)
data("swiss")

# Same model, the SAME 80/20 ratio — only the random split changes
rmse_for_seed <- function(seed) {
  set.seed(seed)
  sp <- initial_split(swiss, prop = 0.80)
  fit <- lm(Fertility ~ ., data = training(sp))
  p   <- predict(fit, testing(sp))
  rmse_vec(testing(sp)$Fertility, p)
}

sapply(c(1, 42, 123, 2024, 7), rmse_for_seed)
[1] 6.801644 6.394985 9.657747 9.970744 6.715289

Le RMSE de test oscille à peu près de 6 à 10 sans que rien d’autre ne change que les lignes qui se sont retrouvées dans l’ensemble de test. Rapporter l’une seule de ces valeurs serait trompeur. Avec seulement 47 observations, l’ approche par ensemble de validation est tout simplement trop à forte variance pour qu’on lui fasse confiance — et elle gaspille des données, puisque le modèle n’apprend jamais que sur 80 % d’entre elles. Cette instabilité est la raison d’être même de la validation croisée.

NoteDeux inconvénients d’une seule division de validation
  1. Forte variance — l’estimation dépend fortement des lignes qui sont réservées (vous venez de la voir osciller d’environ 6 à 10).
  2. Données gaspillées — le modèle n’est entraîné que sur une fraction des données, il peut donc être plus biaisé (moins bon) qu’un modèle qui apprend de tout. La validation croisée corrige les deux en laissant chaque observation servir, à tour de rôle, à la fois à l’entraînement et au test.

Validation croisée k-fold

La correction de référence est la validation croisée k-fold. L’idée est simple et élégante :

  1. Divisez aléatoirement les données en k groupes de taille égale (les « plis ») — disons 10.
  2. Réservez le pli 1, entraînez sur les 9 autres, et enregistrez l’erreur sur le pli 1.
  3. Faites tourner : réservez le pli 2, entraînez sur le reste, enregistrez l’erreur… à travers les k plis.
  4. Moyennez les k erreurs enregistrées. Cette moyenne est l’estimation par validation croisée de l’erreur de test.

Chaque observation est utilisée pour le test exactement une fois et pour l’entraînement k − 1 fois — aucune donnée gaspillée, et la moyenne lisse la chance d’une division isolée.

Dans tidymodels, cela tient en trois lignes. rsample::vfold_cv(v = 10) crée les 10 plis ; tune::fit_resamples() ajuste le workflow sur chacun ; collect_metrics() moyenne les résultats. Nous enveloppons le modèle dans un workflow (recipe + spécification parsnip) afin qu’il puisse être ajusté pli par pli :

library(rsample)
library(parsnip)
library(workflows)
library(recipes)
library(tune)
library(yardstick)
data("swiss")

# A linear-regression workflow (no preprocessing needed here)
lm_spec <- linear_reg() |> set_engine("lm") |> set_mode("regression")
lm_wf   <- workflow() |>
  add_recipe(recipe(Fertility ~ ., data = swiss)) |>
  add_model(lm_spec)

# 10-fold cross-validation
set.seed(123)
folds <- vfold_cv(swiss, v = 10)

cv_res <- fit_resamples(
  lm_wf, resamples = folds,
  metrics = metric_set(rmse, rsq, mae)
)
collect_metrics(cv_res)
# A tibble: 3 × 6
  .metric .estimator  mean     n std_err .config        
  <chr>   <chr>      <dbl> <int>   <dbl> <chr>          
1 mae     standard   6.42     10  0.533  pre0_mod0_post0
2 rmse    standard   7.84     10  0.653  pre0_mod0_post0
3 rsq     standard   0.524    10  0.0974 pre0_mod0_post0

Désormais chaque métrique est accompagnée d’une mean (moyennée sur les 10 plis) et d’une std_err (son erreur standard d’un pli à l’autre). Lisez les deux :

  • Le mean RMSE ≈ 7.8 est votre estimation honnête de l’erreur de prédiction typique — et remarquez qu’il se situe au milieu de cette oscillation 6–10 que nous a donnée la division unique. C’est un nombre bien plus fiable parce qu’il moyenne sur l’ensemble des dix plis réservés au lieu de parier sur un seul.
  • La std_err ≈ 0.65 quantifie de combien cette estimation fluctue d’un pli à l’autre. Une petite erreur standard signifie que le modèle se comporte de manière cohérente sur différentes tranches des données — rassurant.
  • Le mean R² ≈ 0.52 indique que le modèle explique environ la moitié de la variance de la fécondité sur des données qu’il n’a pas vues. (Le R² d’un pli à l’autre est plus bruité que le RMSE, ce qui est normal — c’est un rapport calculé sur de petits plis.)
AstuceÀ retenir, prêt à coller

« Par validation croisée 10-fold, le modèle linéaire prédit la fécondité avec un RMSE de ≈ 7.8 (err. std. 0.65) et un R² en validation croisée de ≈ 0.52 — une estimation stable et honnête de la performance hors échantillon, pas une division unique chanceuse. »

C’est aussi exactement ainsi que vous comparez deux modèles équitablement : ajustez les deux via fit_resamples() sur les mêmes plis et comparez leurs moyennes en validation croisée. Le modèle ayant le RMSE moyen le plus bas est le meilleur prédicteur — et les erreurs standard vous disent si la différence est réelle ou du bruit.

Combien de plis ? Choisir k

La question naturelle : quelle valeur de k ? Le compromis est biais vs variance vs coût de calcul :

  • Petit k (par ex. 2) → chaque ensemble d’entraînement est petit, donc le modèle est entraîné sur peu de données et l’ estimation de l’erreur est biaisée à la hausse (trop pessimiste). À l’extrême, k = 2 est proche de l’ approche par ensemble de validation.
  • Grand k → chaque ensemble d’entraînement est presque la totalité des données (moins de biais), mais les k ensembles réservés se chevauchent fortement, leurs erreurs sont donc corrélées et l’estimation a une plus forte variance — et cela coûte k ajustements de modèle.
  • À l’extrême k = n, vous obtenez le LOOCV (section suivante).
NoteLe choix par défaut en pratique : k = 5 ou k = 10

En pratique, vous utilisez presque toujours la validation croisée à 5 ou 10 plis. On a montré empiriquement que ces valeurs donnent des estimations de l’erreur de test qui ne souffrent ni d’un fort biais ni d’une forte variance, pour un coût de calcul raisonnable (James et al., 2014). Commencez par 10 ; descendez à 5 si l’ajustement est coûteux.

Validation croisée leave-one-out (LOOCV)

Le LOOCV est le k-fold poussé à sa limite : k = n. Vous laissez de côté une seule observation, entraînez sur les n − 1 autres, prédisez le point réservé unique, et recommencez pour chaque observation — puis moyennez les n erreurs. Avec 47 provinces, cela fait 47 ajustements de modèle.

Son attrait : chaque modèle s’entraîne sur la quasi-totalité des données, l’estimation a donc un faible biais, et il n’y a aucune aléatoire — le leave-one-out donne la même réponse à chaque exécution (aucune graine nécessaire). Ses coûts : il est onéreux (un ajustement par observation), et parce que les n ensembles d’entraînement sont presque identiques, l’ estimation peut avoir une forte variance.

rsample::loo_cv() crée les divisions leave-one-out. Il y a une subtilité pratique à connaître : tidymodels ne fait délibérément pas passer loo_cv() par fit_resamples() — moyenner une métrique calculée sur des ensembles de test d’un seul point est statistiquement délicat (vous ne pouvez pas calculer un R² sur un seul point). Donc pour le LOOCV nous parcourons nous-mêmes les divisions, collectons les n prédictions réservées, et les évaluons une seule fois, ensemble — ce qui est de toute façon la bonne manière de résumer le LOOCV :

library(rsample)
library(yardstick)
data("swiss")

# n leave-one-out splits (one held-out point each)
loo <- loo_cv(swiss)

# Fit on each n-1 training set, predict the single held-out point
obs  <- numeric(nrow(loo))
pred <- numeric(nrow(loo))
for (i in seq_len(nrow(loo))) {
  s   <- loo$splits[[i]]
  fit <- lm(Fertility ~ ., data = analysis(s))   # train on n-1 points
  pred[i] <- predict(fit, assessment(s))          # predict the 1 held-out point
  obs[i]  <- assessment(s)$Fertility
}

# Score all n held-out predictions together
data.frame(
  RMSE = rmse_vec(obs, pred),
  MAE  = mae_vec(obs, pred)
)
      RMSE      MAE
1 7.738618 6.116021

Le LOOCV donne un RMSE d’environ 7.7 — rassurant de constater à quel point il est proche de l’estimation 10-fold d’environ 7.8. Lorsque deux méthodes de validation solides s’accordent, vous pouvez faire confiance au nombre. analysis() et assessment() sont les noms de rsample pour les parties d’entraînement et réservée d’une division (les mêmes rôles que training()/testing() jouent pour un initial_split).

NoteLOOCV vs k-fold — lequel utiliser ?

Le LOOCV est presque sans biais mais peut avoir une forte variance et est le plus onéreux (n ajustements). La validation croisée 10-fold donne généralement une meilleure estimation de l’erreur de test pour une fraction du coût — l’avantage moins évident mais important qui fait du k-fold le choix par défaut (James et al., 2014). Tournez-vous vers le LOOCV principalement avec de très petits jeux de données où vous ne pouvez vous passer ne serait-ce que d’un pli, ou lorsque vous voulez une estimation reproductible par construction.

Validation croisée k-fold répétée

Une seule validation croisée 10-fold dépend encore un peu de la manière dont les données ont été partitionnées en plis. Le k-fold répété supprime ce dernier soupçon de hasard : il exécute toute la procédure k-fold plusieurs fois, chacune avec une nouvelle partition aléatoire, et moyenne sur toutes. L’erreur finale est la moyenne sur l’ensemble des repeats × k ajustements.

Un seul argument l’active — repeats :

library(rsample)
library(parsnip)
library(workflows)
library(recipes)
library(tune)
library(yardstick)
data("swiss")

lm_spec <- linear_reg() |> set_engine("lm") |> set_mode("regression")
lm_wf   <- workflow() |>
  add_recipe(recipe(Fertility ~ ., data = swiss)) |>
  add_model(lm_spec)

# 10-fold CV, repeated 5 times = 50 fits, averaged
set.seed(123)
rep_folds <- vfold_cv(swiss, v = 10, repeats = 5)

rep_res <- fit_resamples(
  lm_wf, resamples = rep_folds,
  metrics = metric_set(rmse, rsq, mae)
)
collect_metrics(rep_res)
# A tibble: 3 × 6
  .metric .estimator  mean     n std_err .config        
  <chr>   <chr>      <dbl> <int>   <dbl> <chr>          
1 mae     standard   6.28     50  0.264  pre0_mod0_post0
2 rmse    standard   7.55     50  0.317  pre0_mod0_post0
3 rsq     standard   0.622    50  0.0449 pre0_mod0_post0

Le RMSE moyen bouge à peine par rapport à la seule exécution 10-fold — mais regardez la std_err : elle diminue, parce que l’estimation est maintenant moyennée sur 50 ajustements au lieu de 10. Le k-fold répété vous procure une estimation plus stable (une erreur standard plus serrée), ce qui compte lorsque vous comparez deux modèles proches et devez savoir si une faible différence de RMSE est réelle. Le coût est repeats × le calcul.

Tout réunir : les quatre méthodes comparées

Voici les quatre estimations côte à côte, sur le même modèle et les mêmes données. (Le LOOCV est celui que nous avons calculé à la main ; le reste provient de collect_metrics().)

library(ggplot2)

# Estimates gathered from the sections above (mean RMSE and its std error)
cv_summary <- data.frame(
  method = c("Validation set\n(one split)", "LOOCV", "10-fold CV", "Repeated 10-fold\n(5 repeats)"),
  rmse   = c(9.66, 7.74, 7.84, 7.55),
  se     = c(NA,   NA,   0.65, 0.317)
)
cv_summary$method <- factor(cv_summary$method, levels = cv_summary$method)

ggplot(cv_summary, aes(x = method, y = rmse)) +
  geom_errorbar(aes(ymin = rmse - se, ymax = rmse + se), width = 0.15, color = "gray50") +
  geom_point(color = "#3a86d4", size = 4) +
  labs(
    x = NULL, y = "Estimated RMSE (lower = better)",
    title = "Four ways to estimate out-of-sample error",
    subtitle = "Same model, same data — only the validation strategy changes"
  ) +
  theme_minimal()

Dot-and-error-bar plot of estimated RMSE for the validation-set, LOOCV, 10-fold and repeated 10-fold methods on the swiss data; the validation-set point is the most extreme near 9.7 while the cross-validated estimates cluster between about 7.55 and 7.85, with the repeated 10-fold point lowest and carrying the smallest error bar.

Lisez l’image : l’estimation par ensemble de validation est la valeur aberrante — une seule division a abouti à un 9.7 pessimiste (et une graine différente aurait pu aboutir à 6). Les trois estimations en validation croisée se regroupent étroitement autour de 7.5–7.8, et la barre d’erreur de la validation croisée répétée est la plus petite. La leçon est claire : ne faites pas confiance à une seule division ; validez par validation croisée — et quand vous avez besoin d’un nombre encore plus stable, répétez-la.

La validation croisée estime l’erreur de test — l’erreur attendue sur une observation fraîche tirée de la même population. Pour un modèle ajusté sur un ensemble d’entraînement, l’estimation par validation croisée k-fold est la moyenne des erreurs par pli :

\[ CV_{(k)} = \frac{1}{k} \sum_{j=1}^{k} \mathrm{Err}_j , \]

\(\mathrm{Err}_j\) est l’erreur de prédiction mesurée sur le pli \(j\) (par ex. son RMSE ou MSE), et le modèle utilisé pour évaluer le pli \(j\) a été entraîné sur les autres \(k-1\) plis. Le LOOCV est le cas particulier \(k = n\), moyennant \(n\) erreurs sur un point unique.

Pourquoi ne pas simplement évaluer sur les données d’entraînement ? Parce que l’erreur d’entraînement est biaisée à la baisse — le modèle a été optimisé pour ajuster ces points exacts, il se flatte donc lui-même. L’écart entre erreur d’entraînement et erreur de test est le surapprentissage, et il croît avec la flexibilité du modèle. La validation croisée comble cet écart en évaluant toujours sur des données que le modèle n’a pas vues pendant l’ajustement. Le compromis biais–variance dans le choix de \(k\) vient de deux effets concurrents : des plis d’entraînement plus petits (petit \(k\)) augmentent le biais, tandis que des ensembles d’entraînement fortement chevauchants (grand \(k\)) augmentent la variance de l’estimation moyennée — ce qui explique pourquoi \(k = 5\) ou \(10\) est le compromis empirique idéal.

Vous venez de caret ? La correspondance

Si vous avez fait de la validation croisée avec caret, les concepts sont identiques — seuls les verbes changent. caret regroupe le rééchantillonnage dans trainControl() ; tidymodels fait des rééchantillons un objet rsample explicite que vous passez à fit_resamples() :

caret tidymodels
createDataPartition(y, p = 0.8) rsample::initial_split(data, prop = 0.80) + training()/testing()
trainControl(method = "LOOCV") rsample::loo_cv() (parcourez les divisions ; voir plus haut)
trainControl(method = "cv", number = 10) rsample::vfold_cv(v = 10)
trainControl(method = "repeatedcv", number = 10, repeats = 5) rsample::vfold_cv(v = 10, repeats = 5)
train(y ~ ., data, method = "lm", trControl = ctrl) fit_resamples(workflow, resamples = folds)
model$results (RMSE / Rsquared / MAE) collect_metrics(res) (moyenne + std_err)

Le bénéfice est le même que pour le reste de tidymodels : les rééchantillons, le modèle et les métriques sont des objets séparés et interchangeables — changez la spécification du modèle et le même code de validation croisée se transporte directement vers une forêt aléatoire ou une régression pénalisée.

🟢 Avec un agent IA

Pas sûr que l’excellent score de votre modèle soit réel ? Demandez à Prova « valide ce modèle par validation croisée et dis-moi si le score tient hors échantillon » — elle répond avec du code tidymodels que vous pouvez exécuter sur vos propres données, puis vous aide à lire le RMSE/R² rééchantillonné et à choisir le bon nombre de plis. The runtime is the judge. Ask Prova →

Problèmes courants

  • Votre score en validation croisée est bien pire que votre score d’entraînement. C’est tout l’intérêt — la validation croisée révèle le surapprentissage qu’un score d’entraînement cache. Fiez-vous au nombre de la validation croisée ; c’est le nombre honnête. Si l’écart est énorme, votre modèle est trop flexible pour les données.
  • loo_cv() provoque une erreur dans fit_resamples() (“Leave-one-out cross-validation is not currently supported with tune”). C’est attendu — le LOOCV ne peut pas être résumé par fit_resamples() (un R² sur un pli d’un seul point est indéfini). Parcourez loo$splits et évaluez les prédictions réservées ensemble, comme montré plus haut.
  • Deux exécutions de vfold_cv() donnent des moyennes légèrement différentes. La partition en plis est aléatoire. Appelez set.seed() avant vfold_cv() pour un résultat reproductible, ou utilisez le k-fold répété pour moyenner le hasard.
  • Vous avez fait de la validation croisée, mais avez prétraité tout le jeu de données d’abord. Cela fuit de l’information à travers les plis. Mettez chaque transformation à l’intérieur d’un recipe() et ajoutez-la au workflowfit_resamples() ré-apprend alors le prétraitement uniquement sur la portion d’entraînement de chaque pli, sans fuite.

Questions fréquentes

La validation croisée k-fold divise vos données en k groupes égaux (les « plis »), puis entraîne le modèle sur k − 1 plis et le teste sur le pli réservé — en faisant tourner jusqu’à ce que chaque pli ait servi d’ensemble de test une fois. Les k erreurs réservées sont moyennées pour donner une estimation honnête de la façon dont le modèle prédit sur de nouvelles données. En R avec tidymodels : vfold_cv(data, v = 10) puis fit_resamples().

Cinq ou dix. Ces valeurs sont le compromis empirique idéal — elles estiment l’erreur de test sans ni le fort biais de très peu de plis, ni la forte variance (et le coût) de très nombreux plis. Commencez par v = 10 ; descendez à v = 5 si chaque ajustement de modèle est coûteux. Ne passez au LOOCV (v = n) que sur de très petits jeux de données.

Le LOOCV (leave-one-out) s’entraîne sur la quasi-totalité des données, il est donc quasiment sans biais, mais c’est le plus onéreux (n ajustements) et il peut avoir une plus forte variance parce que les n ensembles d’entraînement sont presque identiques. La validation croisée 10-fold donne généralement une meilleure estimation de l’erreur de test pour une fraction du coût, ce qui explique pourquoi c’est le choix par défaut au quotidien. Utilisez le LOOCV principalement lorsque le jeu de données est trop petit pour se passer d’un pli entier.

Avec tidymodels : construisez un workflow() (recipe + spécification de modèle), créez des rééchantillons avec rsample::vfold_cv(data, v = 10), ajustez le modèle sur chacun avec tune::fit_resamples(wf, resamples = folds), et moyennez avec collect_metrics(). Vous obtenez une moyenne et une erreur standard pour chaque métrique (RMSE, R², MAE). Pour une seule division train/test à la place, utilisez initial_split().

Les deux sont des méthodes de rééchantillonnage pour estimer l’erreur, mais elles échantillonnent différemment. La validation croisée partitionne les données en plis non chevauchants et teste sur chacun à tour de rôle. Le bootstrap tire des échantillons avec remise (certaines lignes se répètent, d’autres sont écartées) et teste sur les lignes écartées. La validation croisée est le choix standard pour estimer l’erreur de prédiction ; le bootstrap excelle pour estimer l’incertitude d’une statistique. Voir rééchantillonnage bootstrap.

Testez vos connaissances

Utilisez le jeu de données Boston (MASS), en prédisant medv (valeur médiane des logements) à partir de tous les autres prédicteurs. Construisez un workflow linear_reg() et estimez son RMSE et son R² hors échantillon avec la validation croisée 10-fold, puis avec la 10-fold répétée (3 répétitions). La moyenne a-t-elle changé ? L’erreur standard a-t-elle diminué ?

La forme est identique à celle de la leçon — remplacez swiss/Fertility ~ . par Boston/medv ~ .. Vous avez besoin de rsample, parsnip, workflows, recipes, tune et yardstick. Activez les répétitions avec vfold_cv(..., repeats = 3).

library(MASS)
library(rsample); library(parsnip); library(workflows)
library(recipes); library(tune); library(yardstick)
data("Boston", package = "MASS")

lm_spec <- linear_reg() |> set_engine("lm") |> set_mode("regression")
lm_wf   <- workflow() |>
  add_recipe(recipe(medv ~ ., data = Boston)) |>
  add_model(lm_spec)

# 10-fold CV
set.seed(123)
folds <- vfold_cv(Boston, v = 10)
collect_metrics(fit_resamples(lm_wf, folds, metrics = metric_set(rmse, rsq)))

# Repeated 10-fold (3 repeats)
set.seed(123)
rfolds <- vfold_cv(Boston, v = 10, repeats = 3)
collect_metrics(fit_resamples(lm_wf, rfolds, metrics = metric_set(rmse, rsq)))

Vous obtiendrez un RMSE en validation croisée autour de 4.8 (milliers de dollars) et un R² proche de 0.72. L’exécution répétée laisse la moyenne essentiellement inchangée mais réduit l’erreur standard — une estimation plus stable de la même quantité.

Vérification rapide. Un collègue rapporte « mon modèle a un R² = 0.95 » — mesuré sur les données qui ont servi à l’entraîner. Pourquoi ce nombre n’est-il pas digne de confiance, et quel changement unique le rend honnête ?

Le R² d’entraînement est optimiste : le modèle a été ajusté à ces points exacts, il se flatte donc lui-même, et un modèle en surapprentissage peut obtenir un score quasi parfait à l’entraînement tout en prédisant mal de nouvelles données. La solution est de rapporter un R² en validation croisée (vfold_cv() + fit_resamples() + collect_metrics()) — une erreur mesurée sur des données que le modèle n’a jamais vues pendant l’ajustement.

Conclusion

Vous avez vu pourquoi on ne peut pas faire confiance à une seule division train/test — son RMSE a oscillé de 6 à 10 sur les minuscules données swiss — et comment la validation croisée corrige cela en moyennant l’erreur sur de nombreuses divisions réservées. Vous avez exécuté les quatre stratégies classiques à travers un seul workflow tidymodels : l’ensemble de validation (initial_split), le LOOCV (loo_cv, en boucle), le k-fold (vfold_cv(v = 10)) et le k-fold répété (repeats = 5), en lisant chaque RMSE/R² rééchantillonné comme une mean ± std_err. La règle pratique : par défaut la validation croisée à 5 ou 10 plis, répétez-la quand vous avez besoin d’un nombre encore plus stable, et tournez-vous vers le LOOCV uniquement sur de très petites données.

Ensuite, plongez dans les métriques elles-mêmes dans métriques de précision d’un modèle de régression, ou découvrez l’autre grande méthode de rééchantillonnage dans rééchantillonnage bootstrap.

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 Validation de modèles 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 métrique et figure a été produite 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 5 — Méthodes de rééchantillonnage.)
  • Kuhn, M., & Silge, J. Tidy Modeling with R. (Chapitre 10 — Rééchantillonnage pour évaluer la performance.) Disponible gratuitement en ligne (CC BY-NC-SA).

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Validation croisée en R : ensemble de validation, LOOCV et
    k-fold},
  date = {2026-06-25},
  url = {https://www.datanovia.com/learn/machine-learning/model-validation/cross-validation},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Validation croisée en R : ensemble de validation, LOOCV et k-fold.” 2026. June 25. https://www.datanovia.com/learn/machine-learning/model-validation/cross-validation.