Heteroskedastic modeling
Bayesian Optimization with Heteroskedastic Noise GP Modeling¶
In this tutorial we demonstrate the use of Xopt to preform Bayesian Optimization on a simple test problem. The problem exibits non-uniform (heteroskedastic) noise which we account for in the GP model. This requires explicit specification of the measurement variance.
Define the test problem¶
Here we define a simple optimization problem, where we attempt to minimize the sin function in the domian [0,2*pi]. Note that the function used to evaluate the objective function takes a dictionary as input and returns a dictionary as the output.
from xopt.vocs import VOCS
from xopt.evaluator import Evaluator
from xopt.generators.bayesian import UpperConfidenceBoundGenerator
from xopt import Xopt
import math
import numpy as np
import torch
# define variables and function objectives
vocs = VOCS(
variables={"x": [0, 2 * math.pi]},
objectives={"f": "MINIMIZE"},
)
Specifying measurement variance¶
We specify variance in the objective function by appending _var
to it. This info
will collected by the model constructor to make a heteroskedastic model.
# define a test function to optimize
# the test function also returns an estimation of the variance, which is
# used to create a Heteroskedastic noise model for the gp
def sin_function(input_dict):
return {"f": np.sin(input_dict["x"]), "f_var": 0.001 * input_dict["x"]}
Create Xopt objects¶
Create the evaluator to evaluate our test function and create a generator that uses the Upper Confidence Bound acquisition function to perform Bayesian Optimization.
evaluator = Evaluator(function=sin_function)
generator = UpperConfidenceBoundGenerator(vocs=vocs)
X = Xopt(evaluator=evaluator, generator=generator, vocs=vocs)
Generate and evaluate initial points¶
To begin optimization, we must generate some random initial data points. The first call
to X.step()
will generate and evaluate a number of randomly points specified by the
generator. Note that if we add data to xopt before calling X.step()
by assigning
the data to X.data
, calls to X.step()
will ignore the random generation and
proceed to generating points via Bayesian optimization.
# call X.random_evaluate() to generate + evaluate 3 initial points
X.random_evaluate(4)
# inspect the gathered data
X.data
Do bayesian optimization steps¶
To perform optimization we simply call X.step()
in a loop. This allows us to do
intermediate tasks in between optimization steps, such as examining the model and
acquisition function at each step (as we demonstrate here).
n_steps = 5
# test points for plotting
test_x = torch.linspace(*X.vocs.bounds.flatten(), 50).double()
for i in range(n_steps):
# get the Gaussian process model from the generator
model = X.generator.train_model()
# visualize model
fig, ax = X.generator.visualize_model(n_grid=len(test_x))
# plot true function
true_f = sin_function({"x": test_x})["f"]
ax[0].plot(test_x, true_f, "C1--")
# do the optimization step
X.step()
# access the collected data
X.data
Getting the optimization result¶
To get the best point (without evaluating it) we ask the generator to predict the optimum based on the posterior mean.
X.generator.get_optimum()
Customizing optimization¶
Each generator has a set of options that can be modified to effect optimization behavior
X.generator.dict()