Skip to content
ml-recall

Topic notes

Autoencoders & VAEs

Encoder–decoder networks trained to reconstruct their own input through a code; denoising and sparse variants, the link to PCA, and the VAE with its ELBO, KL term and reparameterization trick.

4-min read AI-checked

Practise 36 questions Take a test

Key ideas

Formulas

The ELBO and its gap to the log-likelihood3:

L(θ,ϕ;x)=Eqϕ(z∣x)[log⁡pθ(x∣z)]−DKL(qϕ(z∣x) ∥ p(z)),log⁡pθ(x)=L+DKL(qϕ(z∣x) ∥ pθ(z∣x))≥L\mathcal{L}(\theta, \phi; x) = \mathbb{E}_{q_\phi(z \mid x)}[\log p_\theta(x \mid z)] - D_{\mathrm{KL}}(q_\phi(z \mid x) \,\|\, p(z)), \qquad \log p_\theta(x) = \mathcal{L} + D_{\mathrm{KL}}(q_\phi(z \mid x) \,\|\, p_\theta(z \mid x)) \ge \mathcal{L}

KL between a diagonal Gaussian and N(0,I)\mathcal{N}(0, I), in closed form because both are Gaussian3:

DKL=12∑j(μj2+σj2−1−ln⁡σj2)D_{\mathrm{KL}} = \tfrac12 \sum_j \left(\mu_j^2 + \sigma_j^2 - 1 - \ln \sigma_j^2\right)

With logvar =ln⁡σ2= \ln \sigma^2 this is -0.5 * sum(1 + logvar - mu**2 - logvar.exp()), and σ\sigma = exp(0.5 * logvar).

Worked example

An encoder outputs μ=(0.6,0)\mu = (0.6, 0) and σ2=(1,0.5)\sigma^2 = (1, 0.5) for one image. KL to the prior, per dimension:

Total ≈0.277\approx 0.277 nats. A dimension with μ=0\mu = 0 and σ2=1\sigma^2 = 1 costs nothing but also carries no information about xx; when every dimension ends up like that, the code is useless (posterior collapse).

In code

import torch
from torch import nn
enc = nn.Sequential(nn.Conv2d(3, 8, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
                    nn.Conv2d(8, 4, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2))
dec = nn.Sequential(nn.ConvTranspose2d(4, 8, 2, stride=2), nn.ReLU(),     # kernel = stride:
                    nn.ConvTranspose2d(8, 3, 2, stride=2), nn.Sigmoid())  # no uneven overlap
x = torch.rand(16, 3, 32, 32)
code = enc(x)
print(code.shape, code[0].numel())   # torch.Size([16, 4, 8, 8]) 256
print(dec(code).shape)               # torch.Size([16, 3, 32, 32])
mu, logvar = torch.tensor([0.6, 0.0]), torch.log(torch.tensor([1.0, 0.5]))
z = mu + torch.exp(0.5 * logvar) * torch.randn(2)          # reparameterization
kl = -0.5 * torch.sum(1 + logvar - mu**2 - logvar.exp())
print(round(kl.item(), 4))           # 0.2766

Common pitfalls

References

  1. Wikipedia: Autoencoder — Lead section; Mathematical principles › Interpretation (undercomplete vs overcomplete); Variations › Sparse autoencoder (SAE), Denoising autoencoder (DAE); History; Applications › Dimensionality reduction › Principal component analysis; Applications › Anomaly detection
  2. Vincent et al. (2010), Stacked Denoising Autoencoders, JMLR 11 — Sec. 2.3 Merely Retaining Information is Not Enough; Sec. 3.1 The Denoising Autoencoder Algorithm
  3. Kingma & Welling (2014), Auto-Encoding Variational Bayes, arXiv:1312.6114 — Sec. 2.2 The variational bound; Sec. 2.4 The reparameterization trick; Sec. 3 Example: Variational Auto-Encoder; Appendix B
  4. Burgess et al. (2018), Understanding disentangling in β-VAE, arXiv:1804.03599 — Sec. 3 β-VAE
  5. PyTorch documentation: torch.nn.ConvTranspose2d — Class description; Shape
  6. Odena, Dumoulin & Olah (2016), Deconvolution and Checkerboard Artifacts, Distill — Deconvolution & Overlap; Better Upsampling