拡散モデル

学習目標: 拡散モデル(Diffusion Model)の原理と実装を理解する

拡散モデルとは

拡散モデルは、データに徐々にノイズを加えていく過程(拡散過程)と、 ノイズから元のデータを復元する過程(逆拡散過程)を学習する生成モデルです。

拡散過程のイメージ
x₀ (元画像)
x₁ (少しノイズ)
...
xₜ (純粋なノイズ)

生成時は逆方向 ノイズ → 徐々にクリアな画像へ

数学的な定式化

拡散過程(Forward Process)

徐々にノイズを加える

q(xₜ|xₜ₋₁) = N(√(1-βₜ)xₜ₋₁, βₜI)

βₜ: ノイズスケジュール(小さい値から徐々に増加)

逆拡散過程(Reverse Process)

ノイズを除去(ニューラルネットで学習)

p_θ(xₜ₋₁|xₜ) = N(μ_θ(xₜ,t), Σ_θ(xₜ,t))

θ: 学習するパラメータ

PyTorch実装(DDPM)

import torch
import torch.nn as nn
import torch.nn.functional as F

class DiffusionModel:
    def __init__(self, n_steps=1000, beta_start=1e-4, beta_end=0.02):
        # ノイズスケジュール
        self.betas = torch.linspace(beta_start, beta_end, n_steps)
        self.alphas = 1 - self.betas
        self.alpha_bars = torch.cumprod(self.alphas, dim=0)

    def add_noise(self, x0, t, noise=None):
        """拡散過程: x0にノイズを加えてxtを生成"""
        if noise is None:
            noise = torch.randn_like(x0)

        alpha_bar_t = self.alpha_bars[t].view(-1, 1, 1, 1)
        # xt = sqrt(alpha_bar_t) * x0 + sqrt(1 - alpha_bar_t) * noise
        xt = torch.sqrt(alpha_bar_t) * x0 + torch.sqrt(1 - alpha_bar_t) * noise
        return xt, noise

    def sample(self, model, shape, device):
        """逆拡散過程: ノイズから画像を生成"""
        # 純粋なノイズから開始
        x = torch.randn(shape, device=device)

        for t in reversed(range(len(self.betas))):
            t_tensor = torch.full((shape[0],), t, device=device, dtype=torch.long)

            # ノイズを予測
            predicted_noise = model(x, t_tensor)

            # 1ステップ分ノイズを除去
            alpha_t = self.alphas[t]
            alpha_bar_t = self.alpha_bars[t]
            beta_t = self.betas[t]

            # 平均を計算
            mean = (1 / torch.sqrt(alpha_t)) * (
                x - (beta_t / torch.sqrt(1 - alpha_bar_t)) * predicted_noise
            )

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

        return x


# ノイズ予測ネットワーク(U-Net構造)
class NoisePredictor(nn.Module):
    def __init__(self, channels=1, time_embed_dim=256):
        super().__init__()

        # 時間埋め込み
        self.time_mlp = nn.Sequential(
            nn.Linear(1, time_embed_dim),
            nn.ReLU(),
            nn.Linear(time_embed_dim, time_embed_dim)
        )

        # 簡略化したU-Net(実際はもっと複雑)
        self.down1 = nn.Conv2d(channels, 64, 3, padding=1)
        self.down2 = nn.Conv2d(64, 128, 3, stride=2, padding=1)
        self.mid = nn.Conv2d(128, 128, 3, padding=1)
        self.up1 = nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1)
        self.out = nn.Conv2d(64, channels, 3, padding=1)

    def forward(self, x, t):
        # 時間情報を埋め込み
        t_emb = self.time_mlp(t.float().unsqueeze(-1) / 1000)

        # ダウンサンプリング
        h1 = F.relu(self.down1(x))
        h2 = F.relu(self.down2(h1))

        # ミドル(時間情報を追加)
        h = F.relu(self.mid(h2))

        # アップサンプリング
        h = F.relu(self.up1(h))
        return self.out(h + h1)

学習プロセス

# 学習ループ
model = NoisePredictor()
diffusion = DiffusionModel()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)

for epoch in range(epochs):
    for x0 in dataloader:
        # ランダムな時刻tを選択
        t = torch.randint(0, 1000, (x0.size(0),))

        # ノイズを加える
        xt, noise = diffusion.add_noise(x0, t)

        # ノイズを予測
        predicted_noise = model(xt, t)

        # 損失: 予測ノイズと実際のノイズの差
        loss = F.mse_loss(predicted_noise, noise)

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

学習の目標: 任意の時刻tにおいて、ノイズが加わった画像xtから、加えられたノイズを正確に予測すること

主要な拡散モデル

モデル 特徴 用途
DDPM 基本的な拡散モデル 研究・学習用
DDIM 高速サンプリング(非マルコフ) 効率的な生成
Stable Diffusion 潜在空間での拡散 + テキスト条件 テキストから画像生成
DALL-E 2 CLIP + 拡散モデル 高品質テキスト→画像

理解度チェック

Q. 拡散モデルの学習で、ニューラルネットワークは何を予測するように訓練されますか?