Use posterior draws#
Extract draws, compute predictions, and save a finished run.
from pathlib import Path
from tempfile import TemporaryDirectory
import jax
import jax.numpy as jnp
import numpy as np
import pandas as pd
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
Prepare the example#
These examples require the regression model and sampling results from
Sample your first posterior, with sigma_sq included in the stored draws.
Extract the posterior#
samples = results.get_posterior_samples()
sample_shapes = pd.DataFrame(
{
"Variable": ["beta", "sigma_sq"],
"Shape": [samples["beta"].shape, samples["sigma_sq"].shape],
},
)
sample_shapes
| Variable | Shape | |
|---|---|---|
| 0 | beta | (4, 1000, 2) |
| 1 | sigma_sq | (4, 1000) |
The shapes are (4, 1000, 2) and (4, 1000) in the tutorial:
chains, draws, then any parameter dimensions. Preserve the first two axes
when computing diagnostics. get_samples() also includes initial values
and warmup; use get_posterior_samples() for posterior inference.
Record derived values#
Goose records variables updated by kernels. The tutorial also sets
positions_included=["sigma_sq"] to record the derived variance.
Here NUTS samples log_sigma_sq; sigma_sq is its positive transformation.
positions_excluded overrides included positions and affects storage only.
It does not remove a kernel. Excluding sampled parameters can prevent later
predictions or diagnostics that need those draws.
For large intermediate arrays, retain the underlying parameter draws and recompute quantities as needed instead:
variance = model.predict(samples, predict=["sigma_sq"])
gs.SamplesSummary(variance).to_dataframe()[["mean", "sd"]].round(3)
| mean | sd | |
|---|---|---|
| variable | ||
| sigma_sq | 1.039 | 0.065 |
Predict new responses#
For the tutorial’s design matrix variable X, construct a grid with the
same two columns: intercept and covariate.
x_grid = jnp.linspace(0.0, 1.0, 50)
X_grid = jnp.column_stack([jnp.ones_like(x_grid), x_grid])
mean = model.predict(
samples,
predict=["mu"],
newdata={"X": X_grid},
)
mean_summary = gs.SamplesSummary(mean, which=["quantiles"]).to_dataframe()
mean_summary["x"] = x_grid
mean_summary.iloc[[0, -1]][["x", "q_0.05", "q_0.5", "q_0.95"]].round(3)
| x | q_0.05 | q_0.5 | q_0.95 | |
|---|---|---|---|---|
| variable | ||||
| mu | 0.0 | 0.839 | 0.992 | 1.140 |
| mu | 1.0 | 2.741 | 2.888 | 3.041 |
mean["mu"] contains the regression mean at each grid point for every draw.
The displayed endpoints show how the fitted mean rises across the grid.
The 5th and 95th percentiles form a pointwise 90% credible interval for
the mean, as plotted in the first tutorial.
To include observation noise, simulate responses conditional on those draws:
parameter_draws = {name: samples[name] for name in model.parameters}
predictive = model.sample(
sample_shape=(),
seed=jax.random.key(2027),
posterior_samples=parameter_draws,
newdata={"X": X_grid, "y": jnp.zeros_like(x_grid)},
)
y_predictive = predictive["y"]
y_predictive.shape
(4, 1000, 50)
Pass draws for the writable parameters to model.sample. The recorded
sigma_sq is a derived value in this model; it is recomputed from
log_sigma_sq rather than set directly.
The placeholder y gives the response the new grid length while the model
updates its state. These values are replaced by simulated responses; they are
not additional observations used for fitting.
With shape=(), there is one simulated response vector per posterior draw.
Predictive intervals also include observation variation and are generally
wider. See predict() and
sample() for new-data and shape rules. Additional
sample dimensions, if requested, precede the posterior chain/draw dimensions.
Save a finished run#
with TemporaryDirectory() as directory:
path = Path(directory) / "regression-samples.pkl"
results.pkl_save(path)
restored = gs.SamplingResults.pkl_load(path)
This example uses a temporary directory; choose a persistent path to keep your run. Load only trusted pickle files. Keep the model definition, data, and environment information with the result.
Alternatively, pass save_path="regression-samples.pkl" to
run_for_epochs. If that path already exists, Goose loads it and skips
sampling, even when you changed the model, seed, or sampling settings.
Use a fresh path for a new fit. A saved SamplingResults object contains
results; it is not an engine checkpoint for resuming a chain.