Source code for deepsensor.model.nps

from .. import backend
import lab as B

from deepsensor.data.task import Task
from typing import Tuple, Optional, Literal


[docs]def convert_task_to_nps_args(task: Task): """Infer & build model call signature from ``task`` dict. .. TODO move to ConvNP class? Args: task (:class:`~.data.task.Task`): Task object containing context and target sets. Returns: tuple[list[tuple[numpy.ndarray, numpy.ndarray]], numpy.ndarray, numpy.ndarray, dict]: ... """ context_data = list(zip(task["X_c"], task["Y_c"])) if task["X_t"] is None: raise ValueError( f"Running `neuralprocesses` model with no target locations (got {task['X_t']}). " f"Have you not provided a `target_sampling` argument to `TaskLoader`?" ) elif len(task["X_t"]) == 1 and task["Y_t"] is None: xt = task["X_t"][0] yt = None elif len(task["X_t"]) > 1 and task["Y_t"] is None: # Multiple target sets, different target locations xt = backend.nps.AggregateInput(*[(xt, i) for i, xt in enumerate(task["X_t"])]) yt = None elif len(task["X_t"]) == 1 and len(task["Y_t"]) == 1: # Single target set xt = task["X_t"][0] yt = task["Y_t"][0] elif len(task["X_t"]) > 1 and len(task["Y_t"]) > 1: # Multiple target sets, different target locations assert len(task["X_t"]) == len(task["Y_t"]) xts = [] yts = [] target_dims = [yt.shape[1] for yt in task["Y_t"]] # Map from ND target sets to 1D target sets dim_counter = 0 for i, (xt, yt) in enumerate(zip(task["X_t"], task["Y_t"])): # Repeat target locations for each target dimension in target set xts.extend([(xt, dim_counter + j) for j in range(target_dims[i])]) yts.extend([yt[:, j : j + 1] for j in range(target_dims[i])]) dim_counter += target_dims[i] xt = backend.nps.AggregateInput(*xts) yt = backend.nps.Aggregate(*yts) elif len(task["X_t"]) == 1 and len(task["Y_t"]) > 1: # Multiple target sets, same target locations; `Y_t`s along feature dim xt = task["X_t"][0] yt = B.concat(*task["Y_t"], axis=1) else: raise ValueError( f"Incorrect target locations and target observations (got {len(task['X_t'])} and {len(task['Y_t'])})" ) model_kwargs = {} if "Y_t_aux" in task.keys(): model_kwargs["aux_t"] = task["Y_t_aux"] return context_data, xt, yt, model_kwargs
[docs]def run_nps_model( neural_process, task: Task, n_samples: Optional[int] = None, requires_grad: bool = False, ): """Run ``neuralprocesses`` model. Args: neural_process (neuralprocesses.Model): Neural process model. task (:class:`~.data.task.Task`): Task object containing context and target sets. n_samples (int, optional): Number of samples to draw from the model. Defaults to ``None`` (single sample). requires_grad (bool, optional): Whether to require gradients. Defaults to ``False``. Returns: neuralprocesses.distributions.Distribution: Distribution object containing the model's predictions. """ context_data, xt, _, model_kwargs = convert_task_to_nps_args(task) if backend.str == "torch" and not requires_grad: # turn off grad import torch with torch.no_grad(): dist = neural_process( context_data, xt, **model_kwargs, num_samples=n_samples ) else: dist = neural_process(context_data, xt, **model_kwargs, num_samples=n_samples) return dist
[docs]def run_nps_model_ar(neural_process, task: Task, num_samples: int = 1): """Run ``neural_process`` in AR mode. Args: neural_process (neuralprocesses.Model): Neural process model. task (:class:`~.data.task.Task`): Task object containing context and target sets. num_samples (int, optional): Number of samples to draw from the model. Defaults to 1. Returns: tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]: Tuple of mean, variance, noiseless samples, and noisy samples. """ context_data, xt, _, _ = convert_task_to_nps_args(task) # NOTE can't use `model_kwargs` in AR mode (ie can't use auxiliary MLP at targets) mean, variance, noiseless_samples, noisy_samples = backend.nps.ar_predict( neural_process, context_data, xt, num_samples=num_samples, ) return mean, variance, noiseless_samples, noisy_samples
[docs]def construct_neural_process( dim_x: int = 2, dim_yc: int = 1, dim_yt: int = 1, dim_aux_t: Optional[int] = None, dim_lv: int = 0, conv_arch: str = "unet", unet_channels: Tuple[int, ...] = (64, 64, 64, 64), unet_resize_convs: bool = True, unet_resize_conv_interp_method: Literal["bilinear"] = "bilinear", aux_t_mlp_layers: Optional[Tuple[int, ...]] = None, likelihood: Literal["cnp", "gnp", "cnp-spikes-beta"] = "cnp", unet_kernels: int = 5, internal_density: int = 100, encoder_scales: float = 1 / 100, encoder_scales_learnable: bool = False, decoder_scale: float = 1 / 100, decoder_scale_learnable: bool = False, num_basis_functions: int = 64, epsilon: float = 1e-2, ): """Construct a ``neuralprocesses`` ConvNP model. See: https://github.com/wesselb/neuralprocesses/blob/main/neuralprocesses/architectures/convgnp.py Docstring below modified from ``neuralprocesses``. If more kwargs are needed, they must be explicitly passed to ``neuralprocesses`` constructor (not currently safe to use `**kwargs` here). Args: dim_x (int, optional): Dimensionality of the inputs. Defaults to 1. dim_y (int, optional): Dimensionality of the outputs. Defaults to 1. dim_yc (int or tuple[int], optional): Dimensionality of the outputs of the context set. You should set this if the dimensionality of the outputs of the context set is not equal to the dimensionality of the outputs of the target set. You should also set this if you want to use multiple context sets. In that case, set this equal to a tuple of integers indicating the respective output dimensionalities. dim_yt (int, optional): Dimensionality of the outputs of the target set. You should set this if the dimensionality of the outputs of the target set is not equal to the dimensionality of the outputs of the context set. dim_aux_t (int, optional): Dimensionality of target-specific auxiliary variables. internal_density (int, optional): Density of the ConvNP's internal grid (in terms of number of points per 1x1 unit square). Defaults to 100. likelihood (str, optional): Likelihood. Must be one of ``"cnp"`` (equivalently ``"het"``), ``"gnp"`` (equivalently ``"lowrank"``), or ``"cnp-spikes-beta"`` (equivalently ``"spikes-beta"``). Defaults to ``"cnp"``. conv_arch (str, optional): Convolutional architecture to use. Must be one of ``"unet[-res][-sep]"`` or ``"conv[-res][-sep]"``. Defaults to ``"unet"``. unet_channels (tuple[int], optional): Channels of every layer of the UNet. Defaults to six layers each with 64 channels. unet_kernels (int or tuple[int], optional): Sizes of the kernels in the UNet. Defaults to 5. unet_resize_convs (bool, optional): Use resize convolutions rather than transposed convolutions in the UNet. Defaults to ``False``. unet_resize_conv_interp_method (str, optional): Interpolation method for the resize convolutions in the UNet. Can be set to ``"bilinear"``. Defaults to "bilinear". num_basis_functions (int, optional): Number of basis functions for the low-rank likelihood. Defaults to 64. dim_lv (int, optional): Dimensionality of the latent variable. Setting to >0 constructs a latent neural process. Defaults to 0. encoder_scales (float or tuple[float], optional): Initial value for the length scales of the set convolutions for the context sets embeddings. Set to a tuple equal to the number of context sets to use different values for each set. Set to a single value to use the same value for all context sets. Defaults to ``1 / internal_density``. encoder_scales_learnable (bool, optional): Whether the encoder SetConv length scale(s) are learnable. Defaults to ``False``. decoder_scale (float, optional): Initial value for the length scale of the set convolution in the decoder. Defaults to ``1 / internal_density``. decoder_scale_learnable (bool, optional): Whether the decoder SetConv length scale(s) are learnable. Defaults to ``False``. aux_t_mlp_layers (tuple[int], optional): Widths of the layers of the MLP for the target-specific auxiliary variable. Defaults to three layers of width 128. epsilon (float, optional): Epsilon added by the set convolutions before dividing by the density channel. Defaults to ``1e-2``. Returns: :class:`.model.Model`: ConvNP model. Raises: NotImplementedError If specified backend has no default dtype. """ if likelihood == "cnp": likelihood = "het" elif likelihood == "gnp": likelihood = "lowrank" elif likelihood == "cnp-spikes-beta": likelihood = "spikes-beta" elif likelihood == "cnp-bernoulli-gamma": likelihood = "bernoulli-gamma" # Log the call signature for `construct_convgnp` config = dict(locals()) if backend.str == "torch": import torch dtype = torch.float32 elif backend.str == "tf": import tensorflow as tf dtype = tf.float32 else: raise NotImplementedError(f"Backend {backend.str} has no default dtype.") neural_process = backend.nps.construct_convgnp( dim_x=dim_x, dim_yc=dim_yc, dim_yt=dim_yt, dim_aux_t=dim_aux_t, dim_lv=dim_lv, likelihood=likelihood, conv_arch=conv_arch, unet_channels=tuple(unet_channels), unet_resize_convs=unet_resize_convs, unet_resize_conv_interp_method=unet_resize_conv_interp_method, aux_t_mlp_layers=aux_t_mlp_layers, unet_kernels=unet_kernels, # Use a stride of 1 for the first layer and 2 for all other layers unet_strides=(1, *(2,) * (len(unet_channels) - 1)), points_per_unit=internal_density, encoder_scales=encoder_scales, encoder_scales_learnable=encoder_scales_learnable, decoder_scale=decoder_scale, decoder_scale_learnable=decoder_scale_learnable, num_basis_functions=num_basis_functions, epsilon=epsilon, dtype=dtype, ) return neural_process, config
[docs]def compute_encoding_tensor(model, task: Task): """Compute the encoding tensor for a given task. Args: model (...): Model object. task (:class:`~.data.task.Task`): Task object containing context and target sets. Returns: encoding : :class:`numpy:numpy.ndarray` Encoding tensor? #TODO """ neural_process_encoder = backend.nps.Model(model.model.encoder, lambda x: x) task = model.modify_task(task) encoding = B.to_numpy(run_nps_model(neural_process_encoder, task)) return encoding