Bijectors
In this post, we are going to take a look at bijectors which are the objects intense flow probability that implemented by bijective or invertible transformations. This is the summary of lecture "Probabilistic Deep Learning with Tensorflow 2" from Imperial College London.
import tensorflow as tf
import tensorflow_probability as tfp
import numpy as np
import matplotlib.pyplot as plt
tfd = tfp.distributions
tfpl = tfp.layers
tfb = tfp.bijectors
plt.rcParams['figure.figsize'] = (10, 6)
print("Tensorflow Version: ", tf.__version__)
print("Tensorflow Probability Version: ", tfp.__version__)
Overview
Bijector is the term of encapsulation in change of variables for a probability density. Simply speaking, when we have some probability density function, and there is another mapping function, we can derive another density function for mapped variable.
z = tf.constant([1., 2., 3.])
scale = tfb.Scale(2.)
X = scale.forward(z)
X
scale.inverse(tf.constant([5., 3., 1.]))
scale = tfb.Scale(2.)
shift = tfb.Shift(1.)
# Chained with reverse order : scale -> shift
scale_and_shift = tfb.Chain([shift, scale])
scale_and_shift
scale_and_shift.forward(z)
scale_and_shift.inverse(tf.constant([2., 5., 8.]))
Same operation,
another_scale_and_shift = shift(scale)
another_scale_and_shift
In this case, object itself is equivalent to call forward method.
another_scale_and_shift(z)
another_scale_and_shift.forward(z)
another_scale_and_shift.inverse(tf.constant([2., 5., 8.]))
normal = tfd.Normal(loc=0., scale=1.)
z = normal.sample(3)
z
scale_and_shift = tfb.Chain([tfb.Shift(1.), tfb.Scale(2.)])
x = scale_and_shift.forward(z)
x
log_prob_z = normal.log_prob(z)
log_prob_z
log_prob_x = log_prob_z - scale_and_shift.forward_log_det_jacobian(z, event_ndims=0)
log_prob_x
normal = tfd.Normal(loc=0., scale=1.)
n = 10000
z = normal.sample(n)
scale = 4.5
shift = 7
scale_and_shift = tfb.Chain([tfb.Shift(shift), tfb.Scale(scale)])
scale_transform = tfb.Scale(scale)
shift_transform = tfb.Shift(shift)
scale_and_shift_temp = shift_transform(scale_transform)
x = scale_and_shift.forward(z)
x
tf.norm(x - (scale * z + shift))
plt.hist(z.numpy(), bins=50, density=True, label='z')
plt.hist(x.numpy(), bins=50, density=True, label='x')
plt.legend()
plt.show()
inv_x = scale_and_shift.inverse(x)
tf.norm(inv_x - z)
log_prob_x = normal.log_prob(z) - scale_and_shift.forward_log_det_jacobian(z, event_ndims=0)
log_prob_x
log_prob_x = normal.log_prob(scale_and_shift.inverse(x)) + scale_and_shift.inverse_log_det_jacobian(x, event_ndims=0)
log_prob_x
x = tf.random.normal(shape=(100, 1))
softfloor = tfb.Softfloor(temperature=0.01)
y = softfloor.forward(x)
print(y.shape)
softfloor = tfb.Softfloor(temperature=[0.2, 1.])
y = softfloor.forward(x)
print(y.shape)
softfloor = tfb.Softfloor(temperature=[0.01, 0.1, 1.])
y = softfloor.forward(x)
print(y.shape)
def _plot(nparams, bijector, params, x):
bijector_params = tuple(getattr(bijector, name) for name in params)
upper_params = [name[0].upper() + name[1:] for name in params]
fig = plt.figure(figsize=(14, 5))
lines = plt.plot(np.tile(x, nparams), bijector.forward(x))
for l in zip(lines, *bijector_params):
labels = ": {:.2f}, ".join(upper_params) + ': {:.2f}'
l[0].set_label(labels.format(*l[1:]))
plt.legend()
plt.show()
x = np.linspace(-2, 2, 2000)[..., np.newaxis]
_plot(3, softfloor, ['temperature'], x)
exps = tfb.GumbelCDF(loc=[0.5, 1., 1.5, 2., 3.], scale=[1, 2, 2, 3, 4])
x = np.linspace(-10, 10, 2000, dtype=np.float32)[..., np.newaxis]
_plot(5, exps, ['loc', 'scale'], x)