はじめに: 競合する 2 つのネットワーク
Le 敵対的生成ネットワーク (GAN)、2014年にイアン・グッドフェローによって導入されました。 それらは、深層学習の最も革新的なパラダイムの 1 つを表しています。このアイデアはそれ自体エレガントです シンプルさ: 2 つのニューラル ネットワークがゼロサム ゲームで互いに競合します。の ジェネレータ を欺こうとする合成データを作成する ディスクリミネーター、 そして、実際のデータと生成されたデータを区別する方法を学習します。この大会が後押しするのは、 両方のネットワークを継続的に改善します。
GAN は画像生成に革命をもたらしました。存在しないフォトリアリスティックな顔、 art style transfer, super-resolution, data augmentation and much more.この中で この記事では、理論、トレーニングの課題、最も重要なバリエーションを探っていきます。 PyTorch での実用的な実装。
何を学ぶか
- GAN アーキテクチャ: ジェネレーターとディスクリミネーター
- 敵対的損失関数: ミニマックス ゲーム
- トレーニング ループ: 2 つのネットワーク間の交互
- モード崩壊とトレーニングの不安定性: 主な課題
- バリアント: 条件付き GAN、DCGAN、Wasserstein GAN
- アプリケーション: 顔の生成、スタイル転送、データ拡張
- PyTorch での完全な DCGAN 実装
アーキテクチャ: ジェネレーターとディスクリミネーター
ジェネレーター
Il Gジェネレーター 入力としてランダム ノイズ ベクトル z を受け取ります (通常、 ガウス分布または一様分布からサンプリングされ)、それを合成データに変換します (画像など)。ジェネレーターの目標は、騙されるほど現実的な出力を生成することです。 識別子。
差別者
Il 弁別器D およびバイナリ分類子: データ (実際のデータまたは生成されたデータ) を受け取ります。 そしてそれが本物である確率を生み出します。弁別器の目標は正しく区別することです 生成された実際のデータ。
La 敵対的損失 このゲームを形式化する: 弁別器は、 ジェネレーターは正しく分類される確率を最小限に抑えます。 弁別器はその出力を false として検出します。
import torch
import torch.nn as nn
class Generator(nn.Module):
"""Generatore: rumore casuale -> immagine 64x64"""
def __init__(self, latent_dim=100, channels=3):
super().__init__()
self.net = nn.Sequential(
# latent_dim -> 512 x 4 x 4
nn.ConvTranspose2d(latent_dim, 512, 4, 1, 0, bias=False),
nn.BatchNorm2d(512),
nn.ReLU(True),
# 512 x 4 x 4 -> 256 x 8 x 8
nn.ConvTranspose2d(512, 256, 4, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.ReLU(True),
# 256 x 8 x 8 -> 128 x 16 x 16
nn.ConvTranspose2d(256, 128, 4, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU(True),
# 128 x 16 x 16 -> 64 x 32 x 32
nn.ConvTranspose2d(128, 64, 4, 2, 1, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(True),
# 64 x 32 x 32 -> channels x 64 x 64
nn.ConvTranspose2d(64, channels, 4, 2, 1, bias=False),
nn.Tanh() # Output in [-1, 1]
)
def forward(self, z):
return self.net(z.view(z.size(0), -1, 1, 1))
class Discriminator(nn.Module):
"""Discriminatore: immagine 64x64 -> reale/falso"""
def __init__(self, channels=3):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(channels, 64, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(64, 128, 4, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(128, 256, 4, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(256, 512, 4, 2, 1, bias=False),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(512, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
def forward(self, img):
return self.net(img).view(-1, 1)
トレーニング ループ: 敵対ゲーム
GAN トレーニングでは、ディスクリミネーターとジェネレーターの更新が交互に行われます。毎回 反復: (1) 弁別器は実際のデータのバッチとデータのバッチでトレーニングされます。 生成される。 (2) ジェネレーターは、更新されたディスクリミネーターを騙そうとすることでトレーニングされます。
import torch.optim as optim
# Setup
latent_dim = 100
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
G = Generator(latent_dim).to(device)
D = Discriminator().to(device)
criterion = nn.BCELoss()
optimizer_G = optim.Adam(G.parameters(), lr=0.0002, betas=(0.5, 0.999))
optimizer_D = optim.Adam(D.parameters(), lr=0.0002, betas=(0.5, 0.999))
def train_step(real_images):
batch_size = real_images.size(0)
real_labels = torch.ones(batch_size, 1).to(device)
fake_labels = torch.zeros(batch_size, 1).to(device)
# --- Addestra Discriminatore ---
optimizer_D.zero_grad()
# Su dati reali
real_output = D(real_images)
d_loss_real = criterion(real_output, real_labels)
# Su dati generati
z = torch.randn(batch_size, latent_dim).to(device)
fake_images = G(z).detach() # detach: non calcolare gradiente per G
fake_output = D(fake_images)
d_loss_fake = criterion(fake_output, fake_labels)
d_loss = d_loss_real + d_loss_fake
d_loss.backward()
optimizer_D.step()
# --- Addestra Generatore ---
optimizer_G.zero_grad()
z = torch.randn(batch_size, latent_dim).to(device)
fake_images = G(z)
fake_output = D(fake_images)
g_loss = criterion(fake_output, real_labels) # Vuole ingannare D
g_loss.backward()
optimizer_G.step()
return d_loss.item(), g_loss.item()
トレーニングの課題: モードの崩壊と不安定性
モード崩壊
Il モード崩壊 そして GAN の最も一般的な問題: ジェネレーターはほとんどの情報を検出しません 識別子を欺き、多様性を無視してそれらだけを生成し続ける出力 実際のデータの。異なる顔を生成する代わりに、常に同じ顔を生成できます。 小さなバリエーションで。
トレーニングの不安定性
GAN はトレーニングが難しいことで有名です。識別子が強くなりすぎると、 ジェネレーターの勾配はほぼゼロになります (勾配消失)。ジェネレーターが優勢であれば、 弁別器は有用なフィードバックを提供しません。バランスを見つけることは常に課題です。
Wasserstein GAN: トレーニングの安定化
La ワッサースタイン GAN (WGAN) を置き換えることで多くの不安定性の問題を解決します バイナリクロスエントロピーと ワッサーシュタイン距離 (アース・ムーバーの距離)。 これにより、実際の分布と生成された分布が異なる場合でも、大きな勾配が得られます。 非常に異なり、勾配の消失が解消されます。 WGAN は重みクリッピングまたは勾配ペナルティを使用します (WGAN-GP) リプシッツ条件を確認します。
重要なバリアント
条件付きGAN(cGAN)
Le 条件付き GAN 条件情報(クラス、テキストなど)を追加します。 ジェネレーターとディスクリミネーターの両方に。これにより、生成を制御できるようになります。 「猫の写真を生成」または「数字の 7 を生成」。
サイクルガン
サイクルガン ペアのデータなしでドメイン間の変換を可能にします。変身できるよ 写真をモネ風の絵画に変えたり、馬をシマウマに変えたり、夏の風景を 冬は、両方の領域で同じ景色を一度も見たことがありませんでした。
スタイルGAN
スタイルGAN (NVIDIA) 高いフォトリアルな顔生成を実現 解像度。ネットワークのさまざまなレベルで「スタイル」の概念を導入します: 粗いスタイル 彼らは顔の構造をチェックし、細かいものでは髪の質感や色などの詳細をチェックします。
現実世界のアプリケーション
- 顔の生成: ゲーム、映画、アバター用のリアルな顔 (ThispersonDoesNotExist.com)
- データの増強: 医学、製造業における過小評価されているクラスの合成トレーニング データを生成
- 超解像: 写真、衛星、医療用の画像解像度を上げる (SRGAN)
- 画像から画像への変換: スケッチから写真、昼から夜、セグメンテーションから画像 (Pix2Pix)
- 創薬: 目的の特性を持つ分子構造を生成します
シリーズの次のステップ
- 次の記事では、 普及モデル、生成品質において GAN を上回っています。
- 拡散プロセスを見ていきます。ノイズを追加および削除して画像を生成します。
- DALL-E、安定拡散、ControlNet について見ていきます。







