botorch.cross_validation
Cross-validation utilities using batch evaluation mode.
- class botorch.cross_validation.CVFolds(train_X, test_X, train_Y, test_Y, train_Yvar, test_Yvar)[source]
Bases:
NamedTupleCreate new instance of CVFolds(train_X, test_X, train_Y, test_Y, train_Yvar, test_Yvar)
- Parameters:
train_X (Tensor)
test_X (Tensor)
train_Y (Tensor)
test_Y (Tensor)
train_Yvar (Tensor | None)
test_Yvar (Tensor | None)
- train_X: Tensor
Alias for field number 0
- test_X: Tensor
Alias for field number 1
- train_Y: Tensor
Alias for field number 2
- test_Y: Tensor
Alias for field number 3
- train_Yvar: Tensor | None
Alias for field number 4
- test_Yvar: Tensor | None
Alias for field number 5
- class botorch.cross_validation.CVResults(model, posterior, observed_Y, observed_Yvar=None)[source]
Bases:
NamedTupleResults from cross-validation.
This named tuple contains the cross-validation predictions and observed values. For both
batch_cross_validationandefficient_loo_cv, theposteriorfield contains the predictive distribution with mean and variance accessible viaposterior.meanandposterior.variance.For
batch_cross_validation, the posterior has shapen x 1 x mwhere n is the number of folds, 1 is the single held-out point per fold, and m is the number of outputs.For
efficient_loo_cv, the posterior has the same shape structure to maintain consistency, though the underlying distribution is constructed from the efficient LOO formulas rather than from separate model fits.NOTE: When
untransform=Trueis used with a nonlinear outcome transform (e.g.,Log), the posterior will be aTransformedPosteriorrather than aGPyTorchPosterior. For ensemble models, it will be aGaussianMixturePosterior.Create new instance of CVResults(model, posterior, observed_Y, observed_Yvar)
- Parameters:
model (GPyTorchModel)
posterior (Posterior)
observed_Y (Tensor)
observed_Yvar (Tensor | None)
- model: GPyTorchModel
Alias for field number 0
- observed_Y: Tensor
Alias for field number 2
- observed_Yvar: Tensor | None
Alias for field number 3
- botorch.cross_validation.gen_loo_cv_folds(train_X, train_Y, train_Yvar=None)[source]
Generate LOO CV folds w.r.t. to
n.- Parameters:
train_X (Tensor) – A
n x dorbatch_shape x n x d(batch mode) tensor of training features.train_Y (Tensor) – A
n x (m)orbatch_shape x n x (m)(batch mode) tensor of training observations.train_Yvar (Tensor | None) – An
n x (m)orbatch_shape x n x (m)(batch mode) tensor of observed measurement noise.
- Returns:
train_X: A
n x (n-1) x dorbatch_shape x n x (n-1) x dtensor of training features.test_X: A
n x 1 x dorbatch_shape x n x 1 x dtensor of test features.train_Y: A
n x (n-1) x morbatch_shape x n x (n-1) x mtensor of training observations.test_Y: A
n x 1 x morbatch_shape x n x 1 x mtensor of test observations.train_Yvar: A
n x (n-1) x morbatch_shape x n x (n-1) x mtensor of observed measurement noise.test_Yvar: A
n x 1 x morbatch_shape x n x 1 x mtensor of observed measurement noise.
- Return type:
CVFolds NamedTuple with the following fields
Example
>>> train_X = torch.rand(10, 1) >>> train_Y = torch.rand_like(train_X) >>> cv_folds = gen_loo_cv_folds(train_X, train_Y) >>> cv_folds.train_X.shape torch.Size([10, 9, 1])
- botorch.cross_validation.batch_cross_validation(model_cls, mll_cls, cv_folds, fit_args=None, observation_noise=False, model_init_kwargs=None)[source]
Perform cross validation by using GPyTorch batch mode.
- WARNING: This function is currently very memory inefficient; use it only
for problems of small size.
- Parameters:
model_cls (type[GPyTorchModel]) – A GPyTorchModel class. This class must initialize the likelihood internally. Note: Multi-task GPs are not currently supported.
mll_cls (type[MarginalLogLikelihood]) – A MarginalLogLikelihood class.
cv_folds (CVFolds) – A CVFolds tuple. For LOO-CV with n training points, the leading dimension of size n represents the n folds (batch dimension), e.g.,
cv_folds.train_Xhas shapen x (n-1) x dandcv_folds.test_Xhas shapen x 1 x d. This batch structure enables fitting n independent GPs simultaneously.fit_args (dict[str, Any] | None) – Arguments passed along to fit_gpytorch_mll.
model_init_kwargs (dict[str, Any] | None) – Keyword arguments passed to the model constructor.
observation_noise (bool)
- Returns:
A CVResults tuple with the following fields
model: GPyTorchModel for batched cross validation
posterior: GPyTorchPosterior where the mean has shape
n x 1 x morbatch_shape x n x 1 x mobserved_Y: A
n x 1 x morbatch_shape x n x 1 x mtensor of observations.observed_Yvar: A
n x 1 x morbatch_shape x n x 1 x mtensor of observed measurement noise.
- Return type:
Example
>>> import torch >>> from botorch.cross_validation import ( ... batch_cross_validation, gen_loo_cv_folds ... ) >>> >>> from botorch.models import SingleTaskGP >>> from botorch.models.transforms.input import Normalize >>> from botorch.models.transforms.outcome import Standardize >>> from gpytorch.mlls import ExactMarginalLogLikelihood
>>> train_X = torch.rand(10, 1) >>> train_Y = torch.rand_like(train_X) >>> cv_folds = gen_loo_cv_folds(train_X, train_Y) >>> input_transform = Normalize(d=train_X.shape[-1]) >>> >>> cv_results = batch_cross_validation( ... model_cls=SingleTaskGP, ... mll_cls=ExactMarginalLogLikelihood, ... cv_folds=cv_folds, ... model_init_kwargs={ ... "input_transform": input_transform, ... }, ... )
- botorch.cross_validation.loo_cv(model, observation_noise=True, untransform=True)[source]
Compute efficient Leave-One-Out cross-validation for a GP model.
This is a high-level convenience function that automatically dispatches to the appropriate LOO CV implementation based on the model type:
For ensemble models (
_is_ensemble=True): Usesensemble_loo_cvwhich returns aGaussianMixturePosteriorwith both per-member and mixture statistics.For standard GP models: Uses
efficient_loo_cvwhich returns aGPyTorchPosteriorwith the LOO predictive distributions.
Both implementations use efficient O(n³) matrix algebra rather than the naive O(n⁴) approach of refitting models for each fold.
NOTE: This function does not refit the model to each LOO fold. The model hyperparameters are kept fixed, providing a fast approximation to full LOO CV. For models where hyperparameter changes are significant, consider using
batch_cross_validationinstead.NOTE: The
untransformparameter defaults to True, which means results are returned in the original outcome space. Callers that previously relied on results in the model’s internal (transformed) space should passuntransform=Falseexplicitly.- Parameters:
model (GPyTorchModel) – A fitted GPyTorchModel. The model type determines which LOO CV implementation is used.
observation_noise (bool) – If True (default), return the posterior predictive variance (including observation noise). If False, return the posterior variance of the latent function (excluding observation noise). The posterior variance is computed by subtracting the observation noise from the posterior predictive variance.
untransform (bool) – If True (default), untransform the LOO predictions and observed values back to the original outcome space when the model has an outcome transform (e.g.,
Standardize). This makes the results consistent withmodel.posterior()andbatch_cross_validation. If False, return results in the model’s internal (transformed) space.
- Returns:
- A named tuple containing:
model: The fitted GP model.
posterior: The LOO predictive distributions. For ensemble models, this is a
GaussianMixturePosterior; otherwise, it’s aGPyTorchPosterior.observed_Y: The observed Y values.
observed_Yvar: The observed noise variances (if applicable).
- Return type:
Example
>>> import torch >>> from botorch.cross_validation import loo_cv >>> from botorch.models import SingleTaskGP >>> from botorch.fit import fit_gpytorch_mll >>> from gpytorch.mlls import ExactMarginalLogLikelihood >>> >>> train_X = torch.rand(20, 2, dtype=torch.float64) >>> train_Y = torch.sin(train_X).sum(dim=-1, keepdim=True) >>> model = SingleTaskGP(train_X, train_Y) >>> mll = ExactMarginalLogLikelihood(model.likelihood, model) >>> fit_gpytorch_mll(mll) >>> loo_results = loo_cv(model) >>> loo_results.posterior.mean.shape torch.Size([20, 1, 1])
See also
efficient_loo_cv: Direct access to the standard GP implementation.ensemble_loo_cv: Direct access to the ensemble model implementation.batch_cross_validation: Full LOO CV with model refitting.
- botorch.cross_validation.efficient_loo_cv(model, observation_noise=True, untransform=True)[source]
Compute efficient Leave-One-Out cross-validation for a GP model.
NOTE: This function does not refit the model to each LOO fold, in contrast to batch_cross_validation. This is a memory- and compute-efficient way to compute LOO, but it does not account for potential changes in the model parameters due to the removal of a single observation. This is typically ok in cases with a lot of data, but can result in substantial differences (typically over-estimating performance) in the low data regime.
This function leverages a well-known linear algebraic identity to compute all LOO predictive distributions in O(n^3) time, compared to the naive approach which requires O(n^4) time (O(n^3) per fold for n folds).
The efficient LOO formulas for GPs are:
\[ \begin{align}\begin{aligned}\mu_{LOO,i} = y_i - \frac{[K^{-1}(y - \mu)]_i}{[K^{-1}]_{ii}}\\\sigma^2_{LOO,i} = \frac{1}{[K^{-1}]_{ii}}\end{aligned}\end{align} \]where K is the covariance matrix including observation noise. This gives the posterior predictive variance (including noise). To get the posterior variance (excluding noise), we subtract the observation noise:
\[\sigma^2_{posterior,i} = \sigma^2_{LOO,i} - \sigma^2_{noise}\]NOTE: This function assumes the model has already been fitted and that the model’s
forwardmethod returns aMultivariateNormaldistribution.- Parameters:
model (GPyTorchModel) – A fitted GPyTorchModel whose
forwardmethod returns aMultivariateNormaldistribution.observation_noise (bool) – If True (default), return the posterior predictive variance (including observation noise). If False, return the posterior variance of the latent function (excluding observation noise).
untransform (bool) – If True (default), untransform the LOO predictions and observed values back to the original outcome space when the model has an outcome transform (e.g.,
Standardize). This makes the results consistent withmodel.posterior()andbatch_cross_validation. If False, return results in the model’s internal (transformed) space.
- Returns:
- A named tuple containing:
model: The fitted GP model.
posterior: The LOO predictive distributions (typically a
GPyTorchPosterior; with nonlinear outcome transforms likeLog, aTransformedPosterior). The posterior mean and variance have shapen x 1 x morbatch_shape x n x 1 x m, matching the structure ofbatch_cross_validation(n folds, 1 held-out point per fold, m outputs). The underlying distribution has diagonal covariance since LOO predictions at different held-out points are computed independently.observed_Y: The observed Y values with shape
n x 1 x morbatch_shape x n x 1 x m.observed_Yvar: The observed noise variances (if provided) with shape
n x 1 x morbatch_shape x n x 1 x m.
- Return type:
Example
>>> import torch >>> from botorch.cross_validation import efficient_loo_cv >>> from botorch.models import SingleTaskGP >>> from botorch.fit import fit_gpytorch_mll >>> from gpytorch.mlls import ExactMarginalLogLikelihood >>> >>> train_X = torch.rand(20, 2, dtype=torch.float64) >>> train_Y = torch.sin(train_X).sum(dim=-1, keepdim=True) >>> model = SingleTaskGP(train_X, train_Y) >>> mll = ExactMarginalLogLikelihood(model.likelihood, model) >>> fit_gpytorch_mll(mll) >>> loo_results = efficient_loo_cv(model) >>> loo_results.posterior.mean.shape torch.Size([20, 1, 1])
- botorch.cross_validation.ensemble_loo_cv(model, observation_noise=True, untransform=True)[source]
Compute efficient LOO cross-validation for ensemble models.
This function computes Leave-One-Out cross-validation for ensemble models like
SaasFullyBayesianSingleTaskGP. For these models, theforwardmethod returns aMultivariateNormalwith a batch dimension containing statistics for all models in the ensemble.The LOO predictions from each ensemble member form a Gaussian mixture. This function returns a
CVResultswith aGaussianMixturePosteriorthat provides both per-member statistics (viaposterior.meanandposterior.variance) and aggregated mixture statistics (viaposterior.mixture_meanandposterior.mixture_variance).The mixture statistics are computed using the law of total variance:
\[ \begin{align}\begin{aligned}\mu_{mix} = \frac{1}{K} \sum_{k=1}^{K} \mu_k\\\sigma^2_{mix} = \frac{1}{K} \sum_{k=1}^{K} \sigma^2_k + \frac{1}{K} \sum_{k=1}^{K} \mu_k^2 - \mu_{mix}^2\end{aligned}\end{align} \]where K is the number of ensemble members.
NOTE: This function assumes the model has already been fitted (e.g., using
fit_fully_bayesian_model_nuts) and that the model is an ensemble model with_is_ensemble = True.- Parameters:
model (GPyTorchModel) – An ensemble GPyTorchModel (e.g., SaasFullyBayesianSingleTaskGP) whose
forwardmethod returns aMultivariateNormaldistribution with a batch dimension for ensemble members.observation_noise (bool) – If True (default), return the posterior predictive variance (including observation noise). If False, return the posterior variance of the latent function (excluding observation noise).
untransform (bool) – If True (default), untransform the LOO predictions and observed values back to the original outcome space when the model has an outcome transform (e.g.,
Standardize). This makes the results consistent withmodel.posterior()andbatch_cross_validation. If False, return results in the model’s internal (transformed) space.
- Returns:
- A named tuple containing:
model: The fitted ensemble GP model.
posterior: A
GaussianMixturePosteriorwith per-member shapen x num_models x 1 x m. Access per-member statistics viaposterior.meanandposterior.variance, and mixture statistics viaposterior.mixture_meanandposterior.mixture_variance.observed_Y: The observed Y values with shape
n x num_models x 1 x m, matching the posterior layout so that element-wise operations (e.g.,posterior.mean - observed_Y) work correctly.observed_Yvar: The observed noise variances (if provided) with the same shape as
observed_Y.
- Return type:
Example
>>> import torch >>> from botorch.cross_validation import ensemble_loo_cv >>> from botorch.models.fully_bayesian import SaasFullyBayesianSingleTaskGP >>> from botorch.models.fully_bayesian import fit_fully_bayesian_model_nuts >>> >>> train_X = torch.rand(20, 2, dtype=torch.float64) >>> train_Y = torch.sin(train_X).sum(dim=-1, keepdim=True) >>> model = SaasFullyBayesianSingleTaskGP(train_X, train_Y) >>> fit_fully_bayesian_model_nuts(model, warmup_steps=64, num_samples=32) >>> loo_results = ensemble_loo_cv(model) >>> loo_results.posterior.mean.shape # Per-member means torch.Size([20, 32, 1, 1]) >>> loo_results.posterior.mixture_mean.shape # Aggregated mixture mean torch.Size([20, 1, 1])