Part XIII — Generative Models: VAEs, Flow Matching, Score Models & Diffusion Transformers · Chapter 3

VQ-VAE & discrete latent codebooks

Hook

Every generative model in this part so far has a continuous latent space — noise, a smooth density, a point anywhere along a path. But a lot of real data is naturally discrete: words come from a fixed vocabulary, not a continuum. Can an autoencoder's latent space be discrete too?

Intuition
zₑcode 0code 1code 2code 3
zₑ = (1, 1) → snapped to code 0, quantization error = 1.414

Switch images. The continuous "×" never sits exactly on a code — it gets replaced by whichever square is closest, every time. The decoder that comes after this never sees the "×"; it only ever sees one of the four fixed squares.

Formalize

A VQ-VAE — short for Vector-Quantized Variational Autoencoder — keeps a small, learned codebook of KK vectors {e1,,eK}\{e_1, \ldots, e_K\}. The encoder still outputs a continuous ze(x)z_e(x), but before decoding it's replaced by its nearest codebook entry:

zq(x)=ek,k=argminjze(x)ejz_q(x) = e_k, \qquad k = \arg\min_j \lVert z_e(x) - e_j \rVert
  • ze(x)z_e(x) — the encoder's continuous output for input xx, before quantization.
  • zq(x)z_q(x) — the quantized latent actually passed to the decoder.
  • eke_k — the codebook vector selected as the nearest match to ze(x)z_e(x).
  • eje_j — one of the KK learned codebook vectors, indexed by jj.
  • kk — the index of the nearest codebook entry.
  1. The lookup itself isn't learned

    That's the entire mechanism: nearest-neighbor lookup, nothing learned about the lookup itself.

  2. What is learned: the codebook and its encoder/decoder

    The codebook's KK locations and the encoder/decoder around them are what's learned, so that whichever code a given input snaps to still decodes back into something close to the original — training has to shape the encoder's outputs to cluster near useful codes, not just quantize whatever it would have produced anyway.

Play

Four different images, four different codes used — a discrete vocabulary, four entries deep, emerging entirely from where the encoder happens to place each image's continuous output. A fifth image mapping to code 0 again would mean the model judged it more similar to image 0 than to the other three.

Worked example

Codebook at (0,0)(0,0), (3,0)(3,0), (0,3)(0,3), (3,3)(3,3); encoder output ze=(1,1)z_e=(1,1):

  1. Distance to every code
    • To (0,0)(0,0): 12+12=21.414\sqrt{1^2+1^2}=\sqrt{2}\approx1.414
    • To (3,0)(3,0): 22+12=52.236\sqrt{2^2+1^2}=\sqrt{5}\approx2.236
    • To (0,3)(0,3): 12+22=52.236\sqrt{1^2+2^2}=\sqrt{5}\approx2.236 — same as (3,0)(3,0)
    • To (3,3)(3,3): 22+22=82.828\sqrt{2^2+2^2}=\sqrt{8}\approx2.828
  2. Snap to the nearest

    (0,0)(0,0) is closest, so zq=(0,0)z_q = (0,0) — the decoder receives exactly (0,0)(0,0), never (1,1)(1,1).

  3. The gap that's thrown away

    The quantization error is 21.414\sqrt{2}\approx1.414 — everything about zez_e beyond "closest to code 0" is gone. A different image whose encoder output was (0.1,0.1)(0.1, 0.1) would round to the exact same code, indistinguishable to the decoder from this one.

Checkpoint

Find the encoder output, among the four images, with the largest quantization error.

Pick an image to try it
Summary
zq(x)=argminejze(x)ejz_q(x) = \arg\min_{e_j} \lVert z_e(x) - e_j \rVert

A discrete latent space means a generative model over it can be a genuinely categorical model — an autoregressive Transformer over a sequence of codebook indices, exactly like Part IV's tiny text generator, but over image patches instead of words. That's the real payoff: PixelCNN- and Transformer- based image generators, and models like DALL-E's first version, generate by predicting discrete codes one at a time, the same mechanism this whole course already built for text. This closes out the mechanism side of Part XVI's generative models. The capstone wires flow matching's straight-line training target into an actual multi-step sampler, trained (on paper) and run end to end.