pytorch / botorch

Bayesian optimization in PyTorch

Home Page:https://botorch.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to set priors

HaoLiu56 opened this issue · comments

Issue description

Hi, I'm new to BoTorch. I wonder how to set custom priors in SingleTaskGP

Code example

I tried to build a model and set priors by myself, I'll encounter the below errors, could you help me see how to set these priors?

Code example:

model = SingleTaskGP(train_X=init_x, train_Y=init_y,        
               input_transform=Normalize(d=7),
               outcome_transform=Standardize(m=1)

model.likelihood.noise_covar.register_prior(
"noise_prior",
GammaPrior(concentration=2.0, rate=4.0),
lambda: model.likelihood.noise_covar.noise,
lambda v: model.likelihood.noise_covar._set_noise(v),
)

model.covar_module.register_prior(
    "outputscale_prior",
    GammaPrior(concentration=2.0, rate=4.0),
    lambda: model.covar_module.outputscale,
    lambda v: model.covar_module._set_outputscale(v),
)
model.covar_module.base_kernel.register_prior(
    "lengthscale_prior",
    GammaPrior(concentration=2.0, rate=0.2),
    lambda: model.covar_module.base_kernel.lengthscale,
    lambda v: model.covar_module.base_kernel._set_lengthscale(v),
)

mll = ExactMarginalLogLikelihood(model.likelihood, model)
fit_gpytorch_mll(mll);

Error message:

ValueError Traceback (most recent call last)
Cell In[96], line 17
3 model = SingleTaskGP(train_X=init_x, train_Y=init_y,
4 input_transform=Normalize(d=7),
5 outcome_transform=Standardize(m=1))
7 # new_noise_prior = GammaPrior(concentration=0.5, rate=0.1)
8
9 # noise_covar = model.likelihood.noise_covar
(...)
14 # lambda v: noise_covar._set_noise(v),
15 # )
---> 17 model.likelihood.noise_covar.register_prior(
18 "noise_prior",
19 GammaPrior(concentration=2.0, rate=4.0),
20 lambda: model.likelihood.noise_covar.noise,
21 lambda v: model.likelihood.noise_covar._set_noise(v),
22 )
24 model.covar_module.register_prior(
25 "outputscale_prior",
26 GammaPrior(concentration=2.0, rate=4.0),
27 lambda: model.covar_module.outputscale,
28 lambda v: model.covar_module._set_outputscale(v),
29 )
30 model.covar_module.base_kernel.register_prior(
31 "lengthscale_prior",
32 GammaPrior(concentration=2.0, rate=0.2),
33 lambda: model.covar_module.base_kernel.lengthscale,
34 lambda v: model.covar_module.base_kernel._set_lengthscale(v),
35 )

File ~/anaconda3/envs/ActiveLearning/lib/python3.11/site-packages/gpytorch/module.py:247, in Module.register_prior(self, name, prior, param_or_closure, setting_closure)
245 else:
246 if len(inspect.signature(param_or_closure).parameters) == 0:
--> 247 raise ValueError(
248 """As of version 1.4, param_or_closure must operate on a module instance. For example:
249
250 likelihood.noise_covar.register_prior(
251 "noise_std_prior",
252 gpytorch.priors.NormalPrior(0, 1),
253 lambda module: module.noise.sqrt()
254 )
255 """
256 )
257 if inspect.isfunction(setting_closure) and len(inspect.signature(setting_closure).parameters) < 2:
258 raise ValueError(
259 """As of version 1.4, setting_closure must operate on a module instance and a tensor. For example:
260
(...)
267 """
268 )

ValueError: As of version 1.4, param_or_closure must operate on a module instance. For example:

                likelihood.noise_covar.register_prior(
                    "noise_std_prior",
                    gpytorch.priors.NormalPrior(0, 1),
                    lambda module: module.noise.sqrt()
                )

System Info

Please provide information about your setup, including

  • BoTorch Version (0.10.0)
  • GPyTorch Version (1.11)
  • PyTorch Version (2.2.0.post100)
  • Computer OS: mac OS Sonoma

Hi, thanks for reaching out. SingleTaskGP uses a Matern kernel with a Gamma prior by default, but if you want to change the parameters of the Gamma distribution, you can do it like so:

from botorch.models.gp_regression import SingleTaskGP
from botorch.models.transforms.input import Normalize
from botorch.models.transforms.outcome import Standardize
from gpytorch.priors.torch_priors import GammaPrior
from gpytorch.mlls import ExactMarginalLogLikelihood
from botorch.fit import fit_gpytorch_mll
from botorch.models.utils.gpytorch_modules import get_matern_kernel_with_gamma_prior
from gpytorch.kernels import MaternKernel, ScaleKernel
import torch

m = 1
d = 7
n = 3
batch_shape = []

init_x = torch.rand((*batch_shape, n, d), dtype=torch.float64)
init_y = torch.rand((*batch_shape, n, m), dtype=torch.float64)

covar_module = ScaleKernel(
    base_kernel=MaternKernel(
        nu=2.5,
        ard_num_dims=d,
        batch_shape=torch.Size(batch_shape),
        lengthscale_prior=GammaPrior(3.0, 6.0),
    ),
    batch_shape=torch.Size(batch_shape),
    outputscale_prior=GammaPrior(2.0, 0.15),
)

model = SingleTaskGP(
    train_X=init_x,
    train_Y=init_y,        
    input_transform=Normalize(d=d),
    outcome_transform=Standardize(m=m),
    covar_module=covar_module
)

mll = ExactMarginalLogLikelihood(model.likelihood, model)
fit_gpytorch_mll(mll)

Thank you very much! Could you also show me how to self define the 'noise prior'?

And Can I ask what is batch_shape mean here?
Does it means the repetition of my data? (e.g. I replicate my experiment twice, then batch_shape=2)
Or it means different data batch from different experiment?

Could you also show me how to self define the 'noise prior'?

Noise prior is a part of the model's likelihood. You can define a custom likelihood in the same way and pass it in. The default likelihood module is costructed by this helper:

def get_gaussian_likelihood_with_gamma_prior(
    batch_shape: Optional[torch.Size] = None,
) -> GaussianLikelihood:
    r"""Constructs the GaussianLikelihood that is used by default by
    several models. This uses a Gamma(1.1, 0.05) prior and constrains the
    noise level to be greater than MIN_INFERRED_NOISE_LEVEL (=1e-4).
    """
    batch_shape = torch.Size() if batch_shape is None else batch_shape
    noise_prior = GammaPrior(1.1, 0.05)
    noise_prior_mode = (noise_prior.concentration - 1) / noise_prior.rate
    return GaussianLikelihood(
        noise_prior=noise_prior,
        batch_shape=batch_shape,
        noise_constraint=GreaterThan(
            MIN_INFERRED_NOISE_LEVEL,
            transform=None,
            initial_value=noise_prior_mode,
        ),
    )

And Can I ask what is batch_shape mean here?

The batch_shape is the output dimension augmented batch shape of the underlying GPyTorch model. If your train_Y has shape batch x n x m, it'll be batch + m if m>1 and batch when m=1. We use this helper to infer it from the training data:

    def get_batch_dimensions(
        train_X: Tensor, train_Y: Tensor
    ) -> Tuple[torch.Size, torch.Size]:
        r"""Get the raw batch shape and output-augmented batch shape of the inputs.

        Args:
            train_X: A `n x d` or `batch_shape x n x d` (batch mode) tensor of training
                features.
            train_Y: A `n x m` or `batch_shape x n x m` (batch mode) tensor of
                training observations.

        Returns:
            2-element tuple containing

            - The `input_batch_shape`
            - The output-augmented batch shape: `input_batch_shape x (m)`
        """
        input_batch_shape = train_X.shape[:-2]
        aug_batch_shape = input_batch_shape
        num_outputs = train_Y.shape[-1]
        if num_outputs > 1:
            aug_batch_shape += torch.Size([num_outputs])
        return input_batch_shape, aug_batch_shape

Thanks for clarifying!

Closing since this seems to have been answered, but feel free to ask more questions.