Transform parameters#
A positive parameter can be represented by an unconstrained source while the likelihood still receives its positive value. Use a bijection when you want to preserve a prior already defined on the positive scale.
Keep positive values#
Starting with model from Build your first model, copy
it and transform its variance:
import jax
import jax.numpy as jnp
import tensorflow_probability.substrates.jax.bijectors as tfb
import tensorflow_probability.substrates.jax.distributions as tfd
import liesel.goose as gs
import liesel.model as lsl
transformed = model.copy()
variance = transformed.vars["variance"]
variance.biject(tfb.Exp(), name="log_variance")
log_variance = variance.bijected_var
(log_variance.name, float(log_variance.value), float(variance.value))
('log_variance', 0.0, 1.0)
transformed.plot(width=8, height=5, legend=False)
The new strong parameter is log_variance. The original variance is now
weak and calculates exp(log_variance). The exponential bijector’s forward
direction goes from the unconstrained value to the positive value; its inverse
computes the initial log variance. The graph gains that value dependency. Its colors and edges follow the
key in the first tutorial.
biject returns the original variable, which is useful for chaining. Read
.bijected_var to obtain the new parameter. transform()
returns the transformed variable instead. biject("auto") selects the
distribution’s default event-space bijector; an explicit bijector makes your
choice visible.
Preserve the density#
The prior moves to the unconstrained source and includes the change-of-variables Jacobian. If the original prior is on variance \(v\) and \(u=\log(v)\), the transformed log density is \(\log p(\exp(u)) + u\). The parameter flag moves to the source as well.
By comparison, new_calc(jnp.exp, log_variance) only creates a calculation.
A normal prior placed directly on log_variance defines a log-normal prior
on variance; it does not automatically transform an arbitrary prior on variance.
The second tutorial uses this direct modeling choice for a log-scale predictor.
Density modes depend on the parameterization. Even with the correct Jacobian, optimizing the transformed density need not give the transform of the original density’s mode.
Choose parameters#
For an existing distribution, biject_parameters() can
transform its strong parameter inputs. For example, this selects the scale
input of a normal likelihood:
scale = lsl.Var.new_param(
1.0,
dist=lsl.Dist(tfd.LogNormal, loc=0.0, scale=0.5),
name="scale",
)
likelihood = lsl.Dist(tfd.Normal, loc=0.0, scale=scale)
likelihood.biject_parameters({"scale": tfb.Exp()});
scale.bijected_var.name
'h(scale)'
These keys name distribution arguments, not model variables. For precise control
over the source name and its inference configuration, use Var.biject.
Set up inference#
If a variable already has inference settings, supply settings for the new source
or explicitly drop the old ones with inference="drop". For an untransformed
variance prepared for NUTS, the corresponding call is:
prepared = model.copy()
prepared.vars["variance"].biject(
tfb.Exp(),
name="log_variance",
inference=gs.MCMCSpec(gs.NUTSKernel),
);
Sample or optimize log_variance and interpret the back-transformed
variance. Configure the other parameters too before running inference.
See Sample your first posterior for a complete MCMC example and
Choose optimizers for parameter selection in optimization.