pytorch / botorch

Bayesian optimization in PyTorch
https://botorch.org/
MIT License
3.07k stars 390 forks source link

Using `sample_optimal_points` with input and outcome transforms #2174

Open jiwoncpark opened 8 months ago

jiwoncpark commented 8 months ago

Issue description

I'm running sample_optimal_points with a model that has input and outcome transforms. I get this warning:

RuntimeWarning: Could not update `train_inputs` with transformed inputs since GenericDeterministicModel does not have a `train_inputs` attribute. Make sure that the `input_transform` is applied to both the train inputs and test inputs.

Reading through the source code, it seems like model is wrapped GenericDeterministicModel, which does not support transforms. What is the best way we can account for transforms when we run sample_optimal_points?

For context, I'm sampling optimal points to run q-multi-objective PES, MES, and JES. Thanks in advance for your help!

Code example

Below, I simulate inputs x and/or outputs y with extreme values and optimize PES repeatedly. As expected, sample_optimal_points fails eventually with RuntimeError: Only found 1 optimal points instead of 20.

import torch
from botorch.test_functions.multi_objective import BraninCurrin
from botorch.acquisition.multi_objective.utils import (
    sample_optimal_points,
    random_search_optimizer,
)
from botorch.utils.sampling import draw_sobol_samples
from botorch.models.transforms import Standardize, Normalize
from botorch.models.gp_regression import SingleTaskGP
from gpytorch.mlls.exact_marginal_log_likelihood import ExactMarginalLogLikelihood
from botorch.fit import fit_gpytorch_mll
from botorch.acquisition.multi_objective.predictive_entropy_search import qMultiObjectivePredictiveEntropySearch
from botorch.optim.optimize import optimize_acqf

def generate_data(n, seed):
    tkwargs = {"dtype": torch.double, "device": "cpu"}
    problem = BraninCurrin(negate=True)
    bounds = problem.bounds.to(**tkwargs)
    # print(f"problem bounds: {bounds}")
    # print(f"ref point: {problem.ref_point}")
    train_X = draw_sobol_samples(bounds=bounds, n=n, q=1, seed=seed).squeeze(-2)
    train_Y = problem(train_X)
    # Rescale to extreme values
    train_X = (train_X - 100.0)*20.0
    # train_Y = (train_Y - 100.0)*20.0
    return train_X, train_Y, bounds

def fit_model(train_X, train_Y):
    d = train_X.shape[-1]
    M = train_Y.shape[-1]
    model = SingleTaskGP(
        train_X, train_Y,
        input_transform=Normalize(d=d),
        outcome_transform=Standardize(m=M))
    mll = ExactMarginalLogLikelihood(model.likelihood, model)
    fit_gpytorch_mll(mll)
    return model

if __name__ == "__main__":
    import botorch
    import gpytorch
    print(botorch.__version__)
    print(gpytorch.__version__)
    print(torch.__version__)

    d = 2
    M = 2
    n = 6
    init_X, init_Y, bounds = generate_data(n, seed=0)
    model = fit_model(init_X, init_Y)

    for trial_i in range(100):
        print(trial_i)
        ps, _ = sample_optimal_points(
            model=model,
            bounds=bounds,
            num_samples=20,
            num_points=20,
            optimizer=random_search_optimizer,
            optimizer_kwargs={'pop_size': 2000, 'max_tries': 10}
        )

        pes = qMultiObjectivePredictiveEntropySearch(model=model, pareto_sets=ps)

        new_x, _ = optimize_acqf(
            acq_function=pes,
            bounds=bounds,
            q=1,
            num_restarts=10,
            raw_samples=512,
            sequential=True,
        )

        print(new_x)

System Info

Please provide information about your setup, including

esantorella commented 6 months ago

I can reproduce this and confirm that the warning goes away and more samples are produced when the transforms are removed. Here's a smaller repro:

import warnings

import torch
from botorch.test_functions.multi_objective import BraninCurrin
from botorch.acquisition.multi_objective.utils import (
    sample_optimal_points,
    random_search_optimizer,
)
from botorch.utils.sampling import draw_sobol_samples
from botorch.models.transforms import Standardize, Normalize
from botorch.models.gp_regression import SingleTaskGP
from gpytorch.mlls.exact_marginal_log_likelihood import ExactMarginalLogLikelihood
from botorch.fit import fit_gpytorch_mll

def generate_data(n, seed):
    tkwargs = {"dtype": torch.double, "device": "cpu"}
    problem = BraninCurrin(negate=True)
    bounds = problem.bounds.to(**tkwargs)
    train_X = draw_sobol_samples(bounds=bounds, n=n, q=1, seed=seed).squeeze(-2)
    train_Y = problem(train_X)
    return train_X, train_Y, bounds

def fit_model(train_X, train_Y):
    d = train_X.shape[-1]
    M = train_Y.shape[-1]
    model = SingleTaskGP(
        train_X, train_Y,
        input_transform=Normalize(d=d),
        outcome_transform=Standardize(m=M)
    )
    mll = ExactMarginalLogLikelihood(model.likelihood, model)
    fit_gpytorch_mll(mll)
    return model

if __name__ == "__main__":
    d = 2
    M = 2
    n = 6
    init_X, init_Y, bounds = generate_data(n, seed=0)
    model = fit_model(init_X, init_Y)

    with warnings.catch_warnings():
        warnings.simplefilter("error", RuntimeWarning)
        ps, _ = sample_optimal_points(
            model=model,
            bounds=bounds,
            num_samples=20,
            num_points=20,
        )

Produces the following traceback:

Traceback (most recent call last):
  File "/Users/santorella/issue_repros/botorch_2174.py", line 48, in <module>
    ps, _ = sample_optimal_points(
            ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/santorella/botorch/botorch/acquisition/multi_objective/utils.py", line 373, in sample_optimal_points
    ps_i, pf_i = optimizer(
                 ^^^^^^^^^^
  File "/Users/santorella/botorch/botorch/acquisition/multi_objective/utils.py", line 296, in random_search_optimizer
    Y = model.posterior(X).mean
        ^^^^^^^^^^^^^^^^^^
  File "/Users/santorella/botorch/botorch/models/ensemble.py", line 73, in posterior
    self.eval()
  File "/Users/santorella/botorch/botorch/models/model.py", line 263, in eval
    self._set_transformed_inputs()
  File "/Users/santorella/botorch/botorch/models/model.py", line 247, in _set_transformed_inputs
    warnings.warn(
RuntimeWarning: Could not update `train_inputs` with transformed inputs since GenericDeterministicModel does not have a `train_inputs` attribute. Make sure that the `input_transform` is applied to both the train inputs and test inputs.