Chuyển đến nội dung chính

レッスン 4: 拡散モデル — 数学と直観

順方向プロセス: ノイズを段階的に追加します。逆のプロセス: 段階的にノイズを除去します。 DDPM: ノイズ除去拡散確率モデル。スコアベースのモデル。ノイズスケジュール。 DDPM をゼロから実践してみましょう。

🧠 AI と ML — レッスン 3 レッスン 4: 拡散モデル — 数学と方向性 感覚

生成 AI: AI を使用して画像とビデオを作成する

パート 2: 拡散モデル — 革新的なイメージの作成

xdev.asia

はじめに

拡散モデル は安定拡散、DALL-E 2/3、Midjourney の基盤であり、GAN よりも優れた画像生成品質を実現します。天才的なアイデア: 画像が完全にノイズになるまで徐々にノイズを追加し、その後、そのプロセスを逆にする方法を学びます。


1. 前方プロセス - ノイズを追加する

$$q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{1-\beta_t} x_{t-1}, \beta_t I)$$

import torch

def forward_diffusion(x_0, t, betas):
    """Thêm noise vào image tại timestep t"""
    alphas = 1 - betas
    alpha_bar = torch.cumprod(alphas, dim=0)

    sqrt_alpha_bar = torch.sqrt(alpha_bar[t])
    sqrt_one_minus = torch.sqrt(1 - alpha_bar[t])

    noise = torch.randn_like(x_0)
    x_t = sqrt_alpha_bar * x_0 + sqrt_one_minus * noise
    return x_t, noise
t=0 (clean)  →  t=250  →  t=500  →  t=750  →  t=1000 (noise)
  [clear]       [hazy]    [noisy]   [very noisy]  [pure noise]

2. 逆のプロセス — ノイズ除去 (調査)

$$p_\theta(x_{t-1} | x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \sigma_t^2 I)$$

class UNet(nn.Module):
    """Noise predictor: dự đoán noise đã thêm vào"""
    def __init__(self):
        super().__init__()
        # Simplified UNet
        self.time_embed = nn.Sequential(
            nn.Linear(1, 128),
            nn.SiLU(),
            nn.Linear(128, 128),
        )
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 64, 3, padding=1),
            nn.SiLU(),
            nn.Conv2d(64, 128, 3, stride=2, padding=1),
            nn.SiLU(),
        )
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(128 + 128, 64, 4, stride=2, padding=1),
            nn.SiLU(),
            nn.Conv2d(64, 1, 3, padding=1),
        )

    def forward(self, x, t):
        t_emb = self.time_embed(t.float().unsqueeze(-1))
        h = self.encoder(x)
        t_emb = t_emb.view(-1, 128, 1, 1).expand_as(h)
        h = torch.cat([h, t_emb], dim=1)
        return self.decoder(h)

トレーニング

def train_step(model, x_0, optimizer, betas, T=1000):
    """DDPM training step"""
    t = torch.randint(0, T, (x_0.size(0),))

    # Forward: add noise
    x_t, noise = forward_diffusion(x_0, t, betas)

    # Predict noise
    noise_pred = model(x_t, t)

    # Simple MSE loss
    loss = nn.functional.mse_loss(noise_pred, noise)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    return loss.item()

3. サンプリング — 画像の生成

@torch.no_grad()
def sample(model, shape, betas, T=1000):
    """DDPM sampling: noise → image"""
    alphas = 1 - betas
    alpha_bar = torch.cumprod(alphas, dim=0)

    # Start from pure noise
    x = torch.randn(shape)

    for t in reversed(range(T)):
        noise_pred = model(x, torch.tensor([t]))

        # Compute x_{t-1}
        alpha = alphas[t]
        alpha_bar_t = alpha_bar[t]

        mean = (1 / torch.sqrt(alpha)) * (
            x - (betas[t] / torch.sqrt(1 - alpha_bar_t)) * noise_pred
        )

        if t > 0:
            noise = torch.randn_like(x)
            x = mean + torch.sqrt(betas[t]) * noise
        else:
            x = mean

    return x  # Generated image!

4. ノイズのスケジュール設定

# Linear schedule (DDPM original)
betas = torch.linspace(1e-4, 0.02, T)

# Cosine schedule (improved, OpenAI)
def cosine_schedule(T, s=0.008):
    steps = torch.arange(T + 1)
    f = torch.cos((steps / T + s) / (1 + s) * torch.pi / 2) ** 2
    alpha_bar = f / f[0]
    betas = 1 - alpha_bar[1:] / alpha_bar[:-1]
    return torch.clamp(betas, 0.0001, 0.999)

# Cosine schedule → better quality ở low-noise timesteps

5. DDIM — より高速なサンプリング

# DDPM: 1000 steps → slow
# DDIM: 50 steps → fast, deterministic

@torch.no_grad()
def ddim_sample(model, shape, alpha_bar, steps=50):
    """DDIM: deterministic, fewer steps"""
    # Sub-sample timesteps
    timesteps = torch.linspace(999, 0, steps).long()
    x = torch.randn(shape)

    for i in range(len(timesteps) - 1):
        t = timesteps[i]
        t_prev = timesteps[i + 1]

        noise_pred = model(x, torch.tensor([t]))

        # DDIM update (deterministic, η=0)
        x0_pred = (x - torch.sqrt(1 - alpha_bar[t]) * noise_pred) / torch.sqrt(alpha_bar[t])
        x = torch.sqrt(alpha_bar[t_prev]) * x0_pred + \
            torch.sqrt(1 - alpha_bar[t_prev]) * noise_pred

    return x
方法ステップスピード品質決定論的
DDPM1000遅いベストいいえ
DDIM5020 倍高速良いはい
DPM++20-3035 倍高速すばらしいはい
オイラー20-3035 倍高速すばらしい設定可能

6. スコアベースのモデル

$$\nabla_x \log p(x) \おおよそ s_\theta(x)$$

Score function: gradient of log-probability
- Không cần biết p(x) chính xác
- Chỉ cần biết "hướng nào density tăng"
- Langevin dynamics: follow score → sample

Liên hệ: score matching ≡ denoising score matching ≡ diffusion
→ Các framework thực ra equivalent!

概要

コンセプト説明
前進プロセスガウス ノイズを段階的に追加する
逆のプロセスノイズ除去を段階的に学習する
DDPMノイズ除去拡散確率モデル
Uネットニューラル ネットワークによるノイズの予測
DDIM確定的サンプリング、少ないステップ
騒音スケジュール線形とコサイン — 品質への影響
スコアベース対数密度の勾配を学ぶ

📌 次の記事: 安定拡散の詳細 — 潜在拡散、UNet、CLIP コンディショニング。