Custom GP modeling for BO¶
In [ ]:
Copied!
from xopt.generators.bayesian.expected_improvement import ExpectedImprovementGenerator
from xopt.generators.bayesian.models.standard import StandardModelConstructor
from gpytorch.kernels import PeriodicKernel, ScaleKernel
import matplotlib.pyplot as plt
import pandas as pd
import torch
from xopt.vocs import VOCS
# Ignore all warnings
import warnings
warnings.filterwarnings("ignore")
my_vocs = VOCS(
variables={"x": [0, 1]},
objectives={"y": "MAXIMIZE"},
constraints={"c": ["LESS_THAN", 0]},
)
from xopt.generators.bayesian.expected_improvement import ExpectedImprovementGenerator
from xopt.generators.bayesian.models.standard import StandardModelConstructor
from gpytorch.kernels import PeriodicKernel, ScaleKernel
import matplotlib.pyplot as plt
import pandas as pd
import torch
from xopt.vocs import VOCS
# Ignore all warnings
import warnings
warnings.filterwarnings("ignore")
my_vocs = VOCS(
variables={"x": [0, 1]},
objectives={"y": "MAXIMIZE"},
constraints={"c": ["LESS_THAN", 0]},
)
In [ ]:
Copied!
# define test functions
def y(x):
return torch.sin(2 * 3.14 * x)
def c(x):
return 5.0 * torch.cos(2 * 3.14 * x + 0.25)
test_x = torch.linspace(*torch.tensor(my_vocs.bounds.flatten()), 100)
# define training data to pass to the generator
train_x = torch.tensor((0.2, 0.5, 0.6))
train_y = y(train_x)
train_c = c(train_x)
training_data = pd.DataFrame({"x": train_x.numpy(), "y": train_y.numpy(), "c": train_c})
def plot_ground_truth():
fig, ax = plt.subplots()
ax.plot(test_x, y(test_x), "--C0")
ax.plot(test_x, c(test_x), "--C1")
ax.plot(train_x, train_y, "oC0")
ax.plot(train_x, train_c, "oC1")
return ax
plot_ground_truth()
# define test functions
def y(x):
return torch.sin(2 * 3.14 * x)
def c(x):
return 5.0 * torch.cos(2 * 3.14 * x + 0.25)
test_x = torch.linspace(*torch.tensor(my_vocs.bounds.flatten()), 100)
# define training data to pass to the generator
train_x = torch.tensor((0.2, 0.5, 0.6))
train_y = y(train_x)
train_c = c(train_x)
training_data = pd.DataFrame({"x": train_x.numpy(), "y": train_y.numpy(), "c": train_c})
def plot_ground_truth():
fig, ax = plt.subplots()
ax.plot(test_x, y(test_x), "--C0")
ax.plot(test_x, c(test_x), "--C1")
ax.plot(train_x, train_y, "oC0")
ax.plot(train_x, train_c, "oC1")
return ax
plot_ground_truth()
Custom kernel definition¶
In this example we know that the target optimization function is periodic, so it makes sense to use a periodic kernel for the GP model with no noise. Here we define a function to create that model.
In [ ]:
Copied!
# note the creation of options beforehand
# specify a periodic kernel for each output (objectives and constraints)
covar_module = {"y": ScaleKernel(PeriodicKernel())}
gp_constructor = StandardModelConstructor(covar_modules=covar_module)
generator = ExpectedImprovementGenerator(vocs=my_vocs, gp_constructor=gp_constructor)
generator
# note the creation of options beforehand
# specify a periodic kernel for each output (objectives and constraints)
covar_module = {"y": ScaleKernel(PeriodicKernel())}
gp_constructor = StandardModelConstructor(covar_modules=covar_module)
generator = ExpectedImprovementGenerator(vocs=my_vocs, gp_constructor=gp_constructor)
generator
In [ ]:
Copied!
# view custom model from data
generator.add_data(training_data)
model = generator.train_model()
fig, ax = generator.visualize_model(n_grid=len(test_x))
# plot ground truth
ax[0].plot(test_x, y(test_x), "C0-.")
ax[1].plot(test_x, c(test_x), "C2-.");
# view custom model from data
generator.add_data(training_data)
model = generator.train_model()
fig, ax = generator.visualize_model(n_grid=len(test_x))
# plot ground truth
ax[0].plot(test_x, y(test_x), "C0-.")
ax[1].plot(test_x, c(test_x), "C2-.");
In [ ]:
Copied!
model
model
In [ ]:
Copied!
# get the next point from the generator
generator.generate(1)
# get the next point from the generator
generator.generate(1)
Custom prior mean function¶
Here we assume we have some knowledge of the ground truth function, which we can take advantage of to speed up optimization. This "prior mean" function is specified by a pytorch module.
In [ ]:
Copied!
class ConstraintPrior(torch.nn.Module):
def forward(self, X):
return c(X).squeeze(dim=-1)
gp_constructor = StandardModelConstructor(mean_modules={"c": ConstraintPrior()})
generator = ExpectedImprovementGenerator(
vocs=my_vocs,
gp_constructor=gp_constructor,
)
class ConstraintPrior(torch.nn.Module):
def forward(self, X):
return c(X).squeeze(dim=-1)
gp_constructor = StandardModelConstructor(mean_modules={"c": ConstraintPrior()})
generator = ExpectedImprovementGenerator(
vocs=my_vocs,
gp_constructor=gp_constructor,
)
In [ ]:
Copied!
# view custom model from data
generator.add_data(training_data)
model = generator.train_model()
fig, ax = generator.visualize_model(n_grid=len(test_x))
# plot ground truth
ax[0].plot(test_x, y(test_x), "C0-.")
ax[1].plot(test_x, c(test_x), "C2-.");
# view custom model from data
generator.add_data(training_data)
model = generator.train_model()
fig, ax = generator.visualize_model(n_grid=len(test_x))
# plot ground truth
ax[0].plot(test_x, y(test_x), "C0-.")
ax[1].plot(test_x, c(test_x), "C2-.");
In [ ]:
Copied!
model
model
In [ ]:
Copied!
list(model.named_parameters())
list(model.named_parameters())