Source code for baybe.surrogates.gaussian_process.core
"""Gaussian process surrogates."""from__future__importannotationsfromtypingimportTYPE_CHECKING,ClassVarfromattrsimportdefine,fieldfrombaybe.searchspaceimportSearchSpacefrombaybe.surrogates.baseimportSurrogatefrombaybe.surrogates.gaussian_process.kernel_factoryimport(KernelFactory,to_kernel_factory,)frombaybe.surrogates.gaussian_process.presetsimport(GaussianProcessPreset,make_gp_from_preset,)frombaybe.surrogates.gaussian_process.presets.defaultimport(DefaultKernelFactory,_default_noise_factory,)ifTYPE_CHECKING:frombotorch.models.modelimportModelfromtorchimportTensor
[docs]@defineclassGaussianProcessSurrogate(Surrogate):"""A Gaussian process surrogate model."""# Class variablesjoint_posterior:ClassVar[bool]=True# See base class.supports_transfer_learning:ClassVar[bool]=True# See base class.# Object variableskernel_factory:KernelFactory=field(alias="kernel_or_factory",factory=DefaultKernelFactory,converter=to_kernel_factory,)"""The factory used to create the kernel of the Gaussian process. Accepts either a :class:`baybe.kernels.base.Kernel` or a :class:`.kernel_factory.KernelFactory`. When passing a :class:`baybe.kernels.base.Kernel`, it gets automatically wrapped into a :class:`.kernel_factory.PlainKernelFactory`."""# TODO: type should be Optional[botorch.models.SingleTaskGP] but is currently# omitted due to: https://github.com/python-attrs/cattrs/issues/531_model=field(init=False,default=None,eq=False)"""The actual model."""
[docs]@classmethoddeffrom_preset(preset:GaussianProcessPreset)->GaussianProcessSurrogate:"""Create a Gaussian process surrogate from one of the defined presets."""returnmake_gp_from_preset(preset)
[docs]defto_botorch(self)->Model:# noqa: D102# See base class.returnself._model
def_posterior(self,candidates:Tensor)->tuple[Tensor,Tensor]:# See base class.posterior=self._model.posterior(candidates)returnposterior.mvn.mean,posterior.mvn.covariance_matrixdef_fit(self,searchspace:SearchSpace,train_x:Tensor,train_y:Tensor)->None:# See base class.importbotorchimportgpytorchimporttorch# identify the indexes of the task and numeric dimensions# TODO: generalize to multiple task parameterstask_idx=searchspace.task_idxn_task_params=1iftask_idxisnotNoneelse0numeric_idxs=[iforiinrange(train_x.shape[1])ifi!=task_idx]# get the input bounds from the search space in BoTorch Formatbounds=torch.from_numpy(searchspace.param_bounds_comp)# TODO: use target value bounds when explicitly provided# define the input and outcome transforms# TODO [Scaling]: scaling should be handled by search space objectinput_transform=botorch.models.transforms.Normalize(train_x.shape[1],bounds=bounds,indices=numeric_idxs)outcome_transform=botorch.models.transforms.Standardize(train_y.shape[1])# extract the batch shape of the training databatch_shape=train_x.shape[:-2]# create GP meanmean_module=gpytorch.means.ConstantMean(batch_shape=batch_shape)# define the covariance module for the numeric dimensionsbase_covar_module=self.kernel_factory(searchspace,train_x,train_y).to_gpytorch(ard_num_dims=train_x.shape[-1]-n_task_params,active_dims=numeric_idxs,batch_shape=batch_shape,)# create GP covarianceiftask_idxisNone:covar_module=base_covar_moduleelse:task_covar_module=gpytorch.kernels.IndexKernel(num_tasks=searchspace.n_tasks,active_dims=task_idx,rank=searchspace.n_tasks,# TODO: make controllable)covar_module=base_covar_module*task_covar_module# create GP likelihoodnoise_prior=_default_noise_factory(searchspace,train_x,train_y)likelihood=gpytorch.likelihoods.GaussianLikelihood(noise_prior=noise_prior[0].to_gpytorch(),batch_shape=batch_shape)likelihood.noise=torch.tensor([noise_prior[1]])# construct and fit the Gaussian processself._model=botorch.models.SingleTaskGP(train_x,train_y,input_transform=input_transform,outcome_transform=outcome_transform,mean_module=mean_module,covar_module=covar_module,likelihood=likelihood,)mll=gpytorch.ExactMarginalLogLikelihood(self._model.likelihood,self._model)# TODO: This is a simple temporary workaround to avoid model overfitting# via early stopping in the presence of task parameters, which currently# have no prior configured.ifn_task_params>0:botorch.optim.fit.fit_gpytorch_mll_torch(mll,step_limit=200)else:botorch.fit.fit_gpytorch_mll(mll)