How to Work with Autoencoders and Variational Autoencoders (VAEs) for Image Generation
Autoencoders compress images into a latent representation and reconstruct them, while variational autoencoders (VAEs) force that latent space into a standard normal distribution so you can sample new images by decoding random vectors.
Both architectures are fundamental tools for unsupervised learning and generative modeling in computer vision. The Microsoft AI-For-Beginners repository provides complete, runnable implementations in PyTorch and TensorFlow that walk through building convolutional autoencoders and VAEs for image generation tasks.
What Is an Autoencoder vs. a VAE?
An autoencoder consists of two networks: an encoder that squeezes input data into a low-dimensional latent vector, and a decoder that reconstructs the original input from that vector. During training, the network learns to minimize reconstruction loss, typically using binary cross-entropy or MSE.
A variational autoencoder extends this by treating the latent representation as a probability distribution. Instead of outputting a single vector, the encoder predicts mean (μ) and log-variance (log σ²) parameters for each latent dimension. The reparameterization trick samples from this distribution while keeping gradients flowable:
z = μ + ε · σ, where ε ~ N(0, I)
The KL-divergence term in the loss forces these distributions toward a standard normal. After training, you can sample z ~ N(0, I) and feed it through the decoder to generate entirely new images.
Core VAE Architecture Components
The PyTorch implementation in lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb defines three essential modules:
| Component | Purpose | Output Shape (MNIST 28×28) |
|---|---|---|
| VAEEncoder | Convolutional feature extraction → μ and log σ² | μ (latent_dim), logvar (latent_dim) |
| Reparameterization | Sample z with differentiable randomness | z (latent_dim) |
| VAEDecoder | Transpose convolutions reconstruct image | x̂ (1, 28, 28) |
The TensorFlow notebook AutoencodersTF.ipynb follows identical logic using tf.keras.layers.
Building a Convolutional VAE in PyTorch
Below is the complete implementation extracted from the Microsoft repository's PyTorch notebook. This VAE uses 2D convolutions for encoding and transposed convolutions for decoding, trained on MNIST with a 2-dimensional latent space for visualization.
Encoder: Predicting Latent Distribution Parameters
import torch
import torch.nn as nn
import torch.nn.functional as F
latent_dim = 2
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class VAEEncoder(nn.Module):
def __init__(self):
super().__init__()
self.conv = nn.Sequential(
nn.Conv2d(1, 32, 3, stride=2, padding=1), # 28 → 14
nn.ReLU(),
nn.Conv2d(32, 64, 3, stride=2, padding=1), # 14 → 7
nn.ReLU(),
)
self.fc_mu = nn.Linear(64 * 7 * 7, latent_dim)
self.fc_logvar = nn.Linear(64 * 7 * 7, latent_dim)
def forward(self, x):
x = self.conv(x)
x = x.view(x.size(0), -1)
mu = self.fc_mu(x)
logvar = self.fc_logvar(x)
return mu, logvar
Decoder: Reconstructing Images from Latent Vectors
class VAEDecoder(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(latent_dim, 64 * 7 * 7)
self.deconv = nn.Sequential(
nn.ConvTranspose2d(64, 32, 3, stride=2, padding=1, output_padding=1), # 7 → 14
nn.ReLU(),
nn.ConvTranspose2d(32, 1, 3, stride=2, padding=1, output_padding=1), # 14 → 28
nn.Sigmoid(),
)
def forward(self, z):
x = self.fc(z)
x = x.view(-1, 64, 7, 7)
x = self.deconv(x)
return x
Full VAE with Reparameterization
class VAE(nn.Module):
def __init__(self):
super().__init__()
self.encoder = VAEEncoder()
self.decoder = VAEDecoder()
def reparameterize(self, mu, logvar):
std = torch.exp(0.5 * logvar)
eps = torch.randn_like(std)
return mu + eps * std
def forward(self, x):
mu, logvar = self.encoder(x)
z = self.reparameterize(mu, logvar)
recon = self.decoder(z)
return recon, mu, logvar
Training a VAE for Image Generation
The combined loss function balances reconstruction quality against latent space regularization:
def loss_fn(recon, x, mu, logvar):
recon_loss = F.binary_cross_entropy(recon, x, reduction='sum')
kl = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
return recon_loss + kl
Reconstruction loss ensures decoded images match inputs. KL-divergence penalizes deviations from N(0, I), creating a smooth, continuous latent space where interpolation yields meaningful image blends.
Training Loop
model = VAE().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(10):
for batch, _ in train_loader: # MNIST: [B, 1, 28, 28]
batch = batch.to(device)
recon, mu, logvar = model(batch)
loss = loss_fn(recon, batch, mu, logvar)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f'Epoch {epoch}: loss={loss.item():.2f}')
Generating New Images with a Trained VAE
Once training converges, sample from the prior distribution and decode:
with torch.no_grad():
# Sample 64 latent vectors from standard normal
z = torch.randn(64, latent_dim).to(device)
samples = model.decoder(z).cpu()
# samples shape: [64, 1, 28, 28] — new handwritten digits
Because the KL term constrained the latent space, these samples resemble the training distribution even though the model never saw them during training.
TensorFlow/Keras Implementation
The repository's AutoencodersTF.ipynb implements the identical architecture using tf.keras:
- Encoder:
Denselayers orConv2D→Flatten→ separateDenseheads for μ and log σ - Reparameterization:
Lambdalayer implementingz = mu + exp(log_var * 0.5) * epsilon - Decoder:
Dense→Reshape→Conv2DTranspose→Sigmoid - Loss:
binary_crossentropy+ KL term added viaadd_loss
Both frameworks achieve equivalent results; choose based on your existing pipeline.
Key Source Files in AI-For-Beginners
| File Path | Contents |
|---|---|
lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb |
Full PyTorch VAE with CNN encoder/decoder, training, and generation |
lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb |
TensorFlow/Keras equivalent using functional and sequential APIs |
lessons/4-ComputerVision/09-Autoencoders/README.md |
Mathematical foundations: ELBO, KL divergence, and reparameterization trick |
The PyTorch notebook's implementation (cells 965–1036) specifically defines VAEEncoder, VAEDecoder, and VAEAutoEncoder classes used in the code examples above.
Practical Tips for Training VAEs
- Start with
latent_dim=2for visualization; increase to 20–50 for complex datasets - Use batch normalization in deeper encoders for stable training
- Monitor KL divergence separately — if it collapses to zero, the model ignores the latent variable (posterior collapse)
- Anneal the KL weight progressively if reconstruction quality suffers early in training
- Try β-VAE (multiply KL by β > 1) for more disentangled latent representations
Summary
- Autoencoders learn compressed representations via encoder-decoder reconstruction
- VAEs add probabilistic constraints: encoder outputs μ and log σ², trained with reconstruction loss plus KL-divergence
- The reparameterization trick enables gradient flow through random sampling:
z = μ + ε·σ - After training, sample
z ~ N(0, I)and decode to generate new images - Microsoft AI-For-Beginners provides complete PyTorch and TensorFlow notebooks for hands-on learning
Frequently Asked Questions
What is the difference between an autoencoder and a variational autoencoder?
An autoencoder learns a deterministic compression of input data, mapping each image to a single point in latent space. A variational autoencoder learns a distribution over latent codes, producing similar images from nearby points and enabling generation by sampling from the prior distribution.
Why does VAE training require the reparameterization trick?
Backpropagation cannot flow through random sampling operations. The reparameterization trick moves the randomness to an auxiliary noise variable ε, allowing gradients to propagate through the deterministic operations μ + ε·σ that compute the latent sample z.
What causes VAE posterior collapse and how can I prevent it?
Posterior collapse occurs when the KL term drives encoder outputs to match the prior regardless of input, causing the decoder to ignore the latent code. Solutions include KL annealing (starting with zero weight and increasing), free bits (capping minimum KL), or using more powerful decoders. The AI-For-Beginners notebooks use β-weighting to balance these terms.
Can VAEs generate high-resolution images comparable to GANs?
Standard VAEs produce blurrier outputs than GANs because the likelihood objective averages over modes. For sharper images, combine VAEs with adversarial training (VAEGAN), use hierarchical architectures (NVAE), or adopt diffusion-based decoders. The repository's basic implementation is best suited for understanding fundamentals and generating MNIST-level resolutions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →