DCGANによるポケモン生成


DCGANでポケモン生成

今回はkaggelで配布されているデータセット https://www.kaggle.com/kvpratama/pokemon-images-dataset を教師データ(正解画像)として用います。
実際のコードは以下からダウンロードできます。
https://github.com/Kohey1480/pokemon_dcgan
 同じ階層にデータセットを用意しimagechange.py->pokemon_dcgan.pyの順で実行してください。

教師データについて

配布されているポケモンデータセットは第7世代721体のポケモンのメガシンカやフォルムチェンジを含んだの819枚画像で、256(pixel)×256(pixel)×3(channel)になっています。メモリのオーバーフローを避けるため今回使うデータは64(pixel)×64(pixel)×3(channel)に圧縮しました。

DCGANの簡単な説明

詳しくは論文 に書いてありますが、DCGANは以下のように構成されています。

 大まかに言うとGeneratorとDiscriminatorで構成されていて、ランダムに生成されたノイズからGenerator部分で画像を生成し、
Discriminator部分では実際の画像であるかgeneratorであるかを判別するようにできていて、Discriminator正しく判断し、generatorは学習されたDisciminatorをだませるように学習を行っていくという構成です。

実装

 まず、Generatorについては、UpSampling2Dという関数を用いて、畳み込みの逆方向の操作を行うことで画像を生成していきます。また、
 ・ 出力層の活性化関数はtanhを用いる
 ・ バッチノルムを用いる。
といったテクニックがあるようです。

今回実装したGenerator部分のニューラルネットワークは以下のコードで構成されています。

def generator(self):
    noise_shape = (self.z_dim,)

    model = Sequential()
    model.add(Dense(128 * 16 * 16, activation="relu", input_shape=noise_shape))
    model.add(Reshape((16, 16, 128)))
    model.add(BatchNormalization(momentum=0.8))
    model.add(UpSampling2D())
    model.add(Conv2D(128, kernel_size=3, padding="same"))
    model.add(Activation("relu"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(UpSampling2D())
    model.add(Conv2D(64, kernel_size=3, padding="same"))
    model.add(Activation("relu"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Conv2D(3, kernel_size=3, padding="same"))
    model.add(Activation("tanh"))
    model.summary()

    return model

 Discminatorについては畳み込みニューラルネットワーク(CNN)で構成されていて出力層については、正解画像か生成された画像であるか生成された画像であるかの2クラス問題であるため、出力層のノードは1個か2個でもどちらでもよく、1個で設定されているものが一般的であるようであるが、直感的にわかりやすくするために今回は出力層のノードを2個で構成しました。

 また、DCGANに限ってはDiscmininatorは
  ・ pooling層ではなくストライド幅を大きくした畳み込み層を用いること
  ・ 全結合層を用いないこと(層が深ければ深いほど用いないほうが良い)
  ・ 活性化関数はLeakyReLUを用いること
  ・ バッチノルム用いること
などといったテクニックがあるようです。

def build_discriminator(self):
    img_shape = (self.img_rows, self.img_cols, self.channels)

    model = Sequential()
    model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=img_shape, padding="same"))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))
    model.add(Conv2D(64, kernel_size=3, strides=2, padding="same"))
    #model.add(ZeroPadding2D(padding=((0, 1), (0, 1))))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))
    model.add(GlobalAveragePooling2D())
    model.add(Dense(2, activation='sigmoid'))
    model.summary()

    return model

結果

以下100000epoch学習した推移です。

↓100000epoch学習後の出力です。

ポケモンっぽい...??
(これでもパラメーターやネットワークのチューニングに数日かかりました。)