Nothing
#' @title Hyperparameter Optimization with Successive Halving
#'
#' @name mlr_optimizers_successive_halving
#' @templateVar id successive_halving
#'
#' @description
#' Optimizer using the Successive Halving Algorithm (SHA).
#' SHA is initialized with the number of starting configurations `n`, the proportion of configurations discarded in each stage `eta`, and the minimum `r_min` and maximum `_max` budget of a single evaluation.
#' The algorithm starts by sampling `n` random configurations and allocating the minimum budget `r_min` to them.
#' The configurations are evaluated and `1 / eta` of the worst-performing configurations are discarded.
#' The remaining configurations are promoted to the next stage and evaluated on a larger budget.
#' The following table is the stage layout for `eta = 2`, `r_min = 1` and `r_max = 8`.
#'
#' | `i` | `n_i` | `r_i` |
#' | --: | ----: | ----: |
#' | 0 | 8 | 1 |
#' | 1 | 4 | 2 |
#' | 2 | 2 | 4 |
#' | 3 | 1 | 8 |
#'
#' `i` is the stage number, `n_i` is the number of configurations and `r_i` is the budget allocated to a single configuration.
#'
#' The number of stages is calculated so that each stage consumes approximately the same budget.
#' This sometimes results in the minimum budget having to be slightly adjusted by the algorithm.
#'
#' @section Resources:
#' The [gallery](https://mlr-org.com/gallery-all-optimization.html) features a collection of case studies and demos about optimization.
#'
#' * [Tune](https://mlr-org.com/gallery/series/2023-01-15-hyperband-xgboost/) the hyperparameters of XGBoost with Hyperband (Hyperband can be easily swapped with SHA).
#' * Use data [subsampling](https://mlr-org.com/gallery/series/2023-01-16-hyperband-subsampling/) and Hyperband to optimize a support vector machine.
#'
#' @template section_dictionary_optimizers
#'
#' @section Parameters:
#' \describe{
#' \item{`n`}{`integer(1)`\cr
#' Number of configurations in the base stage.}
#' \item{`eta`}{`numeric(1)`\cr
#' With every stage, the budget is increased by a factor of `eta` and only the best `1 / eta` configurations are promoted to the next stage.
#' Non-integer values are supported, but `eta` is not allowed to be less or equal to 1.}
#' \item{`sampler`}{[paradox::Sampler]\cr
#' Object defining how the samples of the parameter space should be drawn.
#' The default is uniform sampling.}
#' \item{`repetitions`}{`integer(1)`\cr
#' If `1` (default), optimization is stopped once all stages are evaluated.
#' Otherwise, optimization is stopped after `repetitions` runs of SHA.
#' The [bbotk::Terminator] might stop the optimization before all repetitions are executed.}
#' \item{`adjust_minimum_budget`}{`logical(1)`\cr
#' If `TRUE`, the minimum budget is increased so that the last stage uses the maximum budget defined in the search space.}
#' }
#'
#' @section Archive:
#' The [bbotk::Archive] holds the following additional columns that are specific to SHA:
#' * `stage` (`integer(1))`\cr
#' Stage index. Starts counting at 0.
#' * `repetition` (`integer(1))`\cr
#' Repetition index. Start counting at 1.
#'
#' @template section_custom_sampler
#' @template section_progress_bars
#' @template section_logging
#'
#' @source
#' `r format_bib("jamieson_2016")`
#'
#' @export
#' @template example_optimizer
OptimizerBatchSuccessiveHalving = R6Class("OptimizerBatchSuccessiveHalving",
inherit = OptimizerBatch,
public = list(
#' @description
#' Creates a new instance of this [R6][R6::R6Class] class.
initialize = function() {
param_set = ps(
n = p_int(lower = 1, default = 16),
eta = p_dbl(lower = 1.0001, default = 2),
sampler = p_uty(custom_check = function(x) check_r6(x, "Sampler", null.ok = TRUE)),
repetitions = p_int(lower = 1L, default = 1, special_vals = list(Inf)),
adjust_minimum_budget = p_lgl(default = FALSE)
)
param_set$values = list(n = 16L, eta = 2L, sampler = NULL, repetitions = 1, adjust_minimum_budget = FALSE)
super$initialize(
param_classes = c("ParamLgl", "ParamInt", "ParamDbl", "ParamFct"),
param_set = param_set,
properties = c("dependencies", "single-crit", "multi-crit"),
packages = "mlr3hyperband",
label = "Successive Halving",
man = "mlr3hyperband::mlr_optimizers_successive_halving"
)
}
),
private = list(
.optimize = function(inst) {
pars = self$param_set$values
n = pars$n
eta = pars$eta
sampler = pars$sampler
search_space = inst$search_space
budget_id = search_space$ids(tags = "budget")
# check budget
if (length(budget_id) != 1) stopf("Exactly one parameter must be tagged with 'budget'")
assert_choice(search_space$class[[budget_id]], c("ParamInt", "ParamDbl"))
# required for calculation of hypervolume
if (inst$archive$codomain$length > 1) require_namespaces("emoa")
# sampler
search_space_sampler = search_space$clone()$subset(setdiff(search_space$ids(), budget_id))
if (is.null(sampler)) {
sampler = SamplerUnif$new(search_space_sampler)
} else {
assert_set_equal(sampler$param_set$ids(), search_space_sampler$ids())
}
# r_min is the budget of a single configuration in the first stage
# r_max is the maximum budget of a single configuration in the last stage
# the internal budget is rescaled to a minimum budget of 1
# for this, the budget is divided by r_min
# the budget is transformed to the original scale before passing it to the objective function
r_max = search_space$upper[[budget_id]]
r_min = search_space$lower[[budget_id]]
# maximum budget of a single configuration in the last stage (scaled)
r = r_max / r_min
# number of stages if each configuration in the first stage uses the minimum budget
# and each configuration in the last stage uses no more than maximum budget
sr = floor(log(r, eta))
# reduce number of stages if n < r_max so that
# the last stages evaluates at least one configuration
sn = floor(log(n, eta))
# s_max + 1 is the number of stages
s_max = min(sr, sn)
# increase r_min so that the last stage uses the maximum budget
if (pars$adjust_minimum_budget) r_min = r * eta^-s_max
repetition = 1
while (repetition <= pars$repetitions) {
# iterate stages
for (i in 0:s_max) {
# number of configurations in stage
ni = floor(n * eta^(-i))
# budget of a single configuration in stage
ri = r_min * eta^i
if (search_space$class[[budget_id]] == "ParamInt") ri = round(ri)
if (i == 0) {
xdt = sampler$sample(ni)$data
} else {
# get performances of previous stage
archive = inst$archive
xdt = if (archive$codomain$length == 1) {
archive$best(batch = archive$n_batch, n_select = ni)
} else {
archive$nds_selection(batch = archive$n_batch, n_select = ni)
}
xdt = xdt[, archive$cols_x, with = FALSE]
}
# increase budget and stage
set(xdt, j = budget_id, value = ri)
set(xdt, j = "stage", value = i)
set(xdt, j = "repetition", value = repetition)
inst$eval_batch(xdt)
}
repetition = repetition + 1
}
}
)
)
#' @include aaa.R
optimizers[["successive_halving"]] = OptimizerBatchSuccessiveHalving
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.