Advanced Techniques & Workflows
AI/ML Documentation » Advanced Techniques
This page is a reference for image-generation techniques that go beyond choosing a prompt, sampler, and step count. It covers latent-space manipulation, regional control, guidance methods that improve on plain classifier-free guidance, few-step distillation and flow matching, multi-stage pipelines, and the kernels, caches, and quantization formats that make large models practical on consumer GPUs. It assumes familiarity with the material in Stable Diffusion Fundamentals and basic ComfyUI workflows.
Most of these techniques are consumed rather than implemented: a LoRA, a distilled checkpoint, a node, or a launch flag. Knowing what each one changes explains its trade-offs and which combinations are compatible.
Where the Cost of an Image Goes
The cost of an image is roughly the product of three factors. Each technique family on this page attacks one of them.
flowchart LR
C["Cost of one image"] --> S["Number of steps"]
C --> P["Forward passes per step"]
C --> F["Cost of one pass"]
S --> S1["Distillation: LCM, Lightning,<br/>DMD2, Turbo, schnell, klein"]
S --> S2["Better solvers and schedules;<br/>straighter flow-matching paths"]
P --> P1["Guidance distillation<br/>(CFG 1: one pass instead of two)"]
F --> F1["Attention kernels:<br/>SDPA, FlashAttention, SageAttention"]
F --> F2["Quantization: fp8, GGUF,<br/>NVFP4, SVDQuant"]
F --> F3["Step caching: TeaCache,<br/>first-block cache"]
Quality techniques (guidance variants, detailers, multi-pass upscaling) usually add cost. The practical skill is spending that budget where it shows.
Latent-Space Techniques
The latent is a tensor, so it can be manipulated directly before or between sampling passes: blended, interpolated, or composited by region.
Linear and spherical interpolation
Linear interpolation (LERP) blends two latents with a weight $\alpha$:
\[\mathbf{x}_\alpha = (1-\alpha)\,\mathbf{x}_a + \alpha\,\mathbf{x}_b, \qquad \alpha \in [0, 1].\]For initial noise this is the wrong operation. A high-dimensional Gaussian sample lies close to a sphere of radius $\sqrt{d}$, and the midpoint of two independent samples has norm about $\sqrt{d/2}$. The sampler then receives noise with too little variance and produces flat, washed-out images. Spherical linear interpolation (SLERP) follows the arc between the two vectors and keeps the norm approximately constant:
\[\operatorname{slerp}(\mathbf{a}, \mathbf{b}; \alpha) = \frac{\sin\big((1-\alpha)\theta\big)}{\sin\theta}\,\mathbf{a} + \frac{\sin(\alpha\theta)}{\sin\theta}\,\mathbf{b}, \qquad \theta = \arccos\!\left(\frac{\mathbf{a}\cdot\mathbf{b}}{\lVert\mathbf{a}\rVert\,\lVert\mathbf{b}\rVert}\right)\]The angle must be computed over the whole latent of each sample, not per channel:
import torch
def slerp(a: torch.Tensor, b: torch.Tensor, alpha: float, eps: float = 1e-6) -> torch.Tensor:
"""Spherical interpolation between two batches of latents of shape [B, C, H, W]."""
a_flat, b_flat = a.flatten(1), b.flatten(1)
a_unit = a_flat / a_flat.norm(dim=1, keepdim=True)
b_unit = b_flat / b_flat.norm(dim=1, keepdim=True)
dot = (a_unit * b_unit).sum(dim=1).clamp(-1 + eps, 1 - eps)
theta = torch.acos(dot)
sin_theta = torch.sin(theta)
wa = (torch.sin((1 - alpha) * theta) / sin_theta).unsqueeze(1)
wb = (torch.sin(alpha * theta) / sin_theta).unsqueeze(1)
out = wa * a_flat + wb * b_flat
# Nearly parallel vectors: SLERP degenerates, fall back to LERP
lerp = (1 - alpha) * a_flat + alpha * b_flat
out = torch.where(sin_theta.abs().unsqueeze(1) < 1e-4, lerp, out)
return out.view_as(a)
Sweeping $\alpha$ while holding the prompt fixed produces a smooth morph between two seeds. Interpolating the text conditioning instead, or as well, morphs between concepts. For video-quality transitions, dedicated video models (Wan, LTX-Video, HunyuanVideo) now do far better than frame-by-frame latent walks.
Latent composition
Instead of blending globally, latents can be composited by region with masks: latent A where mask A is set, latent B where mask B is set. Seams blend more cleanly than pasted pixels because a subsequent low-denoise pass and the VAE decode harmonize the boundary. This is the latent-space counterpart of regional prompting.
Regional and Layout Control
A single prompt applies to the whole image. On CLIP-conditioned models, “a robot on the left, a forest on the right” often bleeds the two together. Two remedies exist: route different prompts to different regions, or use a model whose text encoder understands layout.
| Technique | Mechanism | Where available |
|---|---|---|
| Area / mask conditioning | Each prompt’s conditioning is restricted to a rectangle or mask; the predictions are combined per region | ComfyUI Conditioning (Set Area) and Conditioning (Set Mask); Forge Regional Prompter |
| Attention masking | Cross-attention from each region’s pixels is restricted to that region’s prompt tokens | Regional-prompting node packs; built into some FLUX and SDXL regional nodes |
| GLIGEN | Extra gated attention layers ground phrases in bounding boxes | SD 1.5 (largely superseded) |
Composable diffusion (AND) |
Each sub-prompt is denoised separately and the guided predictions are summed | A1111/Forge syntax; ComfyUI via Conditioning (Combine) |
| Prompt scheduling | The prompt changes partway through sampling ([cat:dog:0.4] switches at 40%) |
A1111/Forge syntax; ComfyUI via timestep-range conditioning nodes |
| Layout in the prompt | The model’s LLM text encoder resolves “left”, “behind”, and counts directly | FLUX.2, Qwen-Image, Z-Image, HiDream |
On current LLM-encoded models, spatial phrasing in the prompt succeeds often enough that regional conditioning is needed mainly for strict layouts, or for several characters that each need their own LoRA.
Guidance Beyond Plain CFG
Classifier-free guidance combines an unconditional (or negative-prompt) prediction $\epsilon_u$ with a conditional one $\epsilon_c$:
\[\hat{\epsilon} = \epsilon_u + w\,(\epsilon_c - \epsilon_u).\]A large $w$ improves prompt adherence but pushes the prediction outside the range the model was trained on. The result is oversaturated colors, burnt contrast, and reduced diversity. The methods below keep the benefit and reduce the damage, or recover guidance for models that cannot run true CFG.
| Method | What it does | Typical use |
|---|---|---|
| CFG rescale (Lin et al., 2023) | Rescales the guided prediction to match the standard deviation of $\epsilon_c$, then blends with factor $\phi \approx 0.7$ | High CFG on v-prediction and zero-terminal-SNR models |
| Dynamic thresholding (Imagen) | Clamps the predicted clean image to a per-step percentile of its magnitude | Very high CFG on pixel or latent models |
| Guidance interval (Kynkäänniemi et al., 2024) | Applies CFG only in the middle of the noise range and skips it at the highest and lowest noise levels | Better diversity and fewer artifacts at no extra cost |
| APG (Sadat et al., 2024) | Splits the guidance update into components parallel and orthogonal to the conditional prediction and down-weights the parallel part | Removes oversaturation at high guidance scales |
| PAG (Ahn et al., 2024) | Guides away from a prediction made with self-attention maps replaced by identity, which degrades structure | Sharper structure; works without a prompt |
| SAG (Hong et al., 2023) | Guides away from a version blurred in the regions the model attends to | Mild detail enhancement |
| NAG (Chen et al., 2025) | Applies guidance in attention-feature space with normalization, using the negative prompt | Negative prompts on few-step and guidance-distilled models that run at CFG 1 |
APG shows how simple these fixes can be. With $D_c$ the conditional denoised prediction and $\Delta = D_c - D_u$ the guidance direction:
\[\Delta_\parallel = \frac{\langle \Delta, D_c\rangle}{\lVert D_c\rVert^2}\,D_c, \qquad \Delta_\perp = \Delta - \Delta_\parallel, \qquad \hat{D} = D_c + (w-1)\left(\Delta_\perp + \eta\,\Delta_\parallel\right)\]With $\eta = 1$ this reduces to standard CFG. Setting $\eta$ near $0$ removes the component that mainly inflates saturation, while the orthogonal component that improves detail and adherence is kept.
Guidance-distilled models. FLUX.1 [dev] and similar models embed guidance strength as an input and run a single forward pass per step at cfg = 1.0. Raising CFG on them doubles the cost and usually harms the image. Negative prompts on these models need NAG, or a true CFG pass that deliberately runs the model twice at a low scale.
Samplers and Noise Schedules
-
Ancestral samplers (
euler_ancestral,dpmpp_2s_ancestral, and other_aor SDE variants) inject fresh noise at every step. They never converge to a fixed image as the step count rises, which trades reproducibility for variety and texture. Deterministic samplers (euler,dpmpp_2m,uni_pc) converge. -
Karras schedule. Places the noise levels on the curve below, with $\rho = 7$, which concentrates steps at low noise levels where fine detail forms:
\[\sigma_i = \left(\sigma_{\max}^{1/\rho} + \frac{i}{n-1}\big(\sigma_{\min}^{1/\rho} - \sigma_{\max}^{1/\rho}\big)\right)^{\rho}, \qquad i = 0, \dots, n-1.\]dpmpp_2mwithkarrasremains a strong default for SD 1.5 and SDXL. -
Timestep shift for flow models. Flow-matching models are sampled on $t \in [0,1]$ with a shift that spends more steps at high noise for larger images. It corresponds to ComfyUI’s
ModelSamplingFlux,ModelSamplingSD3, andModelSamplingAuraFlownodes. Useeulerordpmpp_2mwith thesimpleorbetascheduler, and raise the shift if large images come out incoherent. - Restart sampling (Xu et al., 2023) periodically re-adds a large amount of noise and re-integrates. This recovers some of the quality advantage of stochastic sampling at deterministic-sampler step counts.
- Best-of-N. Generating 4–8 seeds and choosing one is still the cheapest quality lever. Automatic selection with an aesthetic or preference model (for example, a VLM judge) works at scale but inherits the scorer’s biases.
Few-Step Generation
Standard diffusion needs 20–50 steps because each step moves only a short distance along a curved trajectory from noise to image. Two strategies reduce this: make the path straighter (flow matching and reflow) or train a student that takes large jumps (distillation).
flowchart LR
T["Teacher model<br/>(20-50 steps, CFG)"] --> GD["Guidance distillation<br/>CFG folded into weights"]
T --> CD["Consistency distillation<br/>LCM, TCD"]
T --> AD["Adversarial distillation<br/>ADD, LADD, Lightning"]
T --> DM["Distribution matching<br/>DMD2, Decoupled-DMD"]
GD --> S1["1 pass / step<br/>FLUX.1 dev"]
CD --> S2["4-8 steps<br/>portable LoRA"]
AD --> S3["1-4 steps<br/>SDXL-Turbo, SD3.5 Turbo, schnell"]
DM --> S4["4-8 steps<br/>SDXL DMD2, Z-Image-Turbo"]
| Method | Idea | Steps | Examples | Trade-off |
|---|---|---|---|---|
| Guidance distillation | Student reproduces the CFG output in one pass, with guidance scale as an input | Unchanged (halves passes) | FLUX.1 [dev] | Negative prompts need extra techniques |
| Consistency distillation | Student maps any point on a trajectory to its endpoint | 4–8 | LCM-LoRA, TCD | Softer detail; ships as a LoRA |
| Adversarial distillation | Discriminator loss keeps few-step outputs sharp | 1–4 | SDXL-Turbo (ADD), SD3-Turbo and SD3.5 Large Turbo (LADD), FLUX.1 [schnell] | Lower diversity; CFG must stay near 1 |
| Progressive adversarial distillation | Step count halved in stages with an adversarial loss | 2–8 | SDXL-Lightning, Hyper-SD | LoRA or full checkpoint; per-step-count variants |
| Distribution matching (DMD2 and variants) | Student’s output distribution is matched to the teacher’s through score differences, plus a GAN loss | 1–8 | SDXL DMD2, Z-Image-Turbo (Decoupled-DMD) | Among the best quality-per-step today |
| Size and step distillation | Smaller student distilled from a large flow model | 4 | FLUX.2 [klein] | Smaller model, lower ceiling |
Most of these ship both as full checkpoints and as LoRAs that retrofit an existing fine-tune. Use the sampler, scheduler, and CFG listed on the distilled model’s card. They are usually euler or LCM, few steps, and CFG between 1 and 2, and defaults tuned for a 30-step model give poor results.
Flow matching
SD3, FLUX, Qwen-Image, and Z-Image are trained with flow matching (rectified flow). With $\mathbf{x}_0$ noise and $\mathbf{x}_1$ data, the model learns a velocity field along the straight-line interpolant:
\[\mathbf{x}_t = (1-t)\,\mathbf{x}_0 + t\,\mathbf{x}_1, \qquad \mathcal{L} = \mathbb{E}_{t,\mathbf{x}_0,\mathbf{x}_1}\Big[\big\lVert \mathbf{v}_\theta(\mathbf{x}_t, t) - (\mathbf{x}_1 - \mathbf{x}_0)\big\rVert^2\Big]\]Libraries differ in the direction of $t$. Diffusers and the SD3 paper put noise at $t = 1$, so check the convention before reusing code. SD3 found that sampling $t$ from a logit-normal distribution, which concentrates training on intermediate noise levels, works better than uniform sampling.
import torch
import torch.nn.functional as F
def flow_matching_loss(model, x_data, cond):
"""Rectified-flow training loss; noise at t=0, data at t=1."""
noise = torch.randn_like(x_data)
t = torch.sigmoid(torch.randn(x_data.shape[0], device=x_data.device)) # logit-normal
t_b = t.view(-1, *([1] * (x_data.dim() - 1))) # broadcastable
x_t = (1 - t_b) * noise + t_b * x_data
return F.mse_loss(model(x_t, t, cond), x_data - noise)
@torch.no_grad()
def sample_euler(model, shape, cond, steps=28, device="cuda"):
"""Integrate dx/dt = v(x, t) from noise (t=0) to data (t=1)."""
x = torch.randn(shape, device=device)
ts = torch.linspace(0, 1, steps + 1, device=device)
for t0, t1 in zip(ts[:-1], ts[1:]):
x = x + (t1 - t0) * model(x, t0.expand(shape[0]), cond)
return x
The ideal paths are straight, but the paths the model actually learns are only close to straight. That is why flow models still use about 20–30 steps, and why reflow (retraining on the model’s own noise–image pairs) and distillation are still needed to reach 1–4 steps.
Multi-Stage Workflows
The strongest results rarely come from one pass. Multi-stage pipelines establish composition at native resolution, then add resolution and detail in controlled increments. Each later pass uses a lower denoise so that it refines the image rather than redrawing it.
flowchart LR
Base["Base pass<br/>native res, denoise 1.0"] --> Up["Upscale 1.5-2x<br/>(model or latent)"]
Up --> Ref["Refine pass<br/>denoise 0.3-0.5"]
Ref --> Det["Detailer<br/>detect face / hands"]
Det --> Crop["Crop, upscale,<br/>re-sample at 0.3-0.45"]
Crop --> Comp["Composite back<br/>with feathered mask"]
Comp --> Final["Final image"]
Progressive upscaling
| Stage | Resolution | Denoise | Purpose |
|---|---|---|---|
| Base | Native (about 1 MP) | 1.0 | Composition |
| Upscale 1 | ~1.5× | 0.4–0.5 | Structure and coherence at the new scale |
| Upscale 2 (tiled) | ~2× | 0.25–0.35 | Fine detail |
| Final (optional) | Target | 0.15–0.25 | Polish without redrawing |
Beyond about 2 MP, sample in overlapping tiles (Ultimate SD Upscale, or tiled-diffusion nodes), optionally with a Tile ControlNet so that each tile stays faithful to the low-resolution image. FLUX.2 and Qwen-Image generate natively at higher resolutions than the U-Net models, so they need fewer stages. Dedicated restoration upscalers (SUPIR, SeedVR2) are an alternative when the goal is fidelity rather than added detail.
Detailers
A detailer finds a region with a detector (a YOLO face or hand model, or a segmentation model such as SAM 2), crops it with padding, upscales the crop so the model works at its native resolution, re-samples it at low denoise, and pastes it back with a feathered mask. ComfyUI’s Impact Pack FaceDetailer and Forge’s ADetailer implement this pattern. It fixes small faces and hands far more cheaply than regenerating the whole image.
Editing models as a stage
Instruction-editing models (FLUX.1 Kontext, FLUX.2 with reference images, Qwen-Image-Edit-2511) can now replace several classic stages: changing an outfit, relighting, removing an object, or keeping a character consistent across shots. They take the image plus a text instruction and need no mask. See Inpainting and Editing for when a mask-based approach is still better. Differential diffusion generalizes the binary mask to a continuous per-pixel change strength and gives seamless, feathered edits.
Style mixing
To combine the character of several models, the options, from most to least controllable, are: one base model with stacked style LoRAs at reduced strengths; a merged checkpoint (a fixed weighted blend of models of the same architecture); and switching checkpoints between passes, for example composing with one model and refining with another at low denoise. Averaging latents across different model families does not work, because their latent spaces differ.
Performance and Memory
When resolution, batch size, or model size exceeds the GPU, there are three kinds of lever: make each forward pass cheaper, skip redundant computation, or trade speed for memory. The Optimization Guide covers the underlying numerics.
Faster forward passes
| Technique | Effect | Notes |
|---|---|---|
| PyTorch SDPA / FlashAttention | Fused, memory-efficient attention | Default in current PyTorch, ComfyUI, and diffusers; xFormers is no longer needed |
| SageAttention (2 and 3) | 8-bit (and on Blackwell GPUs, FP4) attention kernels | ComfyUI --use-sage-attention; large gains on video and high-resolution work, small quality cost |
torch.compile |
Fuses operations into optimized kernels | Warm-up compile on first run; recompiles when shapes change |
| fp8 weights | Halves memory versus bf16 with little quality loss | Native fp8 compute on Ada, Hopper, and Blackwell GPUs; ComfyUI --fast enables fp8 matrix multiplication |
| SVDQuant / Nunchaku | 4-bit weights and activations, with a low-rank branch that absorbs outliers | About 3.5× less memory than bf16 FLUX.1 with ~3× speedup over NF4 (ICLR 2025); community ComfyUI nodes for FLUX, Qwen-Image, and others |
| NVFP4 | Hardware 4-bit floating point on Blackwell (RTX 50-series, B200) | Official NVFP4 checkpoints exist for FLUX.2 [klein] and other models |
| Token merging (ToMe) | Merges redundant tokens before attention | Mainly SD 1.5 and SDXL; less useful on DiTs |
Skipping redundant computation
Adjacent sampling steps produce very similar intermediate features. Step caching methods reuse them. TeaCache (CVPR 2025) estimates from the timestep-modulated input how much the output will change and skips the transformer when the change is small. First-block caching compares the first block’s residual between steps for the same purpose. Speedups of 1.5–2× are common, and the cost is a loss of fine detail if the threshold is set too aggressively. Caching is especially valuable for video models.
Fitting in memory
-
Offloading. ComfyUI loads and unloads models automatically, and
--lowvramor--novramforce weights to stream from system RAM. diffusers offersenable_model_cpu_offload()and sequential offload. Both are slower but let 20–30B models run on 12–16 GB cards. - GGUF quantization (Q8 down to Q4 and below) through the ComfyUI-GGUF nodes. Q8 is nearly lossless; Q4 visibly degrades fine detail.
-
Tiled VAE decode. The VAE decode of a large image can use more memory than the denoiser. Use
VAE Decode (Tiled), whose peak memory scales with tile size. - Text-encoder placement. A 7–24B text encoder can run on the CPU or in fp8, or be unloaded after encoding, because it runs only once per prompt.
- Gradient checkpointing matters only for training (see LoRA Training).
Advanced ControlNet Use
These patterns extend the basics covered in the ControlNet guide.
-
Control windowing. Apply structure strongly early and release it before the final steps (
start_percentandend_percenton Apply ControlNet). Late steps then add detail that the control map never specified, which avoids the “traced” look. - Union and multi-condition models. Single ControlNets that accept several control types (for example the Union models for SDXL and FLUX) replace a stack of per-type models, which saves VRAM.
- Reference instead of control. For identity and style, reference-image conditioning (IP-Adapter, FLUX Redux, or the reference inputs of FLUX.2 and Qwen-Image-Edit) often beats structural ControlNets.
- Temporal consistency. Per-frame control maps flicker because each is estimated independently. For video, use a video model with native control inputs (Wan VACE, for example) rather than image ControlNets applied frame by frame.
Automation and Experiment Design
Once a workflow works, run it reproducibly and at scale through ComfyUI’s HTTP and WebSocket API. The ComfyUI guide shows the request format.
- Parameter sweeps. Load an API-format workflow, patch the fields to vary (seed, CFG, LoRA strength, shift), queue each variant, and store the patched values with each output. ComfyUI also embeds the full workflow in each PNG’s metadata.
- Change one variable at a time. Hold seeds fixed and vary a single axis. Lay the results out as an XY grid, so each difference has a single known cause.
- Real-time loops. Keep the model resident, cache text encodings for repeated prompts, and use a 1–4-step distilled model (FLUX.2 [klein], Z-Image-Turbo, SDXL with DMD2 or Lightning). This combination makes live preview responsive.
- Profile before optimizing. Measure wall-clock time and peak VRAM per stage, including model load, text encoding, sampling, and VAE decode. The bottleneck is often not the sampler.
See Also
- Stable Diffusion Fundamentals: diffusion, samplers, CFG, and flow matching
- ComfyUI Guide: building the workflows described here
- Base Models Comparison: choosing between SDXL, FLUX, Qwen-Image, Z-Image, and others
- Inpainting and Editing: masks, differential diffusion, and instruction editing
- ControlNet: structural control
- LoRA Training: training custom adapters
- Optimization Guide: precision, quantization, and inference speed
- Output Formats: exporting and using generated content
- AI/ML Documentation Hub