Skip to content

Commit 8b71e38

Browse files
saitcakmakfacebook-github-bot
authored andcommitted
Remove FixedNoiseMultiTaskGP
Summary: Deprecated since #1818. `MultiTaskGP` offers the full functionality of `FixedNoiseMultiTaskGP` without requiring a different class. Reviewed By: dme65 Differential Revision: D56802249 fbshipit-source-id: 44b07d2a1b5551346871a9f8b36ca069b9280019
1 parent 4f362be commit 8b71e38

File tree

5 files changed

+106
-383
lines changed

5 files changed

+106
-383
lines changed

botorch/models/__init__.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,14 @@
2727
from botorch.models.higher_order_gp import HigherOrderGP
2828
from botorch.models.model import ModelList
2929
from botorch.models.model_list_gp_regression import ModelListGP
30-
from botorch.models.multitask import (
31-
FixedNoiseMultiTaskGP,
32-
KroneckerMultiTaskGP,
33-
MultiTaskGP,
34-
)
30+
from botorch.models.multitask import KroneckerMultiTaskGP, MultiTaskGP
3531
from botorch.models.pairwise_gp import PairwiseGP, PairwiseLaplaceMarginalLogLikelihood
3632

3733
__all__ = [
3834
"AffineDeterministicModel",
3935
"AffineFidelityCostModel",
4036
"ApproximateGPyTorchModel",
4137
"FixedNoiseGP",
42-
"FixedNoiseMultiTaskGP",
4338
"SaasFullyBayesianSingleTaskGP",
4439
"SaasFullyBayesianMultiTaskGP",
4540
"GenericDeterministicModel",

botorch/models/multitask.py

Lines changed: 6 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from __future__ import annotations
3131

3232
import math
33-
import warnings
3433
from typing import Any, Dict, List, Optional, Tuple, Union
3534

3635
import torch
@@ -375,7 +374,12 @@ def construct_inputs(
375374

376375
# Call Model.construct_inputs to parse training data
377376
base_inputs = super().construct_inputs(training_data=training_data)
378-
if isinstance(training_data, MultiTaskDataset):
377+
if (
378+
isinstance(training_data, MultiTaskDataset)
379+
# If task features are included in the data, all tasks will have
380+
# some observations and they may have different task features.
381+
and training_data.task_feature_index is None
382+
):
379383
all_tasks = list(range(len(training_data.datasets)))
380384
base_inputs["all_tasks"] = all_tasks
381385
if task_covar_prior is not None:
@@ -387,81 +391,6 @@ def construct_inputs(
387391
return base_inputs
388392

389393

390-
class FixedNoiseMultiTaskGP(MultiTaskGP):
391-
r"""Multi-Task GP model using an ICM kernel, with known observation noise.
392-
393-
DEPRECATED: Please use `MultiTaskGP` with `train_Yvar` instead.
394-
Will be removed in a future release (~v0.10).
395-
"""
396-
397-
def __init__(
398-
self,
399-
train_X: Tensor,
400-
train_Y: Tensor,
401-
train_Yvar: Tensor,
402-
task_feature: int,
403-
covar_module: Optional[Module] = None,
404-
task_covar_prior: Optional[Prior] = None,
405-
output_tasks: Optional[List[int]] = None,
406-
rank: Optional[int] = None,
407-
input_transform: Optional[InputTransform] = None,
408-
outcome_transform: Optional[OutcomeTransform] = None,
409-
) -> None:
410-
r"""
411-
Args:
412-
train_X: A `n x (d + 1)` or `b x n x (d + 1)` (batch mode) tensor
413-
of training data. One of the columns should contain the task
414-
features (see `task_feature` argument).
415-
train_Y: A `n x 1` or `b x n x 1` (batch mode) tensor of training
416-
observations.
417-
train_Yvar: A `n` or `b x n` (batch mode) tensor of observed measurement
418-
noise.
419-
task_feature: The index of the task feature (`-d <= task_feature <= d`).
420-
task_covar_prior : A Prior on the task covariance matrix. Must operate
421-
on p.s.d. matrices. A common prior for this is the `LKJ` prior.
422-
output_tasks: A list of task indices for which to compute model
423-
outputs for. If omitted, return outputs for all task indices.
424-
rank: The rank to be used for the index kernel. If omitted, use a
425-
full rank (i.e. number of tasks) kernel.
426-
input_transform: An input transform that is applied in the model's
427-
forward pass.
428-
outcome_transform: An outcome transform that is applied to the
429-
training data during instantiation and to the posterior during
430-
inference (that is, the `Posterior` obtained by calling
431-
`.posterior` on the model will be on the original scale).
432-
433-
Example:
434-
>>> X1, X2 = torch.rand(10, 2), torch.rand(20, 2)
435-
>>> i1, i2 = torch.zeros(10, 1), torch.ones(20, 1)
436-
>>> train_X = torch.cat([
437-
>>> torch.cat([X1, i1], -1), torch.cat([X2, i2], -1),
438-
>>> ], dim=0)
439-
>>> train_Y = torch.cat(f1(X1), f2(X2))
440-
>>> train_Yvar = 0.1 + 0.1 * torch.rand_like(train_Y)
441-
>>> model = FixedNoiseMultiTaskGP(train_X, train_Y, train_Yvar, -1)
442-
"""
443-
warnings.warn(
444-
"`FixedNoiseMultiTaskGP` has been deprecated and will be removed in a "
445-
"future release. Please use the `MultiTaskGP` model instead. "
446-
"When `train_Yvar` is specified, `MultiTaskGP` behaves the same "
447-
"as the `FixedNoiseMultiTaskGP`.",
448-
DeprecationWarning,
449-
stacklevel=2,
450-
)
451-
super().__init__(
452-
train_X=train_X,
453-
train_Y=train_Y,
454-
train_Yvar=train_Yvar,
455-
covar_module=covar_module,
456-
task_feature=task_feature,
457-
output_tasks=output_tasks,
458-
rank=rank,
459-
task_covar_prior=task_covar_prior,
460-
input_transform=input_transform,
461-
outcome_transform=outcome_transform,
462-
)
463-
464-
465394
class KroneckerMultiTaskGP(ExactGP, GPyTorchModel, FantasizeMixin):
466395
"""Multi-task GP with Kronecker structure, using an ICM kernel.
467396

botorch/utils/test_helpers.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import torch
1818
from botorch.acquisition.objective import PosteriorTransform
19+
from botorch.exceptions.errors import UnsupportedError
1920
from botorch.models.gpytorch import GPyTorchModel
2021
from botorch.models.model import FantasizeMixin, Model
2122
from botorch.models.transforms.outcome import Standardize
@@ -66,9 +67,24 @@ def standardize_moments(
6667

6768

6869
def gen_multi_task_dataset(
69-
yvar: Optional[float] = None, task_values: Optional[List[int]] = None, **tkwargs
70+
yvar: Optional[float] = None,
71+
task_values: Optional[List[int]] = None,
72+
skip_task_features_in_datasets: bool = False,
73+
**tkwargs,
7074
) -> Tuple[MultiTaskDataset, Tuple[Tensor, Tensor, Optional[Tensor]]]:
71-
"""Constructs a multi-task dataset with two tasks, each with 10 data points."""
75+
"""Constructs a multi-task dataset with two tasks, each with 10 data points.
76+
77+
Args:
78+
yvar: The noise level to use for `train_Yvar`. If None, uses `train_Yvar=None`.
79+
task_values: The values of the task features. If None, uses [0, 1].
80+
skip_task_features_in_datasets: If True, the task features are not included in
81+
Xs of the datasets used to construct the datasets. This is useful for
82+
testing `MultiTaskDataset`.
83+
"""
84+
if task_values is not None and skip_task_features_in_datasets:
85+
raise UnsupportedError( # pragma: no cover
86+
"`task_values` and `skip_task_features_in_datasets` can't be used together."
87+
)
7288
X = torch.linspace(0, 0.95, 10, **tkwargs) + 0.05 * torch.rand(10, **tkwargs)
7389
X = X.unsqueeze(dim=-1)
7490
Y1 = torch.sin(X * (2 * math.pi)) + torch.randn_like(X) * 0.2
@@ -81,24 +97,27 @@ def gen_multi_task_dataset(
8197
Yvar1 = None if yvar is None else torch.full_like(Y1, yvar)
8298
Yvar2 = None if yvar is None else torch.full_like(Y2, yvar)
8399
train_Yvar = None if yvar is None else torch.cat([Yvar1, Yvar2])
100+
feature_slice = slice(1, None) if skip_task_features_in_datasets else slice(None)
84101
datasets = [
85102
SupervisedDataset(
86-
X=train_X[:10],
103+
X=train_X[:10, feature_slice],
87104
Y=Y1,
88105
Yvar=Yvar1,
89-
feature_names=["task", "X"],
106+
feature_names=["task", "X"][feature_slice],
90107
outcome_names=["y"],
91108
),
92109
SupervisedDataset(
93-
X=train_X[10:],
110+
X=train_X[10:, feature_slice],
94111
Y=Y2,
95112
Yvar=Yvar2,
96-
feature_names=["task", "X"],
113+
feature_names=["task", "X"][feature_slice],
97114
outcome_names=["y1"],
98115
),
99116
]
100117
dataset = MultiTaskDataset(
101-
datasets=datasets, target_outcome_name="y", task_feature_index=0
118+
datasets=datasets,
119+
target_outcome_name="y",
120+
task_feature_index=None if skip_task_features_in_datasets else 0,
102121
)
103122
return dataset, (train_X, train_Y, train_Yvar)
104123

test/models/test_contextual_multioutput.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,14 @@ def test_FixedNoiseLCEMGP(self):
123123
self.assertIsInstance(model(test_x), MultivariateNormal)
124124

125125
def test_construct_inputs(self) -> None:
126-
for with_embedding_inputs, yvar in ((True, None), (False, 0.01)):
126+
for with_embedding_inputs, yvar, skip_task_features_in_datasets in zip(
127+
(True, False), (None, 0.01), (True, False), strict=True
128+
):
127129
dataset, (train_x, train_y, train_yvar) = gen_multi_task_dataset(
128-
yvar=yvar, dtype=torch.double, device=self.device
130+
yvar=yvar,
131+
skip_task_features_in_datasets=skip_task_features_in_datasets,
132+
dtype=torch.double,
133+
device=self.device,
129134
)
130135
model_inputs = LCEMGP.construct_inputs(
131136
training_data=dataset,
@@ -139,11 +144,18 @@ def test_construct_inputs(self) -> None:
139144
),
140145
)
141146
# Check that the model inputs are valid.
142-
LCEMGP(**model_inputs)
147+
model = LCEMGP(**model_inputs)
143148
# Check that the model inputs are as expected.
144-
self.assertAllClose(model_inputs.pop("train_X"), train_x)
149+
self.assertEqual(model.all_tasks, [0, 1])
150+
if skip_task_features_in_datasets:
151+
# In this case, the task feature is appended at the end.
152+
self.assertAllClose(model_inputs.pop("train_X"), train_x[..., [1, 0]])
153+
# all_tasks is inferred from data when task features are omitted.
154+
self.assertEqual(model_inputs.pop("all_tasks"), [0, 1])
155+
else:
156+
self.assertAllClose(model_inputs.pop("train_X"), train_x)
145157
self.assertAllClose(model_inputs.pop("train_Y"), train_y)
146-
if yvar is not None:
158+
if train_yvar is not None:
147159
self.assertAllClose(model_inputs.pop("train_Yvar"), train_yvar)
148160
if with_embedding_inputs:
149161
self.assertEqual(model_inputs.pop("embs_dim_list"), [2])
@@ -155,7 +167,6 @@ def test_construct_inputs(self) -> None:
155167
model_inputs.pop("context_cat_feature"),
156168
torch.tensor([[0.4], [0.5]]),
157169
)
158-
self.assertEqual(model_inputs.pop("all_tasks"), [0, 1])
159170
self.assertEqual(model_inputs.pop("task_feature"), 0)
160171
self.assertIsNone(model_inputs.pop("output_tasks"))
161172
# Check that there are no unexpected inputs.

0 commit comments

Comments
 (0)