13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

#GANとは
GAN(Generative Adversarial Networks)とは、訓練データを学習し、
それらに似た新しいデータを生成するモデルのことで、生成モデルと呼ばれるネットワークの一種です。
DCGAN(Deep Convolutional GAN)とは、
CNN(Convolutional Neural Network)を使ったGANのことです。

#GANの仕組み
GANではgenerator(画像生成器)とdiscriminator(識別器)という2つのネットワークを使用します。
generatorではノイズから正解データに似た画像を生成し、
discriminator(識別器)を通して、それらが訓練データか、generatorによって生成されたデータかを識別します。
図1.png

discriminatorでは訓練データのラベルが「1」、生成したデータのラベルが「0」と識別されるよう学習していきますが、
generatorでは、生成したデータをdiscriminatorに通したときに、
出力が「1」に近づく(生成データが訓練データと判断される)ように学習していきます。
最終的にdiscriminatorの正答率が「50%」になれば、訓練データと見分けがつかないデータを生成できていることになります。

この関係は、よく紙幣の偽造者と警察の関係に例えられます。
偽造者はなるべく本物の紙幣に似ている紙幣を造ろうとし、警察はそれらを本物の紙幣と見分けようとします。
次第に警察の能力が上がり、本物と偽造紙幣をうまく見分けられるようになれば、
偽造者は、更に本物に近い偽造紙幣を造るようになります。
最終的に偽造者は、本物と区別が付かない偽造紙幣を製造できるようになる…ということですね。

#実際にやってみる
今回の訓練データはこちらです。
スクリーンショット 2018-12-03 9.58.09.png
また、実装に関してはこちらの記事を参考にさせていただきました。
以下、今回実装したコードになります。

①まずは必要なライブラリをインポートします。

from keras.models import Sequential
from keras.layers import Dense, Activation, Reshape
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import UpSampling2D, Conv2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers import Flatten, Dropout
from keras.preprocessing.image import img_to_array, load_img
from keras.optimizers import Adam
import math
import numpy as np
import os
from PIL import Image

②generatorとdiscriminatorを作成します。

generator
def generator_model():
    model = Sequential()
    model.add(Dense(input_dim=100, units=1024))
    model.add(BatchNormalization())
    model.add(Activation('relu'))
    model.add(Dense(32 * 32 * 128))
    model.add(BatchNormalization())
    model.add(Activation('relu'))
    model.add(Reshape((32, 32, 128), input_shape=(32 * 32 * 128,)))
    model.add(UpSampling2D((2, 2)))
    model.add(Conv2D(64, (5, 5), padding="same"))
    model.add(BatchNormalization())
    model.add(Activation('relu'))
    model.add(UpSampling2D((2, 2)))
    model.add(Conv2D(3, (5, 5), padding="same"))
    model.add(Activation('tanh'))
    return model

generatorでは2x2のアップサンプリング(転置畳み込み)を2回行っているので、最終的な画像サイズは4倍になることに注意が必要です。
今回、訓練データの画像サイズは128×128のため、128÷4=32を最初のノードに設定しています。

discriminator
def discriminator_model():
    model = Sequential()
    model.add(Conv2D(64, (5,5), strides=(2, 2), input_shape=(128, 128, 3), padding="same"))
    model.add(LeakyReLU(0.2))
    model.add(Conv2D(128, (5,5), strides=(2, 2)))
    model.add(LeakyReLU(0.2))
    model.add(Flatten())
    model.add(Dense(256))
    model.add(LeakyReLU(0.2))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    model.add(Activation('sigmoid'))
    return model

input_shapeのみ対応する画像サイズに変更する必要があります。
また、出力層を除いて活性化関数にLeaky ReLUを使用しているのも特徴です。

③生成画像を並べて表示するための関数を用意します。(※必須ではありません。)

def combine_images(generated_images):
    total = generated_images.shape[0]
    cols = int(math.sqrt(total))
    rows = math.ceil(float(total)/cols)
    width, height, ch= generated_images.shape[1:]
    output_shape = (
        height * rows,
        width * cols,
        ch
    )
    combined_image = np.zeros(output_shape)

    for index, image in enumerate(generated_images):
        i = int(index/cols)
        j = index % cols
        combined_image[width*i:width*(i+1), height*j:height*(j+1)] = image[:, :, :]
    return combined_image

④最後に学習部分を作成します。

def train():
    # 訓練データ読み込み
    img_list = os.listdir(TRAIN_IMAGE_PATH)
    X_train = []
    for img in img_list:
        img = img_to_array(load_img(TRAIN_IMAGE_PATH+img, target_size=(128,128,3)))
        # -1から1の範囲に正規化
        img = (img.astype(np.float32) - 127.5)/127.5
        X_train.append(img)
    # 4Dテンソルに変換(データの個数, 128, 128, 3)
    X_train = np.array(X_train)

    # generatorとdiscriminatorを作成
    discriminator = discriminator_model()
    d_opt = Adam(lr=1e-5, beta_1=0.1)
    discriminator.compile(loss='binary_crossentropy', optimizer=d_opt)
    # discriminatorの重みを固定(dcganの中のみ)
    discriminator.trainable = False
    generator = generator_model()

    dcgan = Sequential([generator, discriminator])
    g_opt = Adam(lr=2e-4, beta_1=0.5)
    dcgan.compile(loss='binary_crossentropy', optimizer=g_opt)

    num_batches = int(X_train.shape[0] / BATCH_SIZE)
    print('Number of batches:', num_batches)
    for epoch in range(NUM_EPOCH):

        for index in range(num_batches):
            noise = np.array([np.random.uniform(-1, 1, 100) for _ in range(BATCH_SIZE)])
            image_batch = X_train[index*BATCH_SIZE:(index+1)*BATCH_SIZE]
            generated_images = generator.predict(noise, verbose=0, batch_size=BATCH_SIZE)

            # 生成画像を出力
            if (index+1) % (num_batches) == 0:
                image = combine_images(generated_images)
                image = image*127.5 + 127.5
                if not os.path.exists(GENERATED_IMAGE_PATH):
                    os.mkdir(GENERATED_IMAGE_PATH)
                Image.fromarray(image.astype(np.uint8))\
                    .save(GENERATED_IMAGE_PATH+"%04d_%04d.png" % (epoch, index))

            # discriminatorを更新
            X = np.concatenate((image_batch, generated_images))
            # 訓練データのラベルが1、生成画像のラベルが0になるよう学習する
            y = [1]*BATCH_SIZE + [0]*BATCH_SIZE
            d_loss = discriminator.train_on_batch(X, y)

            # generator更新
            noise = np.array([np.random.uniform(-1, 1, 100) for _ in range(BATCH_SIZE)])
            # 生成画像をdiscriminatorにいれたときに
            # 出力が1に近くなる(訓練画像と識別される確率が高くなる)ように学習する
            g_loss = dcgan.train_on_batch(noise, [1]*BATCH_SIZE)

            print("epoch: %d, batch: %d, g_loss: %f, d_loss: %f" % (epoch, index, g_loss, d_loss))

        generator.save_weights('generator.h5')
        discriminator.save_weights('discriminator.h5')

これで実装は完了です。
画像のパスやバッチサイズなどは自由に設定してください。

#結果
epoch = 1
0000_0004.png
epoch = 100
0100_0004.png
epoch = 200
0200_0004.png
epoch = 600
0600_0004.png
epoch = 1000
0999_0004.png
思いのほか猫っぽくなりましたね。

#まとめ
今回初めてDCGANを触ってみました。
DCGANを任意の画像でやろうとすると処理が重たいので、CPU環境ではMNISTなどで試すのがオススメです。
細かい理論についてはまだ詰め切れていない部分があるので、またの機会に。

#参考リンク
はじめてのGAN
GANについて概念から実装まで ~DCGANによるキルミーベイベー生成~
GAN(論文)
DCGAN(論文)

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?