
Nested cross-validation with Bayesian optimization inside
Source:R/nested-tune-bayes.R
nested_tune_bayes.Rdnested_tune_bayes() estimates how well a model tuned with
tune::tune_bayes() performs on new data. It runs the loop of
nested_tune_grid(), with a Bayesian search on each outer fold's inner
resamples.
Usage
nested_tune_bayes(object, ...)
# Default S3 method
nested_tune_bayes(object, ...)
# S3 method for class 'model_spec'
nested_tune_bayes(
object,
preprocessor,
resamples,
...,
iter = 10,
param_info = NULL,
metrics = NULL,
event_level = "first",
eval_time = NULL,
objective = tune::exp_improve(),
initial = 5,
select = selection_rule()
)
# S3 method for class 'workflow'
nested_tune_bayes(
object,
resamples,
...,
iter = 10,
param_info = NULL,
metrics = NULL,
event_level = "first",
eval_time = NULL,
objective = tune::exp_improve(),
initial = 5,
select = selection_rule()
)Arguments
- object
A
workflows::workflow()with at least one parameter marked withtune::tune(), or a parsnip model specification.- ...
On the generic, the arguments of the method. On a method, pass
control = tune::control_bayes()here to set the control. Name every argument after....- preprocessor
A formula or recipe, when
objectis a model specification.- resamples
A nested resampling design from
nested_resamples()orrsample::nested_cv().- iter
The maximum number of search iterations.
- param_info
A
dials::parameters()object orNULL. If none is given, a parameters set is derived from other arguments. Passing this argument can be useful when parameter ranges need to be customized.- metrics
A
yardstick::metric_set(), orNULLfor tune's default set. Under every rule but"desirability", the first metric selects each fold's candidate, so put the metric you report first. The Bayes, race and annealing searches also steer on it. Every metric is scored on the outer folds.- event_level
"first"(the default) or"second". It names which level of a two-class outcome is the event. It applies to the inner tuning run and the outer scoring fit alike.- eval_time
A numeric vector of evaluation times for a censored regression model, or
NULL(the default) to leave the choice to tune. Times that are not numeric, empty, missing, negative or not finite are an error. Zero, repeated and unordered times are passed on to tune.- objective
An acquisition function from tune. It decides which candidate the Gaussian process proposes next:
tune::exp_improve()(the default),tune::prob_improve()ortune::conf_bound().- initial
The number of candidates each fold scores before the first iteration, a whole number of at least 2. A
tune_resultsobject is an error, because one tuning run cannot serve every outer fold.- select
A
selection_rule()that each outer fold uses to pick its candidate. The default istune::select_best()on the first metric.
Value
A nested_results, as nested_tune_grid() describes. Each fold's
.inner_metrics has an .iter column. It is 0 for the initial
candidates and i for the candidate the i-th iteration proposed.
There is no grid attribute.
Details
Each outer fold scores initial candidates from a space-filling design. A
Gaussian process then proposes iter more candidates, one at a time. The
fold picks a candidate with select, fits it on its analysis set and
scores it on the rows it held out. nested_tune_grid() documents what the
loop functions share, such as the design, what the estimate describes, the
seeds, failed folds and parallel execution.
iter = 0 scores the initial candidates and proposes nothing. tune stops a
fold's search early when no new candidate remains, or after no_improve
iterations in a row without improvement (ten by default). The fold then
keeps the candidates it scored.
tune::tune_bayes() does not accept a parameter range that depends on the
data, such as mtry(). Set its range in param_info before the call, or
every fold fails.
Differences from calling tune directly
A tune::control_bayes() passed through ... as control reaches the
inner tune_bayes() in every fold, and nested_final_fit() runs again
with it. For example, control = control_bayes(no_improve = 5) stops each
search sooner. The result keeps the control that ran, without seed, as
extract_procedure(res)$control. Its settings differ from a direct call as
follows.
Forced: allow_par, seed. allow_par is FALSE, because the outer
folds carry the parallelism. seed is set to the fold's tuning seed. Left
to tune, it is drawn from the random stream, so the proposals depend on
how much of the stream tune used before it.
Settable as its own argument: event_level. Set the level with the
event_level argument, as on nested_tune_grid(). A control left at the
default level, "first", takes the argument's level. A control set to
another level than the argument's is an error.
iter, initial, objective and eval_time are arguments of tune_bayes(), not control settings.
Refused: none. No setting is refused on its own. A control of another class is an error.
Passed through: no_improve, uncertain, time_limit, verbose,
verbose_iter, save_gp_scoring, pkgs, parallel_over,
workflow_size. Each reaches tune_bayes() as given:
no_improveanduncertainact on each fold's search as in a direct call.time_limitstops a search by the clock, so two runs with the same seed can differ from one machine to another.verboseandverbose_iterprint from each fold, and only a serial run shows the output.save_gp_scoringwrites its files to the temporary directory of the process that tuned the fold.
pkgs, parallel_over and workflow_size behave as on
nested_tune_grid().
Kept from the outer fit: save_pred, extract. As on
nested_tune_grid(), the outer fit's predictions and extracts are kept as
.predictions and .extracts when you ask for them. The inner search's
own are still discarded.
Not returned: save_workflow. The nested run does not keep the inner
tuning results, so this setting returns nothing there.
nested_final_fit() keeps its own tuning run as $tuning, which holds
what the setting saved.
Inert: backend_options. Options for a parallel backend, with no
backend to reach at allow_par = FALSE.
Examples
data(mtcars)
rec <- recipes::recipe(mpg ~ ., data = mtcars) |>
recipes::step_pca(recipes::all_predictors(), num_comp = tune::tune())
wf <- workflows::workflow(rec, parsnip::linear_reg())
set.seed(1)
folds <- nested_resamples(
mtcars,
outside = rsample::vfold_cv(v = 2),
inside = rsample::vfold_cv(v = 2)
)
set.seed(2)
res <- nested_tune_bayes(wf, folds, iter = 2, initial = 2)
collect_metrics(res)
#> # A tibble: 2 × 5
#> .metric .estimator mean n std_err
#> <chr> <chr> <dbl> <int> <dbl>
#> 1 rmse standard 3.06 2 0.539
#> 2 rsq standard 0.731 2 0.0853
# What the first fold searched: the initial candidates at `.iter` 0,
# then one proposal per iteration.
res$.inner_metrics[[1]]
#> # A tibble: 8 × 8
#> num_comp .metric .estimator mean n std_err .config .iter
#> <int> <chr> <chr> <dbl> <int> <dbl> <chr> <int>
#> 1 2 rmse standard 3.46 2 0.207 pre1_mod0_post0 0
#> 2 2 rsq standard 0.737 2 0.0434 pre1_mod0_post0 0
#> 3 4 rmse standard 3.24 4 0.362 pre2_mod0_post0 0
#> 4 4 rsq standard 0.693 4 0.00655 pre2_mod0_post0 0
#> 5 4 rmse standard 3.24 4 0.362 iter1 1
#> 6 4 rsq standard 0.693 4 0.00655 iter1 1
#> 7 3 rmse standard 3.37 2 0.292 iter2 2
#> 8 3 rsq standard 0.754 2 0.00240 iter2 2