Python · cell 1
How the diffusion and flow models of the last chapter become systems that turn a sentence into a photograph or a minute of film. Latent spaces, diffusion transformers, text encoders, autoregressive and masked token generators, video at scale, honest evaluation, and control, with every cost counted and every published number checked.
Latent diffusion is how nearly every large image and video generator works. It does not run a diffusion model on the pixels of an image. It first shrinks each image into a much smaller grid of numbers with an autoencoder, a pair of networks that compress an image and expand it back. The diffusion model learns to generate those small grids. The second half of the autoencoder turns a generated grid into a picture once, at the very end. Stable Diffusion, SDXL, Stable Diffusion 3, FLUX, and most video models are built this way.
The reason is cost. A diffusion model or a flow model starts from pure noise and calls a network dozens of times. Each call moves the sample a little closer to clean data. §33.6 built one that turns noise into points on a ring in the plane. A 512 by 512 color photograph has numbers, and every call reads and writes all of them.
Two real networks show what that costs. ADM, the strongest diffusion network on pixels in 2021 (Dhariwal and Nichol 2021), needs 1,983 billion multiply-adds per call at 512 by 512. DiT-XL/2, a transformer that denoises a compressed version of the same image, needs 524.6 billion, about a quarter. Its samples are also better. With guidance it reaches an FID of 3.04, against 3.85 for ADM with classifier guidance and a separate upsampling model, which costs more still (Peebles and Xie 2023, Section 5.1 and Table 4). FID is the standard distance between generated and real images, lower is better, and §34.6 derives it.
The saving rests on one observation (Rombach et al. 2022). Most of the numbers in a photograph describe detail you would not miss, such as the grain of a texture or the noise of a camera sensor. A model on pixels spends its capacity, its training, and every sampling step on that detail. You met the fix in §1.10, where Stable Diffusion turned a photograph into a grid, 48 times fewer numbers, before generating anything.
This section builds latent diffusion and counts what it buys and what it costs. We define the model, count the numbers, tokens, and FLOPs it saves, and prove its price, a floor on quality that no generator can get below. Then we fix the two things that go wrong when the data is a latent rather than an image, its scale and its size. We follow one prompt through a whole generation, compare Stable Diffusion 1.5, SDXL, SD3, and FLUX, and end with the alternatives and with decoding very large images. Most interview questions on this topic are arithmetic of exactly this kind, done on a whiteboard.
Compressing an image does two different jobs. Perceptual compression removes fine detail that a viewer would not miss. Semantic compression keeps what the image is about, its layout, its objects, and its light. Rombach et al. (2022) separate the two jobs. A diffusion model is an excellent but expensive tool for the second job and a wasteful one for the first. Deciding where a dog sits does not require modeling every hair of its fur.
So the work is split in two. An autoencoder does the perceptual compression. Its encoder maps an image to a smaller grid of vectors, called the latent. Its decoder maps the grid back to pixels and paints in plausible fine texture. The autoencoder is trained once and then frozen, never updated again. The diffusion model then does the semantic part inside the small grid. The split pays off because the two halves run at different rates. The autoencoder runs once per image, and the diffusion model runs dozens of times.
Here is the split in miniature, with an encoder so simple you can run it by hand.
The smooth part of the image survived the round trip and the pixel-level texture did not. A learned decoder does better than copying, since it can paint in a texture that looks right. But it cannot know which checkerboard was there. A real autoencoder makes the same trade as block averaging, only far better. It keeps the content and gives up exact detail.
The autoencoder inside Stable Diffusion keeps 4 learned numbers for every 8 by 8 block of RGB pixels. It is trained with the three losses that §32.6 built for image tokenizers.
The encoder outputs a Gaussian for every latent number rather than a single number, as a variational autoencoder does (§32.3). A KL penalty pulls each Gaussian toward a standard normal. Its weight is about , so small that the encoder is almost deterministic, and the latent behaves like an ordinary compressed code. In particular, the penalty does not pin down the latent's overall scale. That is why the definition below carries a scale factor.
Here is the whole system in one place. Write for an image with rows and columns of pixels, with values scaled to . Write (bold) for whatever the model is conditioned on, such as a class label or a sentence. The italic is a different thing, the number of latent channels.
In plain words, compress every training image once, standardize the codes, learn to generate codes, and decode a generated code once at the end. Nothing in the diffusion machinery changes. Only the data does.
The training loss is the flow-matching loss of §33.6, applied to latents. Here it is again in brief. A clean latent at time and a noise sample at are joined by a straight line, . Moving along that line at constant speed has velocity . The network sees the point and the time and learns to predict that velocity. This path is called rectified flow. This book, SD3, and FLUX all put data at and noise at .
Training draws a time from a density and a noise sample of the latent's shape, and minimizes
In plain words, encode an image, normalize it, mix it with noise in a random proportion, and ask the network which way the straight line from data to noise points. SD3 draws from a logit-normal density, with standard normal, which practices the middle of the path most (§33.6 explains why). The original latent diffusion model used the noise-prediction loss of §33.2 instead, with . In both, no gradient reaches or .
Freezing the autoencoder has a practical payoff that is easy to miss. Its output for a given image never changes, so large training runs encode the whole dataset once and train on the stored latents. SD3 does exactly this (Esser et al. 2024, Appendix E.1). The encoder then costs nothing per training step, and each step touches 48 times fewer numbers than a pixel model would at Stable Diffusion's settings.
Labs ask candidates to write this training step. The math is short, and every convention in it is a place to slip.
You are given a frozen encoder frozen_encoder(x) and a velocity network velocity_model(z_t, t). The encoder maps images x of shape (B, H, W, 3) to a pair (mu, logvar), each of shape (B, h, w, c), channels last. The network takes latents of shape (B, h, w, c) and times of shape (B,).
Implement latent_fm_loss(x, t, eps_enc, eps, scale, shift) in four steps.
eps_enc, where eps_enc is a given standard normal array.scale and shift .eps.Two whole numbers describe the autoencoder, the factor and the channel count . A third, the patch size , describes how a transformer denoiser reads the latent. A transformer works on tokens, a sequence of vectors. To make tokens from a grid, it cuts the grid into squares, called patches, and flattens each square into one vector. The Vision Transformer of §27.2 does the same to pixels.
Three real ratios, worked by hand. Stable Diffusion 1 uses and , so . SD3 and FLUX.1 use and , so , a quarter of the compression, for reasons the quality floor below makes clear. The deep-compression autoencoders (DC-AE) of Chen et al. (2024) reach with and . Their models with , and , have the same ratio of 96, because they trade spatial resolution for channels.
How much compression is right? Rombach et al. (2022) trained the same class-conditional ImageNet model in latent spaces with , with the same compute, number of steps, and parameter count.
The other side of the choice is how well each autoencoder reconstructs. Two scores measure it. The reconstruction FID (rFID) is the FID between real images and their own reconstructions, so lower is better. The PSNR, peak signal-to-noise ratio, is of the squared pixel range over the mean squared error, in decibels, so higher is better. Each 3 dB more means about half the squared error. The latent diffusion paper reports both for autoencoders with the KL penalty, trained on OpenImages and tested on ImageNet validation images (its Table 8).
| rFID | PSNR (dB) | |||
|---|---|---|---|---|
| 2 | 2 | 6 | 0.086 | 32.47 |
| 4 | 3 | 16 | 0.27 | 27.53 |
| 8 | 4 | 48 | 0.90 | 24.19 |
| 16 | 16 | 48 | 0.87 | 24.08 |
| 16 | 8 | 96 | 2.63 | 21.94 |
| 32 | 64 | 48 | 2.04 | 22.27 |
| 32 | 16 | 192 | 7.3 | 20.38 |
Reconstruction gets worse as the ratio grows. At the same ratio of 48, though, the three autoencoders still differ, so the ratio is not the whole story. More spatial downsampling at the same ratio means fewer, fatter latent vectors. That is good for the denoiser, which sees fewer tokens, and harder for the autoencoder to learn.
Now the network that runs many times. A FLOP is one floating-point operation, and a multiply-add counts as two. A transformer has blocks (its depth), and each token is a vector of numbers (its width). §5.6 counted the forward FLOPs of one block exactly, and §27.2 applied the count to images. Here it is for a diffusion transformer.
Per block, the attention projections are four matrices, and the MLP has a and a matrix. That is weights, each doing one multiply-add per token, so FLOPs. The scores and the weighted sum each take multiply-adds summed over the heads, so FLOPs. Multiply both by . The ratio of the two terms is , which exceeds 1 exactly when . Since , doubling both sides multiplies by 4, a term linear in by 4, and a term in by 16. ∎
A sampler with steps makes network calls. The samplers of §33.4 use from one to a few hundred. Guidance doubles that, and interviewers probe this point. Classifier-free guidance (§33.5) runs the network twice per step, once with the prompt and once with an empty prompt, giving velocities and . It then moves along with a guidance scale , further in the direction the prompt adds. So steps cost evaluations. Stacking the two inputs into one batch halves the number of sequential calls and keeps the GPU busier, but the arithmetic is the same. A guidance-distilled model such as FLUX.1 [dev] takes the guidance scale as an input and needs one evaluation per step.
The cell counts numbers, tokens, and FLOPs for five real configurations.
Two rows can be checked against published numbers. DiT-XL/2 at 256 by 256 costs 237 GFLOPs (billions of FLOPs), which is 118.4 billion multiply-adds, against the 118.6 that Peebles and Xie (2023) list. They call these numbers Gflops, but the match shows that they count multiply-adds. At 512 it costs 524.3 billion multiply-adds against their 524.6. The small gaps are the patch embedding and the output head, which the formula leaves out. The SD3 and FLUX rows use the published depths and widths, so they are estimates in the same sense. SD3's width is 64 times its depth, 2,432 for depth 38 (Esser et al. 2024, Section 4). FLUX.1 has width 3,072 and 19 plus 38 blocks. The DC-AE row shows the point of deep compression. A 1024 by 1024 image costs what a 512 by 512 one did, because both give 1,024 tokens. And attention stays under a quarter of each call even for SD3, so at 1024 by 1024 the linear layers still dominate.
The widget puts the whole budget on one screen. A preset sets the model's width and depth. You then set the resolution, the patch size , the autoencoder, the steps, and whether guidance is on. Each autoencoder button is a real one, so and change together. The widget shows the tokens, the denoiser's FLOPs split into linear layers and attention, the decoder's share, and a rough time on one H100.
In practice these counts decide the design. Before any training run, a team writes down for its target resolution and checks it against . An interviewer will ask you to do the same on a whiteboard.
Implement latent_budget(H, W, f, c, p), which returns the tuple (h, w, T, n_latent, ratio).
h and w are the latent height and width.T is the number of image tokens that a denoiser with patch size p sees.n_latent is the number of latent numbers, .ratio is the compression ratio , as a float.Raise ValueError if or is not divisible by . Return Python integers for the counts, even when the inputs are NumPy integers.
The decoder is the last thing every generated image passes through. If it cannot draw a face as fine as the one in a training photo, no generator upstream can produce that face either. Even a perfect generator of latents only produces the latents of real images, and decoding them gives the autoencoder's reconstructions, flaws included. This part makes that intuition exact. We need a way to measure how far apart two distributions of images are, and a way to say that the decoder does not stretch distances too much.
In plain words, pair every sample of with a sample of so that the average distance between partners is as small as possible, and is that smallest average. A push-forward is what you get by running every sample through the map. A Lipschitz map never stretches a distance by more than the factor . is the of §33.6 with distance in place of squared distance, and the sliced Wasserstein distance of §33.7 averages one-dimensional versions of it.
The theorem says the same thing for any data, decoder, and generator.
In plain words, the whole system can be no closer to the data than the decoder's range is, however good the generator. A perfect generator of latents gets you exactly as close as the autoencoder's reconstructions. Everything else the generator gets wrong is added on top, stretched by at most the decoder's Lipschitz constant.
Lower bound. Take any coupling of and . Its first coordinate always lies in , so for every pair. Taking expectations gives , and the infimum over keeps the inequality.
Upper bound. is a metric, so the triangle inequality through the distribution of reconstructions gives
For the first term, let be any coupling of and . Pushing both coordinates through gives a coupling of and with cost . Taking the infimum over gives . For the second term, the pair with is one coupling of the two distributions, with cost , and is at most the cost of any coupling. When the encoder is optimal and , the first term vanishes and the right side equals the left. ∎
Two honest caveats. FID is not , since it compares Gaussian fits to the features of an image classifier (§34.6). And a deep decoder's Lipschitz constant can be large. So the theorem explains a trend rather than giving a number to compute. The trend is real. Practitioners treat the autoencoder's rFID as a rough floor on the generation FID, and the SD3 authors write that the autoencoder's reconstruction quality upper-bounds the achievable image quality (Esser et al. 2024, Section 5.2.1). Fine text, small faces, and hands are often lost by the autoencoder before the generator ever runs, and no amount of denoiser training brings them back.
The theorem can be checked exactly on a linear autoencoder. Cut images into patches of RGB pixels, and keep the top principal components of each patch, the linear autoencoder of §32.1. Its decoder has orthonormal rows, so it never stretches distances, and . Decoding after encoding is the orthogonal projection onto the decoder's range, so the encoder is optimal, and each reconstruction error is the distance to the range. The first cell measures reconstruction error against the compression ratio. Its random images have spectra that fall off with frequency like those of natural images.
At the same ratio of 48, the , coder (relative error 0.287) beats the , coder (0.334). For a linear coder this is guaranteed. Four patches tile one patch, so the coder is one particular rank-4 linear map on patches, and principal components give the best rank-4 map. Bigger patches let a coder exploit correlations across a wider area. For learned nonlinear autoencoders the catch is training, since autoencoders with large turned out to be hard to optimize. DC-AE solved that problem, as we will see below.
The second cell checks both bounds of the theorem on samples. It computes between two equal-size samples exactly, as the cheapest one-to-one pairing, which an assignment solver finds. The latent generator is the true latents plus Gaussian noise of relative size .
With a perfect latent generator (), all three numbers agree at 6.028, the floor. As the generator gets worse, the system's distance grows, always between the two bounds. The inequalities hold exactly here, not just on average. The theorem applies to any pair of distributions, including the empirical distributions of two finite samples.
What this changes in practice is where you look first. If generated images lose fine text, compare against reconstructions of real images before touching the generator. An interviewer will ask why a better generator cannot fix a weak autoencoder, and this theorem is the answer.
A diffusion model's noise schedule mixes a clean sample and noise as , where is the signal's weight and the noise's weight at time (§33.1). Every schedule is designed for data whose coordinates have variance about 1. Pixels scaled to are roughly that size. Latents need not be. The KL penalty is too weak to pin their scale, and the raw latents of Stable Diffusion 1's autoencoder have a standard deviation of about 5.5.
Big latents look less noisy than the schedule intends. The measure of noisiness is the signal-to-noise ratio, , the variance of the signal part over the variance of the noise part for unit-variance data. Its natural log is the log-SNR . One unit of log-SNR is one nat, a factor of in SNR. Here is a tiny case first. Take a time where the schedule intends an SNR of 1, with . A latent coordinate with standard deviation 5.49 has signal variance against noise variance , so its true SNR is 30.
We need one more tool from §33.1. The time shift with parameter maps each time to . Here is a plain number, not the signal weight . It keeps and fixed, and for it moves every time in between toward noise. On the straight path of rectified flow it lowers every log-SNR by exactly . SD3 introduced it to train at high resolution.
In plain words, latents that are too big look less noisy than the schedule intends, at every step, by the same factor in SNR. For SD1's latents, , so the offset is nats, a factor of 30. For rectified flow, oversized latents act like a time shift with below 1, toward less noise.
Two different failures follow, depending on the schedule.
Rombach et al. (2022) met the first failure. On the raw latent the SNR was so high that the model committed to semantic detail early in the reverse process, and rescaling brought the SNR down (their Appendix D.1). Their rule, in Appendix G, measures one standard deviation over every entry of the first batch of latents, across all images, channels, and positions, and divides by it. For SD1's autoencoder that gives the well-known . The autoencoders in the same paper that round their latents to a codebook needed no rescaling, since their latents already have variance close to 1 (Appendix D.1).
The cell measures the offset on stand-in latents with standard deviation 5.49 under Stable Diffusion's schedule.
The measured offset is 3.41 at every step, as the theorem says. For rectified flow, the next cell checks the equivalence with a time shift. It uses the raw scale of the FLUX.1 autoencoder, .
A model trained on raw FLUX latents at is effectively trained at , and at it is effectively at . Most of its training would happen at low noise.
The widget shows both failures. The upper panel plots the log-SNR the schedule intends and the one raw latents actually get. The lower panel noises one row of latent values at six times, each strip drawn at its own scale so you see the balance of signal and noise.
Later models normalize with a shift as well as a scale, , both computed over a subset of the training data (Esser et al. 2024, Appendix B.2). The constants are part of each autoencoder's released configuration. Every pipeline must apply them in both directions, before training and before decoding.
A per-channel normalization, with one mean and one standard deviation for each latent channel, is a reasonable refinement when channels have very different spreads. The exercise implements all three variants.
Latents arrive channels last, as an array Z of shape (N, h, w, c). Implement fit_latent_norm(Z, mode), which returns (shift, scale) for three modes.
"global" is the rule of the original latent diffusion paper. The shift is 0.0, and the scale is , with the standard deviation of all entries, computed around their mean."affine" is the rule of SD3 and FLUX. The shift is the mean of all entries, and the scale is one over their standard deviation."channel" gives one mean and one scale per channel, as arrays of shape (c,).Every standard deviation here is the population one, which divides by the number of entries. Then implement normalize(Z, shift, scale), which returns , and denormalize(z0, shift, scale), its exact inverse.
A larger image needs more noise to hide its layout, and §33.1 showed why. Neighboring pixels repeat each other while the noise in them is independent. So averaging a block of pixels multiplies the SNR of the coarse structure by , and a schedule tuned at a small size leaves the layout readable at a large one. SD3 compensates with the time shift defined above, with for an image of pixels and a base resolution of pixels, which lowers every log-SNR by . Esser et al. (2024) derived the formula from a constant image. At 1024 by 1024 they then chose by a human preference study, where the formula from their 256 by 256 base says 4.
Two things change when the data is a latent. The first is harmless. With and fixed, the latent has positions, so the ratio is the same whether you count pixels, latent positions, or tokens, and the formula needs no translation.
The second matters when the autoencoder itself changes. The constant-image argument counts independent noisy observations of the same signal, and in a latent every channel of every position is one such observation. So when or changes between the base model and the target, the count that matters is the number of latent numbers. Zheng et al. (2025) call it the effective data dimension, the number of tokens times their dimension,
In plain words, count every number in the latent, not the tokens, and take the square root of the ratio. The patch size cancels, because cutting a grid into patches regroups its numbers without changing how many noisy observations of the image there are.
The constant-image argument assumes the observations are independent copies of one value. Latent channels are not. They are correlated, and a learned autoencoder may spread one feature over several of them. So treat as the right first guess, and tune the final value as SD3 did. An interviewer who asks how you would set the noise schedule for a new autoencoder wants exactly this count, and the caveat.
Describe a configuration by the tuple (H, W, f, c, p). Implement three functions.
effective_dim(cfg) returns the number of tokens times the numbers per token.alpha_for(target, base) returns the shift .shifted_times(t, target, base) applies elementwise to an array of times.Every large image model trains in stages of increasing resolution. Most of what a model learns, such as objects, composition, and how text maps to content, is learned as well at 256 by 256 as at 1024, with a sixteenth of the tokens. Stable Diffusion 1 trained 237,000 steps at 256 before moving to 512 (its model card). SDXL pretrained 600,000 steps at 256 with batch size 2,048, then 200,000 at 512, then fine-tuned on several aspect ratios of about one megapixel each (Podell et al. 2023, Section 2.5). SD3 pretrained at 256, fine-tuned at 1024 with mixed aspect ratios, and switched on the time shift for the high-resolution stage.
Aspect ratios need care, because every image in a batch must have the same shape. SDXL sorts the data into buckets of different aspect ratios, each with close to pixels and sides in multiples of 64. Every batch comes from one bucket, and the model is told the target size as a conditioning input (Podell et al. 2023, Section 2.3; §34.3 covers size and crop conditioning). Sides in multiples of 64 keep every latent side a multiple of 8, so the network's own downsampling stages divide evenly.
SD3 builds its grid of 2D positions for the largest height and width it will see, and crops from it for each bucket (Esser et al. 2024, Section 5.3.2). A transformer can instead pack images of different shapes into one sequence, with a mask that lets each image's tokens attend only to each other (Dehghani et al. 2023). That is the packing of §31.5.
Every Stable Diffusion-style pipeline has the same five parts.
The image encoder is not used at all to generate from text. It is needed only for training and for editing a given image.
The two cells below run all five parts on a toy problem small enough to read every number. The images are 32 by 32 and show a red or blue square on the left or right, with four captions. The autoencoder is block averaging, as at the start of the section, with , so each image becomes a latent of 48 numbers. The text encoder is a table with one random vector per word. The toy denoiser cannot read vectors, so the cell picks the training caption whose mean word vector is closest to the prompt's and conditions on it.
The denoiser is not a trained network. It is the exact best velocity for the stored training latents, the one a perfect network would learn, , computed as in §33.6. It weighs every stored latent by how likely it is to have produced the noisy point, and those weights are a softmax, the soft lookup of §4.1.
Follow the numbers. The prompt becomes 6 tokens and a context. The image's 3,072 numbers become a latent of 48, scaled by . At the model knows nothing and gives each caption a belief of 0.25. Its guided guess of the clean latent is far off, 151 percent, because a guidance scale of 3 overshoots by extrapolating three times the difference between the two guesses. One step later, at , the model is 94 percent sure, and its guess is within 20 percent of the final latent. From then on it only refines details. Eight steps cost 16 evaluations and one decode, and the image is a blue square on the left, as asked.
The layout is decided at the noisiest steps. That is why the time shifts above move training toward high noise, where the decisions are made.
The widget runs the same toy pipeline in your browser, with its own random draws, so its numbers differ a little from the cell's. Press Play to walk through the five parts in turn, or click a part to jump to it. During the steps, the bars show how sure the unguided model is of each caption.
A real system replaces each toy part with a large network. The text tokens enter the denoiser through cross-attention or joint attention, whose maps show which image regions each word steers (§34.3). And the denoiser must generalize. The exact velocity of a finite training set can only ever return one of its stored latents, which is memorization (§34.6 measures it in real models).
The other systems differ in what each part is, and the arithmetic above tells you what each change costs.
| system | year | denoiser | latent | text encoder | sampler |
|---|---|---|---|---|---|
| SD 1.5 | 2022 | 860M U-Net | , | CLIP ViT-L text half, about 123M | noise prediction, typically 50 guided steps |
| SDXL | 2023 | 2.6B U-Net | , | two CLIP text encoders, 817M | noise prediction, plus an optional refiner |
| SD3 | 2024 | up to 8B joint-attention transformer | , | two CLIP encoders and T5-XXL | rectified flow |
| FLUX.1 | 2024 | 12B transformer | , | CLIP and T5-XXL | rectified flow, guidance-distilled [dev] |
| FLUX.2 | 2025 | 32B open-weight [dev] | , , a new autoencoder | a 24B vision-language model | rectified flow |
The pattern is visible in the table. The denoiser grew from 0.9B to 32B parameters. The autoencoder stayed at and grew from 4 channels to 16, then 32. The text encoder grew from CLIP's text half to a 24B vision-language model, and §34.3 explains why.
Here is the whole pipeline in PyTorch, small enough to train on a laptop CPU. A frozen linear autoencoder (, , principal components of patches) compresses 32 by 32 images of horizontal or vertical stripes. The latents are encoded once and normalized with one global shift and scale. A small convolutional velocity network is trained with logit-normal times and 10 percent condition dropout. Sampling uses Euler steps with batched guidance, then one decode.
With a production library the same steps are a few lines, and the normalization is the line people get wrong. In the diffusers library the autoencoder's constants live in its configuration.
A diffusion transformer can reduce its tokens in two places. The autoencoder can downsample more, with a larger , or the transformer can use larger patches, with a larger . Both give . DiT's own sweep (Peebles and Xie 2023) showed that larger patches, and so fewer tokens, cost quality. Chen et al. (2024) asked the cleaner question of where to downsample at a fixed token count.
The obstacle was the autoencoder. Making Stable Diffusion's autoencoder downsample further in the standard way degraded its rFID on ImageNet 256 by 256 from 0.90 at to 28.3 at . DC-AE changed how such autoencoders are built and trained, and reached to with good reconstruction. On ImageNet 512 by 512, the test was the U-ViT-H diffusion transformer (Bao et al. 2022). Against the standard autoencoder, the DC-AE made it 19.1 times faster at inference and 17.9 times faster to train, while FID with guidance improved from 3.55 to 3.01 (Chen et al. 2024, Table 3). Without guidance it was slightly worse, 13.96 against 11.04.
The arithmetic of this section predicts most of that speedup. The token count falls from , a latent cut into patches, to , 16 times fewer. The attention term falls even faster, 256 times, since it scales with . That is how the speedup can exceed 16.
Why not add channels and keep ? Because reconstruction and generation pull in different directions, the trade-off that §32.6 measured. In SD3, 16 channels reconstructed far better than 4 or 8, with rFID 1.06 against 2.41 and 1.56. But a generator trained on them needed more capacity. Its gap to 8 channels shrank as the model grew and became negligible at depth 22 (Esser et al. 2024, Figure 10). A richer latent holds more fine detail, which is close to noise and hard to predict.
Latent diffusion is not the only way to make large images. Two other designs compete with it.
A cascade generates a small image and then enlarges it with further diffusion models, each conditioned on the output of the last. Imagen (Saharia et al. 2022) runs a 2B-parameter model at 64 by 64, then super-resolution models of 600M and 400M parameters up to 256 and 1024. The trap is that errors pile up. Each upsampler is trained on real low-resolution images and then fed generated ones, with their small defects. The cascaded diffusion models (CDM) of Ho et al. (2022) fixed this with conditioning augmentation. They corrupt the low-resolution input with Gaussian noise or blur during training, so the upsampler learns to ignore small defects. Imagen's version is called noise conditioning augmentation. Their cascade reached ImageNet FIDs of 1.48 at 64, 3.52 at 128, and 4.88 at 256, with no classifier help.
A pixel-space model runs one diffusion model on the full image. Simple diffusion (Hoogeboom et al. 2023) gets there with a noise schedule shifted for resolution, a first layer that downsamples, and scaling up only the low-resolution part of the U-Net. Hourglass diffusion transformers (HDiT, Crowson et al. 2024) use a hierarchical transformer whose cost grows linearly with the number of pixels, and train directly at 1024 by 1024. Plain transformers with large pixel patches, trained to predict clean images, compete at 256 and 512 (JiT, Li and He 2025, recapped in §32.6).
| Latent diffusion | Cascade | Pixel space | |
|---|---|---|---|
| What the main model sees | a frozen autoencoder's latent, 12 to 96 times fewer numbers | a small image, then upsampling stages | every pixel |
| Quality ceiling | the autoencoder's reconstructions (the floor theorem) | the upsamplers, with errors compounding across stages | none from compression |
| Cost per step at 1024 by 1024 | lowest, and the decoder runs once | low at the base, high in the last stage | highest unless the architecture downsamples internally |
| Stages to train | two (autoencoder, then generator) | one per resolution | one |
| Editing and reuse | latents are shared by many models and tools | each stage separately | direct |
| Examples | SD 1 to 3, SDXL, FLUX, PixArt, most video models | Imagen, DALL-E 2's decoder, CDM | ADM, simple diffusion, HDiT, JiT |
Practitioners largely chose latents, for the low cost per step, the single model, and the shared tools. The open question in 2026 is whether latents aligned with semantic features, or pixel transformers, will narrow the gap between reconstruction and generation. Whether to compress at all is settled. The same two-stage pattern returns twice more in the book. Video models compress time as well as space with causal 3D autoencoders (§34.5). Speech and music generators run on the tokens of neural audio codecs, which play the autoencoder's role for sound.
The decoder runs once, but it runs at full resolution, and memory becomes the limit. The last stage of SD's decoder holds 128 channels at the output resolution. In 16-bit floats that is 268 MB per activation at 1024 by 1024, and 4.3 GB at 4096 by 4096. Its middle block has a self-attention layer over the latent positions. Stored whole, its score matrix would take 0.54 GB at 1024 by 1024 and 137 GB at 4096 by 4096. So large images are decoded in tiles. The naive way leaves visible seams. Each tile's convolutions see zero padding at the tile's border, where the full decoder would have seen the neighboring latents.
The fix rests on one fact about locality. Suppose the decoder's output at a latent position depends only on latents within a radius , its receptive field.
A real decoder is not strictly local, since its attention layer and its group-norm statistics see the whole tile. So production tiling is approximate, and the blending ramps hide what is left.
The setup defines a local decoder decode(z) and a helper tile_starts(n, tile, ov). The decoder maps a latent tile of shape (a, b) to an output of shape (F a, F b) with F = 2. Its output at a latent position depends only on latents within radius R = 3, and it zero-pads at the edges of whatever it is given. The helper returns the start offsets of tiles of side tile, overlapping by at least ov, that cover range(n).
blend_weights(n, starts, tile, r, ov). It returns an array of shape (len(starts), n) whose row is the weight of tile at each latent position. The weight is zero outside the tile. A cut edge is a tile edge that is not the start or end of the whole range. Near a cut edge the weight is zero for the first r positions, then rises linearly and reaches 1 after ov - 2r more positions. Precisely, with , start , and end , the raw weight at position inside the tile is for a cut on the left and for a cut on the right. Clip it below at 0, and take the product of the two when both edges are cuts. Finally, normalize each column to sum to 1.tiled_decode(z, tile, ov), which decodes a latent z of shape (h, w) tile by tile, never passing anything larger than tile by tile to decode. Blend the tile outputs with the outer product of the row and column weights, each repeated F times to reach output resolution. With ov at least 2 R, the result must equal decode(z) exactly.