今回は、Keras ACGAN を使って、愛の告白文を生成をしてみます。
こんにちは cedro です。
GAN(Generative Adversarial Network)が学習して行く段階で、最初生成するノイズに、少しづつ変化が現れ、徐々に意味のある形に変化して行く、あのプロセスが好きです。
なので、愛の告白文を、ノイズから徐々に文章に変えていったら、ドキドキしてロマンチックかもしれないなと思ったわけです。
但し、通常のGANでは、何の文字を生成するかは、運しだいなので文章になりません。そこで、ACGANの登場です。
ACGANは画像データをラベルと共に学習するので、ラベルを指定してやると、意図した画像を生成することができます。これなら文章が作れます。
ということで、今回は、Keras ACGAN を使って、愛の告白文を生成をしてみます。
データセットを準備します
今回、使うデータセットは、NDL Lab の文字画像セット(平仮名73文字版)です。
このデータセットは、国立国会図書館デジタルコレクションのインターネット公開資料から、平仮名の文字画像を切り出したもので、合計80,000画像あります。
データ形式は、グレースケール48×48のPNGです。各文字の収録画像は、「ぱぴぷぺぽ」を除くと、すべて1,000文字以上あります。
今回、愛の告白に使う文字は、この10種類とします。もう、愛の告白の内容が分かってしまいましたね(笑)。
「hiragana」フォルダー作り、その下に「0」~「9」のフォルダーを作って、該当する文字をそのまま格納します(前処理は一切不要です)。
プログラムを改造して動かします
今回は、この ACGAN のプログラム(acgan.py)を改造して使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from __future__ import print_function, division from keras.datasets import mnist from keras.layers import Input, Dense, Reshape, Flatten, Dropout, multiply from keras.layers import BatchNormalization, Activation, Embedding, ZeroPadding2D from keras.layers.advanced_activations import LeakyReLU from keras.layers.convolutional import UpSampling2D, Conv2D from keras.models import Sequential, Model from keras.optimizers import Adam from PIL import Image ### import matplotlib.pyplot as plt import numpy as np import glob ### |
新たに必要なパッケージを追加でインポートします。Image と glob は今回のデータセットの読み込みに使うパッケージです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# Load the dataset #(X_train, y_train), (_, _) = mnist.load_data() folder = ["0","1","2","3","4","5","6","7","8","9"] image_size = 28 x = [] y = [] for index, name in enumerate(folder): dir = "./hiragana/" + name files = glob.glob(dir + "/*.png") ### フォルダー内の画像名を全て取得 for i, file in enumerate(files): ### 1つづつ画像を処理する image = Image.open(file) ### 画像を開く image = image.convert("L") ### グレースケールで読み込む(カラーは"RGB") image = image.resize((image_size, image_size)) ### 28×28 にリサイズ data = np.asarray(image) ### 配列に格納 x.append(data) ### 画像配列を順次格納 y.append(index) ### フォルダー名を順次格納 X_train = np.array(x) ### numpy配列の形式に変更 y_train = np.array(y) ### numpy配列の形式に変更 |
データセットの読み込み部分です。まず、MNIST用の部分は無効にします(削除でもOK)。
そして、定番のデータ読み込み部分です。画像を読み込む時に、グレースケールかカラーが選べたり、読み込んだ後にリサイズが(クロップも)自由自在な Pillow は便利ですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def sample_images(self, epoch): r, c = 1, 10 ### 1行10列 noise = np.random.normal(0, 1, (r * c, 100)) sampled_labels = np.array([num for _ in range(r) for num in range(c)]) gen_imgs = self.generator.predict([noise, sampled_labels]) # Rescale images 0 - 1 gen_imgs = 0.5 * gen_imgs + 0.5 fig, axs = plt.subplots(r, c, figsize=(10,2)) ### 表示範囲を指定 cnt = 0 for i in range(r): for j in range(c): axs[j].imshow(gen_imgs[cnt,:,:,0], cmap='gray') ### 1列だけの表示指定 axs[j].axis('off') cnt += 1 fig.savefig("images/%d.png" % epoch) plt.close() |
生成した画像を表示させる部分です。今回、どういう風に表示させるかですが、デフォルトのままだと「ぼくはあなたがすきだ」が10行並び、ストーカーの様になってしまいます(笑)。まずは、シンプルに1行だけ表示することにします。
そのため、r, c = 1, 10 で1行10列とし、それに伴い fig.axs =plt.subplots( r, c) のところに、figsize(10,2) というオプションを追加し縦の表示範囲を狭くしています。
後、画像表示の位置指定に使っている、axs[ i, j ]は1行のみの指定の場合はエラーになってしまうので、axs[ j ] に変更します。
さて、epochs = 2400, sample_interval = 30 の設定(最終行にあります)でプログラムを動かして、出力結果をGIF動画にしてみると
こんな感じです。リケジョならロマンチックと感じてくれるでしょうか? やっぱり、ストーカーみたいですね(笑)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def sample_images(self, epoch): p, q = 1, 10 noise = np.random.normal(0, 1, (p * q, 100)) sampled_labels = np.array([num for _ in range(p) for num in range(q)]) ### 0~9のラベルを生成 gen_imgs = self.generator.predict([noise, sampled_labels]) # Rescale images 0 - 1 gen_imgs = 0.5 * gen_imgs + 0.5 r, c = 3, 3 ### 3行×3列で表示する fig, axs = plt.subplots(r, c, figsize=(5,5)) ### 表示範囲を指定 cnt = 0 for i in range(r): for j in range(c): axs[i,j].imshow(gen_imgs[cnt,:,:,0], cmap='gray') axs[i,j].axis('off') cnt += 1 fig.savefig("images/%d.png" % epoch) plt.close() |
さて、今度は3行×3列で表示させてみましょう。この場合、単純に r, c = 3, 3 としてしまうと、後半の文字を並べて表示する部分はOKですが、前半のラベル生成が0~2の繰り返しになってしまいます。なので、別の変数 p, q を設定し、p, q = 1, 10 としています。
また、3行×3列ということで表示範囲は、figsize(5,5) としています。
さて、epochs = 2400, sample_interval = 30 の設定で再びプログラムを動かして、出力結果をGIF動画にしてみると
こんな感じ。本来の ACGAN の使い方とは違いますが、こういう生成過程を楽しむというのも、アリではないでしょうか。
では、また。