tune tune_grid() in R: Hyperparameter Search on Resamples
The tune tune_grid() function in R fits a tidymodels workflow at every point of a hyperparameter grid against a resample set, returning a tibble of per-resample metrics you can rank with show_best() and feed into finalize_workflow().
tune_grid(wf, resamples = folds) # default 10-point Latin hypercube tune_grid(wf, resamples = folds, grid = 20) # 20-point space-filling grid tune_grid(wf, resamples = folds, grid = my_grid) # user grid as a tibble tune_grid(wf, resamples = folds, metrics = mset) # custom metric set tune_grid(wf, resamples = folds, control = ctrl) # save predictions, parallel tune_grid(model_spec, recipe, resamples = folds) # spec + recipe shortcut tune_grid(wf, resamples = folds, param_info = params) # custom parameter ranges tune_grid(wf, resamples = folds, eval_time = c(5, 10)) # survival metrics at times
Need explanation? Read on for examples and pitfalls.
What tune_grid() does in one sentence
tune_grid() fits a workflow once per candidate per resample. You hand it a workflow that contains at least one parameter marked with tune(), a resample object such as a v-fold cross-validation set, and either a grid size or a tibble of candidate values. The function loops every candidate across every fold, scores each fit with the chosen metric set, and returns a tune_results object that downstream helpers like collect_metrics(), show_best(), and select_best() know how to read.
This is the workhorse of tidymodels hyperparameter search. It is deterministic given a seed, parallel-friendly, and decoupled from any specific model engine, so the same call shape works for elastic net, xgboost, random forest, kknn, or any spec that exposes tunable arguments.
Set up a tunable workflow
You need three pieces before calling tune_grid(). A model spec with tune() placeholders, a preprocessing recipe or formula, and a resample object that splits the training data.
The recipe below scales numeric predictors and dummies the factors, which lets the same workflow drive a regularized regression. Two arguments, penalty and mixture, are flagged with tune() so tune_grid() knows which knobs to vary.
tune_grid() syntax and arguments
The signature is small but the defaults matter.
| Argument | Description |
|---|---|
object |
A workflow or a model spec. If a spec, pass preprocessor next. |
resamples |
An rset such as vfold_cv(), bootstraps(), or validation_split(). |
grid |
Integer = space-filling Latin hypercube of that size; tibble = explicit candidates. |
metrics |
A metric_set(); defaults are RMSE+R-squared (regression) or accuracy+ROC AUC (classification). |
param_info |
A parameters() object when defaults need updating, e.g. penalty(range = c(-4, 0)). |
control |
Returns of control_grid(). Toggles save_pred, save_workflow, verbose, parallel options. |
grid = 25 builds a 25-row space-filling design from the parameter set. A tibble lets you script grid_regular(), grid_random(), or a hand-crafted set of candidates.Examples by use case
Pass an integer first, then move to explicit grids as you learn the model. Two-knob regularized regression is the cleanest demo.
Once results are back, collect_metrics() averages across folds and show_best() ranks candidates by the chosen metric.
For a regular grid, build it explicitly so reviewers can see what was tried.
Finalize the workflow with the winning row and fit on the full training set.
tune_grid() versus tune_bayes() and fit_resamples()
Pick by search style, not by sophistication.
| Function | When to reach for it |
|---|---|
tune_grid() |
A fixed candidate set: regular grid, Latin hypercube, or scripted tibble. Easiest to reason about, parallelizes perfectly. |
tune_bayes() |
Continuous parameters with expensive fits. Uses a Gaussian process surrogate to propose new candidates iteratively. |
fit_resamples() |
No tuning at all; just resample a fixed workflow to score it. Same return type as tune_grid(), so downstream code is identical. |
finetune::tune_race_anova() |
A grid run that drops losing candidates after a few folds. Useful when one fold takes minutes. |
The fastest workflow is usually: prototype with tune_grid(grid = 10), then either refine the ranges and rerun, or switch to tune_bayes() once you trust the search space.
Common pitfalls
Three errors account for most failed runs.
- No tune() placeholder. Calling tune_grid() on a workflow with no tunable arguments throws
! No tuning parameters have been detected. Fix by addingtune()to at least one model argument. - Grid columns do not match parameter ids. A custom grid must use the names returned by
extract_parameter_set_dials(wf). If your spec usestune("lambda"), the grid column has to belambda, notpenalty. - Metric direction confusion.
show_best()sorts by the metric's default direction (lower for RMSE, higher for ROC AUC). Pass the metric name explicitly so the ranking matches what you intend.
tune_results carry per-resample notes. Always inspect collect_notes(res) after a run; engine warnings such as glmnet convergence messages live there and never raise.Try it yourself
Try it: Tune a kknn classifier on the iris data over a 5-point grid of neighbors. Use 5-fold cross-validation, score by accuracy, and identify the best k.
Click to reveal solution
Explanation: A scripted tibble with column name neighbors matches the tune() placeholder, so tune_grid() knows where each candidate goes. Five folds plus five candidates produce 25 fits, and show_best() ranks them by mean accuracy.
Related tidymodels functions
Most tune_grid() calls live inside a short chain of helpers.
tune_bayes()for surrogate-driven search when fits are expensive.fit_resamples()to score a workflow that has no tunable arguments.last_fit()to refit the finalized workflow on the full train and score the test set.finalize_workflow()andselect_best()to lock in the winning hyperparameters.grid_regular(),grid_random(),grid_space_filling()for candidate construction.control_grid()to toggle prediction saving, parallel backends, and progress output.
External reference: the official tune package documentation at tune.tidymodels.org.
FAQ
How do I make tune_grid() run in parallel?
Register a parallel backend before the call. With the doFuture package, library(doFuture); plan(multisession, workers = 4) is enough. Inside control_grid(), leave parallel_over = "resamples" for most workflows; switch to "everything" only when both folds and candidates are large. tune_grid() detects the backend automatically and prints i Creating pre-processing data to finalize unknown parameter ranges once before workers start.
What is the difference between grid as an integer and grid as a tibble?
An integer triggers grid_space_filling() (Latin hypercube on the parameter set), so coverage is good with few points. A tibble lets you control exactly which combinations get scored, which matters when you want a regular grid for plots or when only a few discrete settings are valid. Use an integer to prototype, a tibble once you know what to vary.
Why does select_best() pick a different model than show_best()?
It does not, but tie-breaking can look that way. show_best() sorts by the mean metric, ascending or descending by metric direction. select_best() returns the single top row by that same rule. When two candidates have nearly identical means, std_err and config name decide ordering, so always include metric = "..." explicitly to avoid surprises.
Can I tune a recipe step and a model argument at the same time?
Yes. Mark a recipe step argument with tune() (for example, step_pca(num_comp = tune())) and any model argument with tune() as usual. extract_parameter_set_dials(wf) shows the combined set, and param_info lets you update the recipe-side range. The grid then has columns for both, scored jointly per candidate.
How do I save predictions from every candidate?
Set control = control_grid(save_pred = TRUE). The resulting tune_results object carries a .predictions list column; collect_predictions(res) flattens it into one tidy tibble with .row, .pred, truth, and the candidate identifier. Useful for residual diagnostics, calibration plots, and stacking via the stacks package.