Autoencoders
From deterministic autoencoders to variational and vector-quantised models, and the gradient-estimation tricks (reparameterisation, Gumbel-Softmax, REINFORCE) that make them trainable.
Overview
An autoencoder is a neural network trained to copy its input back to its output, but through a deliberate bottleneck. We compress an input with an encoder into a compact latent code , and then hand that code to a decoder that tries to reconstruct the input as . The whole network is trained so that resembles as closely as possible.
The bottleneck is the point. If the code is lower-dimensional (or otherwise constrained) than the input, the network cannot simply memorise and pass the input through unchanged. It is forced to discard noise and keep only the structure that matters for reconstruction. A good latent is therefore one that has learned and stored the important features of the input. Because we never need labels — the input is its own target — autoencoders are an unsupervised (or self-supervised) method.
Notation. Throughout the variational sections we follow the convention of the original VAE paper: are the encoder parameters (the recognition / inference network ), and are the decoder parameters (the generative network ). Some texts swap these letters, so watch for it when reading other sources.
The Standard (Vanilla) Autoencoder
Encoder
Each layer of the encoder is an ordinary neural-network layer. For weights , bias , and an activation function (e.g. ReLU or sigmoid), a layer’s output is:
Stacking several such layers maps the input down to the latent code .
Decoder
The decoder mirrors this, mapping the code back up to a reconstruction. With its own weights , bias , and activation :
Loss function
We want to match , so we penalise their squared distance (the reconstruction error):
Substituting the decoder, and then the encoder, makes the full composition explicit:
This is fully deterministic and differentiable, so it trains by ordinary gradient descent.
Why the latent space is a problem
A standard autoencoder is trained on a finite, discrete set of data points, so it only ever learns to place those points somewhere in latent space. This creates two issues:
- Gaps. The latent space ends up as a scatter of isolated points with empty regions in between. If you pick a point from one of those gaps and decode it, the decoder has never been trained there and tends to produce nonsense.
- Crowding. Points that are unrelated can land very close together, which makes it hard for the decoder to reconstruct them distinctly.
Both problems stem from the same root cause: the encoder maps each input to a single point, with no pressure to organise the space smoothly. This is exactly what the variational autoencoder fixes.
From a Point to a Distribution: the Variational Autoencoder
Deterministic vs variational encoders
A deterministic encoder takes an input and returns one latent vector — same in, same out, every time.
A variational encoder does not output a code directly. Instead it outputs the parameters of a probability distribution over . For a Gaussian latent, the encoder reads and produces a mean vector and a variance . The actual code is then a sample from the distribution those parameters define:
Why is a random variable?
Because we built it to be one. The encoder produces a distribution, and is a draw from it.
Why design to be random?
There are two complementary reasons.
The Bayesian reason. Given an observed , which latent codes could have produced it? Rarely a single one. Many codes could plausibly have generated the same image, and some are more likely than others. The spread of plausible explanations is a distribution — the posterior . The posterior is inherently a distribution because it encodes our uncertainty about the hidden cause. The encoder is our learned approximation to that posterior, so it has to be a distribution too, and a sample from it has to be random.
The practical reason. Because is sampled with genuine noise around , and because a regularising term (the KL term, below) continually pulls toward a smooth prior , the latent space gets organised into a continuous, well-behaved region. Nearby points decode to similar outputs, and there are no “dead” regions that decode to nonsense. This is what lets us sample a fresh from the prior and decode a brand-new plausible data point, or smoothly interpolate between two encodings. A deterministic autoencoder gives no such guarantee.
Bayesian Foundations
The encoder is learning to infer the latent from an observation — exactly the situation Bayes’ theorem describes:
Each piece has a name and a role:
- — the posterior: the distribution over latents after seeing the data. This is what we want.
- — the likelihood: how likely the data is given a latent. This is what the decoder models.
- — the prior: our belief about latents before seeing any data (we will choose ).
- — the evidence (or marginal likelihood): how probable the data is overall.
Writing the denominator out shows where the trouble starts:
To compute we would have to sum (or integrate) over every possible value of . For any realistic latent space this is intractable. Since we cannot compute the exact posterior, we approximate it — and the tool for that is variational inference, leading to the ELBO.
Variational Inference and the ELBO
A family of approximate posteriors
We pick a family of tractable distributions and search within it for the member that best approximates the true posterior. We index a member by a variational parameter :
If the family is Gaussian, then is just the mean and variance: knowing those pins down the exact distribution. For the -th data point :
Because is chosen per data point, .
Measuring the approximation with KL divergence
The Kullback–Leibler (KL) divergence measures how far one distribution is from another. It is the expected log-ratio of the two densities; it is always and equals only when the distributions are identical. We use it to ask: how well does approximate ?
Now we expand it, one step at a time. Split the log of a ratio into a difference of logs:
Rewrite the posterior using :
Split that second log too, and note does not depend on , so its expectation is just :
Defining the ELBO
The first two terms are exactly the negative of what we call the Evidence Lower BOund (ELBO):
Substituting that name back in gives the central identity of variational inference:
Rearranged (this is the correct direction of the rearrangement):
Why this is the whole point
Look at the identity carefully:
- is a fixed constant with respect to — changing our approximation cannot change how probable the data actually is.
- always.
So — it is genuinely a lower bound on the evidence, hence the name. And because is constant, maximising the ELBO is exactly the same as minimising the KL divergence between our approximation and the true posterior. The optimal approximation is:
The intractable has dropped out of anything we need to optimise. We can differentiate the ELBO and run gradient descent. That is the breakthrough that makes the VAE trainable.
The per-data-point ELBO: reconstruction + regulariser
We assume local latent variables: each data point has its own latent , shared with no other point. The ELBO then splits into a sum over data points, and using we can rewrite each term as:
(The derivation: .)
Since the encoder is the network and the decoder is , training minimises the negative ELBO, which is the VAE loss:
Both and appear, so a single gradient step on this loss updates both networks at once.
- Reconstruction term. This encourages the decoder to reconstruct the data accurately. If reconstruction is poor, is very negative — the model is saying the observed was unlikely under the decoder. The expectation over is stochastic with respect to : the quantity inside depends on a random draw. In practice we do not integrate it analytically — we draw a few samples of from and average over them (a Monte-Carlo estimate, i.e. approximating an expectation by an average over samples).
- Regulariser. We want to follow a standard normal, , and the encoder is learning to approximate it. This KL term penalises the encoder whenever it chooses a far from the prior. That is what prevents the autoencoder from collapsing the data onto isolated points and instead keeps the latent space continuous.
Computing the Gaussian loss in closed form
For diagonal-Gaussian (dimension ) against the standard-normal prior , the KL term has a clean closed form — no sampling needed:
Reading the terms: pulls the mean toward ; is minimised at , pulling the variance toward . Together they pull each latent dimension toward the unit Gaussian.
The reconstruction term depends on the decoder’s chosen output distribution:
- Gaussian decoder : then , so reconstruction reduces to mean-squared error.
- Bernoulli decoder (binary pixels): becomes binary cross-entropy.
[TODO] Full derivation of the reconstruction term from for the Gaussian and Bernoulli cases, including the role of the (often fixed) decoder variance.
Connection to Expectation–Maximisation
The ELBO is not unique to VAEs — it is the same object that powers the EM algorithm. EM maximises the marginal likelihood , where the latent variables have been marginalised out (; note it is , not , that is integrated away). For any distribution over the latents:
where is the entropy of . The two parts are the ELBO and the KL gap between and the true posterior. EM alternates:
- E-step. Set to maximise by choosing . This drives the KL gap to , so the lower bound touches the true objective .
- M-step. Holding fixed, improve to increase .
A VAE is essentially amortised variational EM: instead of solving for the optimal exactly for every data point (a per-point E-step), an encoder network predicts the variational parameters in one forward pass, and both and are improved together by gradient ascent on the ELBO.
Computing the Gradients: Why Backprop Breaks
Training is gradient ascent on the ELBO (equivalently, descent on the negative-ELBO loss above). The decoder is no trouble: once a value of is given, the gradient flows straight through . The encoder is the problem, because lives inside the distribution we sample from, .
Two conditions backprop requires
For gradients to flow, every node from inputs to loss must be:
- a deterministic function of its inputs, and
- differentiable.
A deterministic node, e.g. . Ask: if I nudge up by , how does change? Answer: goes up by exactly , every single time. The map is a genuine function with a well-defined derivative .
A sampling node, e.g. . Ask the same question: if I nudge up by , how does change? There is no answer. For a fixed , is not a single number — it is a freshly drawn random value that could land anywhere under the bell curve. The map is one-to-many: one input, infinitely many possible outputs. No derivative exists.
Why not just differentiate the expectation directly?
The loss is an expectation, which we can write as an integral. (Using a generic for the parameters of the sampling distribution and for the thing inside.)
Push the gradient inside and apply the product rule:
Here is the key observation: depends only on the decoder and the value . The encoder does not appear in at all, so:
and the whole expression collapses to:
💡 This is why backprop breaks. The encoder exerts its influence entirely through the shape of the distribution that we sample from. The gradient therefore lives inside the object — a density that measures how the parameters reshape the latent space — while only answers “how well do we reconstruct if the latent happens to be ?”. If we replace “the distribution” with “one concrete sample ”, we throw away every trace of : the sampled is just a number, and has vanished from the computation graph. The autograd engine, seeing only the graph after was sampled, faithfully reports a zero gradient — because in the graph it can see, there is no path from to the loss.
The differentiation graph can only begin after has been sampled, and from there it differentiates , which doesn’t depend on . ’s influence is locked inside the sampling step, where backprop can’t reach.
The Reparameterisation Trick
The fix is to relocate ‘s influence out of the sampling step and into a deterministic node. Instead of drawing directly, we write:
Now the randomness lives entirely in , an external input with no parameters. Given , the latent is a deterministic, differentiable function of (through and ). The graph now has a clean path , and flows through it normally.
Why this is valid: the location–scale property
This works because of a specific property of the Gaussian, the location–scale property. The distribution
is identical to
A standard normal can be shifted by (location) and scaled by (scale) to produce any Gaussian. So the reparameterised samples follow exactly the same distribution as before — we have changed how we draw the sample, not what we are drawing — but the new form is differentiable in the parameters.
The resulting gradient is itself an expectation we can Monte-Carlo estimate by drawing a few :
This estimator tends to have low variance, which is one reason it works so well in practice (contrast with the score-function estimator below).
Discrete Latents: the VQ-VAE
A vanilla VAE assumes the prior and posterior are continuous Gaussians. But sometimes a discrete latent is the better model.
Motivation. Images may belong to categories like “cat” and “car”, and it makes little sense to interpolate between such categories — a half-cat-half-car is not a meaningful image. Discrete codes are also easier to model: each category is a single value. The VQ-VAE (Vector-Quantised VAE) therefore replaces the continuous normal latent with a discrete one:
- the encoder produces continuous vectors which are matched to a categorical codebook,
- sampling returns an integer index into a dictionary of embedding vectors,
- the indexed embedding is passed to the decoder.
Dimensions
= batch size, = image height, = image width, = input channels, = hidden channels, = number of codebook entries.
The Vector-Quantisation layer
This layer takes the encoder’s continuous embeddings , snaps each to its nearest codebook entry, and outputs the quantised tensor . The decoder then consumes and outputs .
- Reshape. Combine all dimensions except the last, giving vectors, each of dimensionality .
- Compute distances. For each of the vectors, measure its distance to each of the codebook embeddings, producing a matrix of shape .
- Argmin. For each vector, find the index of the nearest of the codebook vectors.
- Index. Replace each vector with its nearest codebook vector, giving .
- Reshape back to .
- Copy gradients. The step has zero gradient almost everywhere, so backprop cannot flow through it. We approximate by copying the gradients arriving at back onto , so some training signal still reaches the encoder.
The straight-through estimator
How do we “copy the gradients” and pretend the VQ layer isn’t there during backprop? Note that and are vectors of the same dimensionality , and is the codebook entry nearest to , so geometrically they sit close together in the same space. The straight-through estimator says: only in the backward pass, pretend the quantisation step was the identity function, and pass whatever gradient arrives at straight through to .
The justification is that the copied gradient points in a sensible direction — “produce outputs that make the decoder happier”. The forward pass uses the true (discrete) quantisation; the backward pass uses the identity. It is an approximation, but it works well in practice.
The VQ-VAE loss (three components)
Here is the stop-gradient operator: it is the identity in the forward pass but lets no gradient flow through whatever it wraps in the backward pass.
- Reconstruction loss optimises the decoder, and the encoder via the straight-through gradient.
- Codebook (VQ) loss. Because the straight-through path gives the codebook entries no gradient, they would never learn on their own. This term — a dictionary-learning / k-means-style update — uses an error to pull each chosen codebook vector toward the encoder output it was matched with. With on , only the codebook moves. This is what actually trains the embedding table.
- Commitment loss. The embedding space is dimensionless, so its volume could grow arbitrarily if the embeddings train slower than the encoder. With on , this term pulls the encoder output toward its chosen code, discouraging the encoder from drifting between codebook entries and keeping and close. The hyperparameter weights it against the other terms.
Reparameterising a Discrete Distribution
We just saw how the reparameterisation trick relocates randomness so we can differentiate through continuous latents. The Gumbel-Max trick is the analogue for a categorical distribution.
The core difficulty: the map from continuous parameters (class probabilities / logits) to a discrete outcome is a step function. Nudging the class probabilities by a tiny amount produces a discontinuous jump in the (one-hot) output — there is no smooth response to differentiate. For a continuous distribution the output can move continuously with the parameters; a discrete random variable cannot, because its output lives in a finite set.
The Gumbel-Max trick
Suppose the categorical is specified by logits (unnormalised log-probabilities). The trick gives a routine to draw a sample:
- Draw independent Gumbel noise , where each with .
- Output the index .
This produces an exact draw from the softmax categorical. But is still a non-differentiable step function — so this alone doesn’t solve our gradient problem; it just isolates the non-differentiability into a single clean operation.
What is the Gumbel distribution?
It comes from extreme value theory. If you take the maximum of many independent random variables and rescale it appropriately, the result converges to one of three limiting shapes. When the underlying distribution has a light tail (decaying exponentially or faster) — like the normal, the exponential, or the logistic — the maximum converges to the Gumbel distribution. Since the trick is built around an over (noised) logits, the Gumbel is the natural noise to inject.
Why are there two logs?
This is inverse-transform sampling: a way to turn plain uniform randomness into a sample from any target distribution. If and is the CDF (cumulative distribution function) of the target, then has exactly that distribution. The proof is short:
The last step holds because is uniform, so . The Gumbel’s CDF is . Inverting it (set and solve for ) gives:
— hence the two nested logs.
[TODO] Proof of the Gumbel-Max identity: that , i.e. the routine really does sample from the intended categorical.
The Gumbel-Softmax (Concrete) trick
To make the sample differentiable, replace the hard with a temperature-controlled softmax:
This produces a soft vector — a point in the probability simplex (the set of non-negative vectors summing to ), rather than a hard one-hot. Crucially, the softmax is smooth, so gradients flow back to the logits .
The temperature controls how peaky the output is, and sets a trade-off:
- As , approaches an honest one-hot — samples are nearly discrete and faithful — but the function approaches a step again, so the gradients become high-variance and spiky.
- At high , the gradients are smooth and well-behaved, but the samples only loosely resemble the discrete distribution we actually want.
In practice is often annealed (started high and decreased during training) to balance faithful samples against usable gradients.
The Score-Function Estimator (REINFORCE)
Motivation
The recurring problem is optimising an objective that is an expectation over a distribution that itself depends on the parameters:
where scores a sample , and is drawn through the very parameters we want to optimise. If we naively move the gradient inside the integral:
we hit a snag: is not a probability distribution — it doesn’t integrate to and can be negative. So this integral is not an expectation under , and we cannot directly approximate it by Monte-Carlo sampling (Monte Carlo requires a genuine expectation). Both REINFORCE and the reparameterisation trick are ways of rewriting this quantity as a proper expectation we can sample.
The log-derivative (score-function) trick
Start from the derivative of a log:
Rearranging gives an identity that reintroduces the density as a multiplicative factor:
The term is called the score.
Now the derivation. Write the objective as an integral, push in the gradient, and use that does not depend on (so , eliminating one product-rule term):
Substitute the log-derivative identity:
This is a proper expectation under , so we can estimate it by Monte-Carlo sampling:
Notice the estimator only ever evaluates — it never differentiates it. So and need not be differentiable in , which is exactly why this estimator works for discrete distributions where reparameterisation does not apply.
REINFORCE vs the Reparameterisation Trick
Same objective , two different rewrites.
The reparameterisation trick applies a transformation with , turning the objective into an expectation over the parameter-free noise :
Now the gradient can move inside and be computed by the chain rule:
The contrast:
- Score function (REINFORCE) differentiates the distribution (via the score) and only evaluates . It works for discrete and non-differentiable , but is typically high-variance.
- Reparameterisation differentiates through and the sample path (it needs and a differentiable , i.e. a continuous, reparameterisable distribution), and is typically lower-variance.
This is precisely why continuous Gaussian latents (the VAE) use reparameterisation, while discrete latents (the VQ-VAE) must fall back on the straight-through estimator, Gumbel-Softmax, or REINFORCE.
[TODO] Variance reduction for REINFORCE — baselines and control variates. The estimator’s high variance is its main practical drawback, and baselines are the standard remedy.
Learning Path
What should I learn before Autoencoders?
Start with gradient-descent before studying Autoencoders.
How does Autoencoders connect to other AI topics?
Autoencoders belongs to Deep Learning and connects to unsupervised-learning, generative-models, representation-learning, autoencoders, variational-inference, vae, vq-vae, reparameterisation, gumbel-softmax, reinforce, elbo, kl-divergence.