Découpez, prétraitez, ajustez, prédisez et validez par validation croisée un modèle de régression — le workflow que réutilise chaque méthode supervisée
Un guide pratique et complet du workflow tidymodels en R. Découpez vos données avec rsample, construisez un recipe, spécifiez un modèle linéaire avec parsnip, regroupez le tout dans un workflow, ajustez sur le jeu d’entraînement, prédisez sur le jeu de test, et mesurez une performance honnête avec yardstick (RMSE, R², MAE). Passez ensuite d’un découpage unique à la validation croisée en k blocs et à un bref aperçu du réglage — avec une table de correspondance depuis caret.
Date de publication
25 juin 2026
Modifié
7 juillet 2026
AstuceL’essentiel
tidymodels est une famille de packages R qui partagent un même workflow prédictif cohérent : découper → prétraiter → ajuster → prédire → évaluer — le successeur moderne de caret.
Découpez d’abord avec rsample::initial_split(data, strata = outcome), puis training() / testing(). Le jeu de test n’est touché qu’une seule fois, tout à la fin — c’est ce qui rend l’estimation de la performance honnête.
Un recipe() déclare le prétraitement (par ex. step_normalize()) ; parsnip déclare le modèle (linear_reg()) ; un workflow() les regroupe pour que les mêmes étapes s’appliquent à l’entraînement et au test.
Évaluez avec yardstick — rmse (erreur typique, dans les unités de la variable cible), rsq (part de la variance expliquée, 0–1), mae (erreur absolue moyenne). Lisez les nombres, ne vous contentez pas de les afficher.
Un découpage unique est bruité. Utilisez vfold_cv() + fit_resamples() pour une estimation validée par validation croisée qui ne dépend pas d’un seul découpage chanceux, et tune_grid() pour choisir honnêtement les hyperparamètres.
Ce workflow est la rampe d’accès prédictive à la régression. Pour interpréter les coefficients et les hypothèses à la place, voyez Régression linéaire.
Vous avez mené une campagne marketing sur trois canaux — YouTube, Facebook et la presse — et vous voulez maintenant un modèle qui prédit les ventes du trimestre prochain à partir d’un budget planifié. C’est une question supervisée, prédictive : vous disposez d’une variable cible étiquetée (sales) et vous vous souciez moins de savoir quel coefficient est « significatif » que de la précision avec laquelle le modèle prédit des ventes qu’il n’a jamais vues.
C’est exactement le travail pour lequel tidymodels est conçu. Plutôt qu’un simple appel à lm(), tidymodels vous offre un ensemble réduit et cohérent de verbes — découper, prétraiter, ajuster, prédire, évaluer — qui passent à l’échelle d’un modèle linéaire à une forêt aléatoire sans changer la forme de votre code. Apprenez le workflow une bonne fois ici ; chaque leçon ultérieure de ce pilier (régression pénalisée, classification, arbres, boosting) ne fait que substituer un modèle différent et réutilise tout le reste.
Utilisez ce workflow prédictif lorsque votre objectif est une prédiction précise sur de nouvelles données — et vous validerez cette précision honnêtement avec un jeu de test mis de côté et une validation croisée. Si en revanche votre objectif est d’interpréter le modèle (lire les coefficients, les p-value, vérifier les hypothèses), c’est un autre parcours — voyez Régression linéaire dans le pilier Biostatistiques. Mêmes données, deux questions, à un clic l’une de l’autre.
NotePrédire ou interpréter
Prédire (cette leçon) : « Étant donné un budget, combien allons-nous vendre, et de combien me tromperai-je ? » → découper, valider, régler. Interpréter (biostatistiques) : « Le budget YouTube influence-t-il significativement les ventes, à budgets des autres canaux constants ? » → coefficients, intervalles de confiance, hypothèses. Choisissez la page qui correspond à votre vraie question.
Les données
Nous utiliserons le jeu de données marketing du package datarium : 200 campagnes publicitaires, chacune avec le budget dépensé (en milliers de dollars) sur youtube, facebook et newspaper, et les ventes (sales) qui en résultent. La variable cible sales est continue — une cible de régression propre.
library(datarium)data("marketing", package ="datarium")# 200 campaigns: three ad budgets (predictors) + sales (outcome)head(marketing, 4)
data("marketing", package ="datarium")# A quick look at the ranges and the outcomesummary(marketing)
youtube facebook newspaper sales
Min. : 0.84 Min. : 0.00 Min. : 0.36 Min. : 1.92
1st Qu.: 89.25 1st Qu.:11.97 1st Qu.: 15.30 1st Qu.:12.45
Median :179.70 Median :27.48 Median : 30.90 Median :15.48
Mean :176.45 Mean :27.92 Mean : 36.66 Mean :16.83
3rd Qu.:262.59 3rd Qu.:43.83 3rd Qu.: 54.12 3rd Qu.:20.88
Max. :355.68 Max. :59.52 Max. :136.80 Max. :32.40
Les ventes vont d’environ 1.9 à 32.4 (en milliers d’unités) ; les trois budgets sont à des échelles différentes (les budgets YouTube sont bien plus élevés que ceux de la presse). Cette différence d’échelle est exactement le genre de chose qu’un recipe va gérer pour nous dans un instant.
Une image avant un modèle
Cela vaut toujours la peine de regarder avant d’ajuster. Le budget YouTube a la relation la plus nette avec les ventes :
library(ggpubr)data("marketing", package ="datarium")ggscatter( marketing, x ="youtube", y ="sales",add ="reg.line", color ="#3a86d4",add.params =list(color ="gray30"),xlab ="YouTube budget (k$)", ylab ="Sales (k units)") +theme_minimal()
Une tendance positive forte et à peu près linéaire — un modèle linéaire est un point de départ raisonnable.
Étape 1 — Dépensez vos données : découpez en entraînement et test
La première règle d’une prédiction honnête : ne jugez jamais un modèle sur des données sur lesquelles il a été entraîné. Il paraîtra meilleur qu’il ne l’est réellement. On dépense donc nos données — la plus grande partie pour entraîner, une tranche mise de côté pour tester.
C’est ce que fait rsample::initial_split(). L’argument strata = sales stratifie le découpage sur la variable cible, de sorte que les jeux d’entraînement et de test aient une distribution de ventes similaire (important quand la variable cible est asymétrique). training() et testing() extraient les deux jeux :
On fixe une graine pour obtenir exactement ce découpage à chaque fois — la même discipline que nstart dans les k-means. Environ 160 campagnes pour apprendre, 40 mises de côté pour faire office de juge.
NoteLe jeu de test est sacré
Vous ne regarderez plus test_data avant la toute fin. Chaque choix — prétraitement, modèle, réglage — se fait en utilisant uniquement le jeu d’entraînement (et la validation croisée à l’intérieur de celui-ci). Toucher au jeu de test trop tôt est la manière classique de se leurrer et de mettre en production un modèle qui échoue.
Étape 2 — Un recipe : déclarez votre prétraitement
Un recipe est une spécification de la façon de préparer vos prédicteurs — écrite une fois, puis appliquée de façon identique à l’entraînement et au test. Ici, nous normalisons les trois budgets (centrer + réduire à moyenne 0, écart-type 1) pour les mettre sur un pied commun :
La formule sales ~ . indique au recipe que sales est la variable cible et que tout le reste est un prédicteur ; recipe() ne lit train_data que pour apprendre les rôles et les types des colonnes — il ne transforme rien pour l’instant. step_normalize(all_numeric_predictors()) déclare « centrer et réduire chaque prédicteur numérique. » Les moyennes et les écarts-types sont appris à partir des seules données d’entraînement, puis réutilisés automatiquement sur le jeu de test — aucune fuite. (Le feature engineering est un sujet en soi — voyez Feature engineering avec recipes pour la boîte à outils complète.)
Étape 3 — Une spécification de modèle : parsnip
parsnip vous permet de décrire un modèle indépendamment du package qui l’ajuste. Pour une régression linéaire ordinaire, linear_reg() avec le moteur "lm" :
Linear Regression Model Specification (regression)
Computational engine: lm
set_mode("regression") indique que l’on prédit un nombre (par opposition à "classification"). La beauté de la chose est que pour essayer un modèle différent plus tard — disons une régression pénalisée — vous ne changez que ce seul objet (linear_reg(penalty = 0.1) |> set_engine("glmnet")) et le reste du workflow reste inchangé.
Étape 4 — Un workflow : regroupez recipe + modèle
Un workflow relie le recipe et la spécification du modèle en un seul objet. Cela garantit que le prétraitement et le modèle voyagent toujours ensemble — quand vous faites un predict() sur de nouvelles données, le recipe est appliqué d’abord, à chaque fois :
Le workflow ajusté contient le modèle appris. (Vous voulez la table soignée des coefficients pour interpréter ? Récupérez-la avec broom::tidy(sales_fit) — mais pour une interprétation en bonne et due forme, y compris les p-value et la vérification des hypothèses, rendez-vous sur Régression linéaire.)
Étape 6 — Prédisez sur le jeu de test
Maintenant — et seulement maintenant — le jeu de test mis de côté gagne sa place. predict() applique le recipe et le modèle à des données que le modèle n’a jamais vues, en renvoyant une colonne soignée .pred :
library(workflows)library(recipes)library(parsnip)library(rsample)library(yardstick)data("marketing", package ="datarium")set.seed(123)split <-initial_split(marketing, prop =0.80, strata = sales)train_data <-training(split); test_data <-testing(split)sales_rec <-recipe(sales ~ ., data = train_data) |>step_normalize(all_numeric_predictors())lm_spec <-linear_reg() |>set_engine("lm") |>set_mode("regression")sales_wf <-workflow() |>add_recipe(sales_rec) |>add_model(lm_spec)sales_fit <-fit(sales_wf, data = train_data)# Predictions on the test set, joined back to the true salesresults <-predict(sales_fit, new_data = test_data)results <-cbind(results, sales = test_data$sales)head(results, 5)
Chaque .pred est la prévision de ventes du modèle pour une campagne sur laquelle il ne s’est pas entraîné, placée à côté des vraies ventes (sales). Elles semblent proches — mesurons à quel point.
Étape 7 — Évaluez avec yardstick (et lisez les métriques)
Afficher des prédictions n’est pas une évaluation. yardstick les transforme en nombres honnêtes. Pour la régression, trois métriques racontent l’histoire :
# A tibble: 3 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 rmse standard 1.88
2 rsq standard 0.902
3 mae standard 1.39
Lisez chaque nombre en langage clair — c’est tout l’enjeu :
RMSE (racine de l’erreur quadratique moyenne) ≈ la taille typique d’une erreur de prédiction, dans les unités propres de la variable cible (ici, en milliers d’unités de ventes). Un RMSE d’environ 1.9 signifie qu’une prévision typique se trompe d’environ 1 900 unités. Elle pénalise davantage les gros écarts (à cause du carré), c’est donc la métrique à surveiller si les grosses erreurs coûtent cher. Plus c’est bas, mieux c’est ; 0 est parfait.
R² (rsq, le coefficient de détermination) = la part de la variation des ventes que le modèle explique, de 0 (pas mieux que de prédire la moyenne) à 1 (parfait). Un R² d’environ 0.90 signifie que le modèle rend compte d’environ 90 % de la variabilité des ventes sur des données nouvelles — fort.
MAE (erreur absolue moyenne) = l’erreur absolue moyenne, également en unités de ventes, et contrairement au RMSE elle ne surpondère pas les gros écarts. C’est la plus directement interprétable : « en moyenne, nous nous trompons de tant. » Le MAE est toujours ≤ RMSE ; un écart entre les deux signale quelques grosses erreurs.
AstuceÀ recopier tel quel
« Sur un jeu de test mis de côté, le modèle prédit les ventes trimestrielles avec un RMSE d’environ 1.9k unités et explique environ 90 % de la variance (R² ≈ 0.90, MAE ≈ 1.4k unités) — assez précis pour planifier des budgets. »
Voyez-le : prédit vs réel
Le diagnostic le plus utile pour un modèle de régression est prédit vs réel sur le jeu de test. Des points qui collent à la diagonale = des prédictions précises :
Les points collent à la ligne pointillée à 45 degrés — le modèle prédit bien les ventes du jeu de test. coord_equal() impose des axes égaux pour que la diagonale soit une vraie référence.
Étape 8 — Un seul découpage est bruité : faites une validation croisée
Ce 0.90 provient d’un seul découpage 80/20. Un découpage aléatoire différent donnerait un nombre légèrement différent — peut-être 0.88, peut-être 0.92. Pour obtenir une estimation stable qui ne dépend pas de la chance, utilisez la validation croisée en k blocs : découpez les données d’entraînement en k blocs, entraînez sur k−1 et testez sur le bloc mis de côté, parcourez tous les k blocs, puis faites la moyenne.
rsample::vfold_cv() crée les blocs ; tune::fit_resamples() ajuste le workflow sur chacun ; collect_metrics() fait la moyenne des résultats :
library(rsample)library(recipes)library(parsnip)library(workflows)library(tune)library(yardstick)data("marketing", package ="datarium")set.seed(123)split <-initial_split(marketing, prop =0.80, strata = sales)train_data <-training(split)sales_rec <-recipe(sales ~ ., data = train_data) |>step_normalize(all_numeric_predictors())lm_spec <-linear_reg() |>set_engine("lm") |>set_mode("regression")sales_wf <-workflow() |>add_recipe(sales_rec) |>add_model(lm_spec)set.seed(456)folds <-vfold_cv(train_data, v =10) # 10-fold CV on the TRAINING setcv_res <-fit_resamples( sales_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 1.59 10 0.102 pre0_mod0_post0
2 rmse standard 2.06 10 0.161 pre0_mod0_post0
3 rsq standard 0.887 10 0.0202 pre0_mod0_post0
Maintenant chaque métrique est accompagnée d’une mean (moyenne sur les 10 blocs) et d’un std_err (de combien elle a fluctué d’un bloc à l’autre). Le R² moyen est une estimation bien plus fiable que le nombre d’un découpage unique, et la petite erreur standard vous dit que la performance du modèle est cohérente, pas un coup de chance. C’est exactement ainsi que l’on compare équitablement deux modèles candidats — ajustez-les tous deux via fit_resamples() et comparez leurs moyennes validées par validation croisée.
NotePourquoi faire la validation croisée à l’intérieur de l’entraînement ?
La validation croisée vit entièrement à l’intérieur du jeu d’entraînement — c’est ainsi que vous prenez vos décisions de modélisation sans jamais toucher au jeu de test. Le jeu de test mis de côté reste intact pour le score final unique (last_fit(sales_wf, split) réalise l’ajustement final et le test en un seul appel). Validez pour décider ; testez une seule fois pour rapporter.
Étape 9 — Un avant-goût du réglage
Certains modèles ont des hyperparamètres — des réglages que vous ne pouvez pas lire dans les données et que vous devez choisir, comme la force de pénalité dans la régression lasso/ridge. tidymodels les choisit honnêtement : marquez un paramètre avec tune(), construisez une petite grille de valeurs candidates, et évaluez-en chacune par validation croisée, en gardant la meilleure.
library(parsnip)library(tune)library(rsample)# A penalized model with the penalty left to be tunedglmnet_spec <-linear_reg(penalty =tune(), mixture =1) |>set_engine("glmnet")tune_wf <-workflow() |>add_recipe(sales_rec) |>add_model(glmnet_spec)grid <-grid_regular(penalty(range =c(-3, 0)), levels =20)set.seed(789)tuned <-tune_grid(tune_wf, resamples = folds, grid = grid)select_best(tuned, metric ="rmse") # the penalty with the lowest CV error
Vous construisez la grille, tune_grid() valide par validation croisée chaque candidate, et select_best() rend la gagnante — que vous passez ensuite à finalize_workflow() et last_fit(). Le traitement complet (lasso, ridge, elastic net) se trouve dans régression pénalisée ; ici il suffit de savoir que le workflow que vous avez déjà appris est le workflow de réglage, avec un paramètre fixé à tune().
Vous venez de caret ? La table de correspondance
Si vous avez appris la modélisation prédictive avec caret, les idées se transposent directement — seuls les verbes changent. tidymodels découpe le train() monolithique de caret en petits morceaux composables :
caret
tidymodels
createDataPartition()
rsample::initial_split() + training()/testing()
preProcess()
un recipes::recipe() avec step_*()
train(..., method = "lm")
parsnip::linear_reg() \|> set_engine("lm") dans un workflow()
trainControl(method = "cv")
rsample::vfold_cv() + tune::fit_resamples()
train(..., tuneGrid = ...)
tune::tune_grid() + dials::grid_regular()
predict(fit, newdata)
predict(fit, new_data) (colonne soignée .pred)
postResample() / RMSE()
yardstick::metric_set(rmse, rsq, mae)
Le gain : chaque morceau est réutilisable et interchangeable. Changez la spécification du modèle et rien d’autre ne change — le même découpage, le même recipe, la même validation croisée et les mêmes métriques se reportent à une forêt aléatoire ou à xgboost.
NoteLes maths derrière le RMSE, le R² et le MAE (optionnel)
Pour \(n\) prédictions de test \(\hat{y}_i\) comparées aux vraies valeurs \(y_i\) (de moyenne \(\bar{y}\)) :
Le RMSE met les erreurs au carré (de sorte que les gros écarts dominent) puis prend une racine carrée pour revenir aux unités de la variable cible ; le MAE fait la moyenne des erreurs absolues brutes (chaque écart pesé de manière égale). Le \(R^2\) compare l’erreur résiduelle de votre modèle \(SS_{res}\) à l’erreur consistant à prédire simplement la moyenne \(SS_{tot}\) — la fraction de variance que vous avez retirée. Calculé sur le jeu de test, le \(R^2\) peut même devenir légèrement négatif si le modèle prédit moins bien que la moyenne (un signal d’alerte utile).
🟢 Avec un agent IA
Bloqué sur vos propres données ? Demandez à Prova« construis-moi un workflow tidymodels qui prédit cette variable cible et me dit à quel point il est précis » — elle répond avec du code R que vous pouvez exécuter sur votre jeu de données, puis vous aide à lire le RMSE/R²/MAE et à décider si vous pouvez faire confiance au modèle. The runtime is the judge.Demander à Prova →
Problèmes courants
Votre R² est superbe à l’entraînement mais catastrophique au test. C’est du surapprentissage — le modèle a mémorisé les données d’entraînement. Rapportez toujours les métriques sur le jeu de test mis de côté (ou issues de la validation croisée), jamais sur les données sur lesquelles vous avez ajusté.
« variable lengths differ » ou une colonne que le recipe n’attendait pas. Le recipe apprend les rôles des colonnes à partir des données que vous passez à recipe(). Assurez-vous que new_data dans predict() a les mêmes colonnes de prédicteurs qu’à l’entraînement — le workflow réapplique le recipe, donc ne pré-transformez pas le jeu de test vous-même.
Vous avez normalisé tout le jeu de données avant de le découper. Cela fait fuiter de l’information du jeu de test vers l’entraînement (les moyennes/écarts-types ont vu les lignes de test). Placez step_normalize()à l’intérieur du recipe et découpez d’abord — le recipe apprend alors les statistiques à partir du seul entraînement et les applique au test.
Le moteur glmnet génère une erreur à propos d’un unique prédicteur / de facteurs. Les moteurs pénalisés ont besoin d’une matrice de modèle numérique ; ajoutez step_dummy(all_nominal_predictors()) au recipe pour que les facteurs soient encodés avant le modèle.
Questions fréquentes
NoteQu’est-ce que tidymodels en R ?
tidymodels est une collection de packages R — rsample, recipes, parsnip, workflows, tune, yardstick et compagnie — qui partagent une même grammaire cohérente pour la modélisation prédictive : découpez vos données, prétraitez-les, spécifiez un modèle, ajustez, prédisez et évaluez. C’est le successeur moderne et modulaire du package caret et il fonctionne de la même façon que vous ajustiez un modèle linéaire ou un arbre à gradient boosté.
Notecaret ou tidymodels — lequel utiliser ?
Les deux font de la modélisation prédictive, mais caret condense tout dans une seule fonction train(), tandis que tidymodels découpe le travail en petits morceaux composables (recipe, spécification du modèle, workflow, tune_grid). caret reste très bien pour un travail rapide, mais tidymodels est activement développé, plus flexible, et constitue le standard actuel — et passer de l’un à l’autre est surtout un changement de vocabulaire (voyez la table ci-dessus).
NoteComment faire un découpage entraînement/test en R ?
Utilisez rsample::initial_split(data, prop = 0.80, strata = outcome), puis training(split) et testing(split) pour obtenir les deux jeux. L’argument strata maintient la distribution de la variable cible similaire dans les deux, et fixer set.seed() au préalable rend le découpage reproductible. Gardez le jeu de test de côté jusqu’à la toute fin.
NoteComment faire une validation croisée d’un modèle en R ?
Créez les blocs avec rsample::vfold_cv(train_data, v = 10), puis exécutez votre workflow sur chacun avec tune::fit_resamples(wf, resamples = folds) et faites la moyenne des résultats avec collect_metrics(). La validation croisée donne une estimation de performance bien plus stable qu’un découpage unique — et elle vous permet de comparer des modèles sans jamais toucher au jeu de test.
NoteQuelle est la différence entre RMSE, R² et MAE ?
Les trois évaluent une régression. RMSE et MAE sont l’erreur de prédiction typique dans les unités propres de la variable cible (le RMSE pénalise davantage les gros écarts ; le MAE pèse chaque écart de manière égale), donc plus c’est bas, mieux c’est. R² est la part de variance que le modèle explique, de 0 à 1, donc plus c’est haut, mieux c’est. Rapportez au moins une métrique d’erreur (RMSE ou MAE) et le R² pour avoir une image complète.
Testez vos connaissances
ImportantExercice : prédire la valeur médiane des logements avec les données Boston
Construisez le workflow complet sur le jeu de données Boston (MASS), en prédisant medv (valeur médiane des logements) à partir de toutes les autres variables. Découpez 75/25 stratifié sur medv, normalisez les prédicteurs dans un recipe, ajustez un workflow linear_reg(), et rapportez le RMSE et le R² sur le jeu de test.
AstuceIndice
La forme est identique à celle de la leçon — il suffit de remplacer les données et la formule medv ~ .. Vous aurez besoin de rsample, recipes, parsnip, workflows et yardstick.
Vous obtiendrez un RMSE d’environ 4–5 (en milliers de dollars) et un R² autour de 0.7 — une base linéaire correcte que vous pourriez maintenant tenter de battre avec des modèles pénalisés ou à base d’arbres, via le même workflow.
Vérification rapide. Un collègue normalise tout le jeu de données, puis le découpe en entraînement et test, et est ravi d’un R² de test de 0.95. Pourquoi devriez-vous être méfiant, et quelle est la correction ?
NoteAfficher la réponse
Normaliser avant de découper fait fuiter l’information du jeu de test vers l’entraînement — les moyennes et les écarts-types ont été calculés en utilisant les lignes de test, donc le jeu de test n’est pas vraiment inédit, et le score est optimiste. La correction : découpez d’abord, puis placez step_normalize()à l’intérieur du recipe, pour que les statistiques soient apprises à partir des seules données d’entraînement et simplement appliquées au jeu de test.
Conclusion
Vous avez construit l’arc tidymodels complet sur un vrai problème de prédiction : découper les données avec rsample, déclarer le prétraitement dans un recipe, spécifier un modèle avec parsnip, les regrouper dans un workflow, ajuster sur l’entraînement, prédire sur le jeu de test mis de côté, et mesurer une performance honnête avec yardstick — puis transformer un découpage unique et bruité en validation croisée et avoir un aperçu du réglage. C’est la colonne vertébrale que réutilise chaque leçon supervisée de ce pilier ; à partir d’ici, vous ne ferez jamais que substituer la spécification du modèle.
Prouvez que vous savez le faire. Maîtrisez toute la série Modélisation prédictive en R — suivez votre parcours, construisez des projets et obtenez un certificat.
Cette leçon est reproductible : chaque métrique et chaque 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.
Pour aller plus loin
Kuhn, M., & Silge, J. Tidy Modeling with R — la référence canonique de tidymodels (dépense des données, workflows, rééchantillonnage, réglage). Gratuit en ligne (CC BY-NC-SA).
@online{2026,
author = {},
title = {tidymodels en R : construire, valider et prédire},
date = {2026-06-25},
url = {https://www.datanovia.com/learn/machine-learning/foundations/build-models-with-tidymodels},
langid = {fr}
}