Diffusion Models: как работают Stable Diffusion и DALL-E

opensourceaimldeep-learningit
← Back to Blog

Введение: от GAN к Diffusion

До 2022 года генерацией изображений правили GAN (Generative Adversarial Networks):

GAN архитектура:
  Generator: noise → изображение
  Discriminator: изображение → real/fake
  
  Generator и Discriminator соревнуются:
    Generator пытается обмануть Discriminator
    Discriminator пытается поймать Generator
  
  Проблема: нестабильное обучение, mode collapse

Diffusion Models (2015-2020) — новый подход, который оказался стабильнее и качественнее:

Diffusion Model:
  Forward: изображение → шум за T шагов
  Reverse: шум → изображение за T шагов
  
  Обучение: предсказывать шум на каждом шаге
  Стабильнее GAN, нет проблем с mode collapse

Ключевые статьи:


Forward process: добавление шума

Как работает diffusion

Forward process (детерминированный):
  x₀ = исходное изображение
  xₜ = √ᾱₜ × x₀ + √(1-ᾱₜ) × ε,    ε ~ N(0, I)
  
  где ᾱₜ = ∏ᵢ₌₁ᵗ (1 - βᵢ)
  βᵢ — schedule noise (от 0.0001 до 0.02)

T = 1000 шагов:
  x₀  →  x₁  →  x₂  →  ...  →  x₉₉₉  →  x₁₀₀₀
  
  x₁₀₀₀ ≈ N(0, I)  — чистый Gaussian noise

Визуализация

Forward diffusion (1000 шагов):

Шаг 0:   🖼️  [чёткое изображение]
Шаг 10:  🖼️  [лёгкий шум]
Шаг 100: 🖼️  [умеренный шум]
Шаг 500: 🖼️  [сильный шум]
Шаг 1000: 📺  [белый шум, N(0, I)]

Noise schedule

# Noise schedule (linear)
T = 1000
beta = torch.linspace(0.0001, 0.02, T)
alpha = 1 - beta
alpha_hat = torch.cumprod(alpha)  # кумулятивный продукт

# Для шага t=500:
# alpha_hat[500] ≈ 0.5
# x_500 = sqrt(0.5) * x_0 + sqrt(0.5) * noise

Reverse process: удаление шума

Что обучает модель

Reverse process (генерация):
  p_θ(x_{t-1} | x_t) = N(x_{t-1}; μ_θ(x_t, t), σ_t²)
  
  Модель U-Net предсказывает:
    1. Mean: μ_θ(x_t, t)
    2. Variance: σ_t² (часто фиксировано)
  
  Или модель предсказывает noise ε_θ:
    ε_θ(x_t, t) → предсказание шума
    x_{t-1} = (x_t - (1-α_t) × ε_θ) / sqrt(α_t) + σ_t × z

U-Net архитектура

U-Net для diffusion:

              ┌──────────┐
              │  x_t     │  ← зашумлённое изображение
              │  (t, 64) │
              └────┬─────┘
                   │
              ┌────▼─────┐
              │  Time    │  ← timestep embedding
              │ embed    │     t → sin/cos embedding
              └────┬─────┘
                   │
        ┌──────────▼──────────┐
        │     Encoder         │
        │  (downsampling)     │
        │  64 → 128 → 256     │
        └──────────┬──────────┘
                   │
        ┌──────────▼──────────┐
        │     Bottleneck      │
        │  (256 channels)     │
        └──────────┬──────────┘
                   │
        ┌──────────▼──────────┐
        │     Decoder         │
        │  (upsampling)       │
        │  256 → 128 → 64     │
        └──────────┬──────────┘
                   │
              ┌────▼─────┐
              │  ε_θ     │  ← предсказанный шум
              └──────────┘

Training loss

# Simple MSE loss на предсказание шума
def diffusion_loss(model, x_0, t):
    """
    x_0: исходное изображение
    t: timestep (0..T-1)
    """
    noise = torch.randn_like(x_0)
    
    # Add noise (reparameterization)
    x_t = sqrt_alpha_hat[:, t] * x_0 + sqrt_one_minus_alpha_hat[:, t] * noise
    
    # Predict noise
    predicted_noise = model(x_t, t)
    
    # MSE loss
    loss = MSE(noise, predicted_noise)
    return loss

Latent Diffusion (Stable Diffusion)

Проблема: pixel-space diffusion медленный

Pixel-space DDPM:
  Изображение: 512 × 512 × 3 = 786,432 пикселей
  U-Net обрабатывает 786K значений × 1000 шагов
  
  Для 1024×1024: 4× больше памяти и времени!

Решение: Latent Diffusion

VAE (Variational Autoencoder):
  Encoder: изображение → latent space (8× меньше)
  Decoder: latent → изображение
  
  512 × 512 × 3 → 64 × 64 × 4 = 16,384 latent dims
  1024 × 1024 × 3 → 128 × 128 × 4 = 65,536 latent dims

Latent Diffusion:
  Diffusion происходит в latent space
  U-Net обрабатывает 64×64 вместо 512×512
  = 64x быстрее по памяти, ~8x быстрее по времени

Stable Diffusion архитектура

Stable Diffusion v1.4 (512×512):

  VAE Encoder:
    512 × 512 × 3 → 4 × 64 × 64 (latent)
  
  Latent Diffusion:
    U-Net в latent space
    Cross-attention с text embeddings
  
  VAE Decoder:
    4 × 64 × 64 → 512 × 512 × 3

Text-to-Image: CLIP и Cross-Attention

Как текст управляет генерацией

Text-to-Image pipeline:

  Text → CLIP Text Encoder → text embeddings (77 × 768)
  
  U-Net:
    Input: latent + timestep
    Cross-Attention:
      Q = from U-Net features
      K, V = from text embeddings
    
    Attention(Q, K, V) = softmax(Q @ K^T / sqrt(d)) @ V
    
    → U-Net "смотрит" на текст на каждом шаге

Визуализация cross-attention

Для каждого шага reverse diffusion:

  Текст: "a cat sitting on a windowsill"
  
  Cross-attention позволяет U-Net:
    - Знать, ЧТО рисовать (cat)
    - Знать, ГДЕ рисовать (windowsill)
    - Знать, КАК рисовать (sitting)

Генерация: пошаговый процесс

Полный pipeline

# Генерация изображения (упрощённо)
def generate_image(prompt, steps=50, cfg=7.5):
    # 1. Encode text
    text_emb = clip_encoder(prompt)
    
    # 2. Start from random noise
    x_T = torch.randn(1, 4, 64, 64)  # latent space
    
    # 3. Reverse diffusion
    for t in reversed(range(T)):
        # Predict noise
        noise_pred = unet(x_t, t, text_emb)
        
        # Classifier-free guidance
        noise_uncond = unet(x_t, t, None)
        noise = noise_uncond + cfg * (noise_pred - noise_uncond)
        
        # Step back
        x_t = step_back(x_t, noise, t)
    
    # 4. Decode latent to image
    image = vae_decoder(x_0)
    
    return image

Classifier-Free Guidance

CFG — управление стилем/точностью:

  cfg = 1.0  → только текст (менее точный)
  cfg = 7.5  → баланс (стандарт Stable Diffusion)
  cfg = 15.0 → строго по тексту (может быть артефакты)

  noise = noise_unconditional + cfg × (
    noise_conditional - noise_unconditional
  )

Steps и качество

Steps → качество:
  20 steps:  быстрая генерация, среднее качество
  50 steps:  стандарт, хорошее качество
  100 steps: максимальное качество
  
  Для 512×512 на A100:
    20 steps: ~2 секунды
    50 steps: ~5 секунд
    100 steps: ~10 секунд

Ускорение генерации

DPM-Solver и другие samplers

Обычный Euler sampler:
  x_{t-1} = x_t - β_t × ε_θ
  
  Требует 50-100 шагов для хорошего качества

DPM-Solver (2022):
  Более умная дискретизация ODE
  
  10-20 шагов = 50 шагов Euler
  
  На 3-5x быстрее при том же качестве

Latent Consistency Models (LCM)

LCM — distilled LCM:
  Обучается на 1-4 шага
  
  LCM-SDXL:
    4 steps = качество 50 steps SDXL
    На диск 20x быстрее Stable Diffusion
    
  LCM-Lora:
    Допил для существующих моделей
    4 steps за ~1 секунду на RTX 4090

TensorRT-LLM и оптимизации

Оптимизации для inference:
  1. FP16 / FP8 квантизация
  2. TensorRT / ONNX Runtime
  3. Kernel fusion (UNet + VAE)
  4. Batch processing
  
  RTX 4090:
    Обычный:  8 секунд на изображение
    Оптимиз:  1.5 секунды на изображение

ControlNet и IP-Adapter

ControlNet: контроль позы/линий

ControlNet — добавляет контроль:
  - Pose (поза человека)
  - Canny (края изображения)
  - Depth (глубина)
  - Scribble (набросок)
  
  Работает так:
    Копия U-Net заморажена
    Обучается только "zero convolution" слой
    Не портит оригинальную модель

IP-Adapter: контроль по изображению

IP-Adapter — по аналогии с CLIP:
  Input: reference image → image embedding
  Cross-attention с image embedding
  
  Результат:
    Сохраняет стиль/reference image
    Не требует дообучения

Open Source экосистема

Stable Diffusion модели

Stable Diffusion v1.5:
  512×512, 860M параметров
  Лучшая для community моделей
  
  Checkpoints:
    DreamShaper — универсальная
    RealisticVision — фотореализм
    DreamLike — арт
  
  LoRA:
    Hundreds of community LoRA
  
  ControlNet:
    10+ control types

SDXL

SDXL (Stable Diffusion XL):
  1024×1024
  2.6B параметров
  Better composition, text
  
  Refiner model:
    Base: 1024×1024 (rough)
    Refiner: 1024×1024 (details)
  
  Turbo: 1 step generation

ComfyUI и Automatic1111

Automatic1111:
  Web UI для SD
  Простой интерфейс
  Extensions: ADetailer, ControlNet
  
ComfyUI:
  Node-based UI
  Более гибкий
  Better performance
  Custom workflows

Diffusion для не-изображений

Audio diffusion

Audio Diffusion:
  Audio → spectrogram → 2D "image"
  Diffusion на spectrogram
  Griffin-Lim для обратного преобразования
  
  Примеры:
    AudioLDM — text-to-audio
    MusicLDM — text-to-music

Video diffusion

Video Diffusion:
  3D U-Net (time + spatial)
  Space-time attention
  
  Примеры:
    Stable Video Diffusion
    ModelScope Text-to-Video
    AnimateDiff (animation from text)

3D diffusion

3D Diffusion:
  Point clouds / voxels / meshes
  Генерация 3D объектов
  
  Примеры:
    DreamFusion — text-to-3D
    Zero123 — image-to-3D

Сравнение: GAN vs Diffusion

GAN (StyleGAN3):
  + Быстрая генерация (1 проход)
  + Хорошие результаты
  - Нестабильное обучение
  - Mode collapse
  - Трудно контролировать

Diffusion (Stable Diffusion):
  + Стабильное обучение
  + Хороший контроль (text, pose, etc.)
  + Better diversity
  - Медленная генерация (1000 шагов)
  - Больше памяти

Итоги

Diffusion models — state-of-the-art для:

  • Text-to-image (Stable Diffusion, DALL-E)
  • Image editing (inpainting, outpainting)
  • Super-resolution (ESRGAN + diffusion)
  • 3D generation (DreamFusion)
  • Video generation (Sora, SVD)
Рекомендации:
  1. Для генерации изображений — Stable Diffusion
  2. Для контроля — ControlNet + IP-Adapter
  3. Для скорости — LCM или DPM-Solver
  4. Для качества — SDXL + Refiner

Ссылки