Latent Spaces and Manifold Learning
Real data fills a tiny curved sliver of the space it is stored in. A latent space is the coordinate system on that sliver, and it is where generation works.
Why Does This Exist?
Here is a claim worth taking literally, because most of generative modelling follows from it. Real data occupies a vanishingly small, curved, low-dimensional subset of the space it is stored in.
Count it. A 100 by 100 greyscale image is 10,000 numbers, each 0 to 255, so there are possible images, a 1 followed by about 24,082 zeros. Sample one uniformly at random and you get static. Sample every microsecond for the age of the universe and you still get static, every time. You will never hit a face, a cat, or a page of text.
So the set of images that look like anything is unimaginably tiny inside the space that holds it. And it is smooth: rotate a head two degrees and you are still in the set of faces, brighten it slightly and you are still in it. A tiny, smooth, curved subset is what mathematicians call a manifold, and the claim above is the manifold hypothesis. The intrinsic dimension is how many numbers actually vary. A photo of one person under studio lighting might be described by pose, expression, lighting angle and a handful of other things, perhaps 30 numbers, stored as 10,000.
This page is about the coordinate system on that sliver: what a latent space is, why distance there beats distance in storage, and why generation lives there. The linear projection method that finds flat slivers is dimensionality reduction. The lookup vectors you actually store and search are vector embeddings. The sampling machinery that uses the space to make new rows is synthetic data generation.
The one idea you need first. A vector is a list of numbers, and distance between two vectors is a single number saying how far apart they sit. Pixel distance counts per-position disagreements. Latent distance counts disagreements along the surface. That difference is the whole page, and the practical face of it is vector embeddings.
Think of It Like This
A road across a mountain range
You are standing in a village. Another village sits 40km away, and a road joins them: winding, over a pass, along a valley floor.
Now draw a straight line between the two villages on the map and walk it. Every point on that line is a real place with real coordinates, and most of them are halfway up a cliff. The line is geometrically valid and physically useless.
The road is the manifold. It is one-dimensional, one number, how far along, locates you exactly, even though it is drawn on a two-dimensional map and climbs through three-dimensional space. Points on the road are places you can be. Points off it are coordinates.
Interpolating in latent space means walking the road. Interpolating in the input space means walking the straight line.
Where it stops: a road is fixed ground you can walk twice. A learned latent space is an estimate that shifts with the model, the seed and the training data, so coordinates from one run mean nothing in another.
How It Actually Works
A model that compresses data learns three objects. An encoder maps an input to a short vector , the latent code. A decoder maps a code back to data, . The set of codes the decoder handles well is the latent space, with intrinsic dimension , the small count of numbers that actually vary. The bottleneck has to be narrow: forcing 10,000 numbers through 30 makes the model find the 30 that matter. A latent space is what an embedding is a point in.
Why latent distance beats input distance
Take a photo and shift every pixel one column to the right. In pixel space that is an enormous distance, nearly every pixel changed. To you it is the same photograph, and in a good latent space the two codes sit almost on top of each other.
That single fact is the argument for the whole approach. Distance in the stored representation measures storage accidents. Distance on the manifold measures the thing you care about.
Interpolation is where generation lives
Average two face images pixel by pixel and you get a double exposure, a ghost, not a face, because the midpoint of two points on a curved surface is not on the surface. Average their latent codes and decode, and you get a plausible third face.
That is why the latent space is the working surface for generation. Diffusion models and VAEs both sample there rather than in pixel space, for exactly this reason.
Disentanglement is the property where one latent axis moves one human-meaningful attribute. It is genuinely useful and it mostly does not happen by default. The axes a model picks are whatever made compression easiest, and they arrive tangled.
Off the manifold, a model is guessing
A model is fitted where data lives. Hand it a point off the manifold and it produces something, confidently, because nothing in the architecture reports it has never seen this region.
Two consequences matter in practice. Adversarial examples are off-manifold points a hair away from real ones, which is why a few imperceptibly changed pixels can flip a label. And interpolating in feature space invents rows that may sit in an impossible region: this is exactly the caveat on SMOTE, which builds a synthetic minority row partway between two real ones and can land it inside the majority class. Mixup gets away with the same operation because it interpolates the label too, so the model is told the point is a blend rather than a member.
Useful flip side: reconstruction error is an out-of-distribution detector. Encode, decode, measure the difference. Points on the manifold come back cleanly. Points off it do not.
Worked example
Take the unit circle, a one-dimensional manifold sitting in a two-dimensional plane. Every point is exactly one number, its angle , with coordinates and radius exactly 1.
Two real points at 0 and 90 degrees: and . Average their coordinates and the midpoint is , whose radius is . It sits about 29 percent inside the circle, which is to say it is not on the manifold and is not data.
Average their angles instead: 45 degrees, coordinates , radius exactly 1.0. Still on the circle.
Same two points, same halfway instruction. One route stays in the data and one leaves it, and the only difference is which coordinate system you did the arithmetic in.
Code
The point of this snippet is the check: the chord midpoint fails the radius test while the angle midpoint passes it.
import numpy as np
def radius(p: np.ndarray) -> float: """Distance from the centre. On the manifold this is exactly 1.""" return float(np.linalg.norm(p))
theta: np.ndarray = np.array([0.0, np.pi / 2])pts: np.ndarray = np.stack([np.cos(theta), np.sin(theta)], axis=1)chord: np.ndarray = pts.mean(axis=0)arc: np.ndarray = np.array([np.cos(np.pi / 4), np.sin(np.pi / 4)])print(np.round(pts, 3).tolist()) # -> [[1.0, 0.0], [0.0, 1.0]]print(np.round(chord, 4).tolist()) # -> [0.5, 0.5]print(round(radius(chord), 4)) # -> 0.7071print(round(radius(arc), 4)) # -> 1.0print(int(round(10_000 * np.log10(256)))) # -> 24082chord is a real pair of numbers that is not a member of the set. That is the whole failure mode, in two dimensions where you can see it.
Watch Out For
Interpolating in the input space and calling the result an example
Averaging two rows feels like the obvious way to invent a third. On any curved data distribution it produces points that do not belong to it.
The concrete case is oversampling a minority class by interpolating between neighbours. Two fraudulent transactions differ in amount and hour, and the point between them is a transaction with an amount and an hour that no fraudster ever produced, sometimes sitting squarely inside the legitimate cluster. You handed the model a mislabelled row and told it to fit it.
Same failure in images. Pixel-averaging two photos gives a double exposure, so training on it teaches the model that ghosts are faces.
Two ways out. Interpolate in a learned latent space, where the coordinates are on the surface. Or interpolate the label along with the input, which is what mixup does and why it works: the model learns this is 60 percent cat rather than this ghost is a cat.
Reading latent dimensions as attributes, or comparing them across runs
Dimension 7 of your embedding correlates with sentiment across a few hundred examples, so it goes into a dashboard as the sentiment axis.
The space is only defined up to rotation and relabelling. Nothing in the objective asked for one attribute per axis, and unless you trained for disentanglement you almost certainly did not get it. Dimension 7 carries a mixture, and the correlation you found holds on your sample and drifts off it.
The version that breaks pipelines is comparing coordinates across training runs. Retrain with a different seed and dimension 7 means something else entirely, so a stored vector from the old run and a fresh vector from the new one are not comparable, and cosine between them is noise. That is the same versioning rule as vector embeddings: the space belongs to the exact model that produced it.
Probe with a small trained classifier on the whole vector rather than reading axes, and re-embed everything when the encoder changes.
The Quick Version
- Real data occupies a tiny, smooth, curved subset of the space that stores it. Sample a 100 by 100 image uniformly and you get static, every time, out of about possibilities.
- That subset is a manifold, and the intrinsic dimension is how many numbers actually vary, perhaps 30 of the 10,000 you store.
- A latent space is the coordinate system a model learns on that surface. The encoder maps onto it, the decoder maps back.
- A one-pixel shift is enormous in pixel space and tiny in latent space, which is the whole argument for working there.
- Interpolating latent codes gives plausible new examples. Interpolating inputs gives a double exposure, because the chord leaves the surface.
- On the unit circle, the coordinate midpoint of the 0 and 90 degree points has radius 0.7071, about 29 percent inside. The angle midpoint sits exactly on it.
- Off-manifold inputs get confident meaningless answers, which is what adversarial examples exploit and what SMOTE risks.
- Reconstruction error is a usable out-of-distribution detector.
- Latent axes are not named attributes, and coordinates from two training runs are not comparable.