Offical Code for the paper "Diffusion posterior sampling for simulation-based inference in tall data settings".
- Create a conda environment with python version 3.10.
- Install the repository as a python package with all its dependencies via
pip install -e .inside the repository folder (this executes thesetup.pyfile). - Make sure you have a CUDA compatible PyTorch version (see
torchinstallation instructions). - For the SBI benchmark experiment (section 4.2), a CUDA compatible Jax version is required (see
jaxinstallation instructions). - To run the Jansen and Rit simulator (section 4.3) please follow the instructions in
tasks/jrnnm/requirements.md.
All experiments were run with torch version 2.1.0+cu118 and jax version 0.4.25+cuda12.cudnn89 installed via:
pip install torch==2.1.0+cu118 --index-url https://download.pytorch.org/whl/cu118
pip install --upgrade "jax[cuda12_pip]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html
The environment.yml file details the environment (packages and versions) used to produce the results of all experiments. By specifying the prefix=<path_to_env> variable, you can run the file to install the environment with all its dependencies directly.
-
Diffusion generative modeling and posterior sampling
nse.py: implementation of the conditional neural score estimator class (NSE) and corresponding loss function (NSELoss). TheNSEclass has integrated LANGEVIN, DDIM and Predictor-Corrector samplers for single observation and tall posterior inference tasks. Theddimmethod combined with thefactorized_scorecorresponds to our algorithm (GAUSSandJAC), theannealed_langevin_geffnermethod corresponds to theF-NPSEmethod from Geffner et al., 2023.pf_nse.pyimplements the partially factorized neural score estimator class (PF_NSE). It extends theNSEclass to allow inputs with sets of context observationsxwith variable size (instead of single context observations). Existing samplers (e.g.ddim,annealed_langevin_geffner) are modified to split the context observations into sub sets of smaller sizen_maxbefore passing them to thefactorized_scoremethod, resulting in a "partially" factorized tall posterior sampler, such asPF-NPSEdevelopped by Geffner et al., 2023.sm_utils.py: train function forNSE.tall_posterior_sampler.py: utility functions (e.g.tweedies_approximation) for our tall posterior sampler (GAUSSandJACalgorithms).vp_diffused_priors.py: analytic diffused (uniform and gaussian) prior for a VP-SDE diffusion process.
-
Evaluation with the L-C2ST diagnostic
lc2st_new.py: implementation of the L-C2ST diagnostic to evaluate the accuracy of the sampling algorithms. Code taken from the implementation of thesbitoolbox.
-
Experiment utils
embedding_nets.py: implementation of some networks for the score model (used in the toy models experiments from section 4.1)experiment_utils.py: implementation of the metrics and other utilities for the result computation of all experiments.plot_utils.py: plotting styles for to reproduce figures from paper and functions to plot multivariate posterior pairplots from samplestasks: folder containing implementations of the simulator, prior and true posterior (if known) for each task (toy example from section 4.1, SBI benchmark examples from section 4.2 and the Jansen and Rit Neural Mass Model from section 4.3). Pre-computed training and reference data for thesbibmtasks can be found in thetasks/sbibm/datafolder.
Other files include the scripts to run experiments and reproduce the figures from the paper as described below.
- To generate the raw data (samples and metadata), run the scripts
gen_gaussian_gaussian.py path_to_saveandgen_mixt_gauss_gaussian.py path_to_savewherepath_to_saveis the path one wants to save the raw files. - To generate a CSV with sliced wasserstein, run
treat_gaussian_data.py path_to_saveandtreat_mixt_gaussian_data.py path_to_savewherepath_to_saveis as above. This will create CSV files inpath_to_save. - To reproduce the results from the paper (with correct stlye) run the scripts
toy_example_gaussian_results.py path_to_saveandtoy_example_gaussian_mixture_results.py path_to_save. The figures will be saved in thefigures/folder and the time table datas in thedata/folder in thepath_to_savedirectory (hereresults/toy_models/<gaussian/gaussian_mixture>/).
The script to reproduce experiments and generate figures are sbibm_posterior_estimation.py and sbibm_results.py. The tasks for which the results are shown in the main paper are task_name = lotka_volterra, sir and slcp. We also computes results for the following extra tasks: gaussian_linear, gaussian_mixture/_uniform, bernoulli_glm/_raw, two_moons.
-
To generate the training and reference data/posterior samples run:
python sbibm_posterior_estimation.py --setup <all/train_data/reference_data/reference_posterior> --task <task_name>The data will be saved in the
tasks/sbibm/data/<task_name>folder. Pre-computed data that was used for our experiments is available in this folder. -
To train the score models run:
python sbibm_posterior_estimation.py --run train --n_train <1000/3000/10000/30000> --lr <1e-3/1e-4> --task <lotka_volterra/sir/slcp>The trained score models will be saved in
results/sbibm/<task_name>/. Pre-trained models that were used for our experiments are available in this folder. -
To sample from the approximate posterior for all observations (
num_obs = 1, ... 25) and number of observations (n_obs = 1,8,14,22,30), run:python sbibm_posterior_estimation.py --run sample_all --n_train <1000/3000/10000/30000> --lr <1e-3/1e-4> --task <lotka_volterra/sir/slcp>and add the arguments
--cov_mode <GAUSS/JAC>and--langevin geffnerwith optional--clipto indicate which algorithm should be used. The samples will be saved in the same folder as the used trained score model. -
To reproduce the figures for the
sW(resp.MMDorC2ST) metric, first runpython sbibm_results.py --compute_dist --tasks <main/extra/all>to compute all the distances. This might take some time. By default, precomputed results are loaded and can be found in
results/sbibm/<task_name>/metrics/.To quickly generate the figures, run
python sbibm_results.py --plot_dist --swd(resp
--mmdor--c2st). -
Use the
--lossesargument to plot the loss functions and the--plot_samplesargument to visualize the paiplots of reference and estimated posterior samples.
The script to reproduce experiments and generate figures are jrnnm_posterior_estimation.py and jrnnm_results.py. Results are saved in results/jrnnm/<theta_dim>d/.
-
To train the score models run:
python jrnnm_posterior_estimation.py --run train --lr <1e-3/1e-4> --theta_dim <3/4> -
To sample from the approximate posterior for
n_obs = 1,8,14,22,30, run:python jrnnm_posterior_estimation.py --run sample_all --lr <1e-3/1e-4> --theta_dim <3/4>and add the arguments
--cov_mode <GAUSS/JAC>and--langevinwith optional--clipto indicate which algorithm should be used. Specifying--run sample --n_obs 30 --single_obswill generate results for each of then_obs=30observation seperately.
Results are saved at the following directory: results/jrnnm/<theta_dim>d/<path_to_score_network>/<path_to_sampler>/<lc2st_results>/.
- To generate the calibration datasets used to compute the L-C2ST results run:
python jrnnm_posterior_estimation.py --run sample_cal_all --lr <1e-3/1e-4> --theta_dim <3/4>
and add the arguments --cov_mode <GAUSS/JAC> and --langevin with optional --clip to indicate which algorithm should be used.
- To compute the L-C2ST results first train the classifiers by running
python jrnnm_lc2st.py --run <train_data, train_null> --all_nobs
Pretrained "data" classifiers can be found at the above mentioned directory under the filenames clfs_data.... (The "null" classifiers (there are 100) take-up too much memory and we only save the corresponding test statistics).
Then compute the test statistics/p-values by running:
python jrnnm_lc2st.py --run <eval_data, eval_null> --all_nobs
Again, in each case you should add the arguments --cov_mode <GAUSS/JAC> or --langevin geffner with optional --clip to indicate which sampling algorithm should be evaluated.
Precomputed results can be found at the above mentioned directory under the filenames results_lc2st..., which includes the test statistics computed over all trials under the null hypothesis, the test statistics for the observed data and the corresponding p-values.
- To reproduce the figures run
python jrnnm_results.pywith the argumentslc2stand--dirac_distfor the plots respectively associated to the L-C2ST results and posterior concentration quantified with theMMD to Diracmetric,--pairplotfor the full pairplots with 1D and 2D histograms of the posterior, and--single_multi_obsfor the 1D histograms in the 3D case.
Two main features have been added to extend our proposition:
- Classifier-free guidance: it is possible to implicitly learn the prior score by randomly dropping the context variables during the training of the posterior score. This is useful in cases where the diffused prior score cannot be computed analytically. To do so, specify the "drop rate" in the
classifier_free_guidancevariable of the training function implemented insm_utils.py. - Partially factorized samplers:
pf_nse.pyimplements thePF_NSEclass. It extends theNSEclass to allow inputs with sets of context observationsxwith variable size (instead of single context observations). Existing samplers (e.g.ddim,annealed_langevin_geffner) are modified to split the context observations into sub sets of smaller sizen_maxbefore passing them to the factorized score methods, resulting in a "partially" factorized tall posterior sampler, such asPF-NPSEdevelopped by Geffner et al., 2023.
These features were integrated into the scripts of the SBI benchmark experiment and results can be found in the Appendix of the paper.
- To learn the prior score while training the posterior score model and then use it for sampling the tall posterior, run the
sbibm_posterior_estimation.pyscript as explained above, and add the--clf_free_guidanceargument in the command line. To generate results (compute metrics and plot figures) run thesbibm_results.py, again with added--clf_free_guidanceargument. - To reproduce the
PF-NSEexperiment, use thesbibm_pf_npse.pyscript. Figures are obtained by running thesbibm_results.pyscript as explained above (to compute and plot distances) with additional argument--pf_nse.
To address the limitations of Langevin Dynamics, we compared the algorithm proposed by Geffner et al., 2023 to a (more stable) tamed ULA, as proposed by Brosse et al. (2017).
- To obtain samples with tamed ULA, specify
--langevin tamedwhen running thesbibm_posterior_estimation.pyscript in addition to the other comand line arguments. - To reproduce the Figure from the paper, add the
--langevin_comparisoncomand line argument when runningpython sbibm_results.pyto compute and plot the sW and MMD distances between the estimated and true posterior samples.
We compare our methods (GAUSS, JAC) and LANGEVIN with the deterministic sampler proposed in Geffner et al., 2023 (Appendix D), referred to as DET_GEF. This sampler avoids Langevin dynamics by composing Gaussian reverse transitions with shared isotropic variance. Results are reported in Appendix M of the paper.
The DET_GEF sampler is implemented in nse.py as a new method named deterministic_gaussian_geffner.
- Toy Models: Just rerun the same experiments. The
DET_GEFmethod is now hard coded in the experiments scripts. - SBIBM: To sample from the approximate posterior using
DET_GEF, run thesbibm_posterior_estimation.pyscript like before, but add the--det_geffcommand argument. To reproduce the figures from Appendix M, just rerun thesbibm_results.pylike before. TheDET_GEFmethod is now hard coded in the experiments scripts.