はじめに
こんにちは、swim-loverです。Pythonをとりかかりとして、Pytorch Tensorflowで機械学習を勉強していいます。「使いながら覚える」をコンセプトに勉強しています。
このBlogでは、識別モデルの物体検出について何度か取り扱ってきましたが、今回、生成モデルの一つである、GAN”ギャン”についても試してみます。
Part(1)では、Generatorについて取り上げました。今回は、Discriminatorについて進めていきます。
DCGNA Discriminatorモデルの作成
Conv2D()で畳み込み演算されています。
LeakyReLU()は活性化関数。
こちらに詳しく説明されていました。
Dropout()は、過学習の防止とのこと。出力の30%は、無効化されるようです。
こちらに詳しく説明されていました。
6行目のDropoutが終わった時点で、出力データは、14x14x64となります。
10行目のDropoutが終わった時点で、出力データは、7x7x128となります。
Flatten()の多次元配列を一次元配列へ変換。
Dense(1)で、1出力に全結合します。
def make_discriminator_model():
model = tf.keras.Sequential()
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Flatten())
model.add(layers.Dense(1))
return model
Descriminatorのモデルを確認します。
generatorが生成したイメージをDescriminatorに入力してみます。
generator=make_generator_model()
noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)
plt.imshow(generated_image[0, :, :, 0], cmap='gray')
discriminator = make_discriminator_model()
decision = discriminator(generated_image)
print (decision)
本物である確率が、-0.00020956ということでしょうか?まだ何もトレーニングしていないので、とりあえず動かしてみたということでしょう。
DCGAN 損失関数の設定
Generator、Descriminatorの出力は、本物、偽物の2つのどちらかなので、損失関数には、BinaryCrossentropyを用いています。Crossentropyについては、別の回で取り上げました。
from_logit=Trueは、クロスエントロピーの入力データである予測値y_predをlogit値(ロジット -inf~inf)として扱うか、確率値(0~1)として扱うかの設定のようです。
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
DCGAN Discriminatorの損失関数
real_outputは本物の画像をDiscriminatorに入れた時の出力、ones_like(real_output)は本物の画像ラベルを示しています。corss_entropyの結果であるreal_lossが、1であれば、正しく本物を判定したことになります。
逆に、
Fake_outputは偽物の画像をDiscriminatorに入れた時の出力、Zeros_like(fake_output)は偽物の画像ラベルを示しています。corss_entropyの結果であるfake_lossが、0であれば、正しく偽物を判定したことになります。
Real_lossとfake_lossの合計をtotal_lossとしています。
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
DCGAN Generatorの損失関数
Generatorの損失関数は、偽物画像をDiscriminatorに入れた時の出力であるfake_outputとones_like(fake_output)を比較していることに注目します。
もしfake_outputが1であれば、Genaratorが作り出した画像は、Discriminatorで偽物と判断できなかったことになります。
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
DCGAN Optimiztor 最適化アルゴリズム
DiscriminatorとGeneratorのそれぞれにOptimiztorを用意します。Optimiztorは、損失を少なくしていくために用いるアルゴリズムですが、ここでは、Adamを用いています。
最適化アルゴリズムについては、以下で詳細に説明されていました。
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
DCGAN チェックポイント 再開用ファイルの保存
途中で学習を中断した場合、それまで学習した内容の保存し、再開する機能がTensorflowに予め用意されているようです。実用上、必須の機能だともいますので、サンプルコードをそのまま使用します。
from google.colab import drive
drive.mount('/content/drive')
cd drive/MyDrive/machine/dcgan
!pwd
今回は、checkpointをMyDrive/machne/dcgan以下に保存することにしました。
checkpoint_dir = './'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
discriminator_optimizer=discriminator_optimizer,
generator=generator,
discriminator=discriminator)
DCGAN パラメーター設定とノイズ生成
エポック数を50で定義。100次元のノイズデータを16個生成します。
ここで作成したseedは以降の処理では使われないようです。
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16
# You will reuse this seed overtime (so it's easier)
# to visualize progress in the animated GIF)
seed = tf.random.normal([num_examples_to_generate, noise_dim])
DCGAN トレーニング処理
入力は、本物画像です。
6行目 generatorで画像を生成します。
7行目 本物画像を識別しています。
8行目 偽物画像を識別しています。
11行目 generatorのLoss計算
12行目 discriminatorのLoss計算
14,15行目 gradientの勾配の計算(Optimizorで使用)
17,18行目 Optimizerの実行
@tf.function
def train_step(images):
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True)
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
DCGAN トレーニング本体
入力は、データセット、エポック数です。
6行目でtrain_step()を呼び出しています。データセット数分繰り返し、その外(2行目)で、エポック数回繰り返します。
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
train_step(image_batch)
# Produce images for the GIF as you go
display.clear_output(wait=True)
generate_and_save_images(generator,
epoch + 1,
seed)
# Save the model every 15 epochs
if (epoch + 1) % 15 == 0:
checkpoint.save(file_prefix = checkpoint_prefix)
print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
# Generate after the final epoch
display.clear_output(wait=True)
generate_and_save_images(generator,
epochs,
seed)
以下は、生成した画像の保存処理です。
def generate_and_save_images(model, epoch, test_input):
# Notice `training` is set to False.
# This is so all layers run in inference mode (batchnorm).
predictions = model(test_input, training=False)
fig = plt.figure(figsize=(4, 4))
for i in range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
plt.axis('off')
plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()
DCGAN トレーニング実施!
いよいよトレーニングを実施します。
train(train_dataset, EPOCHS)
学習過程を確認してみました。
エポック1
エポック5
エポック10、数字っぽくなってきた感じ。
エポック15
エポック20
エポック30 はっきり数字と認識できる画像もあります。
エポック40
エポック50 最終のエポックです。
まとめ
今回、生成モデルの一つである、DCGANを試してみました。エポック50では、ほとんどの画像で数字と認識できるレベルまで学習できていると思います。
組み込み系ソフトエンジニアをしています。これまでフロントエンド技術は避けてきましたが、食わず嫌いをやめて、勉強を始めました。
趣味は、水泳、ロードバイク、ランニング、登山です。
組み込み系技術ネタ、勉強したフロントエンド技術、たまに趣味の運動について発信していきます。
どうぞよろしくお願いします。
コメント