diff --git a/pyrobolearn/models/gp/gplvm.py b/pyrobolearn/models/gp/gplvm.py new file mode 100644 index 0000000..6257da6 --- /dev/null +++ b/pyrobolearn/models/gp/gplvm.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +"""Provide the Gaussian Process Latent Variable Model (GPLVM) + +This file provides the GPLVM and shared GPLVM. +""" + +import torch +import gpytorch + +from pyrobolearn.models.gp.gp import GPR + + +__author__ = "Brian Delhaisse" +__copyright__ = "Copyright 2018, PyRoboLearn" +__credits__ = ["Brian Delhaisse"] +__license__ = "MIT" +__version__ = "1.0.0" +__maintainer__ = "Brian Delhaisse" +__email__ = "briandelhaisse@gmail.com" +__status__ = "Development" + + +class GPLVM(GPR): + r"""Gaussian Process Latent Variable Model (GPLVM) + + The GPLVM is a non-linear, probabilistic model which reduces the dimensional of the original observational space + by projecting it to a latent space. This can be seen as one of the non-linear version of probabilistic PCA. + + The optimization carried out by the GPLVM is similar to the one undertaken by the GP regression, instead that, in + addition to optimize the kernel hyperparameters, we also optimize the latent variables. This is mathematically + formulated by: + + .. math:: + + {X^*, \Phi^*} = \arg \max_{X, \Phi} p(Y|X; \Phi) + + where :math:`X` are the latent variables, :math:`\Phi` are the kernel hyperparameters, :math:`Y` are the + variables that are observed, and :math:`p(Y|X; \Phi)` is the marginal likelihood. + Note that the initialization of the latent space of the GP-LVM can have drastic consequences on the results; + which can be good or bad depending on the initialization scheme. Different initialization schemes can be used + such as random, or PCA. + + A GPLVM provides a smooth mapping from the latent space to the observational space, however the converse is not + true. In order to preserve local distances and have a smooth mapping from the observational space to the latent + space, back constraints are often used, which is given by: + + .. math:: + + {W^*, \Phi^*} = \arg \max_{W, \Phi} p(Y|X; \Phi) + + where :math:`X = g(Y; W)` with :math:`g` is the back-constraint function parametrized by the weights :math:`W`, + which are learned during the optimization process. This often has the effect of constraining the latent space (and + thus the latent parameters) making it more robust to overfitting. + + References: + [1] "Gaussian Process Latent Variable Models for Visualisation of High Dimensional Data", Lawrence, 2004 + [2] "Probabilistic Non-linear Principal Component Analysis with Gaussian Process Latent Variable Models", + Lawrence, 2005 + [3] "Local distance preservation in the GP-LVM through back constraints", Lawrence et al., 2006 + """ + + def __init__(self, latent_dim, mean=None, kernel=None, model=None, likelihood=None): + """ + Initialize the GPLVM. + + Args: + latent_dim (int): latent dimensionality. + mean (None, gpytorch.means.Mean): mean prior. If None, it will be set to `gpytorch.means.ConstantMean()`. + kernel (None, gpytorch.kernels.Kernel): kernel prior. If None it will be set to + `gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel() + gpytorch.kernels.WhiteNoiseKernel())` + model (None, gpytorch.module.Module): the prior GP model. If None, it will create `ExactGPModel()`, a GP + model using the provided mean, kernel, and likelihood. + likelihood (None, gpytorch.likelihoods.Likelihood): the likelihood pdf. If None, it will use the + `gpytorch.likelihoods.GaussianLikelihood()` + """ + super(GPLVM, self).__init__(mean=mean, kernel=kernel, model=model, likelihood=likelihood) + self.X = None + latent_dim = int(latent_dim) + if latent_dim <= 0: + raise ValueError("Expecting the latent dimension to be bigger than 0, instead got: {}".format(latent_dim)) + self.latent_dim = latent_dim + + @property + def is_latent(self): + """The GPLVM is a latent model.""" + return True + + # TODO: improve the below function + def fit_latent(self, Y, init='pca', num_iters=100, tolerance=1e-5, optimizer=None, verbose=False): + """Fit the given data by maximizing the marginal likelihood. + + Args: + Y (torch.Tensor, np.array): observational data. + init (str, torch.nn.Module): initialization scheme or back-constraint function. If a string, it is the + initialization scheme. Currently, you can choose between 'pca' and 'random'. If torch.nn.Module, it + is the back-constraint function which has some parameters to optimize. + num_iters (int): number of iterations to optimize the marginal likelihood. + tolerance (float): termination tolerance on function value/parameter changes (default: 1e-5). + optimizer (None, torch.optim.Optimizer): optimizer to use to optimize the marginal likelihood. By default, + it will use L-BFGS. + verbose (bool): if True, it will print information during the optimization. + """ + # convert observation data to torch.tensor + Y = self._convert_to_torch(Y) + + # initialize the latent space + if isinstance(init, torch.nn.Module): # back-constraint function + raise NotImplementedError + elif isinstance(init, str): + init = init.lower() + if init == 'random': + X = torch.rand(Y.shape[0], self.latent_dim) + elif init == 'pca': + X = Y - torch.mean(Y, dim=0).expand_as(Y) + U, S, V = torch.svd(X) + X = torch.mm(X, V[:, :self.latent_dim]) + else: + raise NotImplementedError("Currently, only the 'random' or 'pca' initialization schemes have been " + "implemented") + X.requires_grad = True + X = torch.nn.Parameter(X) + + self.X = X + + # fit the data + self.fit(X, Y, num_iters=num_iters, tolerance=tolerance, optimizer=optimizer, verbose=verbose) + + +class SGPLVM(GPLVM): + r"""Shared Gaussian Process Latent Variable Model (SGPLVM) + + With :math:`k` observational spaces sharing the same latent latent space, the following marginal likelihood to + maximize is then given by: + + .. math:: + + {X^*, {\Phi^*_i}_{i=1}^k} = \arg \max_{X, {\Phi_i}_{i=1}^k} p({Y_i}_{i=1}^k | X; {\Phi_i}_{i=1}^k) + + Back-constraints can be applied on one of the observational space :math:`Y_i`, such that: + + .. math:: + + {W^*, {\Phi^*_i}_{i=1}^k} = \arg \max_{W, {\Phi_i}_{i=1}^k} p({Y_i}_{i=1}^k | X; {\Phi_i}_{i=1}^k) + + where :math:`X = g(Y_i, W)` with :math:`g` is the back-constraint function parametrized by the weights :math:`W`. + + References: + [1] "Shared gaussian process latent variables models" (PhD thesis), Ek, 2009 + """ + + def __init__(self, latent_dim, mean=None, kernel=None, model=None, likelihood=None): + """ + Initialize the shared GPLVM. + + Args: + latent_dim (int): latent dimensionality. + mean (None, gpytorch.means.Mean): mean prior. If None, it will be set to `gpytorch.means.ConstantMean()`. + kernel (None, gpytorch.kernels.Kernel): kernel prior. If None it will be set to + `gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel() + gpytorch.kernels.WhiteNoiseKernel())` + model (None, gpytorch.module.Module): the prior GP model. If None, it will create `ExactGPModel()`, a GP + model using the provided mean, kernel, and likelihood. + likelihood (None, gpytorch.likelihoods.Likelihood): the likelihood pdf. If None, it will use the + `gpytorch.likelihoods.GaussianLikelihood()` + """ + super(SGPLVM, self).__init__(latent_dim=latent_dim, mean=mean, kernel=kernel, model=model, + likelihood=likelihood) + + +class HGPLVM(GPLVM): + r"""Hierarchical Gaussian Process Latent Variable Model. + + References: + [1] "Hierarchical Gaussian Process Latent Variable Models", Lawrence et al., 2007 + """ + pass diff --git a/pyrobolearn/models/nn/bnn.py b/pyrobolearn/models/nn/bnn.py new file mode 100644 index 0000000..7592324 --- /dev/null +++ b/pyrobolearn/models/nn/bnn.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +r"""Provide Bayesian Neural Networks. + +This file provides Bayesian neural networks which add uncertainty to the the prediction of NNs. In a bayesian neural +network, the weights and biases have a prior probability distribution attached to them. The posterior distribution on +these parameters is computed after training the model on some data. + +Several approaches to learning Bayesian neural networks have been proposed in the literature: +- Laplace approximation [5,4] +- Monte Carlo +- MC Dropout [8,9] +- Variational Inference (Bayes by Backprop [7,10]) + +References: + [1] "Deep Learning" (http://www.deeplearningbook.org/), Goodfellow et al., 2016 + [2] PyTorch: https://pytorch.org/ + [3] Pyro: https://pyro.ai/ + [4] "Pattern Recognition and Machine Learning" (section 5.7), Bishop, 2006 + [5] "Practical Bayesian Framework for Backpropagation Networks", MacKay, 1992 + (https://authors.library.caltech.edu/13793/1/MACnc92b.pdf) + [6] "Bayesian Learning for Neural Networks" (PhD thesis), Neal, 1995 + (http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.446.9306&rep=rep1&type=pdf) + [7] "Weight Uncertainty in Neural Networks", Blundell et al., 2015 (https://arxiv.org/abs/1505.05424) + [8] "Dropout as a Bayesian Approximation: Representing Model Uncertainty in Deep Learning", Gal et al., 2015 + (https://arxiv.org/abs/1506.02142 and appendix: https://arxiv.org/abs/1506.02157) + [9] "Uncertainty in Deep Learning" (PhD thesis), Gal, 2016 + [10] "A Comprehensive guide to Bayesian Convolutional Neural Network with Variational Inference", Shridhar et al., + 2019 (https://arxiv.org/pdf/1901.02731.pdf) + [11] Blog post: "Making your Neural Network Say 'I Don't Know' - Bayesian NNs using Pyro and PyTorch": + https://towardsdatascience.com/making-your-neural-network-say-i-dont-know-bayesian-nns-using-pyro-and-pytorch\ + -b1c24e6ab8cd + +Interesting implementations: +- https://github.com/kumar-shridhar/PyTorch-BayesianCNN +- https://github.com/anassinator/bnn +- https://github.com/paraschopra/bayesian-neural-network-mnist +""" + +import torch + +from pyrobolearn.models.nn.dnn import NN + +__author__ = "Brian Delhaisse" +__copyright__ = "Copyright 2018, PyRoboLearn" +__credits__ = ["Brian Delhaisse"] +__license__ = "MIT" +__version__ = "1.0.0" +__maintainer__ = "Brian Delhaisse" +__email__ = "briandelhaisse@gmail.com" +__status__ = "Development" + + +# TODO: implement +class BNN(NN): + r"""Bayesian Neural Networks""" + pass diff --git a/pyrobolearn/models/nn/capsule.py b/pyrobolearn/models/nn/capsule.py new file mode 100644 index 0000000..15f020a --- /dev/null +++ b/pyrobolearn/models/nn/capsule.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +r"""Provide Capsule networks. + +References: + [1] "Deep Learning" (http://www.deeplearningbook.org/), Goodfellow et al., 2016 + [2] PyTorch: https://pytorch.org/ + [3] "Dynamic Routing Between Capsules", Sabour et al., 2017 (https://arxiv.org/abs/1710.09829) + [4] Medium blog post: "Introducing awesome-capsule-networks" + https://medium.com/@sekwiatkowski/introducing-awesome-capsule-networks-897f1b81d1e3 + +Interesting implementations: +- https://github.com/gram-ai/capsule-networks +- https://github.com/danielhavir/capsule-network +- https://github.com/higgsfield/Capsule-Network-Tutorial +""" + +import torch + +from pyrobolearn.models.nn.dnn import NN + +__author__ = "Brian Delhaisse" +__copyright__ = "Copyright 2018, PyRoboLearn" +__credits__ = ["Brian Delhaisse"] +__license__ = "MIT" +__version__ = "1.0.0" +__maintainer__ = "Brian Delhaisse" +__email__ = "briandelhaisse@gmail.com" +__status__ = "Development" + + +# TODO: implement +class CapsuleNN(NN): + r"""Capsule Neural Networks""" + pass