Compare many models with workflowsets
In Lesson 6 you tuned a single decision tree on the lender's loan book and confirmed it once on a sealed test set. But a tuned tree is only as good as the fact that it was a tree. In a real project you rarely commit to one model up front. You line up a few honest contenders, a plain logistic regression, that decision tree, a random forest, and you let them race.
The trick is making the race fair: every contender judged on the exact same folds, by the exact same metric, with no contender peeking at data the others did not. The chart below is the finish line, three models, one ROC AUC bar each, the tallest one wins. This lesson is how you produce that chart with one tool instead of three copy-pasted scripts.
By the end of this lesson you will be able to:
- Explain why a fair model comparison needs the same resamples and the same metric for every contender
- Build a workflow set that crosses your preprocessors with your model specs into one object
- Run the whole bake-off across shared folds in one call, rank the leaderboard, and finalize the winner
Prerequisites: you can run R and use the |> pipe, and from earlier lessons you can bundle a recipe and model into a workflow (Lesson 3), score across cross-validation folds (Lesson 4), [measure with roc_auc](Measure-with-yardstick.html) (Lesson 5), and tune a model honestly (Lesson 6). We reuse that exact loan book here.
One race, same rules for everyone
Here is the tempting, wrong way to compare three models: fit each one however is convenient, glance at a number for each, keep the biggest. It breaks in two quiet ways. First, if each model is scored on a different random split of the data, you are comparing how lucky each split was, not how good each model is. Second, if you score a model on the rows it trained on, the most flexible model always looks best because it can memorize, which is the overfitting trap you have met all course.
The fix is the same discipline you have built up lesson by lesson: judge every contender on one shared set of cross-validation folds with one shared metric. Same questions, same judges, same answer key. That is the entire idea behind a workflow set.
So we start exactly where Lesson 6 ended, the same loan book, the same split, the same five folds. Run this once to rebuild it in this session:
Three honest candidates
A bake-off needs entries. We pick three models that fail in different ways, so the comparison is meaningful rather than three flavors of the same thing:
- Logistic regression, the simple linear baseline. If a fancy model cannot beat this, the complexity is not earning its keep.
- A decision tree at
tree_depth = 4, the moderately shallow tree Lesson 6 tuned to. One tree, interpretable, a little unstable. - A random forest, hundreds of trees averaged. More flexible, harder to interpret, usually steadier.
All three can share one recipe, the leak-free preprocessor from Lesson 1: turn the home factor into dummy columns and put the numeric predictors on a common scale. The recipe learns its scaling from each fold's training rows only, so nothing leaks.
Three model specs, one recipe. Next we hand them to a single object that will keep them all on the same footing.
Cross the recipe with the models
A workflow set is the cross product of your preprocessors and your models. With \(P\) preprocessors and \(M\) model specs you get \(P \times M\) ready-to-fit workflows, each a recipe paired with a model. Here \(P = 1\) and \(M = 3\), so the set holds three workflows; swap in two recipes and you would get six, every combination, without writing them out by hand.
workflow_set builds that table for you. You pass it a named list of preprocessors and a named list of models. Fill in the function name.
Show answer
library(workflowsets)
all_wf <- workflow_set(
preproc = list(base = rec),
models = list(logistic = lr_spec, tree = tree_spec, forest = rf_spec)
)
all_wf
#> # A workflow set/tibble: 3 x 4
#> wflow_id info option result
#> <chr> <list> <list> <list>
#> 1 base_logistic <tibble [1 x 4]> <opts[0]> <list [0]>
#> 2 base_tree <tibble [1 x 4]> <opts[0]> <list [0]>
#> 3 base_forest <tibble [1 x 4]> <opts[0]> <list [0]>Map one scorer over every workflow
You already know how to resample one workflow: in Lesson 4, fit_resamples fit a workflow across the folds and recorded its metrics. workflow_map does that for every workflow in the set, on the same folds, with the same metrics, in a single call. The five-step picture below is the whole tournament.
You hand workflow_map the set, the name of the function to run on each workflow ("fit_resamples"), a seed so the folds are reused identically, and the same resamples and metrics arguments that function expects.
The result column now reads <rsmp[+]>: every workflow has been resampled and carries its scores. Three models, fifteen little fits (three workflows times five folds), one call. No copy-paste, and impossible to accidentally hand one model a different split.
What makes the bake-off fair?
You want to crown the best of your three models honestly. Which setup makes the comparison fair?
Rank the contenders
The scores are in; now read them. rank_results flattens the whole set into one tidy leaderboard, one row per model per metric, sorted by the metric you name. autoplot draws the same thing as a ranked plot.
Read the leaderboard like a results table, not a verdict carved in stone. The random forest tops it, the logistic regression is a close second, and the single tree trails. The board below is that result, the report-ready view of those numbers:
std_err before you celebrate. The forest leads the logistic regression by about 0.03 ROC AUC, and each has a standard error near 0.03. On only 179 training rows the bands overlap, so the lead is real but slim. A ranking is the start of a decision, not the end: weigh it against interpretability and cost. Here the simple logistic regression is competitive enough that many lenders would prefer it for the explanation it gives a declined applicant.Which model do you crown?
On your leaderboard the random forest has the highest cross-validated ROC AUC, while the decision tree fits the training rows a touch harder. Which model do you finalize and take to the sealed test set?
Crown the winner and confirm
You picked the random forest. Two moves finish the job. extract_workflow pulls that workflow back out of the set by its id (base_forest). Then last_fit does the honest finish you learned in Lesson 6: it trains the winner on the full training set and scores it once on the test set you sealed away in step 2. Fill in the function that does that final fit.
Show answer
final_wf <- extract_workflow(all_wf, id = "base_forest")
final_fit <- last_fit(final_wf, split, metrics = metric_set(roc_auc, accuracy))
collect_metrics(final_fit)
#> # A tibble: 2 x 4
#> .metric .estimator .estimate .config
#> <chr> <chr> <dbl> <chr>
#> 1 accuracy binary 0.672 pre0_mod0_post0
#> 2 roc_auc binary 0.668 pre0_mod0_post0References
A few authoritative places to take this further:
- workflowsets package documentation (tidymodels) - the reference for
workflow_set(),workflow_map(),rank_results(), andextract_workflow(). - Tidy Modeling with R, ch. 15: Screening many models - Kuhn and Silge on building and comparing whole sets of model-and-recipe combinations.
- Get Started with tidymodels - the official walk-through of the pieces (recipes, parsnip, workflows, resampling, tuning) this lesson composes.
- An Introduction to Statistical Learning, ch. 5 (free PDF) - resampling and model selection, the statistics under the bake-off.
Lesson 7 complete
You ran a real model bake-off. You stood up three honest contenders on a shared recipe, crossed them into one workflow set, scored every one of them across the same five folds with the same metrics in a single workflow_map call, read the ranked leaderboard, crowned the winner on its cross-validated score (while keeping an eye on the slim margin and interpretability), and confirmed it once on the test set you had sealed all along.
That completes the tidymodels course. You can now take a modeling problem from a raw data frame to a defensible chosen model: a leak-free recipe (Lesson 1), a model spec you can swap engines on (Lesson 2), the two bundled into a workflow (Lesson 3), honest scores from resampling (Lesson 4), the right metric to read them (Lesson 5), tuned hyperparameters (Lesson 6), and a fair comparison that picks the winner (here). Head back to the course page to claim your certificate and see where this fits in the Data Scientist track.