Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge inferred & fixed noise LCE-M models. #1993

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions botorch/models/contextual_multioutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

r"""
References

.. [Feng2020HDCPS]
Q. Feng, B. Latham, H. Mao and E. Backshy. High-Dimensional Contextual Policy
Search with Unknown Context Rewards using Bayesian Optimization.
Advances in Neural Information Processing Systems 33, NeurIPS 2020.
"""

import warnings
from typing import List, Optional

import torch
Expand All @@ -13,22 +23,24 @@
from gpytorch.constraints import Interval
from gpytorch.distributions.multivariate_normal import MultivariateNormal
from gpytorch.kernels.rbf_kernel import RBFKernel
from gpytorch.likelihoods.gaussian_likelihood import FixedNoiseGaussianLikelihood
from linear_operator.operators import InterpolatedLinearOperator, LinearOperator
from torch import Tensor
from torch.nn import ModuleList


class LCEMGP(MultiTaskGP):
r"""The Multi-Task GP with the latent context embedding multioutput
(LCE-M) kernel.
r"""The Multi-Task GP with the latent context embedding multioutput (LCE-M)
kernel. See [Feng2020HDCPS]_ for a reference on the model and its use in Bayesian
optimization.

"""

def __init__(
self,
train_X: Tensor,
train_Y: Tensor,
task_feature: int,
train_Yvar: Optional[Tensor] = None,
context_cat_feature: Optional[Tensor] = None,
context_emb_feature: Optional[Tensor] = None,
embs_dim_list: Optional[List[int]] = None,
Expand All @@ -41,6 +53,9 @@ def __init__(
train_X: (n x d) X training data.
train_Y: (n x 1) Y training data.
task_feature: Column index of train_X to get context indices.
train_Yvar: An optional (n x 1) tensor of observed variances of each
training Y. If None, we infer the noise. Note that the inferred noise
is common across all tasks.
context_cat_feature: (n_contexts x k) one-hot encoded context
features. Rows are ordered by context indices, where k is the
number of categorical variables. If None, task indices will
Expand All @@ -52,11 +67,13 @@ def __init__(
for each categorical variable.
output_tasks: A list of task indices for which to compute model
outputs for. If omitted, return outputs for all task indices.

"""
super().__init__(
train_X=train_X,
train_Y=train_Y,
task_feature=task_feature,
train_Yvar=train_Yvar,
output_tasks=output_tasks,
input_transform=input_transform,
outcome_transform=outcome_transform,
Expand Down Expand Up @@ -126,6 +143,7 @@ def task_covar_matrix(self, task_idcs: Tensor) -> Tensor:

Args:
task_idcs: (n x 1) or (b x n x 1) task indices tensor

"""
covar_matrix = self._eval_context_covar()
return InterpolatedLinearOperator(
Expand All @@ -150,6 +168,8 @@ def forward(self, x: Tensor) -> MultivariateNormal:
class FixedNoiseLCEMGP(LCEMGP):
r"""The Multi-Task GP the latent context embedding multioutput
(LCE-M) kernel, with known observation noise.

DEPRECATED: Please use `LCEMGP` with `train_Yvar` instead.
"""

def __init__(
Expand All @@ -167,7 +187,7 @@ def __init__(
Args:
train_X: (n x d) X training data.
train_Y: (n x 1) Y training data.
train_Yvar: (n x 1) Noise variances of each training Y.
train_Yvar: (n x 1) Observed variances of each training Y.
task_feature: Column index of train_X to get context indices.
context_cat_feature: (n_contexts x k) one-hot encoded context
features. Rows are ordered by context indices, where k is the
Expand All @@ -180,16 +200,23 @@ def __init__(
1 for each categorical variable.
output_tasks: A list of task indices for which to compute model
outputs for. If omitted, return outputs for all task indices.

"""
self._validate_tensor_args(X=train_X, Y=train_Y, Yvar=train_Yvar)
warnings.warn(
"`FixedNoiseLCEMGP` has been deprecated and will be removed in a "
"future release. Please use the `LCEMGP` model instead. "
"When `train_Yvar` is specified, `LCEMGP` behaves the same "
"as the `FixedNoiseLCEMGP`.",
DeprecationWarning,
)

super().__init__(
train_X=train_X,
train_Y=train_Y,
task_feature=task_feature,
train_Yvar=train_Yvar,
context_cat_feature=context_cat_feature,
context_emb_feature=context_emb_feature,
embs_dim_list=embs_dim_list,
output_tasks=output_tasks,
)
self.likelihood = FixedNoiseGaussianLikelihood(noise=train_Yvar)
self.to(train_X)
15 changes: 13 additions & 2 deletions test/models/test_contextual_multioutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
class ContextualMultiOutputTest(BotorchTestCase):
def testLCEMGP(self):
d = 1
for dtype in (torch.float, torch.double):
for dtype, fixed_noise in ((torch.float, True), (torch.double, False)):
# test with batch evaluation
train_x = torch.rand(10, d, device=self.device, dtype=dtype)
train_y = torch.cos(train_x)
# 2 contexts here
Expand All @@ -31,7 +32,17 @@ def testLCEMGP(self):
)
train_x = torch.cat([train_x, task_indices.unsqueeze(-1)], axis=1)

model = LCEMGP(train_X=train_x, train_Y=train_y, task_feature=d)
if fixed_noise:
train_yvar = torch.ones(10, 1, device=self.device, dtype=dtype) * 0.01
model = LCEMGP(
train_X=train_x,
train_Y=train_y,
task_feature=d,
train_Yvar=train_yvar,
)
else:
model = LCEMGP(train_X=train_x, train_Y=train_y, task_feature=d)

self.assertIsInstance(model, LCEMGP)
self.assertIsInstance(model, MultiTaskGP)
self.assertIsNone(model.context_emb_feature)
Expand Down