Skip to content
AI360XpertBeta

Latent Diffusion

Diffusion's dozens of denoising steps run inside a small compressed latent space instead of on raw pixels, so image generation fits on one consumer GPU.

Latent diffusion runs all 50 denoising steps on a 16,384-value latent grid instead of the 786,432-value pixel grid, then decodes to pixels once
Latent diffusion runs all 50 denoising steps on a 16,384-value latent grid instead of the 786,432-value pixel grid, then decodes to pixels once

Why Does This Exist?

A diffusion model builds an image the slow way. It starts from pure noise and runs dozens of denoising steps, and each step is a full pass of a neural network over the thing being generated. The loop itself is covered in forward and reverse diffusion. What matters here is what every one of those passes has to chew through.

Run the loop on raw pixels and each pass pushes the whole image grid through the network: width times height times channels, fifty times over. For a 512 by 512 color image that is 786,432 values per pass, and the activations a capable network holds mid-pass can swallow most of a consumer GPU's memory. A datacenter card shrugs at that. The card in your desktop doesn't.

Latent diffusion keeps the loop and the quality but shrinks what the loop touches. Denoise a small compressed stand-in instead, then expand to full pixels exactly once. This page is about that compression trick and what it costs you. The loop lives on the diffusion pages; the model that made the trick famous lives at stable diffusion.

You need one idea going in: an autoencoder, a network that squeezes an image into a small code and rebuilds an approximation of the image from that code. If that is new, read variational autoencoders first, then come back. Nothing below makes sense without it.

Think of It Like This

Vacuum-sealing a coat before you repack the suitcase

Your winter coat fills the whole suitcase. Unfold it and refold it at every step of packing and you wrestle its full bulk again and again. Vacuum-seal it first and the coat goes flat, so now every rearrangement moves the small flat bag instead. You open the valve exactly once, at the hotel, and the coat puffs back to full size.

Latent diffusion packs the same way. The image gets compressed once, all fifty-odd rounds of denoising happen on the small version, and the decoder expands it to full pixels once at the end.

Where it stops: a vacuum bag keeps every fiber of the coat, just pressed flat. An autoencoder doesn't. It throws detail away to reach its size, and anything it discarded never comes back, no matter how good the denoising is.

How It Actually Works

Call the full image xx (pixels, the big grid) and its compressed code zz (the latent, the small grid). The encoder EE maps xx down to zz, written z=E(x)z = E(x), and the decoder DD maps back up to a reconstruction x^=D(z)\hat{x} = D(z), where the hat marks an approximation: close to xx, never exactly xx. The diffusion model never sees xx. It lives entirely in zz.

The autoencoder trains first, alone

Before any denoising exists, an autoencoder learns the small space on its own: squeeze images down, rebuild them, adjust until the rebuilds hold up. Variational autoencoders are the usual choice because their latent space is smooth, meaning nearby codes decode to nearby images, which is exactly what a denoiser needs as it walks from noise toward a clean code. Once this training finishes, the autoencoder freezes. Its weights never move again.

The diffusion model never learns to compress anything. It inherits a finished compressor and treats that compressor's output as its whole world.

Every noisy step happens in the small grid

Training goes like this: take a real image, encode it once to get z=E(x)z = E(x), then add noise to zz and teach the denoiser to remove it. That is the familiar forward-and-reverse loop, except ztz_t (the noisy latent at step tt, with tt counting down from the noisiest level to the cleanest) sits wherever pixels used to sit. Sampling runs the same story backward: draw pure noise shaped like zz, run the denoiser for the full step budget, and decode only the final clean latent, x^=D(z0)\hat{x} = D(z_0), where z0z_0 is the fully denoised latent.

Pixels appear exactly twice in the whole pipeline: once going in, once coming out.

Worked example: the 48-fold saving

Take the standard setup: a 512×512512 \times 512 color image, which has 3 channels, compressed to a latent of 64×64×464 \times 64 \times 4. That is an 8×8\times squeeze per side, since 512/64=8512 / 64 = 8, while the channel count grows from 3 to 4. Now count what one denoising pass processes. Pixels: 512×512×3=786,432512 \times 512 \times 3 = 786{,}432. Latent: 64×64×4=16,38464 \times 64 \times 4 = 16{,}384. Divide the two and you get 786,432/16,384=48786{,}432 / 16{,}384 = 48 exactly.

Each pass costs roughly in proportion to the values it touches, so every step is about 48 times cheaper, and that discount multiplies across all 50 steps: 786,432×50=39,321,600786{,}432 \times 50 = 39{,}321{,}600 pixel-values against 16,384×50=819,20016{,}384 \times 50 = 819{,}200 latent-values.

The trade is explicit: 48 times less work per step in exchange for whatever detail the frozen autoencoder dropped, which shows up as softness no denoiser can fix. So skip latent diffusion when pixel-exact detail is the product: measurement, metrology, anything where a texture shift changes the answer. The whole trick assumes the autoencoder reconstructs your images well. Feed it a domain it never saw and the failure lands silently, as blur, after you already paid for all 50 steps.

Code

The same step budget priced over both grids. The point is the ratio, and that the numbers match the hand count above.

from dataclasses import dataclass

@dataclassclass Grid:    height: int    width: int    channels: int
    def cells(self) -> int:        return self.height * self.width * self.channels

def total_cost(grid: Grid, steps: int) -> int:    """One denoising pass touches each value about once."""    return grid.cells() * steps

pixel_grid = Grid(height=512, width=512, channels=3)latent_grid = Grid(height=64, width=64, channels=4)steps = 50
print(pixel_grid.cells())  # -> 786432print(latent_grid.cells())  # -> 16384print(total_cost(pixel_grid, steps))  # -> 39321600print(total_cost(latent_grid, steps))  # -> 819200print(pixel_grid.cells() / latent_grid.cells())  # -> 48.0

The prints agree with the hand count: 786,432 values against 16,384 per step, a ratio of 48 to 1. The step budget itself can shrink too: DDIM sampling reaches the image in far fewer passes, and it stacks with everything on this page.

Watch Out For

Training the compressor and the denoiser together

It is tempting to picture one big model learning compression and denoising end to end. Don't build that. The autoencoder trains first, on reconstruction alone, with no noise and no diffusion objective. Only after it freezes does the denoiser start learning inside its fixed space.

The symptom of getting this wrong is a loss that drifts without ever settling: the denoiser aims at a target distribution that keeps shifting because the compressor underneath it is still changing. The fix is phasing. Finish and freeze the autoencoder, confirm its reconstructions hold up on your images, and only then train diffusion against its frozen codes.

Blaming the denoiser for the autoencoder's blur

Your outputs look soft. So you raise the step count, try new seeds, swap the sampler, and the softness never moves. That immovable blur is the signature of detail the autoencoder already discarded, and no setting downstream of it can paint that detail back.

Isolate the ceiling before you burn GPU hours. Encode a real image and decode it straight back with zero diffusion steps. If that round trip is already soft, the denoiser is innocent: swap or retrain the autoencoder, or accept the softness as the price of the 48-fold discount.

The Quick Version

  • Latent diffusion runs the whole denoise loop on a small compressed latent, never on pixels. Pixels appear only twice: encode going in, decode coming out.
  • A separate autoencoder trains first and then freezes. The diffusion model inherits its latent space and never learns compression.
  • The standard 512 by 512 image becomes a 64 by 64 by 4 latent, so each of 50 steps processes 48 times fewer values.
  • That per-step discount multiplies by the step count, which is what moves image generation from datacenter hardware onto one consumer GPU.
  • The price is frozen-in detail loss. Anything the autoencoder discarded returns as softness that extra denoising cannot repair.