Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

~11 min read2017

Discrete
latents.

A normal autoencoder squeezes an image into a cloud of real numbers — infinitely fine, hard to model. VQ-VAE asks a stranger question: what if the latent had a vocabulary? Instead of a continuous vector, every patch of the image is forced to pick the closest word from a small learned dictionary — a codebook. The picture becomes a little grid of discrete symbols, like text. That one change — quantize the latent — is what lets a Transformer or a diffusion model treat images the way it treats language, and it's the quiet foundation under DALL·E's image tokens and a decade of generative modeling. Here's the nearest-neighbor snap, the gradient trick that makes it trainable, and the commitment loss.

The delta

What changes against the VAE: the latent becomes a sequence of tokens

Rung 1's latent is a continuous vector, which makes the prior over it something you have to assume — the standard normal — rather than something you can model well. This rung's change is to make the latent discrete: the encoder's output at each spatial position is snapped to the nearest entry in a learned codebook, so an image becomes a grid of integer indices.

Why that is a large idea rather than a quantisation detail: a grid of indices is a sequence of tokens. Which means the prior over images can be modelled by exactly the machinery that models language — an autoregressive transformer over a vocabulary — instead of being assumed to be a Gaussian. The generative problem is split cleanly in two: learn a good tokeniser, then learn a strong sequence model over its tokens.

Three pieces make it work and each fixes a specific break. Choosing the nearest codeword is not differentiable, so the gradient arriving at the decoder's input is copied straight through to the encoder's output as if the snapping had not happened — a biased approximation that works. The codebook entries are pulled toward the encoder outputs assigned to them. And a commitment term pulls the encoder's outputs toward their chosen codewords, because without it the encoder can drift away from the codebook indefinitely and the quantisation error grows without bound.

01

A latent with a vocabulary

A standard variational autoencoder maps each input to a continuous latent vector — a point in a smooth, real-valued space. That's flexible, but it's awkward to model: the strongest generative machinery we have — autoregressive Transformers — works over discrete tokens, a finite vocabulary, not a cloud of floats. VQ-VAE (van den Oord, Vinyals & Kavukcuoglu, 2017) closes that gap by making the latent itself discrete.

The move is simple to state: the encoder still outputs a continuous vector, but before it reaches the decoder it's quantized — replaced by the nearest entry in a small learned dictionary of vectors called the codebook. Each latent position becomes one of K possible symbols, an integer index. An image is no longer a fog of real numbers; it's a compact grid of discrete tokens, exactly the kind of thing a language model knows how to devour. The whole paper is about making that snap-to-nearest operation actually learnable.

The one-sentence version

Quantize the encoder's continuous output to the nearest vector in a learned codebook so the latent is a grid of discrete tokens; train it by copying the decoder's gradient straight through the non-differentiable snap, plus a commitment loss that keeps the encoder faithful to its chosen code.

02

The codebook

The codebook is a learned set of K embedding vectors — the discrete vocabulary of the latent space. Think of it as a palette: a fixed number of prototype vectors that the model gets to place wherever they're most useful. When the encoder emits a continuous vector z_e(x), quantization measures its distance to every codebook entry and replaces it with the closest one, z_q. The index of that entry is the discrete token you store.

Both sides learn together. The encoder learns to produce vectors that land near useful codes; the codebook entries drift toward wherever the encoder's outputs actually cluster (in the original paper, the codebook is learned with a codebook-loss term that pulls each code toward the encoder vectors assigned to it; an exponential-moving-average update is the paper's described alternative). Over training, the K codes spread out to tile the space of representations the data needs — a self-organizing dictionary. Choose K and the grid size and you've fixed exactly how many bits the latent can hold: a hard, interpretable information bottleneck.

03

Snap to the nearest code

Quantization is a nearest-neighbor lookup: pick the codebook entry with the smallest squared distance to the encoder's vector.

The forward and backward passes
encodeEncoder outputs a continuous vector z_e(x).
quantizek = argminj ‖z_e − ej‖² — snap to the nearest code z_q = ek.
decodeDecoder reconstructs the input from the discrete code z_q.
backwardargmin has no gradient — copy the decoder's gradient straight back to the encoder (straight-through).

The catch is the backward pass. The argmin — "which code was closest?" — is a hard, non-differentiable choice; there's no gradient telling the encoder how to nudge its output to change the winner smoothly. VQ-VAE sidesteps this with the straight-through estimator: on the way forward it uses the quantized code, but on the way back it pretends the quantization was the identity and copies the decoder's gradient directly onto the encoder's continuous output. The encoder gets trained as if the snap never happened. It's an approximation — but a remarkably effective one, and it's the trick that makes discrete latents learnable end-to-end.

04

The three-part loss

Because the straight-through trick skips the quantizer on the backward pass, the encoder gets a reconstruction gradient but the codebook gets none — so VQ-VAE adds explicit terms to train the codes and to keep the encoder honest. The loss has three parts:

Left: reconstruction. Middle: the codebook loss moves the chosen code e toward the encoder output (sg = stop-gradient, so only e updates). Right: the commitment loss, weighted by β, pulls the encoder's output toward its code so the encoder "commits" and doesn't hop between codes — only z_e updates. The runnable version below computes the quantization and these losses.

The stop-gradient operator sg[·] is what lets one squared-distance be reused for two jobs: freeze the encoder side and the term trains the codebook; freeze the codebook side and (scaled by β) it trains the encoder to commit. The commitment weight β is the one real knob — too small and the encoder's outputs wander freely, growing faster than the codes can chase; the paper found results were robust across a broad range and used β = 0.25.

How it works

How VQ-VAE trains a discrete tokenizer and then a prior, step by step

VQ-VAE, introduced in the paper Neural Discrete Representation Learning, is two models trained one after the other: an autoencoder with a quantized bottleneck, and an autoregressive prior over the codes it produces. While the autoencoder trains, the prior is a fixed uniform distribution; a strong prior is fitted only afterwards.

straight-through: ∇ copied from zq to ze image encoder ze(x) quantize nearest of 512 tokens 17, 3, 402 … decoder x̂ codebook K = 512 vectors codebook loss: ‖sg[ze] − e‖² commitment: β‖ze − sg[e]‖² stage 2: PixelCNN prior fitted on frozen tokens
Forward, the quantizer replaces each vector with a codebook entry and stores its index. Backward, the gradient skips the quantizer (accent arrow), so the codebook learns only from its own loss term and the commitment term, with β = 0.25, holds the encoder near its codes.
  1. Encode to a grid of vectors. A convolutional encoder maps the input to a grid of D-dimensional vectors ze(x). In the CIFAR-10 comparison it was two stride-2 convolutions with 4×4 windows followed by two residual blocks, 256 channels throughout. For 128×128 ImageNet images the grid was 32×32. For raw audio, six stride-2 convolutions made the latent sequence 64 times shorter than the waveform.
  2. Quantize. Replace each grid vector by its nearest neighbor among K learned codebook vectors e1 … eK, and store the index. K = 512 in the image and speech-reconstruction experiments (the phoneme analysis below used 128 codes), so each position holds 9 bits.
  3. Decode. A mirror-image decoder (residual blocks, then two stride-2 transposed convolutions) reconstructs x from the quantized grid zq(x). For speech the decoder was a WaveNet-style dilated convolutional network conditioned on the codes and on a speaker identity.
  4. Send gradients around the argmin. The reconstruction gradient at the decoder's input is copied straight onto ze(x). The codebook gets its own term pulling each selected e toward the encoder outputs assigned to it, and a commitment term with β = 0.25 pulls those outputs toward their codes. Results were reported to change little for β from about 0.1 to 2.0. The paper also describes an alternative that updates each code as an exponential moving average of its assigned vectors.
  5. Drop the KL. The posterior is one-hot and the training prior is uniform over K codes, so the KL term is the constant log K and is left out of the loss.
  6. Fit the prior. Freeze the autoencoder, encode the training set into index grids, and train an autoregressive model on them: a PixelCNN for image grids, a WaveNet for audio sequences. Training the two stages jointly was left to future work.
  7. Sample. Draw indices one position at a time from the prior, look up their codebook vectors and decode. For ImageNet the slow autoregressive model runs over 1,024 positions instead of the 49,152 subpixel values a pixel-level PixelCNN would generate one by one.

Why discrete codes resist posterior collapse

In a VAE the KL to the prior is a cost the encoder can drive to zero by ignoring the input, and a strong decoder lets it. VQ-VAE removes that lever. Every input is assigned some code, the KL is log K whichever code it is, and the code is the decoder's only source of information about x. Capacity is a hard count, K choices per position, rather than a soft penalty, which is why the paper could pair the codes with a powerful autoregressive WaveNet decoder and still have them used.

What the likelihood numbers mean

Because the decoder is only ever trained on zq(x), the paper argues that once training converges it assigns essentially no probability to x under any other code. The log-likelihood is then approximately log p(x | zq(x)) + log p(zq(x)), a reconstruction term plus the prior's cost of the code, which is the kind of bound behind the bits-per-dimension figures below.

The math

Why the KL is log K, and which gradient each term sends

q(z = k | x) = 1 if k = argminj ‖ze(x) − ej‖2, otherwise 0
KL( q(z | x) ‖ p(z) ) = Σk q(k | x) log( q(k | x) / (1/K) ) = 1 · log K = log K
𝓛 = −log p(x | zq(x)) + ‖sg[ze(x)] − e‖2^2 + β ‖ze(x) − sg[e]‖2^2, with ∂𝓛/∂ze taken as ∂𝓛/∂zq
  • q(z = k | x) — deterministic: all mass on one code, so the bottleneck adds no sampling noise and no estimator variance.
  • log K — 9 bits per position for K = 512, a constant with no gradient, so it is dropped. For a whole grid it is the number of positions times log K, the bottleneck's exact capacity.
  • sg[·] — stop-gradient: identity on the forward pass, zero derivative on the backward pass.
  • −log p(x | zq) — trains the decoder and, through the copied gradient, the encoder; it never reaches the codebook.
  • β — commitment weight, 0.25 in every experiment.

At the extremes: with β = 0 nothing ties the encoder to the codebook, and because the embedding space has no natural scale, its outputs can grow faster than the codes can follow. A codebook entry that no input selects gets zero from both of the last two terms, so it never moves, a dead code. And the copied gradient is exact only when zq = ze; the larger the quantization error, the more biased the encoder's update.

The evidence

What VQ-VAE showed on CIFAR-10, ImageNet and speech

The paper is a set of demonstrations across images, audio and video more than a benchmark race, and its one head-to-head likelihood comparison is modest. On CIFAR-10 the authors trained a continuous VAE, VQ-VAE and VIMCO (a discrete-latent model trained with a multi-sample score-function estimator, here with 50 samples) using the same encoder and decoder, Adam at a learning rate of 2×10^−4, batch size 128, evaluated after 250,000 steps. All reported likelihoods are bounds.

ExperimentSetupResult
CIFAR-10 likelihood boundContinuous VAE / VQ-VAE / VIMCO4.51 / 4.67 / 5.14 bits per dimension
ImageNet 128×12832×32 code grid, K = 512, PixelCNN priorAbout 42× fewer bits than the 8-bit image; class-conditional samples from the prior
Speech (VCTK)64× shorter code sequence, K = 512, WaveNet decoderSpeaker conversion: encode one speaker, decode with another's identity, content kept
Unsupervised phonemes128 codes at 25 Hz, each mapped to its most frequent phoneme, 41 classesAbout 49% accuracy, against about 7% for a random latent space

The negative result is the first row. The discrete model is slightly worse than the continuous VAE on the likelihood bound, 4.67 against 4.51 bits per dimension, while clearly better than the previous discrete-latent method. Near parity is what the paper claims, and near parity is what the numbers support; the case for discreteness rests on what the codes make possible downstream, not on likelihood.

The other experiments show reach rather than scores. A code sequence 64 times shorter than the waveform cannot keep every detail of it, and the speech demonstrations emphasize content and speaker rather than waveform fidelity. On DeepMind Lab frames, a second VQ-VAE stacked on the first compressed the codes much further, and an action-conditional video model was trained entirely in the latent space.

Since then

What the authors admit, and what happened next

The characteristic failure is codebook collapse: a subset of entries is never selected, receives no gradient, and stays where it was initialised, so the effective vocabulary is far smaller than the nominal one. Mitigations — reinitialising dead entries, using a moving average to update the codebook, normalising before matching — are standard practice and are all patches on the same dynamic.

The straight-through gradient is an acknowledged approximation, with no guarantee that it points in a helpful direction, and training is two-stage: the tokeniser and the prior are fitted separately, so the tokeniser cannot adapt to what the prior finds hard to model.

How it held up: it became the tokeniser, and that is a bigger role than generator. The reason phrases like "image tokens" and "audio tokens" are ordinary is this rung — discrete codes let images, video and speech be fed to sequence models built for text, which is the substrate under most multimodal generation and under the quantised targets on the multimodal ladder. Diffusion took the image-generation crown; discrete latents took everything that needed a shared interface with language.

SOLVE IT YOURSELF

Quantization, from scratch

VQ-VAE's core operation is nearest-neighbor quantization: measure the squared distance from the encoder's vector to every codebook entry and snap to the closest one. Here it is with no autograd, no tensors — the argmin lookup, the squared L2 distance, the commitment loss (β times the distance to the chosen code), and the three-part total. Change the codebook or the encoder vector and watch which code wins.

YOUR TASK

Write the body of vqvae_loss. Everything else in the file is already written; this is the function the paper is about. Press ▶ Run to check it against the 5 assertions below — they start red.

WHAT IT HAS TO SATISFY — 5 ASSERTIONS
  1. Quantization snaps a vector to its NEAREST codebook entry (index 1 here)
  2. Quantize returns that codebook VECTOR, not the original continuous one
  3. Squared L2 distance is exact -- (3,4) is 9+16 = 25 from the origin
  4. The commitment loss scales with beta (0.25 * distance-2 = 0.5)
  5. The total objective sums reconstruction + codebook + commitment
CPython · WebAssembly
06

A tokenizer for images

VQ-VAE's real legacy is as a tokenizer for continuous data. Once an image is a small grid of discrete codes, you can hand those codes to a powerful separate model — an autoregressive Transformer or a diffusion prior — and generate in that compact token space instead of over millions of raw pixels. This "compress to tokens, then model the tokens" recipe is dramatically cheaper and produces sharper results, and it became the dominant pattern in generative vision.

The line of descent is direct: VQ-VAE-2 stacked hierarchical codes for high-resolution images; DALL·E tokenized images into a discrete grid and modeled them with a Transformer alongside text; VQGAN added a perceptual/adversarial loss for crisper codes; and the same idea powers discrete audio tokenizers behind modern speech and music models. Even latent diffusion — which uses a continuous latent — inherits the core insight that you should generate in a learned, compressed representation rather than pixel space. The specific trick of a nearest-neighbor codebook with a commitment loss turned out to be one of the most reused building blocks in generative AI.

Worth knowing

A recurring failure mode is "codebook collapse": the model learns to use only a handful of the K codes and ignores the rest, wasting capacity. Practical fixes include EMA codebook updates, periodically re-initializing dead codes to active encoder outputs, and normalizing or lowering the code dimension — engineering that later work (VQGAN, improved VQ-VAEs) leaned on heavily to keep the whole vocabulary alive.

Questions

Questions people ask about VQ-VAE

Why does VQ-VAE avoid posterior collapse?

In a VAE the KL term rewards the encoder for matching the prior, and a powerful decoder lets it do that by ignoring the input, so the latent goes unused. VQ-VAE's posterior is one-hot, the single nearest code, and its training prior is uniform over K codes, so the KL equals log K for every input. It is a constant, carries no gradient and is dropped. There is nothing to gain by making the code uninformative: whichever code is chosen costs the same, and the code is the decoder's only route to information about the input. Capacity is limited by a hard count of K choices per position instead of a soft penalty, which is why the paper could pair the codes with a WaveNet decoder and still have them used.

How does the PixelCNN prior work in VQ-VAE, and why is it trained separately?

After the autoencoder is trained, every training image is encoded into a grid of code indices, for 128×128 ImageNet a 32×32 grid of integers from 0 to 511. A PixelCNN, an autoregressive model that predicts each position from the positions above and to the left, is then trained on those grids as if they were small images with 512 possible values per position. To generate, you sample a grid from the PixelCNN one position at a time, look up each index's codebook vector and run the decoder once. The stages are separate because the first uses a fixed uniform prior, and fitting the prior jointly was left to future work. The cost is that the tokenizer never learns which codes the prior finds hard to model.

What is residual vector quantization and how does it extend VQ-VAE?

Residual vector quantization stacks several codebooks. The first quantizes the encoder vector to its nearest code, the second quantizes what was left over, the third quantizes the remaining error, and so on, and the vector is rebuilt as the sum of the chosen codes. With N stages of K codes each you get K to the power N combinations while storing only N times K vectors, and you can drop later stages to trade quality for bitrate. It is not part of the VQ-VAE paper. It became standard in neural audio codecs such as SoundStream and EnCodec, whose stacked code streams are what many speech and music generators predict, and RQ-VAE applied the same idea to images.

What is finite scalar quantization (FSQ), and how does it compare to VQ-VAE?

Finite scalar quantization, proposed by Mentzer and colleagues in 2023, removes the learned codebook. The encoder projects to a few dimensions, each value is bounded and rounded to a small fixed set of levels, and the implied codebook is the grid of all level combinations, for example a handful of dimensions with five to eight levels each. Rounding uses the same straight-through gradient as VQ-VAE, but there are no codebook or commitment losses, and because every grid point is reachable by rounding, the dead-code problem largely disappears. The authors report results competitive with vector quantization on image generation and related tasks, with a much simpler training recipe.

Why does VQ-VAE need a commitment loss, and why is beta 0.25?

The straight-through trick gives the encoder a reconstruction gradient as if quantization were not there, and the codebook term moves codes toward encoder outputs, but nothing stops the encoder's outputs from drifting away from the codes faster than the codes can follow. The embedding space has no fixed scale, so the outputs can grow and the quantization error with them. The commitment term, beta times the squared distance from the encoder output to its frozen code, pulls the encoder back. The authors used beta = 0.25 in every experiment and reported that results changed little across values from about 0.1 to 2.0, so having the term matters far more than its exact weight.

Check yourself

4 questions to test what you just read

Work each one out before you open it — a number, a prediction or a reason, not a definition. Producing the answer is what makes it stick. Your progress is kept on this device.

A 128×128 RGB image is encoded as a 32×32 grid with K = 512. How many bits does the code hold, and what is the compression ratio against 8-bit pixels?

Show answer

Each position stores log2 512 = 9 bits, so 32 × 32 × 9 = 9,216 bits. The image is 128 × 128 × 3 × 8 = 393,216 bits. Ratio ≈ 42.7×, and the limit is exact: no input can ever put more than 9,216 bits through the bottleneck.

How large is the KL term for K = 512, in nats, and why can the loss ignore it?

Show answer

KL = log 512 ≈ 6.24 nats (9 bits) per position, identical for every input because the posterior is one-hot and the prior uniform. A constant has zero gradient, so dropping it changes nothing about training; it matters only when you report a likelihood bound.

If a codebook entry is never the nearest neighbor of any encoder output, what does each loss term do to it, and why does the situation tend to get worse?

Show answer

Reconstruction never reaches the codebook, and the codebook and commitment terms involve only selected codes, so the unused entry gets zero gradient and stays where it started. Meanwhile used codes move toward the data and win even more assignments, so the gap widens: codebook collapse. Restarting dead codes at recent encoder outputs breaks the loop.

Why does the straight-through gradient become less trustworthy as the quantization error grows?

Show answer

Copying ∂𝓛/∂zq onto ze treats the decoder's slope at the code as its slope at the encoder output. That holds only when the two points are close; when ‖ze − zq‖ is large, the slope at zq can point somewhere unhelpful for ze. The commitment term keeps that distance small, which is part of why the approximation works.

Where this sits

Generating pixels

Ten papers from the first latent-variable generator to video and real-time 3D. The diffusion half is strictly cumulative — each rung removes a cost the last one paid.

  1. 1VAE2014
  2. 2GANs2014
  3. 3VQ-VAE2017
  4. 4DDPM2020
  5. 5DDIM2021
  6. 6Classifier-Free Guidance2021
  7. 7Latent Diffusion2022
  8. 8Sora2024
  9. 9NeRF2020
  10. 103D Gaussian Splatting2023

All 110 papers, in 19 ladders →

Neural Discrete Representation Learning · Aaron van den Oord, Oriol Vinyals, Koray Kavukcuoglu · DeepMind · 2017 · read the original paper on arXiv → · Vibe Engines · 2026
Finished this one? 0 / 110 Paper Breakdowns done
NextDDPM Next on the “Generating pixels” ladder · rung 4 of 10

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Paper Breakdowns