[0.5.1] - VAEs
Please send any problems / bugs on the #errata channel in the Slack group, and ask any questions on the dedicated channels for this chapter of material.
If you want to change to dark mode, you can do this by clicking the three horizontal lines in the top-right, then navigating to Settings → Theme.
Links to all other chapters: (0) Fundamentals, (1) Transformer Interpretability, (2) RL.

Introduction
In this section, we're studying Variational Autoencoders (VAEs), one of two important classes of generative image models we cover in this chapter (the other, Generative Adversarial Networks (GANs), is the GANs section). Although these generally aren't SOTA any more (thanks in part to the rise of diffusion models), there are some deep conceptual insights which can be gleaned from studying these models (VAEs in particular) which help lay the groundwork for more advanced models.
These exercises will also hopefully bring much of this chapter full-circle:
- We'll cover transposed convolutions, which will serve as a refresher on some of the ideas behind convolutions (day 2: CNNs & ResNets)
- We'll be assembling NNet architectures from scratch, like in the ResNets exercises (day 2: CNNs & ResNets)
- We'll work with different loss functions, and think intuitively about what it means to optimize them (day 3: Optimization & Hyperparameters)
- We'll be working with
wandb, and will learn how to log outputs produced by our models (day 3: Optimization & Hyperparameters)
Note, many of today's exercises (especially those involving building models & writing forward pass functions) don't have as rigorous unit tests as previous days of content. Part of this is because the design spec for these models is less strict (we're not copying over weights from a pretrained model like last time, all that matters is that our model actually trains correctly, and so small changes to the solution architecture can sometimes be allowed). However, another part is that we're trying to move participants away from being too reliant on unit tests, and towards being able to independently reason through exercises or replications given just a description of the task or a paper implementation to follow. This is an invaluable skill to develop for any aspiring ML practitioner!
For a lecture on this material (it covers both VAEs and GANs), which provides some high-level understanding before you dive into the material, watch the video below:
Content & Learning Objectives
1️⃣ Autoencoders & VAEs
Autoencoders are a relatively simple architecture, at least compared to GANs: you learn a compressed representation of your data (mainly using linear layers and convolutions), then reconstruct it back into an image (with linear layers and transposed convolutions).
Although autoencoders can learn some interesting low-dimensional representations, they are less good for generating images because their latent spaces aren't generally meaningful. This leads to VAEs, which solve this problem by having their encoders map to a distribution over latent vectors, rather than a single latent vector. This incentivises the latent space to be more meaningful, and we can more easily generate images from sample vectors in this space.
We start with some reading material on autoencoders and transposed convolutions (which are often used in parallel with convolutions, to take a latent space and map it back into a full-size image). Then, we actually implement and train VAEs to generate MNIST images, as well as do a bit of exploring our autoencoders' latent spaces.
Learning Objectives
- Learn about the transposed convolution operation
- Understand the basic architecture of autoencoders and VAEs
- Learn about the reparameterization trick for VAEs
- Implement your own autoencoder
- Implement your own VAE, and use it to generate realistic MNIST images
- (optional) Dive deeper into the mathematical underpinnings of VAEs, and learn about the ELBO loss function
☆ Bonus - Transposed Convolutions
In this section, you'll implement the transposed convolution operation manually. This is similar to a regular convolution, but designed for upsampling rather than downsampling (i.e. producing an image from a latent vector rather than producing output from an image). These are very important in many generative algorithms. Once you implement this, you'll be able to build your own GANs and VAEs from scratch, without using any pre-built layers.
Note - the bonus section from the CNNs day is a prerequisite for these bonus exercises. If you haven't completed that section, you'll need to do so before attempting these.
Learning Objectives
- Learn about & implement the transposed convolution operation.
- Implement GANs and/or VAEs entirely from scratch.
Setup code
import os
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal
import einops
import torch as t
import torchinfo
import wandb
from einops.layers.torch import Rearrange
from jaxtyping import Float
from torch import Tensor, nn
from torch.utils.data import DataLoader, Dataset, Subset
import torch.nn.functional as F
from tqdm.auto import tqdm
# Make sure exercises are in the path
chapter = "chapter0_fundamentals"
section = "part51_vaes"
root_dir = next(p for p in Path.cwd().parents if (p / chapter).exists())
exercises_dir = root_dir / chapter / "exercises"
section_dir = exercises_dir / section
if str(exercises_dir) not in sys.path:
sys.path.append(str(exercises_dir))
MAIN = __name__ == "__main__"
# Modules from previous exercises (you can swap in your own implementations if you've completed them).
from part2_cnns.solutions import BatchNorm2d, Conv2d, Linear, ReLU, Sequential
from part51_vaes import tests, utils
from fundamentals_utils import LiveImage, display_data, get_dataset
from plotly_utils import imshow
device = t.device("mps" if t.backends.mps.is_available() else "cuda" if t.cuda.is_available() else "cpu")
NUM_WORKERS = 2 if "google.colab" in sys.modules else min(8, os.cpu_count())
t.set_num_threads(min(4, t.get_num_threads()))