Latent diffusion och text-till-bild
Kunna förklara hur textstyrd bildgenerering fungerar och köra en modell lokalt.
Förkunskaper
Intuition
Diffusion i ett par meningar: lär en modell att ta bort brus. Träna genom att lägga till brus i steg på riktiga bilder och låta modellen förutsäga bruset. Generera genom att börja i rent brus och ta bort lite i taget.
Latent diffusion lägger till ett avgörande steg: gör det i ett komprimerat rum i stället för i pixlar.
bild 512×512×3 ──VAE-encoder──→ latent 64×64×4 ──diffusion här──→ VAE-decoder ──→ bild
786 432 tal 16 384 tal
En faktor 48 färre tal att arbeta med. Det var det som gjorde bildgenerering möjlig på vanliga grafikkort — Stable Diffusion i stället för modeller som krävde ett datacenter.
Tre delar i en text-till-bild-modell:
| Del | Gör |
|---|---|
| Textkodare (CLIP eller T5) | text → vektorer |
| U-Net eller DiT | tar bort brus, styrd av textvektorerna via cross-attention |
| VAE | mellan pixelrum och latent rum |
Formellt
Framåtprocessen lägger till brus enligt ett schema:
Det fina är att kan beräknas direkt för valfritt — ingen iteration behövs vid träning.
Träningsmålet är förvånansvärt enkelt:
Modellen ska gissa vilket brus som lades till, givet den brusiga bilden, tidssteget och betingningen (textembeddingen). Ingen adversariell träning, ingen instabilitet — det är en av anledningarna till att diffusion slog GAN.
Classifier-free guidance är det som gör textstyrningen stark. Under träningen tas betingningen bort slumpmässigt i cirka 10 % av fallen, så att modellen lär sig både betingad och obetingad generering. Vid sampling extrapoleras:
| Effekt | |
|---|---|
| 1 | ingen styrning |
| 5–8 | typiskt — följer prompten väl |
| > 15 | övermättade färger, artefakter, tappad variation |
Kostnaden är att varje steg kräver två modellanrop.
Samplers avgör hur många steg som behövs:
| Sampler | Steg | Kommentar |
|---|---|---|
| DDPM | 1 000 | originalet, opraktiskt |
| DDIM | 20–50 | deterministisk, reproducerbar |
| DPM-Solver++ | 15–25 | snabbast för god kvalitet |
| Konsistensmodeller, LCM | 1–4 | destillerade, lägre kvalitet |
Styrning utöver text:
| Metod | Styr med |
|---|---|
| ControlNet | kantbild, djupkarta, pose |
| Inpainting | mask — generera bara i ett område |
| img2img | startbild plus brusstyrka |
| LoRA | inlärd stil eller karaktär |
| IP-Adapter | referensbild för stil |
Det juridiska och etiska hör ihop med tekniken: träningsdatans upphovsrätt, memorering av enskilda bilder, och märkning av syntetiskt innehåll enligt AI-förordningens artikel 50. Kör en memoreringskontroll innan du publicerar.
Kod
import torch
from diffusers import StableDiffusionXLPipeline, DPMSolverMultistepScheduler
pipe = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16, variant="fp16").to("cuda")
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
pipe.enable_attention_slicing() # mindre minne, något långsammare
generator = torch.Generator("cuda").manual_seed(42) # frö → reproducerbart
bild = pipe(
prompt="ett pedagogiskt diagram över en neuronnätsarkitektur, ren linjekonst, vit bakgrund",
negative_prompt="suddig, text, vattenstämpel, lågupplöst",
num_inference_steps=25,
guidance_scale=7.0,
generator=generator,
).images[0]
bild.save("diagram.png")
# Guidance scale: mät effekten i stället för att gissa
for w in (1.0, 3.0, 7.0, 15.0, 25.0):
b = pipe(prompt="en röd kub på ett blått bord", guidance_scale=w,
num_inference_steps=25,
generator=torch.Generator("cuda").manual_seed(0)).images[0]
b.save(f"cfg_{w}.png")
# w=1: ignorerar prompten · w=7: följer den · w=25: övermättat och sönderskuret
# Steg mot kvalitet och tid
import time
for steg in (10, 20, 25, 50):
t0 = time.perf_counter()
pipe(prompt="en katt", num_inference_steps=steg,
generator=torch.Generator("cuda").manual_seed(0))
print(f" {steg:>2} steg: {time.perf_counter() - t0:.1f} s")
# ↑ över ~25 steg är förbättringen marginell med DPM-Solver++
# Minneskrav
def minne_gb(fp16=True):
delar = {"U-Net": 2.6, "textkodare": 0.8, "VAE": 0.2} # SDXL, ungefärligt
faktor = 1.0 if fp16 else 2.0
return {k: round(v * faktor, 2) for k, v in delar.items()} | {
"summa": round(sum(delar.values()) * faktor, 2)}
print(minne_gb()) # {'U-Net': 2.6, ..., 'summa': 3.6}
# Memoreringskontroll före publicering
def liknar_traningsdata(bild, index, clip_modell, troskel=0.95):
v = clip_modell.encode_image(bild)
v = v / v.norm()
traff = index.search(v, k=5)
return [t for t in traff if t["likhet"] > troskel]
# Icke-tom lista → generera om; bilden ligger för nära ett träningsexempel.
Behärskning innebär
- Förklarar diffusion i latent rum
- Beskriver hur textstyrningen fungerar
- Väljer sampler och steg medvetet
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- arXiv — High-Resolution Image Synthesis with Latent Diffusion Models — arXiv (öppen åtkomst; licens per artikel)
- arXiv — Classifier-Free Diffusion Guidance — arXiv (öppen åtkomst; licens per artikel)
- diffusers — dokumentation (Apache-2.0) — Apache-2.0