ProbAI School 2026
2026-08-05
What is SBI? Introduction, Bayesian model types, normalizing flows.
→ Exercise 1: from MCMC to amortized inference.
Did it work? Calibration checking, model misspecification.
→ Exercise 2: diagnostics on an epidemic time-series model.
How to leverage modern generative models? Diffusion models, flow matching, consistency models.
→ Exercise 3: diffusion models with post-hoc guidance.
Everything today is about transporting a Gaussian onto our target, here a checkerboard.
A prior and a scientific simulator together define the forward process from unknown parameters \boldsymbol{\theta} to observables \mathbf{y}:
\boldsymbol{\theta} \sim p(\boldsymbol{\theta}), \qquad \mathbf{y} = \operatorname{Sim}(\boldsymbol{\theta}, \mathbf{u}),\quad \mathbf{u}\sim\text{RNG}(\cdot)
Parameters and data are drawn from the joint \boldsymbol{\theta}, \mathbf{y} \sim p(\boldsymbol{\theta}, \mathbf{y}): our Bayesian model.
Figure: S. Radev, BayesFlow.
Likelihood-based (explicit)
→ MCMC, VI (Day 1 & 2).
Simulation-based (implicit)
3-segment planar robot arm (Kruse et al., 2021)

We will return to this example in Part 3.
Train a conditional density estimator q_{\boldsymbol{\phi}}(\boldsymbol{\theta}\mid\mathbf{y}) on simulations (\boldsymbol{\theta}, \mathbf{y}) using the forward KL
\mathbb{E}_{p(\mathbf{y})}\big[\operatorname{KL}\!\big(p(\boldsymbol{\theta}\mid\mathbf{y})\,\Vert\, q_{\boldsymbol{\phi}}(\boldsymbol{\theta}\mid\mathbf{y})\big)\big]
Amortization means, we pay the training cost once and at inference on a new \mathbf{y}_{\text{obs}} we only need a single evaluation of q_{\boldsymbol{\phi}}.
→ With a neural network, but an invertible one!
Change of variables gives a tractable density: q_{\boldsymbol{\phi}}(\boldsymbol{\theta}\mid\mathbf{y}) = p\big(f_{\boldsymbol{\phi}}(\boldsymbol{\theta};\mathbf{y})\big)\, \big|\det J_{f_{\boldsymbol{\phi}}}(\boldsymbol{\theta};\mathbf{y})\big|
Forward KL means minimize the log-likelihood of the flow.
The neural network must be invertible, and we must be able to compute \det J.
Coupling flow (Dinh et al., 2017): split \boldsymbol{\theta}=(\boldsymbol{\theta}^A,\boldsymbol{\theta}^B), transform one block conditioned on the other: \mathbf{z}^A = f(\boldsymbol{\theta}, \mathbf{y}) = \boldsymbol{\theta}^A \odot \exp\!\big(s(\boldsymbol{\theta}^B,\mathbf{y})\big) + t(\boldsymbol{\theta}^B,\mathbf{y}), \qquad \mathbf{z}^B = \boldsymbol{\theta}^B
Then the inverse follows as: \boldsymbol{\theta}^A = \left(\mathbf{z}^A-t(\mathbf{z}^B,\mathbf{y})\right) \odot \exp\!\left(-s(\mathbf{z}^B,\mathbf{y})\right), \quad \boldsymbol{\theta}^B=\mathbf{z}^B.
Each coupling layer applies one invertible step.
\boldsymbol{\theta}\sim q_{\boldsymbol{\phi}}\big(\boldsymbol{\theta}\mid \underbrace{s(\mathbf{y})}_{\text{summary}}\big)
The flow needs a fixed-size conditioning vector, and different data structures call for different summary networks:
| Data type | Symmetry | Summary network |
|---|---|---|
| Exchangeable set | permutation invariance | DeepSet / Set Transformer |
| Time series | temporal order | GRU / CNN / Transformer |
| Hierarchical | nested groups | set-of-sets (composed) |
s(\mathbf{y}_{\pi(1)},\dots,\mathbf{y}_{\pi(N)}) = s(\mathbf{y}_1,\dots,\mathbf{y}_N)\quad\text{for all permutations }\pi
Ordering now carries information, so the summary network needs a temporal inductive bias.
Convolutional summaries share local filters across time: s_{t,k} = \sigma\Big(b_k + \sum_{l=-L}^{L}\sum_j w_{l,j,k}\,y_{t+l,j}\Big) Short-range patterns, position-invariant.
Recurrent (GRU/LSTM) summaries carry a hidden state: \mathbf{h}_n = f_{\boldsymbol{\phi}}(\mathbf{y}_n, \mathbf{h}_{n-1}) Long-range dependence via gating.
Transformers with time or positional embeddings also handle irregularly sampled series.
Data comes in groups (subjects, experiments, sites): local variation, shared global structure (Gelman et al., 2013).
\boldsymbol{\eta}\sim p(\boldsymbol{\eta}),\quad \boldsymbol{\theta}^{(r)}\sim p(\boldsymbol{\theta}\mid\boldsymbol{\eta}),\quad \mathbf{y}^{(r)}\sim p(\mathbf{y}\mid\boldsymbol{\theta}^{(r)})
Two coupled inference targets: p(\boldsymbol{\eta}\mid\{\mathbf{y}^{(r)}\}_{r=1}^R),\qquad p(\boldsymbol{\theta}\mid\mathbf{y}^{(r)},\boldsymbol{\eta})
Summary: a set of sets. Encode each group, then aggregate across groups with a permutation-invariant network.

Bottleneck: naive amortization needs many simulator calls per group, so the budget grows with R. Part 3 comes back to this.
An open-source library for the full amortized Bayesian workflow: bayesflow.org




Simulator → adapter → neural approximator (summary + inference net) → diagnostics.
Figure: S. Radev, BayesFlow.
import bayesflow as bf
# 1. Simulator: draw (theta, y) pairs. The only thing you must provide.
simulator = bf.make_simulator([prior, likelihood_simulator])
# 2. Adapter: name/standardize/reshape variables for the networks
adapter = (bf.Adapter()
.standardize()
.concatenate(["beta", "sigma"], into="inference_variables")
.concatenate(["y"], into="inference_conditions"))
# 3. Networks: inference (a normalizing flow) + optional summary net
workflow = bf.BasicWorkflow(
simulator=simulator,
adapter=adapter,
inference_network=bf.networks.CouplingFlow(), # normalizing flow
summary_network=None, # vector data → none needed
)# 4. Train on simulations (online: simulate fresh batches each step)
history = workflow.fit_online(epochs=50, batch_size=512)
# 5. Amortized inference: one forward pass per dataset
post = workflow.sample(conditions={"y": y_obs}, num_samples=2000)
# -> post["beta"]: (2000, d) posterior draws
# 500 new datasets, same network, no retraining:
post_many = workflow.sample(conditions={"y": Y_new_500}, num_samples=2000)From MCMC to Amortized Bayesian Inference
We produce an approximation q(\boldsymbol{\theta}\mid\mathbf{y}) of the true posterior. Before we trust it, we must ask:
Is the inference faithful? Does q recover the correct posterior under the assumed model?
→ calibration checks (SBC, TARP, C2ST).
Is the model adequate? Does the model explain the observed data at all?
→ posterior/prior predictive checks, misspecification detection.
SBC exploits a self-consistency property of the Bayesian joint (Cook et al., 2006; Talts et al., 2018). Define p_{\text{SBC}}(\mathbf{y},\boldsymbol{\theta},\tilde{\boldsymbol{\theta}}) = p(\boldsymbol{\theta})\,p(\mathbf{y}\mid\boldsymbol{\theta})\,q(\tilde{\boldsymbol{\theta}}\mid\mathbf{y}) = p(\mathbf{y})\,p(\boldsymbol{\theta}\mid\mathbf{y})\,q(\tilde{\boldsymbol{\theta}}\mid\mathbf{y})
If q = p(\boldsymbol{\theta}\mid\mathbf{y}), then \boldsymbol{\theta} and \tilde{\boldsymbol{\theta}} are identically distributed given \mathbf{y}.
Testing procedure: for many draws (\boldsymbol{\theta}^{(r)},\mathbf{y}^{(r)})\sim p(\boldsymbol{\theta},\mathbf{y}), sample \tilde{\boldsymbol{\theta}}\sim q(\cdot\mid\mathbf{y}^{(r)}) and compute the rank of the true \boldsymbol{\theta}^{(r)} among posterior draws.
Calibrated ⇒ ranks are uniform.
Plot the ECDF of ranks minus uniform, with simultaneous confidence bands. Inside the band → calibrated. Image: Martin Modrák.
# Simulate a fresh validation set the network never trained on
val = simulator.sample(1000)
post = workflow.sample(conditions=val, num_samples=500)
# Rank-ECDF calibration, per marginal
bf.diagnostics.plots.calibration_ecdf(
estimates=post, targets=val
)
# Recovery: posterior mean vs. ground truth
bf.diagnostics.plots.recovery(
estimates=post, targets=val
)Fix: use data-dependent test quantities T(\boldsymbol{\theta},\mathbf{y}) so discrepancies accumulate instead of cancelling.
\mathbf{y}_{\text{obs}} \nsim p(\mathbf{y}\mid\boldsymbol{\theta})\ \text{for any}\ \boldsymbol{\theta}
Epidemic Time Series & Diagnostics
| Family | Architecture | Sampling | Density |
|---|---|---|---|
| Normalizing Flows | constrained (invertible) | 1 step | fast |
| Diffusion Models | free-form | multi-step | slow |
| Flow Matching | free-form | multi-step | slow |
| Consistency Models | free-form | few-step | N/A |
C2ST across ten benchmark tasks, so lower is better and 0.5 means indistinguishable from the reference posterior. The dashed line is the normalizing flow baseline of Lueckmann et al. (Arruda et al., 2025).
Forward noising (\mathbf{z}_t=\alpha_t\mathbf{z}_0+\sigma_t\epsilon) → train the network on \omega_t L_t(\hat{\mathbf{z}},\mathbf{z}) → backward via an ODE/SDE solver (Arruda et al., 2025).
Start at the target \mathbf{z}_0 = \boldsymbol{\theta}. Gradually add noise via an SDE (Song et al., 2021): \mathrm{d}\mathbf{z}_t = f(t)\,\mathbf{z}_t\,\mathrm{d}t + g(t)\,\mathrm{d}\mathbf{W}_t
The forward transition is Gaussian in closed form, so nothing has to be simulated: p(\mathbf{z}_t\mid\mathbf{z}_0)=\mathcal{N}(\alpha_t\mathbf{z}_0,\sigma_t^2\mathbf{I}) \quad\Longleftrightarrow\quad \mathbf{z}_t = \alpha_t\mathbf{z}_0 + \sigma_t\boldsymbol{\epsilon},\ \ \boldsymbol{\epsilon}\sim\mathcal{N}(\mathbf{0},\mathbf{I}) We can jump to any noise level t directly.
A checkerboard target \mathbf{z}_0 melts into isotropic Gaussian noise as t:0\!\to\!1.
Regress a network \hat{s} onto the score of the (Gaussian) noising kernel: \hat{s} = \arg\min_{s}\; \mathbb{E}_{\mathbf{z}_0,\mathbf{y},\boldsymbol{\epsilon},t}\Big[\omega_t\,\big\Vert s(\mathbf{z}_t,\mathbf{y},t) - \nabla_{\mathbf{z}_t}\log p(\mathbf{z}_t\mid\mathbf{z}_0)\big\Vert_2^2\Big]
This has the same minimizer as regression on the marginal \nabla_{\mathbf{z}_t}\log p(\mathbf{z}_t) (Song et al., 2021; Vincent, 2011).
The neural network can be any architecture which predicts a score, e.g., a MLP (Sharrock et al., 2024) or a transformer (Gloeckler et al., 2024).
Because \mathbf{z}_t = \alpha_t\mathbf{z}_0 + \sigma_t\boldsymbol{\epsilon} is Gaussian, the conditional target is known in closed form: \nabla_{\mathbf{z}_t}\log p(\mathbf{z}_t\mid\mathbf{z}_0) = -\boldsymbol{\epsilon}/\sigma_t
Training reduces to noise prediction: draw (\mathbf{z}_0,\mathbf{y}) from simulations, t\sim\mathcal{U}[0,1], and \boldsymbol{\epsilon}\sim\mathcal{N}(\mathbf{0},\mathbf{I}).
The trained model’s score s(\mathbf{z},t)\approx\nabla_{\mathbf{z}}\log p_t(\mathbf{z}) points toward the data.
The reverse SDE injects noise at every step.
The same trajectory, zoomed into the last time steps.
Flow matching (Lipman et al., 2023; Wildberger et al., 2023) skips the score and regresses the velocity field v directly, along a chosen interpolation between noise and data: \hat{v} = \arg\min_v\; \mathbb{E}_{t,\mathbf{z}_0,\boldsymbol{\epsilon}}\big[\Vert v(\mathbf{z}_t,\mathbf{y},t) - (\dot\alpha_t\mathbf{z}_0 + \dot\sigma_t\boldsymbol{\epsilon})\Vert_2^2\big]
Straighter paths, so usually fewer integration steps.
Density evaluation is available by integrating the Jacobian trace along the ODE, but it is expensive.
The probability-flow ODE has the same marginals as the SDE, but a deterministic path.
Each step jumps straight to data, then re-noises to a lower level.
Diffusion model (stochastic) · flow matching (deterministic ODE) · consistency model (direct jump).
The score is additive, so sampling can be steered after training by adding a gradient term.
\nabla_{\boldsymbol{\theta}_t}\log p(\boldsymbol{\theta}_t\mid\text{extra}) \approx \hat{s}(\boldsymbol{\theta}_t,\mathbf{y},t) + \nabla_{\boldsymbol{\theta}_t}\log g(\boldsymbol{\theta}_t)
→ Impose constraints, change the prior, or compose models without retraining (Bansal et al., 2023; Yang et al., 2026).
→ So re-check calibration after guidance, with the tools from Part 2.
Data comes in groups (subjects, experiments, sites): local variation, shared global structure (Gelman et al., 2013).
\boldsymbol{\eta}\sim p(\boldsymbol{\eta}),\quad \boldsymbol{\theta}^{(r)}\sim p(\boldsymbol{\theta}\mid\boldsymbol{\eta}),\quad \mathbf{y}^{(r)}\sim p(\mathbf{y}\mid\boldsymbol{\theta}^{(r)})
Two coupled inference targets: p(\boldsymbol{\eta}\mid\{\mathbf{y}^{(r)}\}_{r=1}^R),\qquad p(\boldsymbol{\theta}\mid\mathbf{y}^{(r)},\boldsymbol{\eta})
Bottleneck: simulation of a single observation is a set of sets.

Additive scores also solve the hierarchical bottleneck Compose per-group posteriors instead of simulating the full hierarchy (Arruda et al., 2026): \nabla_{\boldsymbol{\eta}}\log p(\boldsymbol{\eta}\mid\{\mathbf{y}^{(r)}\}) = (1-R)\nabla_{\boldsymbol{\eta}}\log p(\boldsymbol{\eta}) + \sum_{r=1}^R \nabla_{\boldsymbol{\eta}}\log p(\boldsymbol{\eta}\mid\mathbf{y}^{(r)})
Train on single groups; at inference, add the scores and sample the reverse SDE. This scales to 250K+ groups, with a total simulation budget smaller than one simulation of the full hierarchical model.
Back to the toy example from Part 1. A diffusion model with a plain MLP score net recovers the multimodal, non-identifiable geometry:
Diffusion Models & Custom Guidance
guidance_kwargs.Some open questions I am thinking about currently:
Whatever you build: check it with simulation-based calibration!


Tutorial review paper: 50+ SBI & diffusion models papers, benchmarks, and discussion of design choices.
▶ Slides & Tutorial with Solutions: https://github.com/arrjon/BayesFlowTutorial
Reach out: jonas.arruda@uni-bonn.de


Jonas Arruda · University of Bonn · ProbAI School 2026