Skip to content

Repository files navigation

bvaex

A convolutional Beta-VAE built from scratch in JAX. No autograd, no neural network libraries. Every forward pass, backward pass, gradient, and parameter update is written manually using jax.numpy and jax.lax.

Trained on the full CelebA dataset on GPU.

Full writeup here: Convolutional Beta VAE from Scratch in JAX


Results

Evaluated on 1,000 held out images.

Metric Value
SSIM 0.9364
PSNR 22.49 dB
Final Reconstruction Loss 95.38
Final KL Divergence 110.14
Channel Balance (R / G / B) 0.56 / 0.49 / 0.44

Top row is the original input, bottom row is the model's reconstruction.

Reconstructed Faces

Faces generated by sampling z from a standard normal and running it through the decoder.

Generated Faces

Linear interpolation between the latent representations of two different identities.

Latent Interpolation

ELBO, reconstruction loss, and KL divergence over 100 epochs.

Loss Curves


What is implemented from scratch

Everything below was written manually with explicit gradient derivations. No autograd anywhere.

  • Conv2D forward and backward (Encoder.py)
  • TransposedConv2D forward and backward (Decoder.py)
  • Dense layers with manual gradients (Dense.py)
  • ReLU and Sigmoid activations (Activation.py)
  • Reparameterization trick (Reparameterize.py)
  • ELBO loss with reconstruction and KL terms (ELBO.py)
  • AdamW optimizer with decoupled weight decay (Adam.py)
  • Xavier/Glorot weight initialization
  • Gradient clipping

Architecture

The encoder is three conv layers that downsample the input from 64x64 down to 8x8, followed by a flatten and two dense heads that output μ and log σ². The latent space is 128-dimensional. Sampling uses the reparameterization trick, z = μ + σ ⊙ ε where ε is drawn from a standard normal.

The decoder mirrors the encoder. A dense layer maps the latent vector back to 8192 dimensions, reshapes it, and three transposed conv layers upsample back to 64x64. Final activation is sigmoid.

Encoder Channels Kernel Stride Activation Output
Conv2D 3 to 32 4 2 ReLU 32 x 32 x 32
Conv2D 32 to 64 4 2 ReLU 64 x 16 x 16
Conv2D 64 to 128 4 2 ReLU 128 x 8 x 8
Flatten + Dense 8192 to 128 μ, log σ²
Decoder Channels Kernel Stride Activation Output
Dense + Reshape 128 to 8192 128 x 8 x 8
TransposedConv2D 128 to 64 4 2 ReLU 64 x 16 x 16
TransposedConv2D 64 to 32 4 2 ReLU 32 x 32 x 32
TransposedConv2D 32 to 3 4 2 Sigmoid 3 x 64 x 64

The objective is ELBO = Reconstruction Loss + β × KL Divergence, with β = 0.5.


Dataset

CelebA, all 202,599 images, resized to 64x64 and stored as CHW tensors. The data loader preloads everything into memory for fast GPU training.


Training

git clone https://github.com/ojayballer/bvaex.git
cd bvaex
pip install jax jaxlib numpy matplotlib pillow
python train.py
Setting Value
GPU NVIDIA Tesla P100, 16GB
Epochs 100
Batch Size 512
Optimizer AdamW, lr=0.001, β₁=0.9, β₂=0.999, weight decay=0.01
Latent Dimension 128
KL Weight β 0.5
Training Time ~2 hours
Platform Kaggle

JAX compiles everything through XLA, so even though the layers and gradients are all manually implemented, they still run efficiently on GPU.

Trained weights are in weights/epoch_100/.


Evaluation

python evaluate.py
python metrics.py
python interpolate.py

Outputs go to results/. The reconstructions are slightly blurry because of MSE loss. More on that in the blog post.


Project structure

bvaex/
├── model/
│   ├── __init__.py
│   ├── Encoder.py
│   ├── Decoder.py
│   ├── Dense.py
│   ├── ELBO.py
│   ├── Adam.py
│   ├── Activation.py
│   ├── Reshape.py
│   ├── Reparameterize.py
│   └── model.py
├── train.py
├── evaluate.py
├── metrics.py
├── interpolate.py
├── plots.py
├── load_data.py
├── results/
│   ├── reconstruction_grid.png
│   ├── generated_faces.png
│   ├── latent_interpolation.png
│   └── loss_curves.png
└── weights/
    └── epoch_100/

References


License

MIT

About

A generative model that learns to reconstruct and sample human faces, built on raw JAX primitives

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages