The main goal of mlrMBO
is to optimize expensive
black-box functions by model-based optimization (aka
Bayesian optimization) and to provide a unified interface for different
optimization tasks and algorithmic MBO variants. Supported are, among
other things:
This vignette gives a brief overview of the features of
mlrMBO
. A more detailed documentation can be found on: https://mlrmbo.mlr-org.com/.
Installing mlrMBO
will also install and load the
dependencies mlr
, ParamHelpers
, and
smoof
. For this tutorial, you also need the additional
packages DiceKriging
and randomForest
.
library(mlrMBO)
smoof
.mlr
learner for surrogate model
(optional).mbo()
.As a simple example we minimize a cosine-like function with an initial design of 5 points and 10 sequential MBO iterations. Thus, the optimizer is allowed 15 evaluations of the objective function in total to approximate the optimum.
Instead of manually defining the objective, we use the smoof package which offers many toy and benchmark functions for optimization.
obj.fun = makeCosineMixtureFunction(1)
obj.fun = convertToMinimization(obj.fun)
print(obj.fun)
## Single-objective function
## Name: Cosine Mixture Function
## Description: no description
## Tags: single-objective, discontinuous, non-differentiable, separable, scalable, multimodal
## Noisy: FALSE
## Minimize: TRUE
## Constraints: TRUE
## Number of parameters: 1
## Type len Def Constr Req Tunable Trafo
## x numericvector 1 - -1 to 1 - TRUE -
## Global optimum objective value of -0.1000 at
## x
## 1 0
ggplot2::autoplot(obj.fun)
You are not limited to these test functions but can define arbitrary objective functions with smoof.
makeSingleObjectiveFunction(
name = "my_sphere",
fn = function(x) {
sum(x*x) + 7
},
par.set = makeParamSet(
makeNumericVectorParam("x", len = 2L, lower = -5, upper = 5)
),
minimize = TRUE
)
## Single-objective function
## Name: my_sphere
## Description: no description
## Tags:
## Noisy: FALSE
## Minimize: TRUE
## Constraints: TRUE
## Number of parameters: 2
## Type len Def Constr Req Tunable Trafo
## x numericvector 2 - -5 to 5 - TRUE -
Check ?smoof::makeSingleObjectiveFunction
for further
details.
Before MBO can really start, it needs a set of already evaluated
points - the inital design, as we have to initially learn our
first machine learning regression model to propose new points for
evaluation. If no design is given (i.e. design = NULL
),
mbo()
will use a Maximin Latin Hypercube
lhs::maximinLHS()
design with
n = 4 * getNumberOfParameters(obj.fun)
points. If the
design does not include function outcomes mbo()
will
evaluate the design first before starting with the MBO algorithm. In
this example we generate our own design.
des = generateDesign(n = 5, par.set = getParamSet(obj.fun), fun = lhs::randomLHS)
We will also precalculate the results:
des$y = apply(des, 1, obj.fun)
Note: mlrMBO uses y
as a default name
for the outcome of the objective function. This can be changed in the
control object.
We decide to use Kriging as our surrogate model because it has proven to be quite effective for numerical domains. The surrogate must be specified as a mlr regression learner:
surr.km = makeLearner("regr.km", predict.type = "se", covtype = "matern3_2", control = list(trace = FALSE))
Note: If no surrogate learner is defined, mbo()
automatically uses Kriging for a numerical domain, otherwise random
forest regression.
The MBOControl
object allows customization of the
optimization run and algorithmic behavior of MBO. It is created with
makeMBOControl()
, and can be modified with further
setter-functions.
For further customization there are the following functions:
setMBOControlTermination()
: It is obligatory to define
a termination criterion like the number of MBO iterations.setMBOControlInfill()
: It is recommended to set the
infill criterion. For learners that support
predict.type = "se"
the Confidence Bound "cb"
and the Expected Improvement "ei"
are a good choice.setMBOControlMultiPoint()
: Needed, in case you want to
evaluate more then just one point per MBO-Iteration you can control this
process here. This makes sense for parallelization.setMBOControlMultiObj()
: Needed, in case you want to
optimize a multi-objective target function.control = makeMBOControl()
control = setMBOControlTermination(control, iters = 10)
control = setMBOControlInfill(control, crit = makeMBOInfillCritEI())
Finally, we start the optimization process and print the result object. It contains the best best found solution and its corresponding objective value.
run = mbo(obj.fun, design = des, learner = surr.km, control = control, show.info = TRUE)
## [mbo] 1: x=0.142 : y = 0.0821 : 0.0 secs : infill_ei
## [mbo] 2: x=-0.0588 : y = -0.0568 : 0.0 secs : infill_ei
## [mbo] 3: x=-0.00787 : y = -0.0992 : 0.0 secs : infill_ei
## [mbo] 4: x=0.000914 : y = -0.1 : 0.0 secs : infill_ei
## [mbo] 5: x=-0.00241 : y = -0.0999 : 0.0 secs : infill_ei
## [mbo] 6: x=0.00421 : y = -0.0998 : 0.0 secs : infill_ei
## [mbo] 7: x=-0.000487 : y = -0.1 : 0.0 secs : infill_ei
## [mbo] 8: x=-0.501 : y = 0.253 : 0.0 secs : infill_ei
## [mbo] 9: x=0.0328 : y = -0.086 : 0.0 secs : infill_ei
## [mbo] 10: x=0.000168 : y = -0.1 : 0.0 secs : infill_ei
print(run)
## Recommended parameters:
## x=0.000168
## Objective: y = -0.100
##
## Optimization path
## 5 + 10 entries in total, displaying last 10 (or less):
## x y dob eol error.message exec.time ei
## 6 0.1424200502 0.08209419 1 NA <NA> 0 -3.210524e-02
## 7 -0.0588246736 -0.05680169 2 NA <NA> 0 -2.895199e-02
## 8 -0.0078732127 -0.09917425 3 NA <NA> 0 -4.124451e-03
## 9 0.0009141747 -0.09998885 4 NA <NA> 0 -1.466116e-03
## 10 -0.0024097515 -0.09992256 5 NA <NA> 0 -1.604071e-04
## 11 0.0042120161 -0.09976347 6 NA <NA> 0 -7.865440e-05
## 12 -0.0004870891 -0.09999684 7 NA <NA> 0 -3.759349e-05
## 13 -0.5011545567 0.25296936 8 NA <NA> 0 -1.285448e-05
## 14 0.0327576367 -0.08597808 9 NA <NA> 0 -1.576915e-05
## 15 0.0001680801 -0.09999962 10 NA <NA> 0 -1.056027e-05
## error.model train.time prop.type propose.time se mean
## 6 <NA> 0.119 infill_ei 0.065 6.915038e-02 -0.10692782
## 7 <NA> 0.007 infill_ei 0.070 7.141779e-02 -0.09923456
## 8 <NA> 0.006 infill_ei 0.063 1.174788e-02 -0.09714734
## 9 <NA> 0.006 infill_ei 0.066 1.928583e-03 -0.10030883
## 10 <NA> 0.006 infill_ei 0.064 4.588194e-04 -0.09994165
## 11 <NA> 0.009 infill_ei 0.064 4.002964e-04 -0.09978701
## 12 <NA> 0.008 infill_ei 0.068 8.531140e-05 -0.09999575
## 13 <NA> 0.006 infill_ei 0.064 1.710841e-01 0.48794187
## 14 <NA> 0.006 infill_ei 0.068 6.453564e-03 -0.08427947
## 15 <NA> 0.006 infill_ei 0.066 2.269187e-05 -0.09999971
For more insights into the MBO process, we can also start the
previous optimization with the function exampleRun()
instead of mbo()
. This augments the results of
mbo()
with additional information for plotting. Here, we
plot the optimization state at iterations 1, 2, and 10.
run = exampleRun(obj.fun, learner = surr.km, control = control, show.info = FALSE)
print(run)
## MBOExampleRun
## Number of parameters : 1
## Parameter names : x
## Parameter types : numericvector
## Global Opt (known) : -1.0000e-01
## Gap for best point : 3.1060e-06
## True points per dim. : 50
## Objectives : 1
## Points proposed per iter : 1
##
## Infill criterion : Expected improvement (ei)
## Direction of optimization : maximize
## Requirement : SE estimation
## Components : se, mean
## Parameters : se.threshold=1e-06
## Infill optimizer : focussearch
## Infill optimizer restarts : 3
## Final point by : best.true.y
## Learner : regr.km
## Learner settings:
## jitter=FALSE,covtype=matern3_2,control=trace=FALSE
## Recommended parameters:
## x=-0.000483
## Objective: y = -1.000e-01
plotExampleRun(run, iters = c(1L, 2L, 10L), pause = FALSE)
## Loading required package: ggplot2