Official implementation of "ARD-VAE: A Statistical Formulation to Find the Relevant Latent Dimensions of Variational Autoencoders" by Surojit Saha, Sarang Joshi, and Ross Whitaker (University of Utah), WACV 2025.
The Automatic Relevancy Detection VAE (ARD-VAE) discovers how many latent dimensions a
dataset actually needs — instead of treating the bottleneck size as a hyperparameter tuned by
trial and error. It replaces the VAE's fixed prior
Code: https://github.com/Surojit-Utah/ARD-VAE
Paper (arXiv): https://arxiv.org/abs/2501.10901
On 3D Shapes, traversing a relevant latent axis changes a single generative factor, while traversing a collapsed (pruned) axis leaves the decoder output essentially unchanged — exactly the behavior ARD-VAE uses to identify the relevant axes.
- Key Contributions
- Method
- Repository Structure
- Installation
- Datasets
- Usage
- Extending ARD-VAE to a new dataset
- Results
- Key Findings
- Citation
- Relevance discovery via a hierarchical prior. ARD-VAE finds the relevant latent axes using an automatic-relevance-determination (ARD) prior, without modifying the VAE ELBO.
-
Architecture- and optimizer-agnostic. Unlike mask/gating methods (MaskAAE,
GECO-
$L_0$ -ARM-VAE), it needs no trainable masks, no$L_0$ regularizer, and no special training recipe — just the standard reconstruction/KL trade-off weight. - Start-high, prune-down. Begin with a generously large latent size and let training collapse the unnecessary axes; the discovered count is robust to the initial size.
- Validated broadly. Matches the known factor count on synthetic data (DSprites, 3D Shapes) and gives the best/near-best FID, precision-recall, and disentanglement (MIG) on real data.
A VAE maximizes the evidence lower bound (ELBO) with an amortized Gaussian posterior
A fixed isotropic prior forces every axis to unit variance, so the bottleneck size
ARD-VAE lets each axis carry its own precision
Marginalizing
Because the Gamma is conjugate to the Gaussian, the posterior
where
The marginalized, data-dependent prior
With enough samples in
Here loss/vae_loss.py as kld_loss(mean, log_var, alpha, beta).
After training, ARD-VAE ranks axes rather than using a hard mask. The estimated variance is
and the relevance score is the weighted variance
Why the relevance score matters. The raw estimated variance alone is noisy and its scale varies with the initial latent size, making a single threshold unreliable. Weighting by the decoder-sensitivity Jacobian squashes spurious axes and yields a clean, size-agnostic profile (right) from which the active count is read off:
The same estimated-variance profile holds across real datasets — a few high-variance relevant axes and many collapsed ones:
Estimated variance b/a per latent axis (sorted) for MNIST, CelebA,
and CIFAR10 — relevant axes carry high variance; the rest collapse toward zero.
- Split
$\mathcal{X}_{train}$ into an SGD set$\mathcal{X}_{sgd}$ and a (smaller) hyperprior set$\mathcal{X}_{\alpha}$ with$|\mathcal{X}_{\alpha}| \approx 10\mathrm{K}$ . - Each epoch, encode
$\mathcal{X}_{\alpha}$ to form$D_{\mathbf{z}}$ and update$\mathbf{a}_L, \mathbf{b}_L$ via the closed forms above (a lagged update stabilizes the KL target); then resample$\mathcal{X}_{\alpha}$ and$\mathcal{X}_{sgd}$ . - For each minibatch from
$\mathcal{X}_{sgd}$ : encode, reconstruct, and minimizereconstruction + kld_scalar * KL(q || p(z|D_z))with Adam. - The single hyperparameter
kld_scalar— the paper's$\beta$ — is tuned to reach a target validation reconstruction; it is insensitive to the initial latent size$L$ .
ARD-VAE/
├── Main.py # Entry point: --run_id, --config_id
├── config/local_config.py # Per-dataset configurations (0=CIFAR10, 1=CelebA, 2=MNIST)
├── data/ # Dataloaders: MNIST, CIFAR10, CelebA (+ datagenerator)
├── models/ # Encoder/Decoder + Sampling per dataset (ae_model_*)
├── loss/vae_loss.py # ARD KL (Gamma target var b/a) + reconstruction (MSE / BCE)
├── train/trainer.py # Training loop + per-epoch hyperprior (a, b) updates
├── lr_schedular/ # ReduceLROnPlateau (CustomReduceLRoP)
├── eval/study_fid/ # FID (Inception), precision/recall, relevant-axis analysis
├── util/ # Plotting, logging, directory helpers
└── README.md
ARD-VAE is implemented in TensorFlow / Keras (no PyTorch). A typical environment:
conda create -n ardvae python=3.9
conda activate ardvae
pip install -r requirements.txt
# or explicitly:
# pip install tensorflow numpy scipy matplotlib imageio tqdm nvidia-ml-py3
Main.pyusesnvidia_smi(fromnvidia-ml-py3) to auto-select a free GPU and falls back to CPU when none is available. FID evaluation loads a frozen Inception-v3 graph viatensorflow.compat.v1.
Real-world benchmarks (initial latent size
| Dataset | Input | Paper |
Notes |
|---|---|---|---|
| MNIST | 16 | auto-downloads via Keras; padded 28→32 | |
| CelebA | 64 | needs a pre-built train_images_npy.npy
|
|
| CIFAR10 | 128 | auto-downloads via Keras | |
| ImageNet (32×32) | 256 | (supplementary) |
Synthetic disentanglement benchmarks with 6 known factors (ground-truth active count),
trained with an initial
| Dataset | Known factors |
|---|---|
| DSprites | 6 |
| 3D Shapes | 6 |
Pixels are scaled to
What's included in this repo. This snapshot ships dataloaders (
data/), models (models/), andMain.pydispatch branches for MNIST, CelebA, and CIFAR10. The paper's full evaluation also covers DSprites, 3D Shapes, and ImageNet, and the training/eval code already includes the hooks for them (e.g.trainer.pyand the sample generator use a Bernoulli/logits decoder forDSprites). Their dataloaders/models simply aren't part of this snapshot — add them by following Extending ARD-VAE to a new dataset. The disentanglement metrics (FactorVAE/MIG) follow the standarddisentanglement_libsetup.
CelebA data.
data/dataloader_CelebA.pyloadstrain_images_npy.npyfrom the current working directory (data_dir = ''). Build that NumPy array of CelebA images (64×64×3, uint8/float) beforehand and runMain.pyfrom the directory that contains it, or editself.data_dir.
Note — shipped config vs. paper.
config/local_config.pyships start-high initial sizes: CIFAR10latent_dim=256, CelebAlatent_dim=64, MNISTlatent_dim=128(withkld_scalar0.05 / 3.0 / 0.5). The paper's main FID/precision-recall table uses MNIST L=16 and CIFAR10 L=128. Setlatent_dimaccordingly to reproduce a specific table; the whole point of ARD-VAE is that the discovered active count is stable across the initial$L$ .
1) Configure the run in config/local_config.py (dataset_name, latent_dim, num_filter,
epochs, batch_size, t_stat_samples, learning_rate, kld_scalar, etc.).
2) Train by selecting a config index and a run id (the run id sets the seed):
python Main.py --run_id 1 --config_id 0--config_id selects a dataset config: 0 = CIFAR10, 1 = CelebA, 2 = MNIST. Each
--run_id is a different seed (the paper trains 5 runs per dataset). Outputs are written to
logs/{dataset}/Dim_{L}/Run_{run_id}/ (checkpoints, reconstructions, generations, latent
projections, TensorBoard logs).
3) Evaluate (FID + precision/recall + active dimensions) via the scripts in
eval/study_fid/ — generate samples from a checkpoint and score them against
the pre-computed Inception statistics (fid_stats_*.npz).
A new dataset requires four wired pieces — mirror an existing one (e.g. CIFAR10):
- Config — add an entry in
config/local_config.py(dataset_name,latent_dim,num_filter,epochs,kld_scalar,t_stat_samples, …). - Dataloader — add
data/dataloader_<NAME>.pyimplementing the trainer contract:__init__(dataset_name, t_stat_samples, batch_size=100)split_train_n_val_data() -> (x_train, x_val)(called once inMain.py)create_val_dataset() -> tf.data.Dataset(called inTrainer.__init__)create_t_stat_n_train_dataset() -> (train_dataset, t_stat_dataset)(called each epoch; draws a disjoint SGD / hyperprior split)- exposes attributes
batch_size,t_stat_samples,x_train,x_val - images as
float32NHWC normalized to[0,1](or[-1,1]like CelebA).
- Model — add
models/ae_model_<NAME>.pywithEncoder(latent_dim, num_filter, conv_kernel_initializer_method, scatter_use_var, axis_samples)returning[mean, log_var](width2*latent_dim) andDecoder(latent_dim, num_filter, input_channels, reg_strength, conv_kernel_initializer_method); both takecall(inputs, use_batch_norm, training). - Dispatch — add the import and an
elif dataset_name=='<NAME>':branch inMain.py.
Use MSE reconstruction (autoencoder_loss) for continuous pixels or BCE (autoencoder_ce_loss)
for binary data (e.g. DSprites). No bandwidth/relevance hyperparameters need tuning — only
kld_scalar.
Evaluation is a separate sub-pipeline. eval/study_fid/ is largely standalone: it has its
own local_config.py and duplicated ae_model_* copies, and expects pre-computed
reference statistics (fid_stats_<dataset>.npz). To score a new dataset (FID / precision-recall /
active dimensions) you must also register it there (config + model) and provide its FID
reference stats — training-side wiring alone is not enough.
All methods are trained 5 times per dataset (mean ± std). Active = number of latent dimensions actually used.
Competing methods use
| Method | DSprites FactorVAE | MIG | Active | 3D Shapes FactorVAE | MIG | Active |
|---|---|---|---|---|---|---|
| VAE (L=6) | 64.78 | 0.06 | 6.00 | 55.85 | 0.13 | 6.00 |
|
|
75.55 | 0.20 | 6.00 | 75.51 | 0.40 | 6.00 |
| DIP-VAE-I (L=6) | 59.47 | 0.05 | 6.00 | 51.94 | 0.06 | 6.00 |
| DIP-VAE-II (L=6) | 60.70 | 0.08 | 6.00 | 63.66 | 0.24 | 6.00 |
| RAE (L=6) | 64.21 | 0.04 | 6.00 | 53.57 | 0.03 | 6.00 |
| ARD-VAE (L=10) | 63.37 | 0.22 | 5.80 | 79.26 | 0.52 | 6.40 |
ARD-VAE recovers ≈6 active dimensions and gives the best MIG on both datasets and the best FactorVAE metric on 3D Shapes.
| Method | MNIST (L=16) Active | FID | Prec | Rec | CIFAR10 (L=128) Active | FID | Prec | Rec |
|---|---|---|---|---|---|---|---|---|
| VAE | 16 | 28.78 | 0.88 | 0.97 | 128 | 147.74 | 0.50 | 0.47 |
|
|
16 | 50.62 | 0.82 | 0.95 | 128 | 180.94 | 0.30 | 0.41 |
| RAE | 16 | 18.79 | 0.87 | 0.95 | 128 | 94.34 | 0.74 | 0.47 |
| WAE | 16 | 25.42 | 0.92 | 0.92 | 128 | 140.49 | 0.42 | 0.31 |
| GECO- |
10.0 | 304.75 | 0.03 | 0.38 | 68.0 | 320.75 | 0.02 | 0.04 |
| MaskAAE | 9.8 | 144.92 | 0.00 | 0.07 | 3.8 | 298.30 | 0.07 | 0.04 |
| ARD-VAE | 12.8 | 22.24 | 0.91 | 0.98 | 105.8 | 87.56 | 0.82 | 0.51 |
ARD-VAE uses fewer dimensions than the nominal
Fixing
| Dataset |
|
|
|
|
|---|---|---|---|---|
| MNIST (L=16) | 12.60 | 22.30 | 12.40 | 22.31 |
| CIFAR10 (L=128) | 116.40 | 86.50 | 117.80 | 87.88 |
-
Discovers the right count. On synthetic data with 6 known factors, ARD-VAE recovers ≈6
active axes from an over-specified
$L$ ; the count is stable for$L \in \{10,15,20,30\}$ . - Best/near-best generation. Best FID, precision, and recall on CIFAR10 and best recall on MNIST, using fewer than the nominal dimensions.
-
Robust where mask/gating methods break. MaskAAE and GECO-
$L_0$ -ARM-VAE are highly sensitive to architecture/hyperparameters and collapse or diverge on the shared setups; ARD-VAE needs only the standardkld_scalar. - No ELBO surgery. The only change from a vanilla VAE is the hierarchical (relevance-aware) prior — so ARD-VAE drops into virtually any VAE architecture.
If you use this code or method, please cite:
@inproceedings{saha2025ardvae,
title = {ARD-VAE: A Statistical Formulation to Find the Relevant Latent Dimensions of Variational Autoencoders},
author = {Saha, Surojit and Joshi, Sarang and Whitaker, Ross},
booktitle = {IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)},
year = {2025},
eprint = {2501.10901},
archivePrefix = {arXiv}
}Authors: Surojit Saha, Sarang Joshi, Ross Whitaker — University of Utah
(surojit.saha@utah.edu, sarang.joshi@utah.edu, whitaker@cs.utah.edu).




