Getting Started#

pyautoencoder is a PyTorch library for building and training state-of-the-art autoencoders. It is designed to host a growing collection of autoencoder variants, with structured outputs and modular loss functions to make experimentation easy.

Installation#

Install pyautoencoder via pip:

pip install pyautoencoder

Or install from source:

git clone https://github.com/andrea-pollastro/pyautoencoder.git
cd pyautoencoder
pip install -e .

Quick Start#

Vanilla Autoencoder (AE)#

Train a simple autoencoder on MNIST:

import torch
import torch.nn as nn
from pyautoencoder.vanilla import AE

# Define encoder and decoder
encoder = nn.Sequential(
    nn.Flatten(),
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 64),
)

decoder = nn.Sequential(
    nn.Linear(64, 256),
    nn.ReLU(),
    nn.Linear(256, 784),
    nn.Unflatten(-1, (1, 28, 28)),  # x_hat must have the same shape as x
)

# Create and build the model
model = AE(encoder=encoder, decoder=decoder)
model.build(torch.randn(1, 1, 28, 28))

# Training loop
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

x_batch = torch.rand(32, 1, 28, 28)  # stand-in for an MNIST batch, values in [0, 1]
output = model(x_batch)
loss_results = model.compute_loss(x_batch, output, likelihood='bernoulli')
optimizer.zero_grad()
loss_results.objective.backward()
optimizer.step()

# Inference: encode/decode track no gradients but keep the train/eval mode
model.eval()
z = model.encode(x_batch).z       # AEEncodeOutput → latent tensor
x_hat = model.decode(z).x_hat     # AEDecodeOutput → reconstruction tensor

Variational Autoencoder (VAE)#

Train a VAE with the reparameterization trick:

import torch
import torch.nn as nn
from pyautoencoder.variational import VAE

# Define encoder and decoder
encoder = nn.Sequential(
    nn.Flatten(),
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 128),
)

decoder = nn.Sequential(
    nn.Linear(64, 256),
    nn.ReLU(),
    nn.Linear(256, 784),
    nn.Unflatten(-1, (1, 28, 28)),  # x_hat must have the same shape as x
)

# Create and build the model (the encoder must output flat features [B, F])
model = VAE(encoder=encoder, decoder=decoder, latent_dim=64)
model.build(torch.randn(1, 1, 28, 28))

# Training loop
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

x_batch = torch.rand(32, 1, 28, 28)  # stand-in for an MNIST batch, values in [0, 1]
output = model(x_batch, S=1)  # S=1: single Monte Carlo sample, x_hat is [32, 1, 1, 28, 28]
loss_results = model.compute_loss(x_batch, output, beta=1, likelihood='bernoulli')
optimizer.zero_grad()
loss_results.objective.backward()
optimizer.step()

Core Concepts#

Model outputs#

All forward passes return structured output dataclasses that are easy to inspect:

output = model(x)  # the AE above
print(output)
# AEOutput(x_hat=Tensor(shape=(32, 1, 28, 28), dtype=torch.float32),
#          z=Tensor(shape=(32, 64), dtype=torch.float32))

Loss Result#

Each model’s compute_loss returns a LossResult with the objective to optimize and a dictionary of diagnostics:

loss_results = model.compute_loss(x, output)
loss_results.objective.backward()      # Optimize this
# Diagnostics are detached 0-dim tensors: call .item() when logging
print({name: val.item() for name, val in loss_results.diagnostics.items()})