# First release of mlrMBO - the toolbox for (Bayesian) Black-Box Optimization

mlrMBO for mlr hyperparameter tuning was already used in an earlier blog post. Nonetheless we want to provide a small toy example to demonstrate the work flow of mlrMBO in this post.

### Example

First, we define an objective function that we are going to minimize:

set.seed(1)
library(mlrMBO)
##
## Attaching package: 'BBmisc'
## The following object is masked from 'package:base':
##
##     isFALSE
fun = makeSingleObjectiveFunction(
name = "SineMixture",
fn = function(x) sin(x[1])*cos(x[2])/2 + 0.04 * sum(x^2),
par.set = makeNumericParamSet(id = "x", len = 2, lower = -5, upper = 5)
)

To define the objective function we use makeSingleObjectiveFunction from the neat package smoof, which gives us the benefit amongst others to be able to directly visualize the function. If you happen to be in need of functions to optimize and benchmark your optimization algorithm I recommend you to have a look at the package!

library(plot3D)
plot3D(fun, contour = TRUE, lightning = TRUE)

# In this simple example we construct the control object with the defaults:
ctrl = makeMBOControl()
# For this numeric optimization we are going to use the Expected
# Improvement as infill criterion:
ctrl = setMBOControlInfill(ctrl, crit = crit.ei)
# We will allow for exactly 25 evaluations of the objective function:
ctrl = setMBOControlTermination(ctrl, max.evals = 25L)

The optimization has to so start with an initial design. mlrMBO can automatically create one but here we are going to use a randomly sampled LHS design of our own:

library(ggplot2)
des = generateDesign(n = 8L, par.set = getParamSet(fun),
fun = lhs::randomLHS)
autoplot(fun, render.levels = TRUE) + geom_point(data = des)

The points demonstrate how the initial design already covers the search space but is missing the area of the global minimum. Before we can start the Bayesian optimization we have to set the surrogate learner to Kriging. Therefore we use an mlr regression learner. In fact, with mlrMBO you can use any regression learner integrated in mlr as a surrogate allowing for many special optimization applications.

sur.lrn = makeLearner("regr.km", predict.type = "se",
config = list(show.learner.output = FALSE))

Note: mlrMBO can automatically determine a good surrogate learner based on the search space defined for the objective function. For a purely numeric domain it would have chosen Kriging as well with some slight modifications to make it a bit more stable against numerical problems that can occur during optimization.

Finally, we can start the optimization run:

res = mbo(fun = fun, design = des, learner = sur.lrn, control = ctrl,
show.info = TRUE)
## Computing y column(s) for design. Not provided.
## [mbo] 0: x=0.897,-3.42 : y = 0.123 : 0.0 secs : initdesign
## [mbo] 0: x=-3.76,-0.767 : y = 0.798 : 0.0 secs : initdesign
## [mbo] 0: x=-3.27,-2.48 : y = 0.623 : 0.0 secs : initdesign
## [mbo] 0: x=-1.53,1.73 : y = 0.291 : 0.0 secs : initdesign
## [mbo] 0: x=4.92,4.84 : y = 1.84 : 0.0 secs : initdesign
## [mbo] 0: x=1.52,-4.57 : y = 0.86 : 0.0 secs : initdesign
## [mbo] 0: x=-0.435,0.603 : y = -0.152 : 0.0 secs : initdesign
## [mbo] 0: x=2.66,3.25 : y = 0.473 : 0.0 secs : initdesign
## [mbo] 1: x=0.29,-0.0602 : y = 0.146 : 0.0 secs : infill_ei
## [mbo] 2: x=5,0.626 : y = 0.627 : 0.0 secs : infill_ei
## [mbo] 3: x=0.13,1.09 : y = 0.0779 : 0.0 secs : infill_ei
## [mbo] 4: x=-2.35,0.637 : y = -0.0493 : 0.0 secs : infill_ei
## [mbo] 5: x=4.65,-2.92 : y = 1.69 : 0.0 secs : infill_ei
## [mbo] 6: x=-1.22,0.511 : y = -0.34 : 0.0 secs : infill_ei
## [mbo] 7: x=-0.638,-2.33 : y = 0.438 : 0.0 secs : infill_ei
## [mbo] 8: x=-1.04,0.344 : y = -0.358 : 0.0 secs : infill_ei
## [mbo] 9: x=-1.4,0.166 : y = -0.406 : 0.0 secs : infill_ei
## [mbo] 10: x=-1.43,-3.82 : y = 1.05 : 0.0 secs : infill_ei
## [mbo] 11: x=-1.31,-0.247 : y = -0.397 : 0.0 secs : infill_ei
## [mbo] 12: x=-1.36,-0.0187 : y = -0.415 : 0.0 secs : infill_ei
## [mbo] 13: x=2.46,0.985 : y = 0.455 : 0.0 secs : infill_ei
## [mbo] 14: x=-1.73,-0.18 : y = -0.365 : 0.0 secs : infill_ei
## [mbo] 15: x=0.355,4.58 : y = 0.822 : 0.0 secs : infill_ei
## [mbo] 16: x=-5,5 : y = 2.14 : 0.0 secs : infill_ei
## [mbo] 17: x=-5,-5 : y = 2.14 : 0.0 secs : infill_ei
res\$x
## \$x
## [1] -1.359755 -0.018670
res\$y
## [1] -0.4148502

We can see that we have found the global optimum of \(y = -0.414964\) at \(x = (-1.35265,0)\) quite sufficiently. Let’s have a look at the points mlrMBO evaluated. Therefore we can use the OptPath which stores all information about all evaluations during the optimization run:

opdf = as.data.frame(res\$opt.path)
autoplot(fun, render.levels = TRUE, render.contours = FALSE) +
geom_text(data = opdf, aes(label = dob))

It is interesting to see, that for this run the algorithm first went to the local minimum on the top right in the 6th and 7th iteration but later, thanks to the explorative character of the Expected Improvement, found the real global minimum.

### Comparison

That is all good, but how do other optimization strategies perform?

#### A fair comarison

… for stochastic optimization algorithms can only be achieved by repeating the runs. mlrMBO is stochastic as the initial design is generated randomly and the fit of the Kriging surrogate is also not deterministic. Furthermore we should include other optimization strategies like a genetic algorithm and direct competitors like rBayesOpt. An extensive benchmark is available in our mlrMBO paper. The examples here are just meant to demonstrate the package.

### Engage

If you want to contribute to mlrMBO we ware always open to suggestions and pull requests on github. You are also invited to fork the repository and build and extend your own optimizer based on our toolbox.