STEP 22:GANsと画像生成

🎨 STEP 22: GANsと画像生成

GAN(敵対的生成ネットワーク)、DCGAN、StyleGAN、Pix2Pix、CycleGAN、
Diffusion Modelsを学び、画像生成の技術を習得します

📋 このステップで学ぶこと

  • GAN(Generative Adversarial Network)の基礎
  • Generator(生成器)とDiscriminator(識別器)の敵対的学習
  • DCGAN(Deep Convolutional GAN)の実装
  • StyleGAN、StyleGAN2の仕組み
  • Pix2Pix(条件付き画像生成)
  • CycleGAN(ペアなしの画像変換)
  • Diffusion Models(簡単な紹介)
  • 実装:DCGANで画像生成

🎯 1. GAN(Generative Adversarial Network)の基礎

GAN(敵対的生成ネットワーク)は、2014年にIan Goodfellowによって発表された画像生成の革新的な手法です。2つのニューラルネットワークが競争することで、本物と見分けがつかないような画像を生成します。

1-1. GANの基本概念

💡 GANとは何か

目的:本物と見分けがつかないような画像を生成する

特徴:2つのニューラルネットワークが競争しながら学習する

生成できるもの:
・人の顔(実在しない人物の顔)
・アニメキャラクター
・風景写真
・手書き数字
・芸術作品 など

1-2. GeneratorとDiscriminator

GANは2つのニューラルネットワークで構成されています。この2つが競争することで、高品質な画像を生成できるようになります。

【GANの2つのネットワーク】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Generator(生成器、G) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 役割: ランダムノイズから画像を生成する 入力: ランダムなノイズベクトル(例: 100次元) 出力: 画像(例: 64×64×3) 目標: Discriminatorを騙せるような 本物に近い画像を生成する ノイズ(乱数) ↓ ┌─────────────┐ │ Generator │ │ (G) │ └─────────────┘ ↓ 偽の画像 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Discriminator(識別器、D) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 役割: 画像が本物か偽物かを判定する 入力: 画像(本物または偽物) 出力: 0〜1のスコア(1=本物、0=偽物) 目標: 本物と偽物を正しく見分ける 画像 ↓ ┌─────────────┐ │Discriminator│ │ (D) │ └─────────────┘ ↓ 「本物」or「偽物」 (0.0 〜 1.0)

1-3. 敵対的学習の仕組み

GANの学習は「ゲーム」に例えられます。GeneratorとDiscriminatorが互いに競争し、両者が向上していきます。

【敵対的学習の例え:偽札犯 vs 警察】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 登場人物 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 偽札犯 = Generator(G) 目標: 本物そっくりの偽札を作る 警察 = Discriminator(D) 目標: 本物の紙幣と偽札を見分ける ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 競争の流れ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ラウンド1: 偽札犯(G): 粗い偽札を作る 警察(D): 簡単に見分けられる 結果: Dが勝ち、Gは「もっと上手く作らないと」と学習 ラウンド2: 偽札犯(G): 少し上手になった偽札を作る 警察(D): まだ見分けられる 結果: Dが勝ち、Gはさらに学習 ラウンド3: 偽札犯(G): かなり精巧な偽札を作る 警察(D): 見分けが難しくなる 結果: Dも「もっと注意深く見ないと」と学習 … 繰り返し … 最終ラウンド: 偽札犯(G): 完璧な偽札を作る 警察(D): 50%の確率でしか見分けられない 結果: 引き分け(ナッシュ均衡) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 実際の訓練ステップ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ステップ1: Generatorが偽画像を生成 ノイズ z → G(z) → 偽画像 ステップ2: Discriminatorを訓練 本物画像 → D → 「本物」と判定させる 偽画像 → D → 「偽物」と判定させる 目標: 正しく判定できるようにDを更新 ステップ3: Generatorを訓練 偽画像 → D → 「本物」と誤判定させる 目標: Dを騙せるようにGを更新 ステップ4: ステップ1に戻って繰り返し ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 理想的な結果 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ・Generator: 本物と見分けがつかない画像を生成 ・Discriminator: 50%の確率でしか見分けられない (完全にランダムな当て推量と同じ) → これが「ナッシュ均衡」と呼ばれる理想的な収束状態

1-4. GANの数学的定式化

GANの学習は「ミニマックスゲーム」として定式化されます。数学が苦手な方は概要だけ理解すればOKです。

【GANの目的関数】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ミニマックスゲーム ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 目的関数 V(D, G): min_G max_D V(D, G) V(D, G) = E[log D(x)] + E[log(1 – D(G(z)))] 意味: ・Discriminator (D): V を最大化したい ・Generator (G): V を最小化したい ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 各項の意味 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第1項: E[log D(x)] x: 本物の画像 D(x): Discriminatorが本物と判定する確率 Dの目標: D(x) = 1(本物を「本物」と判定) → log D(x) = log 1 = 0(最大値) 第2項: E[log(1 – D(G(z)))] z: ノイズ G(z): Generatorが生成した偽画像 D(G(z)): Discriminatorが偽画像を本物と判定する確率 Dの目標: D(G(z)) = 0(偽物を「偽物」と判定) → log(1 – 0) = log 1 = 0(最大値) Gの目標: D(G(z)) = 1(偽物を「本物」と誤判定させる) → log(1 – 1) = log 0 = -∞(最小値) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 簡単なまとめ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Discriminator: 「本物を本物、偽物を偽物と正しく判定したい」 Generator: 「偽物を本物と誤判定させたい」 → 両者が競争することで、両方が上達する

1-5. GANの課題

⚠️ GANの訓練の難しさ

GANの訓練は非常に難しいことで知られています。主な課題を理解しておきましょう。

課題 問題の内容 対策
モード崩壊 Generatorが同じような画像ばかり生成する。多様性が失われる Mini-batch Discrimination、Unrolled GAN
訓練の不安定性 損失が振動し、収束しない。GとDのバランスが崩れる 適切な学習率、Spectral Normalization
勾配消失 Discriminatorが強すぎて、Generatorの勾配が消失 Wasserstein GAN、別の損失関数
評価の困難 生成画像の品質を定量的に評価しにくい Inception Score、FID(Frechet Inception Distance)
【モード崩壊の例】 正常な学習: 生成される画像: 😀 😢 😠 😲 😴 → 多様な表情の顔が生成される モード崩壊: 生成される画像: 😀 😀 😀 😀 😀 → 同じ表情の顔ばかり生成される 原因: Generatorが「この画像ならDiscriminatorを騙せる」と 学習してしまい、その画像ばかり生成するようになる

🖼️ 2. DCGAN(Deep Convolutional GAN)

DCGANは2015年に発表された、CNNを使った安定したGANです。従来のGANの訓練を安定させるためのガイドラインを提供し、高品質な画像生成を可能にしました。

2-1. DCGANの特徴

【DCGANのアーキテクチャガイドライン】 Radford et al.(2015年)が発表した 安定した訓練のためのガイドライン: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. Pooling層を使わない ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 従来: Max Poolingでダウンサンプリング DCGAN: Strided Convolutionで置き換え 理由: ・Poolingは情報を捨ててしまう ・Strided Convolutionは学習可能 ・より滑らかなダウンサンプリング ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2. Batch Normalizationを使用 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ すべての層に適用(出力層以外) 理由: ・訓練を安定させる ・勾配消失を防ぐ ・より深いネットワークが可能 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3. 全結合層を削除 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Fully Convolutional Networkに 理由: ・パラメータ数の削減 ・空間情報の保持 ・訓練の安定化 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4. 活性化関数 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Generator: ・中間層: ReLU ・出力層: Tanh(-1〜1の範囲に正規化) Discriminator: ・すべての層: LeakyReLU(α = 0.2) 理由: ・ReLU: 勾配が流れやすい ・LeakyReLU: 勾配消失を防ぐ(負の値でも小さな勾配) ・Tanh: 画像の値を-1〜1に正規化

2-2. DCGANのアーキテクチャ

【Generatorのアーキテクチャ】 入力: ノイズベクトル z(100次元) 出力: 64×64×3 の画像 処理の流れ(逆畳み込みで画像サイズを拡大): ノイズ z (100次元) │ ▼ Project & Reshape 100 → 4×4×1024 │ ▼ ConvTranspose2d (stride=2) 4×4×1024 → 8×8×512 │ ▼ ConvTranspose2d (stride=2) 8×8×512 → 16×16×256 │ ▼ ConvTranspose2d (stride=2) 16×16×256 → 32×32×128 │ ▼ ConvTranspose2d (stride=2) 32×32×128 → 64×64×3 │ ▼ 出力画像 (64×64×3) 各層の構成: ConvTranspose2d → BatchNorm → ReLU (最終層のみ Tanh) 【Discriminatorのアーキテクチャ】 入力: 64×64×3 の画像 出力: 0〜1 のスコア(本物らしさ) 処理の流れ(畳み込みで画像サイズを縮小): 入力画像 (64×64×3) │ ▼ Conv2d (stride=2) 64×64×3 → 32×32×64 │ ▼ Conv2d (stride=2) 32×32×64 → 16×16×128 │ ▼ Conv2d (stride=2) 16×16×128 → 8×8×256 │ ▼ Conv2d (stride=2) 8×8×256 → 4×4×512 │ ▼ Conv2d (stride=1) 4×4×512 → 1×1×1 │ ▼ Sigmoid │ ▼ 出力スコア (0〜1) 各層の構成: Conv2d → BatchNorm → LeakyReLU(0.2) (最初の層はBatchNormなし)

2-3. 実装:DCGANのGenerator

DCGANのGeneratorをPyTorchで実装します。コードを段階的に説明します。

※ コードが横に長い場合は横スクロールできます

# =================================================== # DCGANの実装(PyTorch) # =================================================== import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import torchvision.utils as vutils import matplotlib.pyplot as plt import numpy as np # =================================================== # 1. ハイパーパラメータの設定 # =================================================== # torch.device(): 計算に使用するデバイスを指定 # CUDAが利用可能ならGPU、そうでなければCPU device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”) # 主要なハイパーパラメータ latent_dim = 100 # ノイズの次元(Generatorへの入力) image_size = 64 # 生成する画像のサイズ(64×64) batch_size = 128 # バッチサイズ num_epochs = 50 # エポック数 lr = 0.0002 # 学習率(DCGANの推奨値) beta1 = 0.5 # Adamオプティマイザのβ1(DCGANの推奨値) feature_maps_g = 64 # Generatorの基本フィーチャーマップ数 feature_maps_d = 64 # Discriminatorの基本フィーチャーマップ数 print(f”使用デバイス: {device}”) print(f”ノイズ次元: {latent_dim}”) print(f”画像サイズ: {image_size}×{image_size}”)

実行結果:

使用デバイス: cuda ノイズ次元: 100 画像サイズ: 64×64
# =================================================== # 2. データセットの準備 # =================================================== # transforms.Compose(): 複数の変換を連結 transform = transforms.Compose([ # Resize(): 画像を指定サイズにリサイズ transforms.Resize(image_size), # CenterCrop(): 中央から指定サイズで切り出し transforms.CenterCrop(image_size), # ToTensor(): PIL画像をTensorに変換(0〜1に正規化) transforms.ToTensor(), # Normalize(): 値を-1〜1に正規化 # (x – 0.5) / 0.5 = 2x – 1 # 0〜1 → -1〜1 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) # データセットの読み込み(CIFAR-10を例として使用) # 本格的な顔生成にはCelebAなどを使用 dataset = datasets.CIFAR10( root=’./data’, # データの保存先 train=True, # 訓練データを使用 download=True, # ダウンロードする transform=transform # 上で定義した変換を適用 ) # DataLoader(): データをバッチで取得するためのイテレータ dataloader = DataLoader( dataset, batch_size=batch_size, shuffle=True, # データをシャッフル num_workers=2 # データ読み込みの並列数 ) print(f”データセットサイズ: {len(dataset)}枚”) print(f”バッチ数: {len(dataloader)}”)

実行結果:

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz データセットサイズ: 50000枚 バッチ数: 391
# =================================================== # 3. Generatorの定義 # =================================================== class Generator(nn.Module): “”” DCGANのGenerator ノイズベクトルから画像を生成する “”” def __init__(self, latent_dim=100, feature_maps=64): “”” Args: latent_dim: 入力ノイズの次元 feature_maps: 基本のフィーチャーマップ数 “”” super(Generator, self).__init__() # nn.Sequential(): 複数の層を順番に適用 self.main = nn.Sequential( # ===== 第1層: 入力 ===== # nn.ConvTranspose2d(): 転置畳み込み(逆畳み込み) # 引数: (入力チャンネル, 出力チャンネル, カーネルサイズ, stride, padding) # latent_dim × 1 × 1 → feature_maps*8 × 4 × 4 nn.ConvTranspose2d(latent_dim, feature_maps * 8, 4, 1, 0, bias=False), nn.BatchNorm2d(feature_maps * 8), nn.ReLU(True), # ===== 第2層 ===== # feature_maps*8 × 4 × 4 → feature_maps*4 × 8 × 8 nn.ConvTranspose2d(feature_maps * 8, feature_maps * 4, 4, 2, 1, bias=False), nn.BatchNorm2d(feature_maps * 4), nn.ReLU(True), # ===== 第3層 ===== # feature_maps*4 × 8 × 8 → feature_maps*2 × 16 × 16 nn.ConvTranspose2d(feature_maps * 4, feature_maps * 2, 4, 2, 1, bias=False), nn.BatchNorm2d(feature_maps * 2), nn.ReLU(True), # ===== 第4層 ===== # feature_maps*2 × 16 × 16 → feature_maps × 32 × 32 nn.ConvTranspose2d(feature_maps * 2, feature_maps, 4, 2, 1, bias=False), nn.BatchNorm2d(feature_maps), nn.ReLU(True), # ===== 出力層 ===== # feature_maps × 32 × 32 → 3 × 64 × 64 nn.ConvTranspose2d(feature_maps, 3, 4, 2, 1, bias=False), nn.Tanh() # 出力を-1〜1に正規化 ) def forward(self, x): “””順伝播””” return self.main(x) # Generatorのインスタンス化 netG = Generator(latent_dim, feature_maps_g).to(device) print(“Generatorを作成しました”) print(netG)

実行結果:

Generatorを作成しました Generator( (main): Sequential( (0): ConvTranspose2d(100, 512, kernel_size=(4, 4), stride=(1, 1), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True) (2): ReLU(inplace=True) (3): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) … ) )
# =================================================== # 4. Discriminatorの定義 # =================================================== class Discriminator(nn.Module): “”” DCGANのDiscriminator 画像が本物か偽物かを判定する “”” def __init__(self, feature_maps=64): “”” Args: feature_maps: 基本のフィーチャーマップ数 “”” super(Discriminator, self).__init__() self.main = nn.Sequential( # ===== 第1層: 入力 ===== # nn.Conv2d(): 通常の畳み込み # 3 × 64 × 64 → feature_maps × 32 × 32 # 最初の層はBatchNormを使わない(DCGANのガイドライン) nn.Conv2d(3, feature_maps, 4, 2, 1, bias=False), nn.LeakyReLU(0.2, inplace=True), # ===== 第2層 ===== # feature_maps × 32 × 32 → feature_maps*2 × 16 × 16 nn.Conv2d(feature_maps, feature_maps * 2, 4, 2, 1, bias=False), nn.BatchNorm2d(feature_maps * 2), nn.LeakyReLU(0.2, inplace=True), # ===== 第3層 ===== # feature_maps*2 × 16 × 16 → feature_maps*4 × 8 × 8 nn.Conv2d(feature_maps * 2, feature_maps * 4, 4, 2, 1, bias=False), nn.BatchNorm2d(feature_maps * 4), nn.LeakyReLU(0.2, inplace=True), # ===== 第4層 ===== # feature_maps*4 × 8 × 8 → feature_maps*8 × 4 × 4 nn.Conv2d(feature_maps * 4, feature_maps * 8, 4, 2, 1, bias=False), nn.BatchNorm2d(feature_maps * 8), nn.LeakyReLU(0.2, inplace=True), # ===== 出力層 ===== # feature_maps*8 × 4 × 4 → 1 × 1 × 1 nn.Conv2d(feature_maps * 8, 1, 4, 1, 0, bias=False), nn.Sigmoid() # 0〜1の確率を出力 ) def forward(self, x): “””順伝播””” # view(-1, 1): バッチサイズ × 1 の形状に変換 # squeeze(1): 不要な次元を削除 return self.main(x).view(-1, 1).squeeze(1) # Discriminatorのインスタンス化 netD = Discriminator(feature_maps_d).to(device) print(“Discriminatorを作成しました”)
# =================================================== # 5. 重みの初期化 # =================================================== def weights_init(m): “”” DCGANの推奨に従って重みを初期化 Conv層: 平均0、標準偏差0.02の正規分布 BatchNorm層: 平均1、標準偏差0.02の正規分布 “”” classname = m.__class__.__name__ if classname.find(‘Conv’) != -1: # nn.init.normal_(): 正規分布で初期化 nn.init.normal_(m.weight.data, 0.0, 0.02) elif classname.find(‘BatchNorm’) != -1: nn.init.normal_(m.weight.data, 1.0, 0.02) nn.init.constant_(m.bias.data, 0) # 重みの初期化を適用 # apply(): モデルの全ての層に関数を適用 netG.apply(weights_init) netD.apply(weights_init) print(“重みを初期化しました”)
# =================================================== # 6. 損失関数とオプティマイザの設定 # =================================================== # nn.BCELoss(): Binary Cross Entropy Loss # 本物=1、偽物=0 の2値分類に使用 criterion = nn.BCELoss() # 固定ノイズ(訓練中の可視化用) # 同じノイズから生成される画像の変化を観察 fixed_noise = torch.randn(64, latent_dim, 1, 1, device=device) # ラベル real_label = 1.0 # 本物のラベル fake_label = 0.0 # 偽物のラベル # オプティマイザ(Adam) # lr=0.0002, beta1=0.5 はDCGANの推奨値 optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999)) optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999)) print(“損失関数とオプティマイザを設定しました”)
# =================================================== # 7. 訓練ループ # =================================================== # 損失の記録用 G_losses = [] D_losses = [] print(“訓練を開始します…”) for epoch in range(num_epochs): for i, (real_images, _) in enumerate(dataloader): # ============================ # (1) Discriminatorの更新 # ============================ # 勾配をリセット netD.zero_grad() # — 本物の画像で訓練 — real_images = real_images.to(device) batch_size_current = real_images.size(0) # 本物のラベル(すべて1) labels = torch.full((batch_size_current,), real_label, dtype=torch.float, device=device) # Discriminatorで判定 output = netD(real_images) # 損失を計算(本物を本物と判定させたい) errD_real = criterion(output, labels) errD_real.backward() # D(x): 本物画像に対するDの出力の平均 D_x = output.mean().item() # — 偽物の画像で訓練 — # ノイズを生成 noise = torch.randn(batch_size_current, latent_dim, 1, 1, device=device) # Generatorで偽画像を生成 fake_images = netG(noise) # 偽物のラベル(すべて0) labels.fill_(fake_label) # Discriminatorで判定 # detach(): Generatorの勾配計算を防ぐ output = netD(fake_images.detach()) # 損失を計算(偽物を偽物と判定させたい) errD_fake = criterion(output, labels) errD_fake.backward() # D(G(z)): 偽画像に対するDの出力の平均 D_G_z1 = output.mean().item() # Discriminatorの総損失 errD = errD_real + errD_fake # Discriminatorのパラメータを更新 optimizerD.step() # ============================ # (2) Generatorの更新 # ============================ # 勾配をリセット netG.zero_grad() # Generatorは偽物を本物と判定させたい labels.fill_(real_label) # Discriminatorで判定(今度はdetachしない) output = netD(fake_images) # 損失を計算 errG = criterion(output, labels) errG.backward() # D(G(z)): Generator更新後の偽画像に対するDの出力 D_G_z2 = output.mean().item() # Generatorのパラメータを更新 optimizerG.step() # 損失を記録 G_losses.append(errG.item()) D_losses.append(errD.item()) # 進捗表示(50バッチごと) if i % 50 == 0: print(f'[{epoch+1}/{num_epochs}][{i}/{len(dataloader)}] ‘ f’Loss_D: {errD.item():.4f} Loss_G: {errG.item():.4f} ‘ f’D(x): {D_x:.4f} D(G(z)): {D_G_z1:.4f}/{D_G_z2:.4f}’) # エポック終了時に画像を生成して保存 if (epoch + 1) % 5 == 0: with torch.no_grad(): fake = netG(fixed_noise).detach().cpu() # 画像を保存 plt.figure(figsize=(8, 8)) plt.axis(“off”) plt.title(f”Generated Images (Epoch {epoch+1})”) plt.imshow(np.transpose( vutils.make_grid(fake, padding=2, normalize=True), (1, 2, 0) )) plt.savefig(f’generated_epoch_{epoch+1}.png’, dpi=150, bbox_inches=’tight’) plt.close() print(“訓練が完了しました!”)

実行結果(例):

訓練を開始します… [1/50][0/391] Loss_D: 1.3862 Loss_G: 5.2341 D(x): 0.5231 D(G(z)): 0.4923/0.0089 [1/50][50/391] Loss_D: 0.8234 Loss_G: 2.1234 D(x): 0.7823 D(G(z)): 0.3421/0.1523 … [50/50][350/391] Loss_D: 0.6234 Loss_G: 1.8234 D(x): 0.7523 D(G(z)): 0.2821/0.2123 訓練が完了しました!
🎯 訓練の監視ポイント

D(x):本物画像に対するDiscriminatorの出力
・理想: 0.5〜0.8程度
・1に近すぎる: Dが強すぎる

D(G(z)):偽画像に対するDiscriminatorの出力
・理想: 0.3〜0.5程度
・0に近すぎる: Gが弱すぎる、Dが強すぎる

Loss_D と Loss_G のバランス:
・両方とも徐々に安定するのが理想
・どちらか一方が極端に下がると問題

# =================================================== # 8. 損失のプロットと画像生成 # =================================================== # 損失の推移をプロット plt.figure(figsize=(10, 5)) plt.title(“Generator and Discriminator Loss During Training”) plt.plot(G_losses, label=”Generator”, alpha=0.7) plt.plot(D_losses, label=”Discriminator”, alpha=0.7) plt.xlabel(“Iterations”) plt.ylabel(“Loss”) plt.legend() plt.savefig(‘dcgan_loss_plot.png’, dpi=150, bbox_inches=’tight’) plt.show() # 新しい画像を生成 with torch.no_grad(): noise = torch.randn(64, latent_dim, 1, 1, device=device) generated_images = netG(noise).detach().cpu() # 生成画像を表示 plt.figure(figsize=(10, 10)) plt.axis(“off”) plt.title(“Final Generated Images”) plt.imshow(np.transpose( vutils.make_grid(generated_images, padding=2, normalize=True), (1, 2, 0) )) plt.savefig(‘dcgan_final_images.png’, dpi=150, bbox_inches=’tight’) plt.show() print(“画像を保存しました”)

🎭 3. StyleGAN(高品質な画像生成)

StyleGANはNVIDIAが2018年に発表した、超高品質な画像を生成できるGANです。特に人の顔の生成で驚異的な性能を発揮し、実在しない人物の顔を生成できます。

3-1. StyleGANの革新

【StyleGANの主な革新点】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. Style-based Generator ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 従来のGAN: ノイズ z → Generator → 画像 StyleGAN: ノイズ z → Mapping Network → w(スタイルベクトル) w → 各層で「スタイル」として注入 → 画像 Mapping Network: 8層の全結合層 z(512次元) → w(512次元) 効果: ・ノイズの分布を「もつれを解いた」空間に変換 ・各属性(年齢、髪型など)が分離される ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2. AdaIN(Adaptive Instance Normalization) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 各層でスタイルベクトル w を注入する仕組み AdaIN(x, w) = w_s × normalize(x) + w_b x: 特徴マップ w_s, w_b: w から学習された係数 効果: ・各層で異なるスタイルを制御 ・粗い層: 顔の形、ポーズ ・中間の層: 顔の特徴、髪型 ・細かい層: 色、質感 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3. Progressive Growing ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 低解像度から始めて徐々に高解像度に 4×4 → 8×8 → 16×16 → 32×32 → 64×64 → 128×128 → 256×256 → 512×512 → 1024×1024 効果: ・訓練が安定 ・高解像度の画像生成が可能 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 結果 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ・1024×1024の超高解像度な顔画像を生成 ・実在しない人物の顔(thispersondoesnotexist.com) ・スタイルの制御(年齢、性別、髪型など)が可能

3-2. StyleGAN2の改善

【StyleGAN2(2019年)の改善点】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ StyleGANの問題点 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ アーティファクト(不自然な模様): ・水滴のような斑点が出現 ・特に顔の周辺に発生 ・AdaINが原因 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ StyleGAN2の改善 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. Weight Demodulation AdaINを置き換え → アーティファクトを除去 2. Path Length Regularization 生成品質を向上 → より滑らかな補間 3. Progressive Growingの廃止 最初から最終解像度で訓練 → より自然な画像 結果: ・アーティファクトのない高品質な画像 ・FID(Frechet Inception Distance)が大幅に改善

3-3. StyleGANの応用

応用 詳細
顔画像生成 実在しない人物の顔を生成。thispersondoesnotexist.com で体験可能
顔編集 年齢変化(若く/老けさせる)、性別変換、表情変更、髪型・髪色の変更
スタイル混合 2つの顔の特徴を混合。粗い特徴と細かい特徴を別々に制御
データ拡張 訓練データの生成、プライバシーを考慮した合成データ
アート生成 芸術作品の生成、アニメキャラクターの生成

🔄 4. Pix2Pix(条件付き画像変換)

Pix2Pixは「画像から画像への変換」を学習するGANです。入力画像と出力画像のペアデータを使って、一方から他方への変換を学習します。

4-1. Pix2Pixの仕組み

【Pix2Pixとは(2016年)】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 目的 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ペアの画像データから、画像→画像の変換を学習 入力画像 x → Pix2Pix → 出力画像 y ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 具体例 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. スケッチ → 写真 手描きのスケッチ → リアルな写真 2. セグメンテーションマップ → 写真 領域分割マップ → 街の風景写真 3. 白黒 → カラー 白黒写真 → カラー写真 4. 昼 → 夜 昼の写真 → 夜の写真 5. エッジ → 写真 エッジ画像 → 元の写真 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 条件付きGAN(cGAN) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 従来のGAN: ノイズ z → G → 画像 Pix2Pix(cGAN): 入力画像 x + ノイズ z → G → 出力画像 y Discriminatorも入力画像を見る: (入力画像 x, 出力画像 y) のペアが 本物か偽物かを判定 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ アーキテクチャ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Generator: U-Net Encoder(ダウンサンプリング): 256×256 → 128×128 → 64×64 → … → 1×1 Decoder(アップサンプリング): 1×1 → … → 64×64 → 128×128 → 256×256 Skip Connections: ┌─────────────────────────────────┐ │ │ Encoder層 ──────────────────→ Decoder層 │ │ └─────────────────────────────────┘ 効果: 細かい情報(エッジ、テクスチャ)を保持 Discriminator: PatchGAN 画像全体ではなく、パッチ(部分領域)単位で判定 70×70の受容野で各パッチを評価 利点: ・パラメータが少ない ・高周波の詳細(テクスチャ)をキャプチャ

4-2. Pix2Pixの損失関数

【Pix2Pixの損失関数】 L(G, D) = L_cGAN(G, D) + λ × L_L1(G) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ L_cGAN: 敵対的損失 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Discriminatorを騙すための損失 → 生成画像をリアルにする ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ L_L1: L1損失(ピクセル単位の差) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ L_L1(G) = E[||y – G(x)||_1] y: 正解画像 G(x): 生成画像 効果: ・生成画像を正解画像に近づける ・ぼやけた画像を防ぐ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ λ: バランスパラメータ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 通常 λ = 100 L1損失の重みを大きくすることで、 正解画像に近い出力を強制

🔁 5. CycleGAN(ペアなしの画像変換)

CycleGANはPix2Pixの制限を克服し、ペアデータなしで画像変換を学習できるGANです。

5-1. CycleGANの革新

【CycleGANとは(2017年)】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Pix2Pixの制限 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Pix2Pixには「ペアデータ」が必要: ・スケッチと写真のペア ・昼と夜のペア(同じ場所) 問題: ペアデータを集めるのが困難 例: 馬とシマウマの変換 同じポーズの馬とシマウマのペアは存在しない ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ CycleGANの解決策 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ペアなし(Unpaired)で学習可能! 必要なデータ: ・ドメインAの画像(例: 馬の画像100枚) ・ドメインBの画像(例: シマウマの画像100枚) 対応関係は不要! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 応用例 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. 馬 ⇄ シマウマ 馬の画像をシマウマに変換、逆も可能 2. 夏 ⇄ 冬 夏の風景を冬に変換 3. 写真 ⇄ 絵画 写真をモネ風、ゴッホ風に変換 4. りんご ⇄ オレンジ 果物の種類を変換 5. 昼 ⇄ 夜 ペアなしで昼夜変換

5-2. Cycle Consistency Loss

【Cycle Consistency Lossの仕組み】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 問題: ペアがないとどう学習する? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ペアがない場合、「正解」がない 例: 馬の画像 → ??? → シマウマ どのシマウマが「正解」なのか分からない ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ CycleGANのアイデア ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 「元に戻せる」ことを制約にする! 2つのGenerator: G: A → B(例: 馬 → シマウマ) F: B → A(例: シマウマ → 馬) Cycle Consistency: 馬 → G → シマウマ → F → 馬(元に戻る) シマウマ → F → 馬 → G → シマウマ(元に戻る) 図解: G 馬 ───────→ シマウマ ↑ │ │ │ F │ ↓ └──────────── 馬′ 馬′ ≈ 馬(元の画像に近い) これを「Cycle Consistency」と呼ぶ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 損失関数 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ L_total = L_GAN(G, D_B) + L_GAN(F, D_A) + λ × L_cyc(G, F) L_GAN: 敵対的損失 生成画像を本物らしくする L_cyc: Cycle Consistency Loss L_cyc = E[||F(G(x)) – x||_1] + E[||G(F(y)) – y||_1] x: ドメインAの画像 y: ドメインBの画像 F(G(x)) ≈ x(Aから出発してAに戻る) G(F(y)) ≈ y(Bから出発してBに戻る) λ = 10(推奨値)

5-3. Pix2PixとCycleGANの比較

項目 Pix2Pix CycleGAN
必要なデータ ペアデータ(入力と出力のペア) ペア不要(2つのドメインの画像)
データ収集の難易度 高い(ペアを用意する必要あり) 低い(対応なしでOK)
生成品質 高い(正解があるため) やや低い(制約が弱い)
Generator数 1つ(A → B のみ) 2つ(A ⇄ B 双方向)
主な用途 スケッチ→写真、カラー化 馬⇄シマウマ、写真⇄絵画

🌊 6. Diffusion Models(新しい画像生成手法)

Diffusion Modelsは2020年代に急速に発展した新しい画像生成手法で、Stable DiffusionやDALL-E 2の基盤技術です。GANとは全く異なるアプローチで高品質な画像を生成します。

6-1. Diffusion Modelsの基本

【Diffusion Modelsの仕組み】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 基本アイデア ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 「ノイズを徐々に除去」することで画像を生成 例え: 絵の具を水に落とすと徐々に広がる(拡散) → この過程を逆再生して絵の具を集める ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Forward Process(拡散過程) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 元画像に徐々にノイズを追加 x_0 → x_1 → x_2 → … → x_T x_0: 元のきれいな画像 x_T: 完全なノイズ(ガウスノイズ) T = 1000 ステップなど 各ステップで少しずつノイズを追加: x_t = √(1-β_t) × x_{t-1} + √(β_t) × ε β_t: ノイズの量(スケジュールで決定) ε: ガウスノイズ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Reverse Process(逆拡散過程) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ノイズから元の画像を復元 x_T → x_{T-1} → … → x_1 → x_0 ニューラルネットワーク(U-Net)で 「このステップで除去すべきノイズ」を予測 各ステップで少しずつノイズを除去: x_{t-1} = (x_t – 予測ノイズ) / スケーリング ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 訓練 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 目標: 各ステップで追加されたノイズを予測 損失関数: L = E[||ε – ε_θ(x_t, t)||²] ε: 実際に追加したノイズ ε_θ(x_t, t): ネットワークが予測したノイズ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 生成 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. 完全なノイズ x_T をサンプリング 2. t = T から t = 0 まで繰り返し: a. ノイズを予測: ε_θ(x_t, t) b. ノイズを除去: x_{t-1} を計算 3. x_0 が生成された画像

6-2. GANとDiffusion Modelsの比較

項目 GAN Diffusion Models
生成の仕組み 1ステップでノイズ→画像 多数のステップで徐々にノイズ除去
訓練の安定性 不安定(GとDのバランスが難しい) 安定(単純な最適化問題)
モード崩壊 発生しやすい 発生しない
生成速度 高速(〜0.01秒) 遅い(数秒〜数十秒)
画像品質 高品質 非常に高品質
多様性 限定的 高い多様性
代表的モデル StyleGAN2、BigGAN Stable Diffusion、DALL-E 2

📝 練習問題

問題1:GANの基本構造(基礎)

GANのGenerator(生成器)とDiscriminator(識別器)の役割と、両者がどのように競争するか説明してください。

解答:
【GANの基本構造】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Generator(生成器)の役割 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 入力: ランダムノイズ(例: 100次元のベクトル) 出力: 画像(例: 64×64×3) 目標: Discriminatorを騙せるような 本物に近い画像を生成する 例え: 偽札を作る犯罪者 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Discriminator(識別器)の役割 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 入力: 画像(本物または偽物) 出力: 0〜1のスコア(1=本物、0=偽物) 目標: 本物と偽物を正しく見分ける 例え: 偽札を見分ける警察 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 競争の仕組み ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. Generatorが偽画像を生成 2. Discriminatorが判定: ・本物の画像 → 「本物」と判定させたい ・偽画像 → 「偽物」と判定させたい 3. Discriminatorを訓練: 正しく判定できるようにパラメータを更新 4. Generatorを訓練: Discriminatorを騙せるようにパラメータを更新 5. 繰り返し 理想的な結果: Discriminatorが50%の確率でしか見分けられない → Generatorが完璧な画像を生成

問題2:DCGANのアーキテクチャ(中級)

DCGANが従来のGANと比べて改善した点を、アーキテクチャの観点から4つ説明してください。

解答:
【DCGANの4つの改善点】 1. Pooling層を使わない 従来: Max Poolingでダウンサンプリング DCGAN: Strided Convolutionで置き換え 効果: より滑らかなダウンサンプリング、学習可能 2. Batch Normalizationを使用 すべての層に適用(出力層以外) 効果: 訓練の安定化、勾配消失の防止 3. 全結合層を削除 Fully Convolutional Networkに 効果: パラメータ削減、空間情報の保持 4. 活性化関数の最適化 Generator: ReLU(中間層)、Tanh(出力層) Discriminator: LeakyReLU(α=0.2) 効果: 勾配が流れやすくなり、訓練が安定

問題3:Pix2PixとCycleGANの違い(中級)

Pix2PixとCycleGANの主な違いを、必要なデータと応用例の観点から説明してください。

解答:
【Pix2PixとCycleGANの比較】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 必要なデータ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Pix2Pix: ペアデータが必要 例: スケッチと写真のペア、同じ場所の昼と夜のペア CycleGAN: ペアデータ不要 例: 馬の画像100枚とシマウマの画像100枚(対応なしでOK) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 応用例 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Pix2Pix: ・スケッチ → 写真 ・セグメンテーションマップ → 写真 ・白黒 → カラー ・エッジ → 写真 CycleGAN: ・馬 ⇄ シマウマ ・写真 ⇄ 絵画(モネ風など) ・夏 ⇄ 冬 ・りんご ⇄ オレンジ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 使い分け ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ペアデータが用意できる → Pix2Pix(品質が高い) ペアデータが難しい → CycleGAN(データ収集が容易)

問題4:GANの訓練の難しさ(上級)

GANの訓練における主な課題(モード崩壊、訓練の不安定性など)と、それらに対する対策を説明してください。

解答:
【GANの訓練の課題と対策】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. モード崩壊(Mode Collapse) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 問題: Generatorが同じような画像ばかり生成 原因: 一部のモードのみを学習してしまう 対策: ・Mini-batch Discrimination ・Unrolled GAN ・複数のDiscriminatorを使用 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2. 訓練の不安定性 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 問題: 損失が振動し、収束しない 原因: GとDのバランスが崩れる 対策: ・適切な学習率(0.0002推奨) ・Spectral Normalization ・TTUR(Two Time-scale Update Rule) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3. 勾配消失 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 問題: Generatorの勾配が消失 原因: Discriminatorが強すぎる 対策: ・Wasserstein GAN(WGAN) ・Least Squares GAN(LSGAN) ・別の損失関数を使用 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4. 評価の困難 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 問題: 生成画像の品質を定量的に評価しにくい 対策: ・Inception Score(IS) ・FID(Frechet Inception Distance) ・人間による評価も併用

問題5:GANとDiffusion Modelsの比較(上級)

GANとDiffusion Modelsの違いを、生成の仕組み、訓練の安定性、生成速度の観点から比較してください。

解答:
【GANとDiffusion Modelsの比較】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 生成の仕組み ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GAN: ・ノイズを1ステップで画像に変換 ・GeneratorとDiscriminatorの敵対的学習 Diffusion: ・ノイズを多数のステップで徐々に除去 ・各ステップでノイズを予測 ・通常1000ステップ程度 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 訓練の安定性 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GAN: ・不安定(GとDのバランスが難しい) ・モード崩壊のリスク ・ハイパーパラメータに敏感 Diffusion: ・安定(単純な最適化問題) ・モード崩壊なし ・ハイパーパラメータに頑健 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 生成速度 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GAN: ・高速(1ステップで生成) ・〜0.01秒 ・リアルタイム生成可能 Diffusion: ・遅い(多数のステップが必要) ・数秒〜数十秒 ・高速化技術(DDIM、Latent Diffusion)で改善中 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 使い分け ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ リアルタイム生成が必要 → GAN 最高品質・多様性が必要 → Diffusion 安定した訓練が必要 → Diffusion 現在のトレンド: Diffusionが主流(Stable Diffusion、DALL-E 2)

📝 STEP 22 のまとめ

✅ このステップで学んだこと

1. GANの基礎
・GeneratorとDiscriminatorの敵対的学習
・ミニマックスゲームとしての定式化
・モード崩壊などの課題と対策

2. DCGAN
・CNNを使った安定したGAN
・アーキテクチャのガイドライン(Batch Norm、LeakyReLUなど)

3. StyleGAN
・超高品質な顔画像生成
・Mapping NetworkとAdaINによるスタイル制御

4. Pix2Pix
・ペアデータを使った画像変換
・U-Net Generator + PatchGAN Discriminator

5. CycleGAN
・ペアなしでの画像変換
・Cycle Consistency Lossの革新

6. Diffusion Models
・ノイズを徐々に除去して画像生成
・GANより安定した訓練、高い品質と多様性

💡 重要ポイント

GANの革新:
・敵対的学習という新しいパラダイム
・高品質な画像生成を可能に

GANの進化:
・DCGAN → StyleGAN → StyleGAN2
・Pix2Pix → CycleGAN

最新トレンド:
・Diffusion Modelsが主流に(Stable Diffusion、DALL-E 2など)
・GANより安定した訓練、高品質な生成

次のSTEP 23では、動画処理と行動認識を学びます!

📝

学習メモ

コンピュータビジョン(CV) - Step 22

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE