Skip to content
coden

Variational autoencoders (VAE): new images from a continuous latent space

A generative model with a latent variable, a VAE written from scratch with the reparameterization trick, an ordered latent space, generating digits from noise compared with an ordinary autoencoder, the effect of β and collapse, interpolation, and where VAEs are used.

12 minutes Matěj Žalmánek 2026-10-16

Series AI basics Part 44 of 52
  1. Map of artificial intelligence: what is what and how it all fits together
  2. A brief history of artificial intelligence: from Turing to language models
  3. Data, model, training, inference: the basic terms on a single example
  4. How to read mathematical notation: symbols, indices, sums and quantifiers
  5. Sets and relations: the building blocks of definitions in AI
  6. Functions and mappings: why every AI model is a function
  7. Logic and proofs: the rules AI reasons by
  8. Graphs and trees: the structure that both search and neural networks stand on
  9. Linear algebra for machine learning: vectors, matrices and why AI mostly multiplies
  10. Derivatives, the gradient and the chain rule: how a model finds out which way to learn
  11. Gradient descent: how a model learns
  12. What probability is: from rolling dice to the Kolmogorov axioms
  13. Conditional probability and Bayes’ theorem: how to update your estimate when you learn something new
  14. Random variables and the cumulative distribution function: one function that tells you everything about a distribution
  15. Probability density and probability mass functions: why a continuous variable has zero probability at a point
  16. Expected value, variance and covariance: where a distribution lies, how wide it is and what goes with what
  17. Overview of probability distributions: Bernoulli, binomial, Poisson, uniform, normal and exponential
  18. Law of large numbers and central limit theorem: why averages behave so predictably
  19. Statistics and parameter estimation: maximum likelihood, MAP and confidence intervals
  20. Information theory: entropy, cross-entropy and KL divergence — where loss functions come from
  21. State-space search: BFS, DFS, UCS and A* with animations
  22. Games and adversaries: minimax, alpha-beta pruning and Monte Carlo tree search
  23. Constraint satisfaction: backtracking, constraint propagation and AC-3 on sudoku and a map of Czech regions
  24. Logic and knowledge systems: forward and backward chaining, resolution and expert systems
  25. Planning: STRIPS, the blocks world, heuristics from relaxation and planning in AI agents
  26. Local search and simulated annealing: a travelling salesman through the Czech regional capitals
  27. Genetic algorithms: population, crossover, mutation and selection on a backpack for a hike
  28. Evolution strategies and genetic programming: from the one-fifth rule to Kepler’s law
  29. Ant colony optimization: how a pheromone trail finds a short path
  30. Particle swarm optimisation: inertia, personal experience and the swarm’s experience
  31. Neuroevolution: evolving the weights and shape of neural networks, and the NEAT algorithm
  32. Fuzzy sets and membership functions: how to write down “somewhat warm”
  33. Fuzzy logic and probability: why “somewhat warm” does not mean “probably warm”
  34. Fuzzy logic: t-norms, implications and IF–THEN rules
  35. Fuzzy control: Mamdani and Sugeno from a fan to a pole on a cart
  36. Fuzzy clustering: the fuzzy c-means algorithm and points between clusters
  37. Neuro-fuzzy systems (ANFIS): fuzzy rules that learn from data
  38. Markov chains: the transition matrix, the stationary distribution and PageRank
  39. Hidden Markov models: the Viterbi algorithm and the dishonest casino
  40. Bayesian networks: why the grass is wet and what it means for the sprinkler
  41. The Kalman filter: how to estimate where a car is and how fast it goes from imprecise measurements
  42. Types of machine learning: supervised, unsupervised, semi-supervised and more
  43. Prompts, context and RAG: how to give a model the right information
  44. Variational autoencoders (VAE): new images from a continuous latent space
  45. Generative adversarial networks (GAN): the forger versus the detective
  46. Diffusion models: how pure noise becomes an image
  47. Markov decision processes: states, actions, rewards and the Bellman equations
  48. Q-learning and DQN: learning the value of actions, from a table to a neural network
  49. Policy gradient and PPO: learning the policy directly
  50. How an AI agent works: a language model in a loop with tools
  51. Programming with AI agents: the task, tests and review
  52. Evaluating and securing AI systems: how to measure and how to keep watch

Full series syllabus 52 of 83 topics published

What you will take away
  • Why an ordinary autoencoder is not good at generating.
  • How a VAE works: a distribution instead of a point, the KL penalty and the reparameterization trick.
  • What β costs, why VAE images are blurry, and where VAEs are used today.

Useful to know first: Information theory: entropy, cross-entropy and KL divergence — where loss functions come from

The autoencoder from the part on representations can squeeze an image into a few numbers and put it back together from them. An obvious question: if the decoder can turn a code into a digit, couldn't we give it a code we made up and get a new digit that nobody ever drew? With an ordinary autoencoder this hardly works. The variational autoencoder is a modification that makes exactly this possible — and it is the first of the generative models we will look at in this section of the series.

It builds on the parts Autoencoders and Information theory (KL divergence).

1. From compression to generation

Definition — generative model

Let 𝒳 be the set of possible data (here 8 × 8 images with pixel brightnesses in [0, 1], i.e. 𝒳 = [0, 1]64), n ≥ 1, and let x1, …, xn ∈ 𝒳 be training data, which we assume to be independent samples from an unknown distribution pdata on 𝒳. Let Θ be a set of admissible parameters and, for every θ ∈ Θ, let pθ be a distribution on 𝒳. A generative model is a family of distributions (pθ)θ ∈ Θ together with a procedure for sampling from the distribution pθ (producing new x ~ pθ). Learning chooses θ so that pθ is as close as possible to pdata, typically as the maximum likelihood estimate

θ̂ = argmaxθ ∈ Θ ∑i=1n log pθ(xi).

A latent variable model additionally introduces a latent space 𝒵 = ℝk (k ≥ 1), a fixed density p(z) on it (the prior), and for every code z ∈ 𝒵 a distribution pθ(x | z) on 𝒳 (the decoder). The distribution of the data is then

pθ(x) = ∫𝒵 pθ(x | z) p(z) dz,

and sampling happens in two steps:

  1. draw z ~ p(z);
  2. draw x ~ pθ(x | z) — in practice (including here) the output of the decoder, i.e. the pixel probabilities, is often taken directly.

The new x thus look like the data but are not copies of it. From here on we drop the index θ and write p(x | z), p(x).

In plain words

A classifier answers “what is this”. A generative model can answer “show me something that could be in the data”. The recipe: roll a few numbers with dice, hand them to a painter (the decoder), and the painter paints a picture from them.

p(x) = ∫ p(x | z) · p(z) dz

p(z)
distribution of the code (the prior) — we choose it ourselves and keep it simple, here N(0, I), because we will need to draw from it when generating
p(x | z)
the decoder — how probable the image x is given the code z; here a network that returns, for every pixel, the probability that it is lit
∫ … dz
a sum over all possible codes — the image could have come from any code; but we cannot compute an integral over the whole latent space, which is why the VAE replaces it with an estimate (below)
Generating is easy (draw z, decode), learning needs p(x) — and that is the hard integral.

The catch with an ordinary autoencoder: we do not know where to draw the code from. The encoder places the digits in the latent space wherever it pleases. In our network with a two-dimensional code, the codes of the test digits lie around the point (-3.9, -0.9) with standard deviations 4.3 and 6.0, and between the groups there are holes where the decoder has never seen anything.

2. The variational autoencoder

Definition — variational autoencoder (VAE)

Let 𝒳 = [0, 1]64 be the set of images, k ≥ 1 the dimension of the code, I the k × k identity matrix and β > 0. All logarithms in this part are natural (log = ln). A variational autoencoder (VAE; Diederik Kingma and Max Welling, 2013) consists of three parts:

  1. the encoder — a neural network that assigns to an image x ∈ 𝒳 vectors μ(x) ∈ ℝk and log σ²(x) ∈ ℝk (logarithm taken componentwise); these determine the distribution of the code q(z | x) = N(μ(x), diag σ²(x)), i.e. the coordinates z1, …, zk are independent and zj ~ N(μj(x), σj²(x)) (the normal distribution);
  2. the decoder — a neural network that assigns to a code z ∈ ℝk a distribution p(x | z) on 𝒳 (here via a vector of pixel probabilities y(z) ∈ (0, 1)64);
  3. the prior p(z) = N(0, I), the standard normal distribution on ℝk.

The parameters of both networks are learned by minimizing the average over the training images of the loss

L(x) = −Ez ~ q(z | x)[log p(x | z)] + β · DKL(q(z | x) ‖ N(0, I)),

where for densities q, p on ℝk, DKL(q ‖ p) = ∫ q(z) log q(z)/p(z) dz is the continuous analogue of the KL divergence from the part on information (with the natural logarithm, in nats). The first term of the loss is the reconstruction error (here the binary cross-entropy of the pixels, see below), the second is a penalty for how much the code distribution differs from the prior. For q = N(μ, diag σ²) it comes out as

DKL(q ‖ N(0, I)) = ½ ∑j=1k (μj² + σj² − log σj² − 1).

A VAE thus encodes an image into a distribution, not a point, and penalizes it for deviating from the distribution that will later be used for generating. With β = 1, −L(x) is a lower bound on log p(x) (the ELBO, theorem below).

In plain words

The encoder may not say “this seven lives exactly here”, only “somewhere in this little cloud”. The decoder then has to be able to draw a seven from any point of the cloud. And the KL penalty pushes all the clouds towards the origin and does not let them get too small — so they overlap, fill the space, and no holes are left between them.

L(x) = −Ez ~ q(z | x)[ log p(x | z) ] + β · DKL( q(z | x) ‖ N(0, I) )

−Ez ~ q[log p(x | z)]
reconstruction error — how badly the decoder draws x from a code drawn from the cloud. The expectation over the whole cloud (not just its centre) forces the decoder to draw correctly in the whole neighbourhood of the code; the minus is there because we want the log-probability as large as possible and the loss as small as possible
β
weight of the penalty — sets the trade-off between accuracy and order in the space; β = 1 is exactly the ELBO, other values are tried in the section on β
DKL(q(z | x) ‖ N(0, I))
distance of the cloud from the distribution we will draw from — without it the encoder would shrink the clouds to points and scatter them far apart (reconstruction would be better), and we would be back to an ordinary autoencoder
The loss for one image; training uses the average over a batch. Both terms are measured in nats (natural logarithm).

DKL(q(z | x) ‖ N(0, I)) = ½ ∑j=1k ( μj² + σj² − log σj² − 1 )

DKL
the penalty from the previous formula, computed exactly for a normal distribution; the code coordinates are independent, hence the sum over j
μj²
shift of the cloud’s centre from the origin — it grows quadratically, so distant codes cost a lot and the encoder pulls them towards zero, where we will be drawing
σj² − log σj²
width of the cloud — the function s − log s has its minimum 1 exactly at s = 1; it penalizes a cloud that is too wide, but above all one that is too narrow (−log σ² → ∞ for σ → 0), so that the cloud does not turn into a point
The “− 1” only shifts the minimum to zero: DKL = 0 exactly when μ = 0 and σ = 1, i.e. when q is itself N(0, I). The derivatives the program adds to the gradient: ∂DKL/∂μj = μj and ∂DKL/∂(log σj²) = 1/2(σj² − 1).
Theorem — the ELBO is a lower bound on the log-likelihood

Let p(z) be a density on ℝk (the prior), for every z ∈ ℝk let p(x | z) be a distribution on 𝒳 (the decoder), let x ∈ 𝒳 be an image with p(x) = ∫ p(x | z) p(z) dz > 0, and let

p(z | x) = p(x | z) p(z)/p(x)

be the posterior density of the code (Bayes’ theorem). Let q(z | x) be any density on ℝk that is positive only where p(z | x) is positive, and for which the expectations Eq = Ez ~ q(z | x) below exist and are finite. Then

log p(x) = Eq[log p(x | z)] − DKL(q(z | x) ‖ p(z)) + DKL(q(z | x) ‖ p(z | x)),

and therefore

log p(x) ≥ Eq[log p(x | z)] − DKL(q(z | x) ‖ p(z)),

with equality if and only if q(· | x) = p(· | x).

Proof. log p(x) does not depend on z, so it equals its own expectation over q. By Bayes’ theorem, for every z with q(z | x) > 0

p(x) = p(x | z) p(z)/p(z | x) = p(x | z) · p(z)/q(z | x) · q(z | x)/p(z | x),

where we multiplied the fraction by q(z | x) in the numerator and the denominator. We take the logarithm and the expectation over z ~ q(z | x):

log p(x) = Eq[log p(x | z)] + Eq[log p(z)/q(z | x)] + Eq[log q(z | x)/p(z | x)].

By definition, the second term is −DKL(q(z | x) ‖ p(z)), the third is DKL(q(z | x) ‖ p(z | x)). The KL divergence is always non-negative and equal to zero exactly for identical distributions (Gibbs’ inequality from the part on information theory, which holds for densities as well), so dropping the third term gives both the inequality and the condition for equality. ∎

The right-hand side of the inequality is the ELBO (evidence lower bound), and with β = 1 it is exactly −L(x). By minimizing L we therefore raise a lower bound on log p(x) without having to compute the integral. The bound is the tighter, the closer q(z | x) is to the true posterior distribution p(z | x).

Definition — binary cross-entropy of pixels

Let x ∈ [0, 1]64 be an image (pixel brightnesses xj, j = 1, …, 64) and y ∈ (0, 1)64 the output of the decoder, where we understand yj as the probability that pixel j is lit. The binary cross-entropy of the image x with respect to the prediction y is

BCE(x, y) = −∑j=164 [ xj log yj + (1 − xj) log(1 − yj) ].

For a black-and-white image x ∈ {0, 1}64 and a decoder under which the pixels are lit independently, each with probability yj(z), exactly BCE(x, y(z)) = −log p(x | z). For grey brightnesses xj ∈ (0, 1) the same expression is used as the reconstruction error.

An example for one pixel: it is lit (x = 1) and the decoder says 0.9 → −ln 0.9 ≈ 0.105; it says 0.5 → ln 2 ≈ 0.693; it is not lit (x = 0) and the decoder says 0.9 → −ln 0.1 ≈ 2.303. A confident mistake costs the most. Because the output is a sigmoid, yj = 1/1 + e−aj, the derivative with respect to its input aj is simply ∂BCE/∂aj = yj − xj — just as in logistic regression.

Definition — reparameterization trick

Let μ(x) ∈ ℝk and σ(x) ∈ (0, ∞)k be the outputs of the encoder for an image x. Instead of drawing z ~ q(z | x) = N(μ(x), diag σ²(x)) directly, the reparameterization trick proceeds as follows:

  1. draw ε = (ε1, …, εk) with independent εj ~ N(0, 1) — noise whose distribution does not depend on the network’s parameters;
  2. for j = 1, …, k set zj = μj(x) + σj(x) · εj.

The z obtained this way has exactly the distribution q(z | x) (a linear transformation of a normal variable is normal), and at the same time it is a differentiable function of the parameters: ∂zj/∂μj = 1 and ∂zj/∂σj = εj. The gradient therefore flows through backpropagation all the way into the encoder, which is not possible with plain sampling z ~ N(μ, σ²).

In plain words

We “factor the randomness out”: instead of “draw a point from the cloud” we say “draw generic noise and shift and stretch it as the encoder says”. Shifting and stretching are ordinary arithmetic, and that can be differentiated.

zj = μj(x) + σj(x) · εj,   σj = exp(½ · log σj²),   εj ~ N(0, 1)

μj(x)
centre of the cloud, an output of the encoder — ∂zj/∂μj = 1, the decoder’s gradient passes into the encoder unchanged
σj(x)
width of the cloud — the encoder does not return σ but log σ², which may be any real number; the exponential always turns it into a positive width, so the network does not have to respect any constraint. ∂zj/∂σj = εj
εj ~ N(0, 1)
all of the randomness — it does not depend on any parameter, so nothing has to be differentiated through it; when generating, we skip the encoder and draw z directly from N(0, I)
A shifted and stretched normal variable is again normal: zj ~ N(μj, σj²), i.e. exactly a sample from q(z | x).
Example — one test digit (0) in the trained VAE with k = 2

encoder: μ = (0.285, 1.587),   log σ² = (−2.695, −1.931) → σ = (0.260, 0.381)

j = 1:   ½ (0.081 + 0.068 + 2.695 − 1) ≈ 0.922

j = 2:   ½ (2.518 + 0.145 + 1.931 − 1) ≈ 1.797   → DKL ≈ 2.72 nats

reparameterization with ε = (0.5, −1.0): z = (0.285 + 0.260 · 0.5, 1.587 − 0.381 · 1.0) = (0.415, 1.206)

(colours as in the formulas for DKL and reparameterization above)

Most of the penalty here comes not from the distance to the origin but from the narrowness of the cloud: the term −log σ² contributes 2.695 and 1.931. The encoder would like the code to be as precise as possible; DKL keeps it wider. The average DKL over the test digits is 3.42 nats. The reconstruction error of this digit (from the centre μ) is 34.6 nats over 64 pixels.

def krok_vae(davka, beta=1.0):
    for x in davka:
        mu, logvar = koder(x)                  # k + k numbers
        sigma = exp(0.5 * logvar)
        eps = normal(0, 1, size=len(mu))       # randomness from outside
        z = mu + sigma * eps                   # reparameterization trick
        y = dekoder(z)                         # pixel probabilities
        rek = -sum(x * log(y) + (1 - x) * log(1 - y))
        kl = 0.5 * sum(mu**2 + exp(logvar) - logvar - 1)
        zpetne_sireni(rek + beta * kl)         # gradient flows z → mu, sigma → encoder
    adam_krok()                                # average gradient of the batch

Our network: 64 pixels → 32 neurons (tanh) → μ and log σ² (k numbers each) → 32 neurons → 64 pixels (sigmoid), trained with Adam for 150 epochs on 600 synthetic digits. The same network, only without the noise ε and without the KL penalty, is an ordinary autoencoder — so the comparison is fair.

3. An ordered latent space

00000000000000000000111111111111111111112222222222222222222233333333333333333333444444444444444444445555555555555555555566666666666666666666777777777777777777778888888888888888888899999999999999999999 ordinary autoencoder code z₁ (unitless) code z₂ (unitless) 00000000000000000000111111111111111111112222222222222222222233333333333333333333444444444444444444445555555555555555555566666666666666666666777777777777777777778888888888888888888899999999999999999999 VAE code z₁ (unitless) code z₂ (unitless)
Codes of 200 test digits (k = 2); dashed circles of radius 1 and 2 around the origin

Both networks group identical digits together. The autoencoder, however, scatters them over a large area — both circles of the normal distribution are small compared with it. The VAE codes lie around the origin with standard deviations 1.14 and 1.04, that is, exactly where we will be drawing from.

What the whole space looks like is shown by a grid: regularly spaced points of the latent space, each passed through the decoder.

autoencoder, centre ± 2 std. dev. code z₁ (unitless) code z₂ (unitless) VAE, coordinates from −2.2 to 2.2 code z₁ (unitless) code z₂ (unitless)
Decoder on a grid of 10 × 10 points of the latent space

In the VAE the digits flow smoothly into one another and almost every cell is a readable digit. To be fair: in the plane the autoencoder’s decoder is also quite smooth, and its grid also contains mostly digits. The difference only shows in full with a code of more dimensions.

4. Generation: new digits from noise

We draw 200 codes and have them decoded: for the VAE from N(0, I), for the autoencoder (to give it a fair chance) from a normal distribution fitted to its actual codes. We measure quality by the distance (mean squared error) of a sample to the nearest training digit. Real test digits are on average 0.017 from the nearest training digit, random noise 0.159.

Definition — distance to the nearest training digit and the fitted distribution

Let x(1), …, x(600) ∈ [0, 1]64 be the training digits. The distance of an image y ∈ [0, 1]64 to the nearest training digit is

d(y) = mini ∈ {1, …, 600} 1/64 ∑j=164 (yj − x(i)j)²,

i.e. the mean squared error (MSE) to the most similar digit. For drawn images y(1), …, y(200), the quality of the generator is the average

d̄ = 1/200 ∑s=1200 d(y(s)).

A small d̄ means “the samples look like some real digits” — beware, a generator that merely copies the training data would also score zero.

Let c(1), …, c(N) ∈ ℝk be the codes that the autoencoder’s encoder assigns to N = 200 test digits. The fitted distribution is N(m, diag s²), where for j = 1, …, k

mj = 1/N ∑i=1N c(i)j,   sj = √1/N ∑i=1N (c(i)j − mj)².

The autoencoder thus draws zj = mj + sj · εj with independent εj ~ N(0, 1), i.e. each coordinate from a normal distribution with the same mean and standard deviation as its actual codes have (for k = 2: m = (−3.9, −0.9), s = (4.3, 6.0)). The coordinates are drawn independently — the fitted distribution does not capture any dependence between them.

00.050.10.15 k = 2 k = 8 k = 16 code size k (count of numbers) distance to a digit (MSE)
Distance of drawn images to the nearest training digit (smaller = more plausible) autoencoder VAE real digits random noise
code size kAE reconstruction (MSE)VAE reconstruction (MSE)AE samplesVAE samples
20.0510.0630.0420.044
80.0250.0360.0900.054
160.0170.0390.1120.055

With a two-dimensional code both networks do about equally well (0.042 versus 0.044). With eight dimensions the autoencoder falls apart: its samples have a distance of 0.090, more than halfway to noise, while the VAE holds at 0.054. With sixteen it is 0.112 versus 0.055. In many dimensions the autoencoder’s codes lie on a thin, twisted surface, and a random point from the fitted normal distribution practically never lands on it.

autoencoder VAE
The first ten drawn images, code with 8 dimensions

The price: the VAE reconstructs worse. With k = 16 its reconstruction error is 0.039 versus 0.017 for the autoencoder, and its images are blurrier. The code is noisy, so the decoder learns to draw the “average” of what could be in the neighbourhood of the code. Blurriness is a well-known weakness of VAEs.

5. Reconstruction versus order: β

The weight β determines how strongly the KL penalty pushes the codes towards the normal distribution. Five VAEs with a code of k = 2 and different β:

βKL (nats per image)reconstruction (MSE)samples (distance)
0.18.180.0530.044
0.54.710.0570.044
13.420.0630.044
22.070.0760.052
50.050.1310.075

A small β leaves the codes looser (KL 8.2) and reconstruction is more accurate. A large β squeezes them towards zero. At β = 5 the KL drops to 0.05: the encoder has stopped storing anything in the code, and the decoder keeps drawing roughly the same thing, whatever code it gets. This is called posterior collapse.

Definition — the nat, and KL as the price of the code

The nat is the unit of information corresponding to the natural logarithm: an event with probability p ∈ (0, 1] carries −ln p nats (instead of −log2 p bits). Since ln p = ln 2 · log2 p,

1 nat = 1/ln 2 bits ≈ 1.443 bits.

Let x be a random image with the data distribution pdata (here a uniformly chosen test digit), let the code z given x have the encoder’s distribution q(z | x), and let p(z) = N(0, I) be the prior. Denote by

q(z) = Ex ~ pdata[q(z | x)]

the distribution of the codes of all images together, and by I(x; z) the mutual information of the image and the code (the continuous analogue of the definition from the part on information). Then

Ex ~ pdata[DKL(q(z | x) ‖ p(z))] = I(x; z) + DKL(q(z) ‖ p(z)) ≥ I(x; z),

so the average KL divergence over the data is an upper bound on the information the code carries about the image. It is the “price” the encoder pays for whatever it stores in the code about the image.

At β = 0.1 this is at most 8.18 nats ≈ 11.8 bits per image, at β = 5 only 0.05 nats ≈ 0.07 bits — less than a tenth of a bit, so the code says almost nothing about the image and the decoder has no choice but to keep drawing the same thing.

β = 0.1 β = 1 β = 5
Images drawn by the VAE (k = 2) at different β

Conversely, β greater than 1 is used deliberately by the β-VAE (Irina Higgins et al., 2017): stronger pressure towards independent code coordinates means that individual dimensions often correspond to understandable properties (rotation, stroke thickness).

6. Smooth transitions

We take the codes of two test digits (networks with an eight-dimensional code, for each digit the image the VAE reconstructs best) and walk along the straight line between them in eight steps: z(t) = (1 − t) · za + t · zb for t = 0, 1/7, 2/7, …, 1, where za, zb are the centres μ of the two digits. We decode every intermediate point.

AE: 1 → 7 VAE: 1 → 7 AE: 0 → 6 VAE: 0 → 6
Interpolation in the latent space (k = 8)

The transition is smooth for both networks: the intermediate steps are plausible shapes between the two digits. Along a straight line between two real codes we do not get far from the data, so here the ordinary autoencoder does not lag behind — its weakness is only drawing blindly. The same principle in large models makes it possible to smoothly change, say, a facial expression or the style of an image.

7. Where VAEs are used

  • Compression for other generators. Stable Diffusion (latent diffusion, Robin Rombach et al., 2022) does not work with pixels but with the code of an autoencoder with a small KL penalty: it compresses a 512 × 512 × 3 image to 64 × 64 × 4, and diffusion runs in that smaller space. Diffusion will be the subject of one of the next parts.
  • Discrete codes. VQ-VAE (Aäron van den Oord et al., 2017) picks entries from a learned codebook instead of a continuous code. An image thus becomes a sequence of “tokens” that a transformer can generate.
  • Scientific data. Molecules, single-cell data, sound — wherever a smooth space is useful in which one can search and interpolate.

In image sharpness VAEs lose to GANs and to diffusion models. But they have stable training, a genuine probabilistic interpretation, and an encoder for free.

8. Summary

  • A generative model learns the distribution of the data so that it can be sampled; with a latent variable it draws a code and decodes it.
  • A VAE encodes into a distribution N(μ, σ²), and loss = reconstruction + β · KL to N(0, I). The trick z = μ + σε makes it possible to differentiate through the sampling.
  • VAE codes lie around the origin and fill the space; autoencoder codes are scattered and full of holes.
  • With an 8-dimensional code: autoencoder samples 0.090, VAE 0.054 (real digits 0.017, noise 0.159).
  • The price is worse reconstruction and blurriness; too large a β leads to collapse (KL 0.05).

Want to get hands-on? Have an AI agent train a VAE with a two-dimensional code on the MNIST dataset and plot a grid of decoded images — you will see a whole “map” of handwritten digits.

Do you see it differently? Write to support@coden-app.com.

An Agent on your computer, driven from your phone