概要
CNN を使用した GAN の一種である DCGAN (Deep Convolutional Generative Adversarial Networks) について解説します。
DCGAN
GAN については Pytorch – GAN の仕組みと Pytorch による実装例 – pystyle を参照してください。前の記事では、全結合層のみの Generator 及び Discriminator を作成して、MNIST データセットの生成モデルを作成しました。画像を1次元配列に潰してしまうと、位置関係の情報が失われてしまうため、MNIST のような簡単な画像であればそれでも十分ですが、人の顔であったりより複雑な画像を生成したい場合は Generator 及び Discriminator に CNN を使うことが有効です。 GAN の学習はハイパーパラメータなどに左右され難しいのですが、DCGAN の論文では、学習が上手くいくモデル構造や学習率などのハイパーパラメータなどを提示しました。
Pytorch の実装例
モデルが CNN になることを除いて GAN の記事と内容はほぼ同じです。
モジュールを import する
各種パラメータを設定する
デバイスを選択する
CelebA データセット
Large-scale CelebFaces Attributes (CelebA) Dataset は、有名人の顔画像を集めたデータセットです。今回はこのデータセットを使って、DCGAN で顔画像の生成モデルを作成します。 torchvision の torchvision.datasets.CelebA で提供されています。

Transform、Dataset、DataLoader を作成する
画像に対して、以下の前処理を行います。
- Resize() でモデルの入力サイズ (img_size, img_size) に収まるようにアスペクト比を固定してリサイズする
- CenterCrop() でモデルの入力サイズ (img_size, img_size) になるようにパディングを行う
- ToTensor() で PIL Image をテンソルに変換する
- Normalize() で値の範囲を に正規化する
標準化とは、 であるため、 とすると、値の範囲が になります。
Files already downloaded and verified
重みを初期化する関数を作成する
論文に記載されている平均 0、標準偏差 0.02 の正規分布に従う乱数で畳み込み層の重みを初期化する関数を作成します。
Generator を作成する
Generator は、ノイズ (latent_dim, 1, 1) を入力として、本物と同じ形状の画像データ (3, 64, 64) を出力する必要があります。モデル内で特徴マップの大きさを (1, 1) から (64, 64) までアップサンプリングするために、convolutional transpose layers を使用します。本物の画像データは値を の範囲に正規化してあるので、それに合わせて値域が である tanh 関数を出力層の活性化関数に設定します。

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, track_running_stats=True) (2): ReLU(inplace=True) (3): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (5): ReLU(inplace=True) (6): ConvTranspose2d(256, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (7): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (8): ReLU(inplace=True) (9): ConvTranspose2d(128, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (10): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (11): ReLU(inplace=True) (12): ConvTranspose2d(64, 3, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (13): Tanh() ) )
Discriminator を作成する
Discriminator は画像データ を入力とし、データが本物である確率を出力するモデルになります。 の範囲の値を出力できるように、出力層の活性化関数はシグモイド関数を使用します。つまり、2クラス分類問題を解く CNN モデルと構造は同じです。 ダウンサンプリングには、通常使われる MaxPooling の代わりに stride=2 の畳み込みを行うことで代用します。畳み込み層にすることで、モデル独自のプーリング方法を学習できます。
Discriminator( (main): Sequential( (0): Conv2d(3, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (1): LeakyReLU(negative_slope=0.2, inplace=True) (2): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (3): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (4): LeakyReLU(negative_slope=0.2, inplace=True) (5): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (6): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (7): LeakyReLU(negative_slope=0.2, inplace=True) (8): Conv2d(256, 512, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (9): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (10): LeakyReLU(negative_slope=0.2, inplace=True) (11): Conv2d(512, 1, kernel_size=(4, 4), stride=(2, 2), bias=False) (12): Sigmoid() (13): Flatten() ) )
損失関数とオプティマイザを作成する
Discriminator を学習する関数を作成する。
Generator を学習する関数を作成する。
Generator で画像を生成する関数を作成する。
データを生成するときは、勾配情報を不要なので、torch.no_grad()
コンテキストで実行します。
GAN の学習を実行する
CNN のモデルなので、学習には時間がかかります。 GeForce GTX 1080 で実行したところ、1エポックあたり5分弱、10エポックが完了するまでに1時間程度かかりました。
損失関数の値の推移を描画する

生成される画像の推移を gif 動画で保存する
pillow を使用して、各エポックの生成画像を gif 動画にして保存します。

学習が終了した段階の Generator が生成する画像を表示します。 一部崩れていたり、ぼやけている部分がありますが、人の顔であると認識できるレベルの画像が生成できることが確認できました。

コメント