Source code for fastbnns.bnn.priors

"""Definitions of prior distributions over neural network parameters."""

from collections.abc import Iterable
import copy
from typing import Any, Optional

import torch
import torch.distributions as dist


[docs] class Distribution(torch.nn.Module): """Distribution wrapper to facilitate device transfers.""" def __init__( self, distribution: Optional[dist.Distribution] = None, *args: Any, **kwargs: Any ): """Initialize wrapper.""" super().__init__() # If `distribution` is passed, we'll build the wrapper automatically. if distribution is not None: self._distribution = copy.deepcopy(distribution) for key, val in distribution.__dict__.items(): if isinstance(val, torch.Tensor): self.register_buffer(key, val) @property def distribution(self) -> dist.Distribution: """Prepare an instance of the distribution.""" for key, val in self._distribution.__dict__.items(): if isinstance(val, torch.Tensor): buffer_val = getattr(self, key) setattr(self._distribution, key, buffer_val) return self._distribution
[docs] def log_prob(self, x: torch.Tensor) -> torch.Tensor: """Compute the log PDF of the prior at points `x`.""" return self.distribution.log_prob(x)
[docs] def sample(self, sample_shape: Iterable = torch.Size()) -> torch.Tensor: """Generate samples from the prior of size `sample_shape`.""" return self.distribution.sample(sample_shape=sample_shape)
[docs] class SpikeSlab(Distribution): """Spike-slab Gaussian Mixture Model prior.""" def __init__( self, loc: torch.Tensor = torch.tensor([0.0, 0.0]), scale: torch.Tensor = torch.tensor([0.1, 1.0]), probs: torch.Tensor = torch.tensor([0.5, 0.5]), ): super().__init__() self.register_buffer("loc", loc) self.register_buffer("scale", scale) self.register_buffer("probs", probs) @property def distribution(self) -> dist.Distribution: """Prepare an instance of the distribution.""" mixture_distribution = dist.Categorical(probs=self.probs) return dist.MixtureSameFamily( mixture_distribution=mixture_distribution, component_distribution=dist.Normal(loc=self.loc, scale=self.scale), )
if __name__ == "__main__": import matplotlib.pyplot as plt # Example using an SpikeSlab prior. prior = SpikeSlab() sample = prior.sample(sample_shape=(100, 1)) x = torch.linspace(-5.0, 5.0, 1000) pdf = torch.exp(prior.log_prob(x)) fig, ax = plt.subplots() ax.hist(sample, density=True) ax.plot(x, pdf) plt.show()