From 7968c663965c1c5a0d921159430af9ef1fa5394d Mon Sep 17 00:00:00 2001
From: Agustinus Kristiadi Applying the GGN (Generalized Gauss-Newton) approximation for the Hessian in the Laplace approximation of the posterior
+turns the underlying probabilistic model from a BNN into a GLM (generalized linear model).
+This GLM (in the weight space) is equivalent to a GP (in the function space), see
+Approximate Inference Turns Deep Networks into Gaussian Processes (Khan et al., 2019) This class implements the (approximate) GP inference through which
+we obtain the desired quantities (posterior predictive, marginal log-likelihood).
+See Improving predictions of Bayesian neural nets via local linearization (Immer et al., 2021)
+for more details. Note that for See Computes log determinant term in GP marginal likelihood For log determinant term := \log | I + D^{1/2}K D^{1/2} | For log determinant term := \log | K + \sigma_2 I | Compute scatter term in GP log marginal likelihood. For scatter term := f K^{-1} f^{T} For scatter term := (y - m)K^{-1}(y -m )^T ,
+where m is the mean of the GP prior, which in our case corresponds to
+ m := f + J (\theta - \theta_{MAP}) Fit the Laplace approximation of a GP posterior. Sample from the posterior predictive on input data GP posterior variance: k_{**} - K_{*M} (K_{MM}+ L_{MM}^{-1})^{-1} K_{M*} GP posterior covariance: k_{**} - K_{*M} (K_{MM}+ L_{MM}^{-1})^{-1} K_{M*} Compute the Laplace approximation to the log marginal likelihood.
+Requires that the Laplace approximation has been fit before.
+The resulting torch.Tensor is differentiable in Laplace approximation with low-rank log likelihood Hessian (approximation).
@@ -732,6 +932,8 @@ Note that only See Interface for asdfghjkl backend. Compute Jacobians \nabla_\theta f(x;\theta) at current parameter \theta
-using asdfghjkl's gradient per output dimension. Compute gradients \nabla_\theta \ell(f(x;\theta, y) at current parameter
-\theta using asdfghjkl's backend. Implementation of the Implementation of the Interface for asdfghjkl backend. For full compatibility, install this package in a fresh virtual env.
+ [!IMPORTANT]
We assume Python >= 3.9 since lower versions are (soon to be) deprecated.
-PyTorch version 2.0 and up is also required for full compatibility.
-To install laplace with Parameters
Subclasses
-
Instance variables
@@ -492,6 +492,206 @@ Inherited members
+
+class FunctionalLaplace
+(model: nn.Module, likelihood: Likelihood | str, n_subset: int, sigma_noise: float | torch.Tensor = 1.0, prior_precision: float | torch.Tensor = 1.0, prior_mean: float | torch.Tensor = 0.0, temperature: float = 1.0, enable_backprop: bool = False, dict_key_x='inputs_id', dict_key_y='labels', backend: type[CurvatureInterface] | None = laplace.curvature.backpack.BackPackGGN, backend_kwargs: dict[str, Any] | None = None, independent_outputs: bool = False, seed: int = 0)
+
likelihood='classification'
, we approximate L_{NN} with a diagonal matrix
+( L_{NN} is a block-diagonal matrix, where blocks represent Hessians of per-data-point log-likelihood w.r.t.
+neural network output f , See Appendix A.2.1 for exact definition). We
+resort to such an approximation because of the (possible) errors found in Laplace approximation for
+multiclass GP classification in Chapter 3.5 of R&W 2006 GP book,
+see the question
+here
+for more details. Alternatively, one could also resort to one-vs-one or one-vs-rest implementations
+for multiclass classification, however, that is not (yet) supported here.Parameters
+
+
+num_data
: int
diagonal_kernel
: bool
diagonal_kernel=True
, only a diagonal of a GP kernel is used. This is (somewhat) equivalent to
+assuming independent GPs across output channels.BaseLaplace
class for the full interface.Ancestors
+
+
+Subclasses
+
+Instance variables
+
+
+var gp_kernel_prior_variance
var log_det_ratio : torch.Tensor
classification
we use eq. (3.44) from Chapter 3.5 from
+GP book R&W 2006 with
+(note that we always use diagonal approximation D of the Hessian of log likelihood w.r.t. f):regression
, we use "standard" GP marginal likelihood:var scatter : torch.Tensor
classification
we use eq. (3.44) from Chapter 3.5 from
+GP book R&W 2006 with \hat{f} = f :regression
, we use "standard" GP marginal likelihood:var prior_precision
Methods
+
+
+
+def fit(self, train_loader: DataLoader | MutableMapping, progress_bar: bool = False)
+
Parameters
+
+
train_loader
: torch.data.utils.DataLoader
train_loader.dataset
needs to be set to access N, size of the data set
+train_loader.batch_size
needs to be set to access b batch_sizeprogress_bar
: bool
+def predictive_samples(self, x: torch.Tensor | MutableMapping[str, torch.Tensor | Any], pred_type: PredType | str = PredType.GLM, n_samples: int = 100, diagonal_output: bool = False, generator: torch.Generator | None = None) ‑> torch.Tensor
+
x
.
+Can be used, for example, for Thompson sampling.Parameters
+
+
+x
: torch.Tensor
or MutableMapping
(batch_size, input_shape)
pred_type
: {'glm'}
, default='glm'
n_samples
: int
diagonal_output
: bool
pred_type='glm'
.generator
: torch.Generator
, optionalReturns
+
+
samples
: torch.Tensor
(n_samples, batch_size, output_shape)
+def functional_variance(self, Js_star: torch.Tensor) ‑> torch.Tensor
+
Parameters
+
+
+Js_star
: torch.Tensor
of shape (N*, C, P)
Returns
+
+
f_var
: torch.Tensor
of shape (N*,C, C)
+def functional_covariance(self, Js_star: torch.Tensor) ‑> torch.Tensor
+
Parameters
+
+
+Js_star
: torch.Tensor
of shape (N*, C, P)
Returns
+
+
f_var
: torch.Tensor
of shape (N*xC, N*xC)
+def optimize_prior_precision(self, pred_type: PredType | str = PredType.GP, method: TuningMethod | str = TuningMethod.MARGLIK, n_steps: int = 100, lr: float = 0.1, init_prior_prec: float | torch.Tensor = 1.0, prior_structure: PriorStructure | str = PriorStructure.SCALAR, val_loader: DataLoader | None = None, loss: torchmetrics.Metric | Callable[[torch.Tensor], torch.Tensor | float] | None = None, log_prior_prec_min: float = -4, log_prior_prec_max: float = 4, grid_size: int = 100, link_approx: LinkApprox | str = LinkApprox.PROBIT, n_samples: int = 100, verbose: bool = False, progress_bar: bool = False) ‑> None
+
optimize_prior_precision_base
from BaseLaplace
with pred_type='gp'
+def log_marginal_likelihood(self, prior_precision: torch.Tensor | None = None, sigma_noise: torch.Tensor | None = None) ‑> torch.Tensor
+
prior_precision
and
+sigma_noise
if these have gradients enabled.
+By passing prior_precision
or sigma_noise
, the current value is
+overwritten. This is useful for iterating on the log marginal likelihood.Parameters
+
+
+prior_precision
: torch.Tensor
, optionalprior_precision
valuesigma_noise
: torch.Tensor
, optionalReturns
+
+
log_marglik
: torch.Tensor
+def state_dict(self) ‑> dict
+
+def load_state_dict(self, state_dict: dict)
+
Inherited members
+
+
+BaseLaplace
:
+
+
class FullLaplace
(model: nn.Module, likelihood: Likelihood | str, sigma_noise: float | torch.Tensor = 1.0, prior_precision: float | torch.Tensor = 1.0, prior_mean: float | torch.Tensor = 0.0, temperature: float = 1.0, enable_backprop: bool = False, dict_key_x: str = 'input_ids', dict_key_y: str = 'labels', backend: type[CurvatureInterface] | None = None, backend_kwargs: dict[str, Any] | None = None)
@@ -718,7 +918,7 @@
Inherited members
class LowRankLaplace
-(model: nn.Module, likelihood: Likelihood | str, sigma_noise: float | torch.Tensor = 1, prior_precision: float | torch.Tensor = 1, prior_mean: float | torch.Tensor = 0, temperature: float = 1, enable_backprop: bool = False, dict_key_x: str = 'inputs_id', dict_key_y: str = 'labels', backend=laplace.curvature.asdfghjkl.AsdfghjklHessian, backend_kwargs: dict[str, Any] | None = None)
+(model: nn.Module, likelihood: Likelihood | str, backend: type[CurvatureInterface] = laplace.curvature.curvature.CurvatureInterface, sigma_noise: float | torch.Tensor = 1, prior_precision: float | torch.Tensor = 1, prior_mean: float | torch.Tensor = 0, temperature: float = 1, enable_backprop: bool = False, dict_key_x: str = 'inputs_id', dict_key_y: str = 'labels', backend_kwargs: dict[str, Any] | None = None)
Inherited members
imes K matrix
if we have a rank of K.
+AsdfghjklHessian
backend is supported. Install it via:
+pip install git+https://git@github.com/wiseodd/asdl@asdfghjklBaseLaplace
for the full interface.Ancestors
@@ -825,6 +1027,19 @@
+FunctionalLaplace
+
+
+fit
predictive_samples
functional_variance
functional_covariance
optimize_prior_precision
log_marginal_likelihood
state_dict
load_state_dict
FullLaplace
Inherited members
-class AsdfghjklInterface
-(model: nn.Module, likelihood: Likelihood | str, last_layer: bool = False, subnetwork_indices: torch.LongTensor | None = None, dict_key_x: str = 'input_ids', dict_key_y: str = 'labels')
-
Ancestors
-
-Subclasses
-
-Methods
-
-
-
-def jacobians(self, x: torch.Tensor | MutableMapping[str, torch.Tensor | Any], enable_backprop: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]
-
Parameters
-
-
-x
: torch.Tensor
(batch, input_shape)
on compatible device with model.enable_backprop
: bool
, default = False
Returns
-
-
Js
: torch.Tensor
(batch, parameters, outputs)
f
: torch.Tensor
(batch, outputs)
-def gradients(self, x: torch.Tensor | MutableMapping[str, torch.Tensor | Any], y: torch.Tensor) ‑> tuple[torch.Tensor, torch.Tensor]
-
Parameters
-
-
-x
: torch.Tensor
(batch, input_shape)
on compatible device with model.y
: torch.Tensor
Returns
-
-
loss
: torch.Tensor
Gs
: torch.Tensor
(batch, parameters)
Inherited members
-
-
-CurvatureInterface
:
-
-
-class AsdfghjklGGN
-(model: nn.Module, likelihood: Likelihood | str, last_layer: bool = False, subnetwork_indices: torch.LongTensor | None = None, dict_key_x: str = 'input_ids', dict_key_y: str = 'labels', stochastic: bool = False)
-
GGNInterface
using asdfghjkl.Ancestors
-
-Inherited members
-
-
-AsdfghjklInterface
:
-
-
-class AsdfghjklEF
-(model: nn.Module, likelihood: Likelihood | None, last_layer: bool = False, dict_key_x: str = 'input_ids', dict_key_y: str = 'labels')
-
EFInterface
using asdfghjkl.Ancestors
-
-Inherited members
-
-
-AsdfghjklInterface
:
-
-
-class AsdfghjklHessian
-(model: nn.Module, likelihood: Likelihood | str, last_layer: bool = False, dict_key_x: str = 'input_ids', dict_key_y: str = 'labels', low_rank: int = 10)
-
Ancestors
-
-Methods
-
-
-
-def eig_lowrank(self, data_loader: DataLoader) ‑> tuple[torch.Tensor, torch.Tensor, torch.Tensor]
-
Inherited members
-
-
-AsdfghjklInterface
:
-
-
class AsdlInterface
(model: nn.Module, likelihood: Likelihood | str, last_layer: bool = False, subnetwork_indices: torch.LongTensor | None = None, dict_key_x: str = 'input_ids', dict_key_y: str = 'labels')
@@ -1056,25 +893,6 @@
BackPackEF
-
-AsdfghjklInterface
-AsdfghjklGGN
-AsdfghjklEF
-AsdfghjklHessian
-
-eig_lowrank
AsdlInterface
jacobians
Table of contents
Setup
-
+
pip
, run the following:
To install laplace with pip
, run the following:
pip install laplace-torch
-For development purposes, clone the repository and then install:
+Additionally, if you want to use the asdfghjkl
backend, please install it via:
pip install git+https://git@github.com/wiseodd/asdl@asdfghjkl
+
+For development purposes, e.g. if you would like to make contributions, +clone the repository and then install:
# first install the build system:
pip install --upgrade pip wheel packaging
-# then install the develop
+# then install the develop
pip install -e ".[all]"
++[!NOTE] +See contributing guideline. +We're looking forward to your contributions!
+
In the following example, a pre-trained model is loaded, @@ -114,9 +125,9 @@
First, we make use of SubnetLaplace
, where we specify the subnetwork by
+generating a list of indices for the active model parameters.
from laplace import Laplace
# Pre-trained model
@@ -264,6 +277,26 @@ Subnetwork Laplace
subnetwork_indices=subnetwork_indices)
la.fit(train_loader)
+Besides SubnetLaplace
, you can, as already mentioned, also treat the last
+layer only using Laplace(..., subset_of_weights='last_layer')
, which uses
+LLLaplace
. As a third method, you may define a subnetwork by disabling
+gradients of fixed model parameters. The different methods target different use
+cases. Each method has pros and cons, please see this
+discussion
+for details. In summary
LLLaplace
+as well. Always use subset_of_weights='all'
for this method.SubnetLaplace
since it avoids calculating full Jacobians firstParameter
level and not for
+individual weights, so this doesn't cover all cases that SubnetLaplace
+offers such as Largest*SubnetMask
or RandomSubnetMask
LLLaplace
: last-layer specific code with improved performance (#145)SubnetLaplace
: more fine-grained partitioning such as
+LargestMagnitudeSubnetMask
As with plain torch
, we support to ways to serialize data.
One is the familiar state_dict
approach. Here you need to save and re-create
@@ -314,7 +347,7 @@
The laplace package consists of two main components:
laplace.BaseLaplace
that implement different sparsity structures: different subsets of weights ("all"
, "subnetwork"
and "last_layer"
) and different structures of the Hessian approximation ("full"
, "kron"
, "lowrank"
and "diag"
). This results in nine currently available options: FullLaplace
, KronLaplace
, DiagLaplace
, the corresponding last-layer variations FullLLLaplace
, KronLLLaplace
, and DiagLLLaplace
(which are all subclasses of laplace.LLLaplace
), laplace.SubnetLaplace
(which only supports "full"
and "diag"
Hessian approximations) and LowRankLaplace
(which only supports inference over "all"
weights). All of these can be conveniently accessed via the laplace.Laplace
function.laplace.BaseLaplace
that implement different sparsity structures: different subsets of weights ('all'
, 'subnetwork'
and 'last_layer'
) and different structures of the Hessian approximation ('full'
, 'kron'
, 'lowrank'
, 'diag'
and 'gp'
). This results in ten currently available options: FullLaplace
, KronLaplace
, DiagLaplace
, FunctionalLaplace
the corresponding last-layer variations FullLLLaplace
, KronLLLaplace
, DiagLLLaplace
and FunctionalLLLaplace
(which are all subclasses of laplace.LLLaplace
), laplace.SubnetLaplace
(which only supports 'full'
and 'diag'
Hessian approximations) and LowRankLaplace
(which only supports inference over 'all'
weights). All of these can be conveniently accessed via the laplace.Laplace
function.laplace.curvature
which provide access to Hessian approximations of
the corresponding sparsity structures, for example, the diagonal GGN.
-def Laplace(model: torch.nn.Module, likelihood: Likelihood | str, subset_of_weights: SubsetOfWeights | str = SubsetOfWeights.LAST_LAYER, hessian_structure: HessianStructure | str = HessianStructure.KRON, *args, **kwargs) ‑> ParametricLaplace
+def Laplace(model: torch.nn.Module, likelihood: Likelihood | str, subset_of_weights: SubsetOfWeights | str = SubsetOfWeights.LAST_LAYER, hessian_structure: HessianStructure | str = HessianStructure.KRON, *args, **kwargs) ‑> BaseLaplace
Simplified Laplace access using strings instead of different classes.
@@ -982,13 +1015,14 @@subset_of_weights
: SubsetofWeights
or {'last_layer', 'subnetwork', 'all'}
, default=SubsetOfWeights.LAST_LAYER
hessian_structure
: HessianStructure
or str in {'diag', 'kron', 'full', 'lowrank'}
, default=HessianStructure.KRON
hessian_structure
: HessianStructure
or str in {'diag', 'kron', 'full', 'lowrank', 'gp'}
, default=HessianStructure.KRON
laplace
: ParametricLaplace
laplace
: BaseLaplace
@@ -1141,7 +1175,7 @@ Parameters
+class FunctionalLaplace
+(model: nn.Module, likelihood: Likelihood | str, n_subset: int, sigma_noise: float | torch.Tensor = 1.0, prior_precision: float | torch.Tensor = 1.0, prior_mean: float | torch.Tensor = 0.0, temperature: float = 1.0, enable_backprop: bool = False, dict_key_x='inputs_id', dict_key_y='labels', backend: type[CurvatureInterface] | None = laplace.curvature.backpack.BackPackGGN, backend_kwargs: dict[str, Any] | None = None, independent_outputs: bool = False, seed: int = 0)
+
Applying the GGN (Generalized Gauss-Newton) approximation for the Hessian in the Laplace approximation of the posterior +turns the underlying probabilistic model from a BNN into a GLM (generalized linear model). +This GLM (in the weight space) is equivalent to a GP (in the function space), see +Approximate Inference Turns Deep Networks into Gaussian Processes (Khan et al., 2019)
+This class implements the (approximate) GP inference through which +we obtain the desired quantities (posterior predictive, marginal log-likelihood). +See Improving predictions of Bayesian neural nets via local linearization (Immer et al., 2021) +for more details.
+Note that for likelihood='classification'
, we approximate L_{NN} with a diagonal matrix
+( L_{NN} is a block-diagonal matrix, where blocks represent Hessians of per-data-point log-likelihood w.r.t.
+neural network output f , See Appendix A.2.1 for exact definition). We
+resort to such an approximation because of the (possible) errors found in Laplace approximation for
+multiclass GP classification in Chapter 3.5 of R&W 2006 GP book,
+see the question
+here
+for more details. Alternatively, one could also resort to one-vs-one or one-vs-rest implementations
+for multiclass classification, however, that is not (yet) supported here.
num_data
: int
diagonal_kernel
: bool
diagonal_kernel=True
, only a diagonal of a GP kernel is used. This is (somewhat) equivalent to
+assuming independent GPs across output channels.See BaseLaplace
class for the full interface.
var gp_kernel_prior_variance
var log_det_ratio : torch.Tensor
Computes log determinant term in GP marginal likelihood
+For classification
we use eq. (3.44) from Chapter 3.5 from
+GP book R&W 2006 with
+(note that we always use diagonal approximation D of the Hessian of log likelihood w.r.t. f):
log determinant term := \log | I + D^{1/2}K D^{1/2} |
+For regression
, we use "standard" GP marginal likelihood:
log determinant term := \log | K + \sigma_2 I |
var scatter : torch.Tensor
Compute scatter term in GP log marginal likelihood.
+For classification
we use eq. (3.44) from Chapter 3.5 from
+GP book R&W 2006 with \hat{f} = f :
scatter term := f K^{-1} f^{T}
+For regression
, we use "standard" GP marginal likelihood:
scatter term := (y - m)K^{-1}(y -m )^T , +where m is the mean of the GP prior, which in our case corresponds to + m := f + J (\theta - \theta_{MAP})
var prior_precision
+def fit(self, train_loader: DataLoader | MutableMapping, progress_bar: bool = False)
+
Fit the Laplace approximation of a GP posterior.
+train_loader
: torch.data.utils.DataLoader
train_loader.dataset
needs to be set to access N, size of the data set
+train_loader.batch_size
needs to be set to access b batch_sizeprogress_bar
: bool
+def predictive_samples(self, x: torch.Tensor | MutableMapping[str, torch.Tensor | Any], pred_type: PredType | str = PredType.GLM, n_samples: int = 100, diagonal_output: bool = False, generator: torch.Generator | None = None) ‑> torch.Tensor
+
Sample from the posterior predictive on input data x
.
+Can be used, for example, for Thompson sampling.
x
: torch.Tensor
or MutableMapping
(batch_size, input_shape)
pred_type
: {'glm'}
, default='glm'
n_samples
: int
diagonal_output
: bool
pred_type='glm'
.generator
: torch.Generator
, optionalsamples
: torch.Tensor
(n_samples, batch_size, output_shape)
+def functional_variance(self, Js_star: torch.Tensor) ‑> torch.Tensor
+
GP posterior variance:
+k_{**} - K_{*M} (K_{MM}+ L_{MM}^{-1})^{-1} K_{M*}
+Js_star
: torch.Tensor
of shape (N*, C, P)
f_var
: torch.Tensor
of shape (N*,C, C)
+def functional_covariance(self, Js_star: torch.Tensor) ‑> torch.Tensor
+
GP posterior covariance:
+k_{**} - K_{*M} (K_{MM}+ L_{MM}^{-1})^{-1} K_{M*}
+Js_star
: torch.Tensor
of shape (N*, C, P)
f_var
: torch.Tensor
of shape (N*xC, N*xC)
+def optimize_prior_precision(self, pred_type: PredType | str = PredType.GP, method: TuningMethod | str = TuningMethod.MARGLIK, n_steps: int = 100, lr: float = 0.1, init_prior_prec: float | torch.Tensor = 1.0, prior_structure: PriorStructure | str = PriorStructure.SCALAR, val_loader: DataLoader | None = None, loss: torchmetrics.Metric | Callable[[torch.Tensor], torch.Tensor | float] | None = None, log_prior_prec_min: float = -4, log_prior_prec_max: float = 4, grid_size: int = 100, link_approx: LinkApprox | str = LinkApprox.PROBIT, n_samples: int = 100, verbose: bool = False, progress_bar: bool = False) ‑> None
+
optimize_prior_precision_base
from BaseLaplace
with pred_type='gp'
+def log_marginal_likelihood(self, prior_precision: torch.Tensor | None = None, sigma_noise: torch.Tensor | None = None) ‑> torch.Tensor
+
Compute the Laplace approximation to the log marginal likelihood.
+Requires that the Laplace approximation has been fit before.
+The resulting torch.Tensor is differentiable in prior_precision
and
+sigma_noise
if these have gradients enabled.
+By passing prior_precision
or sigma_noise
, the current value is
+overwritten. This is useful for iterating on the log marginal likelihood.
prior_precision
: torch.Tensor
, optionalprior_precision
valuesigma_noise
: torch.Tensor
, optionallog_marglik
: torch.Tensor
+def state_dict(self) ‑> dict
+
+def load_state_dict(self, state_dict: dict)
+
BaseLaplace
:
+
+
class LowRankLaplace
-(model: nn.Module, likelihood: Likelihood | str, sigma_noise: float | torch.Tensor = 1, prior_precision: float | torch.Tensor = 1, prior_mean: float | torch.Tensor = 0, temperature: float = 1, enable_backprop: bool = False, dict_key_x: str = 'inputs_id', dict_key_y: str = 'labels', backend=laplace.curvature.asdfghjkl.AsdfghjklHessian, backend_kwargs: dict[str, Any] | None = None)
+(model: nn.Module, likelihood: Likelihood | str, backend: type[CurvatureInterface] = laplace.curvature.curvature.CurvatureInterface, sigma_noise: float | torch.Tensor = 1, prior_precision: float | torch.Tensor = 1, prior_mean: float | torch.Tensor = 0, temperature: float = 1, enable_backprop: bool = False, dict_key_x: str = 'inputs_id', dict_key_y: str = 'labels', backend_kwargs: dict[str, Any] | None = None)
Laplace approximation with low-rank log likelihood Hessian (approximation). @@ -1792,6 +2026,8 @@
Note that only AsdfghjklHessian
backend is supported. Install it via:
+pip install git+https://git@github.com/wiseodd/asdl@asdfghjkl
See BaseLaplace
for the full interface.
+class FunctionalLLLaplace
+(model: nn.Module, likelihood: Likelihood | str, n_subset: int, sigma_noise: float | torch.Tensor = 1.0, prior_precision: float | torch.Tensor = 1.0, prior_mean: float | torch.Tensor = 0.0, temperature: float = 1.0, enable_backprop: bool = False, feature_reduction: FeatureReduction = None, dict_key_x: str = 'inputs_id', dict_key_y: str = 'labels', last_layer_name: str = None, backend: type[CurvatureInterface] | None = laplace.curvature.backpack.BackPackGGN, backend_kwargs: dict[str, Any] | None = None, independent_outputs: bool = False, seed: int = 0)
+
Here not much changes in terms of GP inference compared to FunctionalLaplace class. +Since now we treat only the last layer probabilistically and the rest of the network is used as a "fixed feature +extractor", that means that the X \in \mathbb{R}^{M \times D} in GP inference changes +to \tilde{X} \in \mathbb{R}^{M \times l_{n-1}} , +where l_{n-1} is the dimension of the output +of the penultimate NN layer.
+See FunctionalLaplace
for the full interface.
+def fit(self, train_loader: DataLoader) ‑> None
+
Fit the Laplace approximation of a GP posterior.
+train_loader
: torch.data.utils.DataLoader
train_loader.dataset
needs to be set to access N, size of the data set
+train_loader.batch_size
needs to be set to access b batch_size
+def state_dict(self) ‑> dict
+
+def load_state_dict(self, state_dict: dict)
+
class SubnetLaplace
(model: nn.Module, likelihood: Likelihood | str, subnetwork_indices: torch.LongTensor, sigma_noise: float | torch.Tensor = 1.0, prior_precision: float | torch.Tensor = 1.0, prior_mean: float | torch.Tensor = 0.0, temperature: float = 1.0, backend: Type[CurvatureInterface] | None = None, backend_kwargs: dict | None = None, asdl_fisher_kwargs: dict | None = None)
@@ -2423,6 +2720,10 @@ Class variables
+var GP
+
+
+
@@ -2473,6 +2774,10 @@ Class variables
+var GP
+
+
+
@@ -2663,6 +2968,19 @@ KronLaplace
DiagLaplace
+FunctionalLaplace
+
+
+
LowRankLaplace
@@ -2684,6 +3002,14 @@ KronLLL
DiagLLLaplace
+FunctionalLLLaplace
+
+fit
+state_dict
+load_state_dict
+
+
+
SubnetLaplace
assemble_full_samples
diff --git a/docs/laplace.html b/docs/laplace.html
index f0637b1..c9ca171 100644
--- a/docs/laplace.html
+++ b/docs/laplace.html
@@ -32,7 +32,7 @@ Module laplace.laplace
Functions
-def Laplace(model: torch.nn.Module, likelihood: Likelihood | str, subset_of_weights: SubsetOfWeights | str = SubsetOfWeights.LAST_LAYER, hessian_structure: HessianStructure | str = HessianStructure.KRON, *args, **kwargs) ‑> ParametricLaplace
+def Laplace(model: torch.nn.Module, likelihood: Likelihood | str, subset_of_weights: SubsetOfWeights | str = SubsetOfWeights.LAST_LAYER, hessian_structure: HessianStructure | str = HessianStructure.KRON, *args, **kwargs) ‑> BaseLaplace
-
Simplified Laplace access using strings instead of different classes.
@@ -44,13 +44,14 @@ Parameters
-
subset_of_weights
: SubsetofWeights
or {'last_layer', 'subnetwork', 'all'}
, default=SubsetOfWeights.LAST_LAYER
- subset of weights to consider for inference
-hessian_structure
: HessianStructure
or str in {'diag', 'kron', 'full', 'lowrank'}
, default=HessianStructure.KRON
-- structure of the Hessian approximation
+hessian_structure
: HessianStructure
or str in {'diag', 'kron', 'full', 'lowrank', 'gp'}
, default=HessianStructure.KRON
+- structure of the Hessian approximation (note that in case of 'gp',
+we are not actually doing any Hessian approximation, the inference is instead done in the functional space)
Returns
-laplace
: ParametricLaplace
-- chosen subclass of ParametricLaplace instantiated with additional arguments
+laplace
: BaseLaplace
+- chosen subclass of BaseLaplace instantiated with additional arguments
diff --git a/docs/lllaplace.html b/docs/lllaplace.html
index 339237f..c43df23 100644
--- a/docs/lllaplace.html
+++ b/docs/lllaplace.html
@@ -341,6 +341,67 @@ Inherited members
+
+class FunctionalLLLaplace
+(model: nn.Module, likelihood: Likelihood | str, n_subset: int, sigma_noise: float | torch.Tensor = 1.0, prior_precision: float | torch.Tensor = 1.0, prior_mean: float | torch.Tensor = 0.0, temperature: float = 1.0, enable_backprop: bool = False, feature_reduction: FeatureReduction = None, dict_key_x: str = 'inputs_id', dict_key_y: str = 'labels', last_layer_name: str = None, backend: type[CurvatureInterface] | None = laplace.curvature.backpack.BackPackGGN, backend_kwargs: dict[str, Any] | None = None, independent_outputs: bool = False, seed: int = 0)
+
+
+Here not much changes in terms of GP inference compared to FunctionalLaplace class.
+Since now we treat only the last layer probabilistically and the rest of the network is used as a "fixed feature
+extractor", that means that the X \in \mathbb{R}^{M \times D} in GP inference changes
+to \tilde{X} \in \mathbb{R}^{M \times l_{n-1}} ,
+where l_{n-1} is the dimension of the output
+of the penultimate NN layer.
+See FunctionalLaplace
for the full interface.
+Ancestors
+
+Methods
+
+
+def fit(self, train_loader: DataLoader) ‑> None
+
+-
+
Fit the Laplace approximation of a GP posterior.
+Parameters
+
+train_loader
: torch.data.utils.DataLoader
+train_loader.dataset
needs to be set to access N, size of the data set
+train_loader.batch_size
needs to be set to access b batch_size
+
+
+
+def state_dict(self) ‑> dict
+
+-
+
+
+
+def load_state_dict(self, state_dict: dict)
+
+-
+
+
+
+Inherited members
+
+
@@ -375,6 +436,14 @@ DiagLLLaplace
+
+FunctionalLLLaplace
+
+fit
+state_dict
+load_state_dict
+
+
diff --git a/docs/regression_example.png b/docs/regression_example.png
index cdbd951fa8ce5a0358f3747a423b77bff21a89c6..4b81bb5bfa50d7d86676d2928ab1106a7fc251ab 100644
GIT binary patch
literal 27918
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