GANs, Diffusion and Flow Compared
GAN, DDPM, latent diffusion, DiT with rectified flow, flow matching and VAR. Targets, steps, code and fixes.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Comparisons
- Troubleshooting
- Questions and tips
Sampling cost side by side#
| Family | Passes | Note |
|---|---|---|
| GAN | one forward pass | Fast. Hard to train |
| DDPM | 1000 | Linear beta schedule |
| DDIM | 50 | Deterministic. Same training as DDPM |
| DPM-Solver++ 2M Karras | 20-30 | SD production default |
| Rectified flow (Euler) | 20-30 | SD3, FLUX, Z-Image |
| Schnell, Turbo, LCM | 1-4 | Distilled from a many-step model |
| VAR, K = 10 scales | 10 passes | DiT needs 28-50 at 256x256 |
Training target side by side#
| Family | Target | Loss |
|---|---|---|
| GAN discriminator | P(real) | Binary cross-entropy on real and fake |
| GAN generator | D(G(z)) | High is good. Non-saturating form |
| DDPM | epsilon_theta(x_t, | MSE on the true noise |
| Rectified flow (4.23) | epsilon - x0 | MSE on the velocity |
| Flow matching (8.13) | x1 - x0 | Here x1 is noise, x0 is data |
| VAR | Cross-entropy | On the VQ codes of the next scale |
Use X when#
- GAN real-time generation under 10 ms, style transfer, Pix2Pix or CycleGAN translation.
- SD 1.5 latent diffusion open-source community fine-tunes with LoRA.
- DiT + rectified flow state of the art text to image. FLUX.1-schnell is the open-source default.
- Distilled flow (LCM-LoRA, schnell) latency under 1s, 1-4 steps.
- Flow matching audio, video and science as well as images.
- VAR class-conditional ImageNet. Fewer passes, GPT-style scaling.
Stable Diffusion pipeline#
Tasks in code#
| Task | Code |
|---|---|
| GAN with spectral norm | build_sn_discriminator() |
| Linear beta schedule | linear_beta_schedule(T= |
| DDPM sampling | sample_ddpm(model, |
| DDIM sampling | sample_ddim(..., |
| Rectified flow step | rectified_flow_train_step(model, |
| Euler sampling | rectified_flow_sample(model, |
| Multi-scale VQ tokens | tokenize_multiscale(img, |
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| Sample variety drops | Mode collapse | Spectral norm or label conditioning |
| D accuracy ~100% | D wins, G gradients vanish | Smaller D, lower D learning rate |
| Both losses swing, no trend | G and D trade wins | TTUR: D learns 2-4x faster |
| Samples depend on the batch | BN uses batch stats | Call G.eval() before sampling |
| Heavy artefacts | Guidance scale too high | SD 7.5. Rectified flow 3.5-5 |
diffusers calls#
| Task | Code |
|---|---|
| SD text to image | guidance_scale= |
| Swap the scheduler | DPMSolverMultistepScheduler.from_config(...) |
| Image to image | img2img(..., |
| Inpaint the white area | inpaint(..., |
| Fuse a LoRA | pipe.fuse_lora(lora_scale= |
| FLUX.1-schnell | guidance_scale= |
| SD 3.5 Large | guidance_scale= |
DDPM against rectified flow#
t = torch.randint(0, T, (bs,), device=device)DDPM: integer stepx_t = q_sample(x0, t, noise, schedule)closed-form jumploss = F.mse_loss(pred, noise)predict the noiset = torch.rand(n, device=device)RF: t in [0, 1]x_t = (1 - t[:, None, None, None]) * x0 + t[:, None, None, None] * epsilontarget_v = epsilon - x0velocity, data to noiseloss = F.mse_loss(pred_v, target_v)dt = 1.0 / stepsEuler, steps=20for _ in range(steps): v = model(x, t) x = x - dt * vwalk from noise to data t = t - dtOne GAN training step#
opt_d.zero_grad()d_real = D(real)d_fake = D(G(z).detach())no gradient into Gloss_d = (F.binary_cross_entropy_with_logits(d_real, torch.ones_like(d_real)) + F.binary_cross_entropy_with_logits(d_fake, torch.zeros_like(d_fake)))loss_d.backward()opt_d.step()opt_g.zero_grad()d_fake = D(G(z))loss_g = F.binary_cross_entropy_with_logits(d_fake, torch.ones_like(d_fake))non-saturatingloss_g.backward()opt_g.step()opt_g = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.999))DCGAN defaultKey formulas#
- GAN game
- \min_G \max_D \; \mathbb{E}_x[\log D(x)] + \mathbb{E}_z[\log(1 - D(G(z)))]
- DDPM jump
- x_t = \sqrt{\bar{\alpha}_t} \, x_0 + \sqrt{1 - \bar{\alpha}_t} \, \epsilon, with \bar{\alpha}_t = \prod_{s=1}^{t} \alpha_s
- CFG
- \epsilon = \epsilon_{\text{uncond}} + w \cdot (\epsilon_{\text{cond}} - \epsilon_{\text{uncond}})
- Rectified flow
- x_t = (1 - t) \, x_0 + t \, \epsilon for t \in [0, 1]
- CFG with velocity
- v_{\text{cfg}} = (1+w) v_{\text{cond}} - w v_{\text{uncond}}
- Latent saving
- (3512512)/(46464) = 48x less compute.
VAR next-scale generation#
Questions#
- Why is diffusion easy to train?
- The loss is MSE. There is no adversarial game.
- Why does rectified flow need fewer steps?
- The path is close to a straight line.
- Why is VAR fast?
- One pass makes all tokens of a scale. Length grows with log scale.
- Why does FLUX.1-schnell use guidance 0.0?
- It was trained without CFG.