InferPy: Probabilistic Modeling with Tensorflow Made Easy

_images/logo.png

InferPy is a high-level API for probabilistic modeling written in Python and capable of running on top of Tensorflow. InferPy’s API is strongly inspired by Keras and it has a focus on enabling flexible data processing, easy-to-code probablistic modeling, scalable inference and robust model validation.

Use InferPy if you need a probabilistic programming language that:

  • Allows easy and fast prototyping of hierarchical probabilistic models with a simple and user friendly API inspired by Keras.
  • Automatically creates computational efficient batched models without the need to deal with complex tensor operations.
  • Run seamlessly on CPU and GPU by relying on Tensorflow, without having to learn how to use Tensorflow.

Getting Started:

Installation

Install InferPy from PyPI:

$ pip install inferpy

30 seconds to InferPy

The core data structures of InferPy is a probabilistic model, defined as a set of random variables with a conditional dependency structure. A random varible is an object parameterized by a set of Numpy’s arrays.

Let’s look at a simple (Bayesian) probabilistic component analysis model. Graphically the model can be defined as follows,

Linear Factor Model

Bayesian PCA

We start defining the prior of the global parameters,

import inferpy as inf
from inferpy.models import Normal

# K defines the number of components.
K=10

# d defines the number of dimensions
d=20

#Prior for the principal components
with inf.replicate(size = K):
    w = Normal(loc = 0, scale = 1, dim = d)  # x.shape = [K,d]

InferPy supports the definition of plateau notation by using the construct with inf.replicate(size = K), which replicates K times the random variables enclosed within this anotator. Every replicated variable is assumed to be independent.

This with inf.replicate(size = N) construct is also useful when defining the model for the data:

# Number of observations
N = 1000

# define the generative model
with inf.replicate(size=N):
    z = Normal(0, 1, dim=K)  # z.shape = [N,K]
    x = Normal(inf.matmul(z,w), 1.0, observed=True, dim=d)  # x.shape = [N,d]

As commented above, the variables are surrounded by a with statement to inidicate that the defined random variables will be reapeatedly used in each data sample. In this case, every replicated variable is conditionally idependent given the variable \(\mathbf{w}\) defined above.

Once the random variables of the model are defined, the probablitic model itself can be created and compiled. The probabilistic model defines a joint probability distribuiton over all these random variables.

from inferpy import ProbModel

# Define the model
pca = ProbModel(varlist = [w,z,x])

# Compile the model
pca.compile(infMethod = 'KLqp')

During the model compilation we specify different inference methods that will be used to learn the model.

from inferpy import ProbModel

# Define the model
pca = ProbModel(varlist = [w,z,x])

# Compile the model
pca.compile(infMethod = 'Variational')

The inference method can be further configure. But, as in Keras, a core principle is to try make things reasonbly simple, while allowing the user the full control if needed.

Every random variable object is equipped with methods such as log_prob() and sample(). Similarly, a probabilistic model is also equipped with the same methods. Then, we can sample data from the model anbd compute the log-likelihood of a data set:

# Sample data from the model
data = pca.sample(size = 100)

# Compute the log-likelihood of a data set
log_like = pca.log_prob(data)

Of course, you can fit your model with a given data set:

# compile and fit the model with training data
pca.compile()
pca.fit(data)

#extract the hidden representation from a set of observations
hidden_encoding = pca.posterior(z)

Guiding Principles

Features

The main features of InferPy are listed below.

  • The models that can be defined in Inferpy are those that can be defined using Edward, whose probability distribuions are mainly inherited from TensorFlow Distribuitons package.
  • Edward’s drawback is that for the model definition, the user has to manage complex multidimensional arrays called tensors. By contrast, in InferPy all the parameters in a model can be defined using the standard Python types (compatibility with Numpy is available as well).
  • InferPy directly relies on top of Edward’s inference engine and includes all the inference algorithms included in this package. As Edward’s inference engine relies on TensorFlow computing engine, InferPy also relies on it too.
  • InferPy seamlessly process data contained in a numpy array, Tensorflow’s tensor, Tensorflow’s Dataset (tf.Data API), Pandas’ DataFrame or Apache Spark’s DataFrame.
  • InferPy also includes novel distributed statistical inference algorithms by combining Tensorflow computing engines.

Architecture

Given the previous considerations, we might summarize the InferPy architecture as follows.

InferPy architecture

Note that InferPy can be seen as an upper layer for working with probabilistic distributions defined over tensors. Most of the interaction is done with Edward: the definitions of the distributions, the inference. However, InferPy also interacts directly with Tensorflow in some operations that are hidden to the user, e.g. the manipulation of the tensors representing the parameters of the distributions.

An additional advantage of using Edward and Tensorflow as inference engine, is that all the paralelisation details are hidden to the user. Moreover, the same code will run either in CPUs or GPUs.

For some less important task, InferPy might also interact with other third-party software. For example, reading data is done with Pandas or the visualization tasks are leveraged to MatPlotLib.

Requirements

Python

Currently, InferPy requires Python 2.7 or 3.x. For checking your default Python version, type:

$ python --version

Travis tests are performed on versions 2.7, 3.5 and 3.6. Go to https://www.python.org/ for specific instructions for installing the Python interpreter in your system.

Edward

InferPy requires exactly the version 1.3.5 of Edward. You may check the installed package version as follows.

$ pip freeze | grep edward

Tensorflow

Tensorflow: from version 1.5 up to 1.7 (both included). To check the installed tensorflow version, type:

$ pip freeze | grep tensorflow

Numpy

Numpy 1.14 or higher is required. To check the version of this package, type:

$ pip freeze | grep numpy

Pandas

Pandas 0.15.0 or higher is required. The installed version of this package can be checked as follows:

$ pip freeze | grep pandas

Guide to Building Probabilistic Models

Getting Started with Probabilistic Models

InferPy focuses on hirearchical probabilistic models structured in two different layers:

  • A prior model defining a joint distribution \(p(\mathbf{w})\) over the global parameters of the model. \(\mathbf{w}\) can be a single random variable or a bunch of random variables with any given dependency structure.
  • A data or observation model defining a joint conditional distribution \(p(\mathbf{x},\mathbf{z}|\mathbf{w})\) over the observed quantities \(\mathbf{x}\) and the the local hidden variables \(\mathbf{z}\) governing the observation \(\mathbf{x}\). This data model is specified in a single-sample basis. There are many models of interest without local hidden variables, in that case, we simply specify the conditional \(p(\mathbf{x}|\mathbf{w})\). Similarly, either \(\mathbf{x}\) or \(\mathbf{z}\) can be a single random variable or a bunch of random variables with any given dependency structure.

For example, a Bayesian PCA model has the following graphical structure,

Linear Factor Model

Bayesian PCA

The prior model are the variables \(w_k\). The data model is the part of the model surrounded by the box indexed by N.

And this is how this Bayesian PCA model is denfined in InferPy:

import edward as ed
import inferpy as inf
from inferpy.models import Normal

K, d, N = 5, 10, 200

# model definition
with inf.ProbModel() as m:
    #define the weights
    with inf.replicate(size=K):
        w = Normal(0, 1, dim=d)

    # define the generative model
    with inf.replicate(size=N):
        z = Normal(0, 1, dim=K)
        x = Normal(inf.matmul(z,w), 1.0, observed=True, dim=d)

m.compile()


The with inf.replicate(size = N) sintaxis is used to replicate the random variables contained within this construct. It follows from the so-called plateau notation to define the data generation part of a probabilistic model. Every replicated variable is conditionally idependent given the previous random variables (if any) defined outside the with statement.

Random Variables

Following Edward’s approach, a random variable \(x\) is an object parametrized by a tensor \(\theta\) (i.e. a TensorFlow’s tensor or numpy’s ndarray). The number of random variables in one object is determined by the dimensions of its parameters (like in Edward) or by the ‘dim’ argument (inspired by PyMC3 and Keras):

import inferpy as inf
import tensorflow as tf
import numpy as np


# different ways of declaring 1 batch of 5 Normal distributions

x = inf.models.Normal(loc = 0, scale=1, dim=5)         # x.shape = [5]

x = inf.models.Normal(loc = [0, 0, 0, 0, 0], scale=1)  # x.shape = [5]

x = inf.models.Normal(loc = np.zeros(5), scale=1)      # x.shape = [5]

x = inf.models.Normal(loc = 0, scale=tf.ones(5))       # x.shape = [5]

The with inf.replicate(size = N) sintaxis can also be used to define multi-dimensional objects:

with inf.replicate(size=10):
    x = inf.models.Normal(loc=0, scale=1, dim=5)       # x.shape = [10,5]

Following Edward’s approach, the multivariate dimension is the innermost (right-most) dimension of the parameters.

Note that indexing is supported:

y = x[7,4]                                              # y.shape = [1]

y2 = x[7]                                               # y2.shape = [5]

y3 = x[7,:]                                             # y2.shape = [5]

y4 = x[:,4]                                             # y4.shape = [10]

Moreover, we may use indexation for defining new variables whose indexes may be other (discrete) variables:

z = inf.models.Categorical(logits = np.zeros(5))
yz = inf.models.Normal(loc=x[0,z], scale=1)            # yz.shape = [1]

Any random variable in InferPy contain the following (optional) input parameters in the constructor:

  • validate_args : Python boolean indicating that possibly expensive checks with the input parameters are enabled. By default, it is set to False.
  • allow_nan_stats : When True, the value “NaN” is used to indicate the result is undefined. Otherwise an exception is raised. Its default value is True.
  • name: Python string with the name of the underlying Tensor object.
  • observed: Python boolean which is used to indicate whether a variable is observable or not . The default value is False
  • dim: dimension of the variable. The default value is None

Inferpy supports a wide range of probability distributions. Details of the specific arguments for each supported distributions are specified in the following sections. `

Probabilistic Models

A probabilistic model defines a joint distribution over observable and non-observable variables, \(p(\mathbf{w}, \mathbf{z}, \mathbf{x})\) for the running example. The variables in the model are the ones defined using the with inf.ProbModel() as pca: construct. Alternatively, we can also use a builder,

m = inf.ProbModel(varlist=[w,z,x])
m.compile()

The model must be compiled before it can be used.

Like any random variable object, a probabilistic model is equipped with methods such as sample(), log_prob() and sum_log_prob(). Then, we can sample data from the model and compute the log-likelihood of a data set:

data = m.sample(1000)
log_like = m.log_prob(data)
sum_log_like = m.sum_log_prob(data)

Random variables can be involved in expressive deterministic operations. Dependecies between variables are modelled by setting a given variable as a parameter of another variable. For example:

with inf.ProbModel() as m:
    theta = inf.models.Beta(0.5,0.5)
    z =  inf.models.Categorical(probs=[theta, 1-theta], name="z")


m.sample()

Moreover, we might consider using the function inferpy.case as the parameter of other random variables:

# Categorical variable depending on another categorical variable

with inf.ProbModel() as m2:
    y =  inf.models.Categorical(probs=[0.4,0.6], name="y")
    x = inf.models.Categorical(probs=inf.case({y.equal(0): [0.0, 1.0],
                                               y.equal(1): [1.0, 0.0] }), name="x")
m2.sample()


# Categorical variable depending on a Normal distributed variable

with inf.ProbModel() as m3:
    a = inf.models.Normal(0,1, name="a")
    b = inf.models.Categorical(probs=inf.case({a>0: [0.0, 1.0],
                                               a<=0: [1.0, 0.0]}), name="b")
m3.sample()


# Normal distributed variable depending on a Categorical variable

with inf.ProbModel() as m4:
    d =  inf.models.Categorical(probs=[0.4,0.6], name="d")
    c = inf.models.Normal(loc=inf.case({d.equal(0): 0.,
                                        d.equal(1): 100.}), scale=1., name="c")
m4.sample()

Note that we might use the case function inside the replicate construct. The result will be a multi-batch random variable having the same distribution for each batch. When obtaining a sample from the model, each sample of a given batch in x is independent of the rest.

with inf.ProbModel() as m:
    y =  inf.models.Categorical(probs=[0.4,0.6], name="y")
    with inf.replicate(size=10):
        x = inf.models.Categorical(probs=inf.case({y.equal(0): [0.5, 0.5],
                                                    y.equal(1): [1.0, 0.0] }), name="x")
m.sample()

We can also use the functions inferpy.case_states or inferpy.gather for defining the same model.

with inf.ProbModel() as m:
    y =  inf.models.Categorical(probs=[0.4,0.6], name="y")
    x = inf.models.Categorical(probs=inf.case_states(y, {0: [0.0, 1.0],
                                                         1: [1.0, 0.0] }), name="x")
m.sample()


with inf.ProbModel() as m:
    y =  inf.models.Categorical(probs=[0.4,0.6], name="y")
    with inf.replicate(size=10):
        x = inf.models.Categorical(probs=inf.gather([[0.5, 0.5], [1.0, 0.0]], y), name="x")

m.sample()

We can use the function inferpy.case_states with a list of variables (or multidimensional variables):


y =  inf.models.Categorical(probs=[0.5,0.5], name="y", dim=2)
p = inf.case_states(y, {(0,0): [1.0, 0.0, 0.0, 0.0], (0,1): [0.0, 1.0, 0.0, 0.0],
                        (1, 0): [0.0, 0.0, 1.0, 0.0], (1,1): [0.0, 0.0, 0.0, 1.0]} )

with inf.replicate(size=10):
    x = inf.models.Categorical(probs=p, name="x")

####

y =  inf.models.Categorical(probs=[0.5,0.5], name="y", dim=1)
z =  inf.models.Categorical(probs=[0.5,0.5], name="z", dim=1)


p = inf.case_states((y,z), {(0,0): [1.0, 0.0, 0.0, 0.0], (0,1): [0.0, 1.0, 0.0, 0.0],
                            (1, 0): [0.0, 0.0, 1.0, 0.0], (1,1): [0.0, 0.0, 0.0, 1.0]} )

with inf.replicate(size=10):
    x = inf.models.Categorical(probs=p, name="x")

####

p = inf.case_states([y,z], {(0,0): [1.0, 0.0, 0.0, 0.0], (0,1): [0.0, 1.0, 0.0, 0.0],
                            (1, 0): [0.0, 0.0, 1.0, 0.0], (1,1): [0.0, 0.0, 0.0, 1.0]} )

with inf.replicate(size=10):
    x = inf.models.Categorical(probs=p, name="x")

Supported Probability Distributions

Supported probability distributions are located in the package inferpy.models. All of them have inferpy.models.RandomVariable as superclass. A list with all the supported distributions can be obtained as as follows.

>>> inf.models.ALLOWED_VARS
['Bernoulli', 'Beta', 'Categorical', 'Deterministic', 'Dirichlet', 'Exponential', 'Gamma', 'InverseGamma', 'Laplace', 'Multinomial', 'Normal', 'Poisson', 'Uniform']

Bernoulli

Binary distribution which takes the value 1 with probability \(p\) and the value with \(1-p\). Its probability mass function is

\[\begin{split}p(x;p) =\left\{\begin{array}{cc} p & \mathrm{if\ } x=1 \\ 1-p & \mathrm{if\ } x=0 \\ \end{array} \right.\end{split}\]

An example of definition in InferPy of a random variable following a Bernoulli distribution is shown below. Note that the input parameter probs corresponds to \(p\) in the previous equation.

x = inf.models.Bernoulli(probs=0.5)

# or

x = inf.models.Bernoulli(logits=0)

This distribution can be initialized by indicating the logit function of the probability, i.e., \(logit(p) = log(\frac{p}{1-p})\).

Beta

Continuous distribution defined in the interval \([0,1]\) and parametrized by two positive shape parameters, denoted \(\alpha\) and \(\beta\).

\[p(x;\alpha,\beta)=\frac{x^{\alpha-1}(1-x)^{\beta-1}}{B(\alpha,\beta)}\]

where B is the beta function

\[B(\alpha,\beta)=\int_{0}^{1}t^{\alpha-1}(1-t)^{\beta-1}dt\]

The definition of a random variable following a Beta distribution is done as follows.

x = inf.models.Beta(concentration0=0.5, concentration1=0.5)

# or simply:

x = inf.models.Beta(0.5,0.5)

Note that the input parameters concentration0 and concentration1 correspond to the shape parameters \(\alpha\) and \(\beta\) respectively.

Categorical

Discrete probability distribution that can take \(k\) possible states or categories. The probability of each state is separately defined:

\[p(x;\mathbf{p}) = p_i\]

where \(\mathbf{p} = (p_1, p_2, \ldots, p_k)\) is a k-dimensional vector with the probability associated to each possible state.

The definition of a random variable following a Categorical distribution is done as follows.

x = inf.models.Categorical(probs=[0.5,0.5])

# or

x = inf.models.Categorical(logits=[0,0])

Deterministic

The deterministic distribution is a probability distribution in a space (continuous or discrete) that always takes the same value \(k_0\). Its probability density (or mass) function can be defined as follows.

\[\begin{split}p(x;k_0) =\left\{\begin{array}{cc} 1 & \mathrm{if\ } x=k_0 \\ 0 & \mathrm{if\ } x \neq k_0 \\ \end{array} \right.\end{split}\]

The definition of a random variable following a Beta distribution is done as follows:

x = inf.models.Deterministic(loc=5)

where the input parameter loc corresponds to the value \(k_0\).

Dirichlet

Dirichlet distribution is a continuous multivariate probability distribution parmeterized by a vector of positive reals \((\alpha_1,\alpha_2,\ldots,\alpha_k)\). It is a multivariate generalization of the beta distribution. Dirichlet distributions are commonly used as prior distributions in Bayesian statistics. The Dirichlet distribution of order \(k \geq 2\) has the following density function.

\[p(x_1,x_2,\ldots x_k; \alpha_1,\alpha_2,\ldots,\alpha_k) = {\frac{\Gamma\left(\sum_i \alpha_i\right)} {\prod_i \Gamma(\alpha_i)} \prod_{i=1}^k x_i^{\alpha_i-1}}{}\]

The definition of a random variable following a Beta distribution is done as follows:

x = inf.models.Dirichlet(concentration=[5,1])

# or simply:

x = inf.models.Dirichlet([5,1])

where the input parameter concentration is the vector \((\alpha_1,\alpha_2,\ldots,\alpha_k)\).

Exponential

The exponential distribution (also known as negative exponential distribution) is defined over a continuous domain and describes the time between events in a Poisson point process, i.e., a process in which events occur continuously and independently at a constant average rate. Its probability density function is

\[\begin{split}p(x;\lambda) =\left\{\begin{array}{cc} \lambda e^{-\lambda x} & \mathrm{if\ } x\geq 0 \\ 0 & \mathrm{if\ } x < k_0 \\ \end{array} \right.\end{split}\]

where \(\lambda>0\) is the rate or inverse scale.

The definition of a random variable following a exponential distribution is done as follows:

x = inf.models.Exponential(rate=1)

# or simply

x = inf.models.Exponential(1)

where the input parameter rate corresponds to the value \(\lambda\).

Gamma

The Gamma distribution is a continuous probability distribution parametrized by a concentration (or shape) parameter \(\alpha>0\), and an inverse scale parameter \(\lambda>0\) called rate. Its density function is defined as follows.

\[p(x;\alpha, \beta) = \frac{\beta^\alpha x^{\alpha - 1} e^{\beta x}}{\Gamma(\alpha)}\]

for \(x > 0\) and where \(\Gamma(\alpha)\) is the gamma function.

The definition of a random variable following a gamma distribution is done as follows:

x = inf.models.Gamma(concentration=3, rate=2)

where the input parameters concentration and rate corespond to \(\alpha\) and \(\beta\) respectively.

Inverse-gamma

The Inverse-gamma distribution is a continuous probability distribution which is the distribution of the reciprocal of a variable distributed according to the gamma distribution. It is also parametrized by a concentration (or shape) parameter \(\alpha>0\), and an inverse scale parameter \(\lambda>0\) called rate. Its density function is defined as follows.

\[p(x;\alpha, \beta) = \frac{\beta^\alpha x^{-\alpha - 1} e^{-\frac{\beta}{x}}}{\Gamma(\alpha)}\]

for \(x > 0\) and where \(\Gamma(\alpha)\) is the gamma function.

The definition of a random variable following a inverse-gamma distribution is done as follows:

x = inf.models.InverseGamma(concentration=3, rate=2)

where the input parameters concentration and rate corespond to \(\alpha\) and \(\beta\) respectively.

Laplace

The Laplace distribution is a continuous probability distribution with the following density function

\[p(x;\mu,\sigma) = \frac{1}{2\sigma} exp \left( - \frac{|x - \mu |}{\sigma}\right)\]

The definition of a random variable following a Beta distribution is done as follows:

x = inf.models.Laplace(loc=0, scale=1)

# or simply

x = inf.models.Laplace(0,1)

where the input parameter loc and scale correspond to \(\mu\) and \(\sigma\) respectively.

Multinomial

The multinomial is a discrete distribution which models the probability of counts resulting from repeating \(n\) times an experiment with \(k\) possible outcomes. Its probability mass function is defined below.

\[p(x_1,x_2,\ldots x_k; \mathbf{p}) = \frac{n!}{\prod_{i=1}^k x_i}\prod_{i=1}^k p_i^{x^i}\]

where \(\mathbf{p}\) is a k-dimensional vector defined as \(\mathbf{p} = (p_1, p_2, \ldots, p_k)\) with the probability associated to each possible outcome.

The definition of a random variable following a multinomial distribution is done as follows:

x = inf.models.Multinomial(total_count=4, probs=[0.5,0.5])

# or

x = inf.models.Multinomial(total_count=4, logits=[0,0])

Multivariate-Normal

A multivariate-normal (or Gaussian) defines a set of normal-distributed variables which are assumed to be idependent. In other words, the covariance matrix is diagonal.

A single multivariate-normal distribution defined on \(\mathbb{R}^2\) can be defined as follows.

x = inf.models.MultivariateNormalDiag(
    loc=[1., -1],
    scale_diag=[1, 2.]
)

Normal

The normal (or Gaussian) distribution is a continuous probability distribution with the following density function

\[p(x;\mu,\sigma) = \frac{1}{2\sigma} exp \left( - \frac{|x - \mu |}{\sigma}\right)\]

where \(\mu\) is the mean or expectation of the distribution, \(\sigma\) is the standard deviation, and \(\sigma^{2}\) is the variance.

A normal distribution can be defined as follows.

x = inf.models.Normal(loc=0, scale=1)

# or

x = inf.models.Normal(0,1)

where the input parameter loc and scale correspond to \(\mu\) and \(\sigma\) respectively.

Poisson

The Poisson distribution is a discrete probability distribution for modeling the number of times an event occurs in an interval of time or space. Its probability mass function is

\[p(x;\lambda) = e^{- \lambda} \frac{\lambda^x}{x!}\]

where \(\lambda\) is the rate or number of events per interval.

A Poisson distribution can be defined as follows.

x = inf.models.Poisson(rate=4)

# or

x = inf.models.Poisson(4)

Uniform

The continuous uniform distribution or rectangular distribution assings the same probability to any \(x\) in the interval \([a,b]\).

\[\begin{split}p(x;a,b) =\left\{\begin{array}{cc} \frac{1}{b-a} & \mathrm{if\ } x\in [a,b]\\ 0 & \mathrm{if\ } x\not\in [a,b] \\ \end{array} \right.\end{split}\]

A uniform distribution can be defined as follows.

x = inf.models.Uniform(low=1, high=3)

# or

inf.models.Uniform(1,3)

where the input parameters low and high correspond to the lower and upper bounds of the interval \([a,b]\).

Guide to Approximate Inference

Getting Started with Approximate Inference

The API defines the set of algorithms and methods used to perform inference in a probabilistic model \(p(x,z,\theta)\) (where \(x\) are the observations, \(z\) the local hidden variibles, and \(\theta\) the global parameters of the model). More precisely, the inference problem reduces to compute the posterior probability over the latent variables given a data sample \(p(z,\theta | x_{train})\), because by looking at these posteriors we can uncover the hidden structure in the data. For the running example, the posterior over the local hidden variables \(p(w_n|x_{train})\) tell us the latent vector representation of the sample \(x_n\), while the posterior over the global variables \(p(\mu|x_{train})\) tells us which is the affine transformation between the latent space and the observable space.

InferPy inherits Edward’s approach an consider approximate inference solutions,

\[q(z,\theta) \approx p(z,\theta | x_{train})\]

in which the task is to approximate the posterior \(p(z,\theta | x_{train})\) using a family of distributions, \(q(z,\theta; \lambda)\), indexed by a parameter vector \(\lambda\).

A probabilistic model in InferPy should be compiled before we can access these posteriors,

m.compile(infMethod="KLqp")
m.fit(x_train)
m.posterior(z)

The compilation process allows to choose the inference algorithm through the infMethod argument. In the above example we use 'Klqp'.

Following InferPy guiding principles, users can further configure the inference algorithm. First, they can define a model ‘Q’ for approximating the posterior distribution,

qw = inf.Qmodel.Normal(w)
qz = inf.Qmodel.Normal(z)

qmodel = inf.Qmodel([qw, qz])

m.compile(infMethod="KLqp", Q=qmodel)
m.fit(x_train)
m.posterior(z)

In the ‘Q’ model we should include a q distribution for every non observed variable in the ‘P’ model. Otherwise, an error will be raised during model compilation.

By default, the posterior q belongs to the same distribution family than p , but in the above example we show how we can change that (e.g. we set the posterior over mu to obtain a point mass estimate instead of the Gaussian approximation used by default). We can also configure how these q’s are initialized using any of the Keras’s initializers.

Compositional Inference

Note

not implemented yet

InferPy directly builds on top of Edward’s compositionality idea to design complex infererence algorithms.

pca = ProbModel(vars = [mu,w_n,x_n])

q_mu = inf.inference.Q.PointMass(bind = mu, initializer='zeroes')
q_w_n = inf.inference.Q.Normal(bind = w_n, initializer='random_unifrom')

qlocal = QModel(vars = [q_w_n])
qglobal = QModel(vars = [mu])

infkl_qp = inf.inference.KLqp(Q = qlocal, optimizer = 'sgd', innerIter = 10)
infMAP = inf.inference.MAP(Q = qglobal, optimizer = 'sgd')

sgd = keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True)
infkl_qp = inf.inference.KLqp(Q = qmodel, optimizer = sgd, loss="ELBO")
probmodel.compile(infMethod = [infkl_qp,infMAP])

pca.fit(x_train)
posterior_mu = pca.posterior(mu)

With the above sintaxis, we perform a variational EM algorithm, where the E step is repeated 10 times for every MAP step.

More flexibility is also available by defining how each mini-batch is processed by the inference algorithm. The following piece of code is equivalent to the above one,

pca = ProbModel(vars = [mu,w_n,x_n])

q_mu = inf.inference.Q.PointMass(bind = mu, initializer='zeroes')
q_w_n = inf.inference.Q.Normal(bind = w_n, initializer='random_unifrom')

qlocal = QModel(vars = [q_w_n])
qglobal = QModel(vars = [mu])

infkl_qp = inf.inference.KLqp(Q = qlocal, optimizer = 'sgd', innerIter = 10)
infMAP = inf.inference.MAP(Q = qglobal, optimizer = 'sgd')

emAlg = lambda (infMethod, dataBatch):
   for _ in range(10)
       infMethod[0].update(data = dataBatch)

   infMethod[1].update(data = dataBatch)
   return

pca.compile(infMethod = [infkl_qp,infMAP], ingAlg = emAlg)

pca.fit(x_train, EPOCHS = 10)
posterior_mu = pca.posterior(mu)

Have a look again at Inference Zoo to explore other complex compositional options.

Supported Inference Methods

Guide to Model Validation

Note

not implemented yet

Model validation try to assess how faifhfully the inferered probabilistic model represents and explain the observed data.

The main tool for model validation consists on analyzing the posterior predictive distribution,

\(p(y_{test}, x_{test}|y_{train}, x_{train}) = \int p(y_{test}, x_{test}|z,\theta)p(z,\theta|y_{train}, x_{train}) dzd\theta\)

This posterior predictive distribution can be used to measure how well the model fits an independent dataset using the test marginal log-likelihood, \(\ln p(y_{test}, x_{test}|y_{train}, x_{train})\),

log_like = probmodel.evaluate(test_data, metrics = ['log_likelihood'])

In other cases, we may need to evalute the predictive capacity of the model with respect to some target variable \(y\),

\(p(y_{test}|x_{test}, y_{train}, x_{train}) = \int p(y_{test}|x_{test},z,\theta)p(z,\theta|y_{train}, x_{train}) dzd\theta\)

So the metrics can be computed with respect to this target variable by using the ‘targetvar’ argument,

log_like, accuracy, mse = probmodel.evaluate(test_data, targetvar = y, metrics = ['log_likelihood', 'accuracy', 'mse'])

So, the log-likelihood metric as well as the accuracy and the mean square error metric are computed by using the predictive posterior \(p(y_{test}|x_{test}, y_{train}, x_{train})\).

Custom evaluation metrics can also be defined,

def mean_absolute_error(posterior, observations, weights=None):
    predictions = tf.map_fn(lambda x : x.getMean(), posterior)
    return tf.metrics.mean_absolute_error(observations, predictions, weights)

mse, mae = probmodel.evaluate(test_data, targetvar = y, metrics = ['mse', mean_absolute_error])

Guide to Data Handling

InferPy leverages existing Pandas functionality for reading data. As a consequence, InferPy can learn from datasets in any file format handled by Pandas. This is possible because the method inferpy.ProbModel.fit(data) accepts as input argument a Pandas DataFrame.

In the following code fragment, an example of learning a model from a CVS file is shown:

import inferpy as inf
import pandas as pd


data = pd.read_csv("inferpy/datasets/test.csv")
N = len(data)

with inf.ProbModel() as m:

    thetaX = inf.models.Normal(loc=0., scale=1.)
    thetaY = inf.models.Normal(loc=0., scale=1.)


    with inf.replicate(size=N):
        x = inf.models.Normal(loc=thetaX, scale=1., observed=True, name="x")
        y = inf.models.Normal(loc=thetaY, scale=1., observed=True, name="y")


m.compile()
m.fit(data)

m.posterior([thetaX, thetaY])



Probabilistic Model Zoo

In this section, we present the code for implementing some models in Inferpy. The corresponding code in Edward can be found in the Inferpy vs Edward section.

Bayesian Linear Regression

Graphically, a (Bayesian) linear regression can be defined as follows,

Bayesian Linear Regression

Bayesian Linear Regression

The InferPy code for this model is shown below,

import edward as ed
import inferpy as inf
from inferpy.models import Normal
import numpy as np

d, N =  5, 20000

# model definition
with inf.ProbModel() as m:

    #define the weights
    w0 = Normal(0,1)
    w = Normal(0, 1, dim=d)

    # define the generative model
    with inf.replicate(size=N):
        x = Normal(0, 1, observed=True, dim=d)
        y = Normal(w0 + inf.dot(x,w), 1.0, observed=True)


# toy data generation
x_train = inf.models.Normal(loc=10, scale=5, dim=d).sample(N)
y_train = np.matmul(x_train, np.array([10,10,0.1,0.5,2]).reshape((d,1))) \
          + inf.models.Normal(loc=0, scale=5, dim=1).sample(N)


data = {x.name: x_train, y.name: y_train}


# compile and fit the model with training data
m.compile()
m.fit(data)

print(m.posterior([w, w0]))



Bayesian Logistic Regression

Graphically, a (Bayesian) logistic regression can be defined as follows,

Bayesian Logistic Regression

Bayesian Linear Regression

The InferPy code for this model is shown below,

import edward as ed
import inferpy as inf
from inferpy.models import Normal, Bernoulli, Categorical

d, N =  10, 500

# model definition
with inf.ProbModel() as m:

    #define the weights
    w0 = Normal(0,1)
    w = Normal(0, 1, dim=d)

    # define the generative model
    with inf.replicate(size=N):
        x = Normal(0, 1, observed=True, dim=d)
        y = Bernoulli(logits=w0+inf.dot(x, w), observed=True)


# toy data generation
x_train = Normal(loc=0, scale=1, dim=d).sample(N)
y_train = Bernoulli(probs=0.4).sample(N)
data = {x.name: x_train, y.name: y_train}

# compile and fit the model with training data
m.compile()
m.fit(data)

print(m.posterior([w, w0]))





Bayesian Multinomial Logistic Regression

Graphically, a (Bayesian) multinomial logistic regression can be defined as follows,

Bayesian Multinomial Logistic Regression

Bayesian Linear Regression

The InferPy code for this model is shown below,

import edward as ed
import inferpy as inf
from inferpy.models import Normal, Bernoulli, Categorical
import numpy as np

d, N =  10, 500

#number of classes
K = 3

# model definition
with inf.ProbModel() as m:

    #define the weights
    w0 = Normal(0,1, dim=K)

    with inf.replicate(size=K):
        w = Normal(0, 1, dim=d)

    # define the generative model
    with inf.replicate(size=N):
        x = Normal(0, 1, observed=True, dim=d)
        y = Bernoulli(logits = w0 + inf.matmul(x, w, transpose_b=True), observed=True)



# toy data generation
x_train = Normal(loc=0, scale=1, dim=d).sample(N)
y_train = Bernoulli(probs=np.random.rand(K)).sample(N)
data = {x.name: x_train, y.name: y_train}


# compile and fit the model with training data
m.compile()
m.fit(data)

print(m.posterior([w, w0]))



Mixture of Gaussians

Graphically, a Mixture of Gaussians can be defined as follows,

Mixture of Gaussians

Bayesian Linear Regression

The InferPy code for this model is shown below,

import edward as ed
import inferpy as inf
import numpy as np
import tensorflow as tf



K, d, N, T = 3, 4, 1000, 5000


# toy data generation
x_train = np.vstack([inf.models.Normal(loc=0, scale=1, dim=d).sample(300),
                     inf.models.Normal(loc=10, scale=1, dim=d).sample(700)])

######## Inferpy ##########


# model definition
with inf.ProbModel() as m:

    # prior distributions
    with inf.replicate(size=K):
        mu = inf.models.Normal(loc=0, scale=1, dim=d)
        sigma = inf.models.InverseGamma(concentration=1, rate=1, dim=d,)
    p = inf.models.Dirichlet(np.ones(K)/K)

    # define the generative model
    with inf.replicate(size=N):
        z = inf.models.Categorical(probs = p)
        x = inf.models.Normal(mu[z], sigma[z],observed=True, dim=d)

# compile and fit the model with training data
data = {x: x_train}
m.compile(infMethod="MCMC")
m.fit(data)

# print the posterior
print(m.posterior(mu))


Linear Factor Model (PCA)

A linear factor model allows to perform principal component analysis (PCA). Graphically,
it can be defined as follows,
Linear Factor Model (PCA)

Linear Factor Model (PCA)

The InferPy code for this model is shown below,

import edward as ed
import inferpy as inf

K, d, N = 5, 10, 200

# model definition
with inf.ProbModel() as m:
    #define the weights
    with inf.replicate(size=K):
        w = inf.models.Normal(0, 1, dim=d)

    # define the generative model
    with inf.replicate(size=N):
        z = inf.models.Normal(0, 1, dim=K)
        x = inf.models.Normal(inf.matmul(z,w),
                               1.0, observed=True, dim=d)

# toy data generation
x_train = inf.models.Normal(loc=0, scale=1., dim=d).sample(N)
data = {x.name: x_train}


# compile and fit the model with training data
m.compile()
m.fit(data)

#extract the hidden representation from a set of observations
hidden_encoding = m.posterior(z)

PCA with ARD Prior (PCA)

Similarly to the previous model, the PCA with ARD Prior can be graphically defined as follows,

PCA with ARD Prior

PCA with ARD Prior

Its code in InferPy is shown below,

import edward as ed
import inferpy as inf
from inferpy.models import Normal, InverseGamma

K, d, N = 5, 10, 200

# model definition
with inf.ProbModel() as m:
    #define the weights
    with inf.replicate(size=K):
        w = Normal(0, 1, dim=d)

    sigma = InverseGamma(1.0,1.0)

    # define the generative model
    with inf.replicate(size=N):
        z = Normal(0, 1, dim=K)
        x = Normal(inf.matmul(z,w),
                   sigma, observed=True, dim=d)

# toy data generation
x_train = Normal(loc=0, scale=1., dim=d).sample(N)
data = {x.name: x_train}


# compile and fit the model with training data
m.compile()
m.fit(data)

#extract the hidden representation from a set of observations
hidden_encoding = m.posterior(z)

Inferpy vs Edward

This section shows the equivalent Edward code for those models in Probabilistic Model Zoo section.

Bayesian Linear Regression

# model definition
with inf.ProbModel() as model:

    # define the weights
    w0 = inf.models.Normal(0,1)
    w = inf.models.Normal(0, 1, dim=d)

    # define the generative model
    with inf.replicate(size=N):
        x = inf.models.Normal(0, 1, observed=True, dim=d)
        y = inf.models.Normal(w0 + inf.dot(x,w), 1.0, observed=True)


# compile and fit the model with training data
model.compile()
data = {x: x_train, y: y_train}
model.fit(data)

# print the posterior distributions

Gaussian Mixture


    # define the generative model
    with inf.replicate(size=N):
        z = inf.models.Categorical(probs=p)
        x = inf.models.Normal(mu[z], sigma[z], observed=True, dim=d)

# compile and fit the model with training data
data = {x: x_train}
model.compile(infMethod="MCMC")
model.fit(data)

# print the posterior
print(model.posterior(mu))

############################## Edward ##################################################



# model definition

# prior distributions
p = ed.models.Dirichlet(concentration=tf.ones(K) / K)
mu = ed.models.Normal(0.0, 1.0, sample_shape=[K, d])
sigma = ed.models.InverseGamma(concentration=1.0, rate=1.0, sample_shape=[K, d])
# define the generative model
z = ed.models.Categorical(logits=tf.log(p) - tf.log(1.0 - p), sample_shape=N)
x = ed.models.Normal(loc=tf.gather(mu, z), scale=tf.gather(sigma, z))

# compile and fit the model with training data
qp = ed.models.Empirical(params=tf.get_variable("qp/params", [T, K],
                                                initializer=tf.constant_initializer(1.0 / K)))
qmu = ed.models.Empirical(params=tf.get_variable("qmu/params", [T, K, d],
                                                 initializer=tf.zeros_initializer()))
qsigma = ed.models.Empirical(params=tf.get_variable("qsigma/params", [T, K, d],
                                                    initializer=tf.ones_initializer()))
qz = ed.models.Empirical(params=tf.get_variable("qz/params", [T, N],
                                                initializer=tf.zeros_initializer(),
                                                dtype=tf.int32))

gp = ed.models.Dirichlet(concentration=tf.ones(K))
gmu = ed.models.Normal(loc=tf.ones([K, d]), scale=tf.ones([K, d]))
gsigma = ed.models.InverseGamma(concentration=tf.ones([K, d]), rate=tf.ones([K, d]))
gz = ed.models.Categorical(logits=tf.zeros([N, K]))

inference = ed.MetropolisHastings(
    latent_vars={p: qp, mu: qmu, sigma: qsigma, z: qz},
    proposal_vars={p: gp, mu: gmu, sigma: gsigma, z: gz},
    data={x: x_train})

inference.run()

# print the posterior
print(qmu.params.eval())

Logistic Regression


# define the weights
w0 = ed.models.Normal(loc=tf.zeros(1), scale=tf.ones(1))
w = ed.models.Normal(loc=tf.zeros(d), scale=tf.ones(d))

# define the generative model
x = ed.models.Normal(loc=tf.zeros([N,d]), scale=tf.ones([N,d]))
y = ed.models.Bernoulli(logits=ed.dot(x, w) + w0)

# compile and fit the model with training data
qw = ed.models.Normal(loc=tf.Variable(tf.random_normal([d])),
                      scale=tf.nn.softplus(tf.Variable(tf.random_normal([d]))))
qw0 = ed.models.Normal(loc=tf.Variable(tf.random_normal([1])),
                       scale=tf.nn.softplus(tf.Variable(tf.random_normal([1]))))

inference = ed.KLqp({w: qw, w0: qw0}, data={x: x_train, y: y_train.reshape(N)})
inference.run()

# print the posterior distributions
print([qw.loc.eval(), qw0.loc.eval()])

Multinomial Logistic Regression


# define the weights
w0 = ed.models.Normal(loc=tf.zeros(K), scale=tf.ones(K))
w = ed.models.Normal(loc=tf.zeros([K,d]), scale=tf.ones([K,d]))

# define the generative model
x = ed.models.Normal(loc=tf.zeros([N,d]), scale=tf.ones([N,d]))
y = ed.models.Normal(loc=w0 + tf.matmul(x, w, transpose_b=True), scale=tf.ones([N,K]))

# compile and fit the model with training data
qw = ed.models.Normal(loc=tf.Variable(tf.random_normal([K,d])),
                      scale=tf.nn.softplus(tf.Variable(tf.random_normal([K,d]))))
qw0 = ed.models.Normal(loc=tf.Variable(tf.random_normal([K])),
                       scale=tf.nn.softplus(tf.Variable(tf.random_normal([K]))))

inference = ed.KLqp({w: qw, w0: qw0}, data={x: x_train, y: y_train})
inference.run()

# print the posterior distributions
print([qw.loc.eval(), qw0.loc.eval()])

Linear Factor Model (PCA)


# define the weights
w = ed.models.Normal(loc=tf.zeros([K,d]), scale=tf.ones([K,d]))

# define the generative model
z = ed.models.Normal(loc=tf.zeros([N,K]), scale=tf.ones([N,K]))
x = ed.models.Normal(loc=tf.matmul(z,w), scale=tf.ones([N,d]))

# compile and fit the model with training data
qw = ed.models.Normal(loc=tf.Variable(tf.random_normal([K,d])),
                      scale=tf.nn.softplus(tf.Variable(tf.random_normal([K,d]))))


inference = ed.KLqp({w: qw}, data={x: x_train})
inference.run()

# print the posterior distributions
print([qw.loc.eval()])

PCA with ARD Prior (PCA)


# define the weights
w = ed.models.Normal(loc=tf.zeros([K,d]), scale=tf.ones([K,d]))

sigma = ed.models.InverseGamma(1.,1.)

# define the generative model
z = ed.models.Normal(loc=tf.zeros([N,K]), scale=tf.ones([N,K]))
x = ed.models.Normal(loc=tf.matmul(z,w), scale=sigma)

# compile and fit the model with training data
qw = ed.models.Normal(loc=tf.Variable(tf.random_normal([K,d])),
                      scale=tf.nn.softplus(tf.Variable(tf.random_normal([K,d]))))


inference = ed.KLqp({w: qw}, data={x: x_train})
inference.run()

# print the posterior distributions
print([qw.loc.eval()])

inferpy package

Subpackages

inferpy.criticism package

Submodules
inferpy.criticism.evaluate module

Module with the functionality for evaluating the InferPy models

inferpy.criticism.evaluate.ALLOWED_METRICS = ['binary_accuracy', 'categorical_accuracy', 'sparse_categorical_accuracy', 'log_loss', 'binary_crossentropy', 'categorical_crossentropy', 'sparse_categorical_crossentropy', 'hinge', 'squared_hinge', 'mse', 'MSE', 'mean_squared_error', 'mae', 'MAE', 'mean_absolute_error', 'mape', 'MAPE', 'mean_absolute_percentage_error', 'msle', 'MSLE', 'mean_squared_logarithmic_error', 'poisson', 'cosine', 'cosine_proximity', 'log_lik', 'log_likelihood']

List with all the allowed metrics for evaluation

inferpy.criticism.evaluate.evaluate(metrics, data, n_samples=500, output_key=None, seed=None)[source]

Evaluate a fitted inferpy model using a set of metrics. This function encapsulates the equivalent Edward one.

Parameters:metrics – list of str indicating the metrics or sccore functions to be used.

An example of use:

# evaluate the predicted data y=y_pred given that x=x_test
mse = inf.evaluate('mean_squared_error', data={x: x_test, y: y_pred}, output_key=y)
Returns:A list of evaluations or a single evaluation.
Return type:list of float or float
Raises:NotImplementedError – If an input metric does not match an implemented metric.

inferpy.inferences package

Submodules
inferpy.inferences.inference module

Module with the functionality related to inference methods.

inferpy.inferences.inference.INF_METHODS = ['KLpq', 'KLqp', 'Laplace', 'ReparameterizationEntropyKLqp', 'ReparameterizationKLKLqp', 'ReparameterizationKLqp', 'ScoreEntropyKLqp', 'ScoreKLKLqp', 'ScoreKLqp', 'ScoreRBKLqp', 'WakeSleep', 'MetropolisHastings']

List with all the alowed implemented inference methods

inferpy.inferences.inference.INF_METHODS_ALIAS = {'MCMC': 'MetropolisHastings', 'Variational': 'KLqp'}

Aliases for some of the inference methods

inferpy.inferences.qmodel module

Module with the required functionality for implementing the Q-models and Q-distributions used by some inference algorithms.

class inferpy.inferences.qmodel.Qmodel(varlist)[source]

Bases: object

Class implementing a Q model

A Q model approximates the posterior distribution of a probabilistic model P . In the ‘Q’ model we should include a q distribution for every non observed variable in the ‘P’ model. Otherwise, an error will be raised during model compilation.

An example of use:

import edward as ed
import inferpy as inf


#### learning a 2 parameters of 1-dim from 2-dim data

N = 50
sampling_mean = [30., 10.]
sess = ed.util.get_session()


with inf.ProbModel() as m:

    theta1 = inf.models.Normal(loc=0., scale=1., dim=1)
    theta2 = inf.models.Normal(loc=0., scale=1., dim=1)


    with inf.replicate(size=N):
        x = inf.models.Normal(loc=[theta1, theta2], scale=1., observed=True)


# define the Qmodel



# define with any type
q_theta1 = inf.Qmodel.Uniform(theta1)
q_theta2 = inf.Qmodel.new_qvar(theta2)


qmodel = inf.Qmodel([q_theta1, q_theta2])

inf.Qmodel.Normal(theta1)

m.compile(Q=qmodel)


x_train = inf.models.Normal(loc=sampling_mean, scale=1.).sample(N)
data = {x.name : x_train}


m.fit(data)

m.posterior(theta1).base_object

m.posterior(theta2)




classmethod Bernoulli(v, initializer='ones')

Creates a new q-variable of type Bernoulli

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Beta(v, initializer='ones')

Creates a new q-variable of type Beta

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Categorical(v, initializer='ones')

Creates a new q-variable of type Categorical

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Deterministic(v, initializer='ones')

Creates a new q-variable of type Deterministic

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Dirichlet(v, initializer='ones')

Creates a new q-variable of type Dirichlet

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
static Empirical(v, n_post_samples=500, initializer='ones')[source]
classmethod Exponential(v, initializer='ones')

Creates a new q-variable of type Exponential

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Gamma(v, initializer='ones')

Creates a new q-variable of type Gamma

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod InverseGamma(v, initializer='ones')

Creates a new q-variable of type InverseGamma

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Laplace(v, initializer='ones')

Creates a new q-variable of type Laplace

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Multinomial(v, initializer='ones')

Creates a new q-variable of type Multinomial

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod MultivariateNormalDiag(v, initializer='ones')

Creates a new q-variable of type MultivariateNormalDiag

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Normal(v, initializer='ones')

Creates a new q-variable of type Normal

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Poisson(v, initializer='ones')

Creates a new q-variable of type Poisson

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
classmethod Uniform(v, initializer='ones')

Creates a new q-variable of type Uniform

Parameters:
  • v – Inferpy p-variable
  • initializer (str) – indicates how the new variable should be initialized. Possible values: ‘ones’ , ‘zeroes’.
__init__(varlist)[source]

Initialize self. See help(type(self)) for accurate signature.

add_var(v)[source]

Method for adding a new q variable

static build_from_pmodel(p, empirical=False)[source]

Initializes a Q model from a P model.

Parameters:
  • p – P model of type inferpy.ProbModel.
  • empirical – determines if q distributions will be empirical or of the same type than each p distribution.
static compatible_var(v)[source]
dict

Dictionary where the keys and values are the p and q distributions respectively.

static new_qvar(v, initializer='ones', qvar_inf_module=None, qvar_inf_type=None, qvar_ed_type=None, check_observed=True, name='qvar')[source]

Builds an Inferpy q-variable for a p-variable.

Parameters:
  • v – Inferpy variable to be approximated.
  • initializer (str) – indicates how the new variable should be initialized. Possible values: “ones” , “zeroes”.
  • qvar_inf_module (str) – module of the new Inferpy variable.
  • qvar_inf_type (str) – name of the new Inferpy variable.
  • qvar_ed_type (str) – full name of the encapsulated Edward variable type.
  • check_observed (bool) – To check if p-variable is observed.
Returns:

Inferpy variable approximating in input variable.

static new_qvar_empirical(v, n_post_samples, initializer='ones', check_observed=True, name='qvar')[source]

Builds an empirical Inferpy q-variable for a p-variable.

Parameters:
  • v – Inferpy variable to be approximated.
  • n_post_samples – number of posterior samples.
  • initializer (str) – indicates how the new variable should be initialized. Possible values: “ones” , “zeroes”.
  • check_observed (bool) – To check if p-variable is observed.
Returns:

Inferpy variable approximating in input variable. The InferPy type will be Deterministic while the encapsulated Edward variable will be of type Empirical.

varlist

list of q variables of Inferpy type

inferpy.models package

Submodules
inferpy.models.deterministic module

The Deterministic distribution

class inferpy.models.deterministic.Deterministic(loc=None, dim=None, observed=False, name='Determ')[source]

Bases: inferpy.models.random_variable.RandomVariable

Class implementing a Deterministic variable.

This allows to encapsulate any Tensor object of Edward variable. Moreover, variables of this type can be intilially empty and later initialized. When operating with InferPy random variables, the result is always a deterministic variable.

An example of use:

import inferpy as inf
import edward as ed
import tensorflow as tf


# empty initialized variable
x = inf.models.Deterministic()
x.dist = ed.models.Normal(0.,1.)


# deterministic variable with returning a tensor

x = inf.models.Deterministic()
x.base_object = tf.constant(1.)


# deterministic variable returning an InferPy variable

a = inf.models.Normal(0,1)
b = inf.models.Normal(2,2)

x = a + b

type(x)  # <class 'inferpy.models.deterministic.Deterministic'>

__init__(loc=None, dim=None, observed=False, name='Determ')[source]

Constructor for the Deterministic distribution

loc

Distribution parameter for the mean.

inferpy.models.factory module

This module implements all the methods for definition of each type of random variable. For further details about all the supported distributions, see Guide to Building Probabilistic Models .

class inferpy.models.factory.Bernoulli(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['logits', 'probs']
__init__(*args, **kwargs)[source]

constructor for Bernoulli

logits

property for logits

probs

property for probs

class inferpy.models.factory.Beta(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['concentration1', 'concentration0']
__init__(*args, **kwargs)[source]

constructor for Beta

concentration0

property for concentration0

concentration1

property for concentration1

class inferpy.models.factory.Categorical(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['logits', 'probs']
__init__(*args, **kwargs)[source]

constructor for Categorical

logits

property for logits

probs

property for probs

class inferpy.models.factory.Dirichlet(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['concentration']
__init__(*args, **kwargs)[source]

constructor for Dirichlet

concentration

property for concentration

class inferpy.models.factory.Exponential(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['rate']
__init__(*args, **kwargs)[source]

constructor for Exponential

rate

property for rate

class inferpy.models.factory.Gamma(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['concentration', 'rate']
__init__(*args, **kwargs)[source]

constructor for Gamma

concentration

property for concentration

rate

property for rate

class inferpy.models.factory.InverseGamma(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['concentration', 'rate']
__init__(*args, **kwargs)[source]

constructor for InverseGamma

concentration

property for concentration

rate

property for rate

class inferpy.models.factory.Laplace(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['loc', 'scale']
__init__(*args, **kwargs)[source]

constructor for Laplace

loc

property for loc

scale

property for scale

class inferpy.models.factory.Multinomial(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['total_count', 'logits', 'probs']
__init__(*args, **kwargs)[source]

constructor for Multinomial

logits

property for logits

probs

property for probs

total_count

property for total_count

class inferpy.models.factory.MultivariateNormalDiag(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['loc', 'scale_diag']
__init__(*args, **kwargs)[source]

constructor for MultivariateNormalDiag

loc

property for loc

scale_diag

property for scale_diag

class inferpy.models.factory.Normal(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['loc', 'scale']
__init__(*args, **kwargs)[source]

constructor for Normal

loc

property for loc

scale

property for scale

class inferpy.models.factory.Poisson(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = []
__init__(*args, **kwargs)[source]

constructor for Poisson

class inferpy.models.factory.Uniform(*args, **kwargs)[source]

Bases: inferpy.models.random_variable.RandomVariable

PARAMS = ['low', 'high']
__init__(*args, **kwargs)[source]

constructor for Uniform

high

property for high

low

property for low

inferpy.models.factory.def_random_variable(var)[source]
inferpy.models.prob_model module

Module with the probabilistic model functionality.

class inferpy.models.prob_model.ProbModel(varlist=None)[source]

Bases: object

Class implementing a probabilistic model

A probabilistic model defines a joint distribution over observed and latent variables. This class encapsulates all the functionality for making inference (and learning) in these models.

An example of use:

import inferpy as inf
from inferpy.models import Normal



with inf.ProbModel() as m:

    x = Normal(loc=1., scale=1., name="x", observed=True)
    y = Normal(loc=x, scale=1., dim=3, name="y")


# print the list of variables
print(m.varlist)
print(m.latent_vars)
print(m.observed_vars)

# get a sample

m_sample = m.sample()



# compute the log_prob for each element in the sample
print(m.log_prob(m_sample))

# compute the sum of the log_prob
print(m.sum_log_prob(m_sample))




### alternative definition

x2 = Normal(loc=1., scale=1., name="x2", observed=True)
y2 = Normal(loc=x, scale=1., dim=3, name="y2")

m2 = inf.ProbModel(varlist=[x2,y2])

This class can be used, for instance, for infering the parameters of some observed data:

import edward as ed
import inferpy as inf


#### learning a 1-dim parameter from 1-dim data


N = 50
sampling_mean = [30.]
sess = ed.util.get_session()


with inf.ProbModel() as m:

    theta = inf.models.Normal(loc=0., scale=1.)

    with inf.replicate(size=N):
        x = inf.models.Normal(loc=theta, scale=1., observed=True)

m.compile()

x_train = inf.models.Normal(loc=sampling_mean, scale=1.).sample(N)
data = {x.name : x_train}


m.fit(data)

m.posterior(theta).loc[0]  #29.017122


#### learning a 2 parameters of 1-dim from 2-dim data

N = 50
sampling_mean = [30., 10.]
sess = ed.util.get_session()


with inf.ProbModel() as m:

    theta1 = inf.models.Normal(loc=0., scale=1., dim=1)
    theta2 = inf.models.Normal(loc=0., scale=1., dim=1)


    with inf.replicate(size=N):
        x = inf.models.Normal(loc=[theta1, theta2], scale=1., observed=True)




m.compile()


x_train = inf.models.Normal(loc=sampling_mean, scale=1.).sample(N)
data = {x.name : x_train}


m.fit(data)

m.posterior(theta1).loc
m.posterior(theta2).loc

__init__(varlist=None)[source]

Initializes a probabilistic model

Parameters:varlist – optional list with the variables in the model
add_var(v)[source]

Method for adding a new random variable. After use, the model should be re-compiled

static compatible_var(v)[source]
compile(infMethod='KLqp', Q=None, proposal_vars=None)[source]

This method initializes the structures for making inference in the model.

copy(swap_dict=None)[source]
fit(data, reset_tf_vars=True)[source]

Assings data to the observed variables

static get_active_model()[source]

Return the active model defined with the construct ‘with’

get_config()[source]
get_copy_from(original_var)[source]
get_parents(v)[source]
get_var(name)[source]

Get a varible in the model with a given name

get_vardict()[source]
get_vardict_rev()[source]
static is_active()[source]

Check if a replicate construct has been initialized

Returns:True if the method is inside a construct ProbModel (of size different to 1). Otherwise False is return
is_compiled()[source]

Determines if the model has been compiled

latent_vars

list of latent (i.e., non-observed) variables in the model

log_prob(sample_dict)[source]

Computes the log probabilities of a (set of) sample(s)

no_parents()[source]
observed_vars

list of observed variabels in the model

posterior(latent_var)[source]

Return the posterior distribution of some latent variables

Parameters:latent_var – a single or a set of latent variables in the model
Returns:Random variable(s) of the same type than the prior distributions
predict(target, data, reset_tf_vars=False)[source]
predict_old(target_var, observations)[source]
reset_compilation()[source]

Clear the structues created during the compilation of the model

sample(size=1)[source]

Generates a sample for eache variable in the model

sum_log_prob(sample_dict)[source]

Computes the sum of the log probabilities of a (set of) sample(s)

summary()[source]
to_json()[source]
varlist

list of variables (observed and latent)

inferpy.models.random_variable module

Module implementing the shared functionality across all the variable types

class inferpy.models.random_variable.RandomVariable(base_object=None, observed=False)[source]

Bases: object

Base class for random variables.

__init__(base_object=None, observed=False)[source]

Constructor for the RandomVariable class

Parameters:
  • base_object – encapsulated Edward object (optional).
  • observed (bool) – specifies if the random variable is observed (True) or observed (False).
base_object

Underlying Tensorflow object

batches

Number of batches of the variable

bind
copy(swap_dict=None, observed=False)[source]

Build a new random variable with the same values. The underlying tensors or edward objects are copied as well.

Parameters:
  • swap_dict – random variables, variables, tensors, or operations to swap with.
  • observed – determines if the new variable is observed or not.
dim

Dimensionality of variable

dist

Underlying Edward object

equal(other)

documentation for equal

event_shape
static get_key_from_var(var)[source]
get_local_hidden()[source]

Returns a list with all the local hidden variables w.r.t. this one. Local hidden variables are those latent variables which are in the same replicate construct.

get_replicate_list()[source]

Returns a list with all the replicate constructs that this variable belongs to.

static get_var_with_key(key)[source]
is_generic_variable()[source]

Determines if this is a generic variable, i.e., an Edward variable is not encapsulated.

log_prob(v)[source]

Method for computing the log probability of a sample v (or a set of samples)

mean(name='mean')[source]

Method for obaining the mean of this random variable

name

name of the variable

observed

boolean property that determines if a variable is observed or not

prob(v)[source]

Method for computing the probability of a sample v (or a set of samples)

prod_prob(v)[source]

Method for computing the joint probability of a sample v (or a set of samples)

sample(size=1)[source]

Method for obaining a samples

Parameters:size – scalar or matrix of integers indicating the shape of the matrix of samples.
Returns:Matrix of samples. Each element in the output matrix has the same shape than the variable.
shape

shape of the variable, i.e. (batches, dim)

stddev(name='stddev')[source]

Method for obataining the standard deviation of this random variable

sum_log_prob(v)[source]

Method for computing the sum of the log probability of a sample v (or a set of samples)

variance(name='variance')[source]

Method for obataining the variance of this random variable

inferpy.models.replicate module

Module with the replication functionality.

class inferpy.models.replicate.replicate(size)[source]

Bases: object

Class implementing the Plateau notation

The plateau notation is used to replicate the random variables contained within this construct. Every replicated variable is conditionally idependent given the previous random variables (if any) defined outside the with statement. The with inf.replicate(size = N) sintaxis is used to replicate N times the contained definitions. For example:

import inferpy as inf


with inf.replicate(size=50):
    # Define some random variables here
    print("Variable replicated " + str(inf.replicate.get_total_size()) + " times")


with inf.replicate(size=10):
    # Define some random variables here
    print("Variable replicated " + str(inf.replicate.get_total_size()) + " times")

    with inf.replicate(size=2):
        # Define some random variables here
        print("Variable replicated " + str(inf.replicate.get_total_size()) + " times")
        print(inf.replicate.in_replicate())

# Define some random variables here
print("Variable replicated " + str(inf.replicate.get_total_size()) + " times")

The number of times that indicated with input argument size. Note that nested replicate constructs can be defined as well. At any moment, the product of all the nested replicate constructs can be obtained by invoking the static method get_total_size().

Note

Defining a variable inside the construct replicate with size equal to 1, that is, inf.replicate(size=1) is equivalent to defining outside any replicate construct.

__init__(size)[source]

Initializes the replicate construct

Parameters:size (int) – number of times that the variables contained are replicated.
static get_active_replicate()[source]

Return the active replicate defined with the construct ‘with’

static get_all_replicate()[source]
static get_total_size()[source]

Static method that returns the product of the sizes of all the nested replicate constructs

Returns:Integer with the product of sizes
static in_replicate()[source]

Check if a replicate construct has been initialized

Returns:True if the method is inside a construct replicate (of size different to 1). Otherwise False is return
static print_total_size()[source]

Static that prints the total size

inferpy.util package

Submodules
inferpy.util.error module

Module implementing custom exceptions and errors

exception inferpy.util.error.ScopeException[source]

Bases: Exception

This exception is raised when an InferPy object is declared in a non-valid scope

inferpy.util.format module

Module implementing text formatting operaionts

inferpy.util.format.np_str(s)[source]

Shorten string representation of a numpy object

Parameters:s – numpy object.
inferpy.util.ops module

Module implementing some useful operations over tensors and random variables

inferpy.util.ops.case(d, default=None, exclusive=True, strict=False, name='case')[source]

Control flow operation depending of the outcome of a tensor. Any expression in tensorflow giving as a result a boolean is allowed as condition.

Internally, the operation tensorflow.case is invoked. Unlike the tensorflow operation, this one accepts InferPy variables as input parameters.

Parameters:
  • d – dictionary where the keys are the conditions (i.e. boolean tensor).
  • exclusive – True iff at most one case is allowed to evaluate to True.
  • name – name of the resulting tensor.
Returns:

Tensor implementing the case operation. This is the output of the operation tensorflow.case internally invoked.

inferpy.util.ops.case_states(var, d, default=None, exclusive=True, strict=False, name='case')[source]

Control flow operation depending of the outcome of a discrete variable.

Internally, the operation tensorflow.case is invoked. Unlike the tensorflow operation, this one accepts InferPy variables as input parameters.

Parameters:
  • var – Control InferPy discrete random variable.
  • d – dictionary where the keys are each of the possible values of control variable
  • the values are returning tensors for each case. (and) –
  • exclusive – True iff at most one case is allowed to evaluate to True.
  • name – name of the resulting tensor.
Returns:

Tensor implementing the case operation. This is the output of the operation tensorflow.case internally invoked.

inferpy.util.ops.dot(x, y)[source]

Compute dot product between an InferPy or Tensor object. The number of batches N equal to 1 for one of them, and higher for the other one.

If necessarily, the order of the operands may be changed.

Args:
x: first operand. This could be an InferPy variable, a Tensor, a numpy object or a numeric Python list. x: second operand. This could be an InferPy variable, a Tensor, a numpy object or a numeric Python list.
Retruns:
An InferPy variable of type Deterministic encapsulating the resulting tensor of the multiplications.
inferpy.util.ops.fix_shape(s)[source]

Transforms a shape list into a standard InferPy shape format.

inferpy.util.ops.gather(params, indices, validate_indices=None, name=None, axis=0)[source]

Operation for selecting some of the items in a tensor.

Internally, the operation tensorflow.gather is invoked. Unlike the tensorflow operation, this one accepts InferPy variables as input parameters.

Parameters:
  • params – A Tensor. The tensor from which to gather values. Must be at least rank axis + 1.
  • indices – A Tensor. Must be one of the following types: int32, int64. Index tensor. Must be in range
  • params.shape[axis]) ([0,) –
  • axis – A Tensor. Must be one of the following types: int32, int64. The axis in params to gather indices
  • Defaults to the first dimension. Supports negative indexes. (from.) –
  • name – A name for the operation (optional).
Returns:

A Tensor. Has the same type as params.. This is the output of the operation tensorflow.gather internally invoked.

inferpy.util.ops.matmul(a, b, transpose_a=False, transpose_b=False, adjoint_a=False, adjoint_b=False, a_is_sparse=False, b_is_sparse=False, name=None)[source]

Matrix multiplication.

Input objects may be tensors but also InferPy variables.

Parameters:
  • a – Tensor of type float16, float32, float64, int32, complex64, complex128 and rank > 1.
  • b – Tensor with same type and rank as a.
  • transpose_a – If True, a is transposed before multiplication.
  • transpose_b – If True, b is transposed before multiplication.
  • adjoint_a – If True, a is conjugated and transposed before multiplication.
  • adjoint_b – If True, b is conjugated and transposed before multiplication.
  • a_is_sparse – If True, a is treated as a sparse matrix.
  • b_is_sparse – If True, b is treated as a sparse matrix.
  • name – Name for the operation (optional).
Retruns:
An InferPy variable of type Deterministic encapsulating the resulting tensor of the multiplications.
inferpy.util.ops.param_to_tf(x)[source]

Transforms either a scalar or a random variable into a Tensor

inferpy.util.ops.shape_to_list(a)[source]

Transforms the shape of an object into a list

Parameters:a – object whose shape will be transformed. This could be an InferPy variable, a Tensor, a numpy object or a numeric Python list.
inferpy.util.runtime module

Module with useful definitions to be used in runtime

inferpy.util.runtime.get_session()[source]

Get the default tensorflow session

inferpy.util.wrappers module

Module with useful wrappers used for the development of InferPy.

inferpy.util.wrappers.input_model_data(f)[source]

wrapper that transforms, if required, a dataset object, making it suitable for InferPy inference process.

inferpy.util.wrappers.multishape(f)[source]

This wrapper allows to apply a function with simple parameters, over multidimensional ones.

inferpy.util.wrappers.singleton(class_)[source]

wrapper that allows to define a singleton class

inferpy.util.wrappers.static_multishape(f)[source]

This wrapper allows to apply a function with simple parameters, over multidimensional ones.

inferpy.util.wrappers.tf_run_wrapper(f)[source]

When setted to a function f, this wrappers replaces the output tensor of f by its evaluation in the default tensorflow session. In doing so, the API user will only work with standard Python types.

Submodules

inferpy.version module

Version of inferpy

This module contains a constant string with the version of inferpy, i.e.:

import inferpy as inf
print(inf.VERSION)

Contact and Support

If you have any question about the toolbox or if you want to collaborate in the project, please do not hesitate to contact us. You can do it through the following email address: inferpy.api@gmail.com

For more technical questions, please use Github issues.