Derin Evrişimli Üretken Düşman Ağı

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Not defterini indir

Bu öğretici, bir Derin Evrişimli Üretken Düşman Ağı (DCGAN) kullanarak el yazısı rakamların görüntülerinin nasıl oluşturulacağını gösterir. Kod, birtf.GradientTape eğitim döngüsü iletf.GradientTape Sequential API kullanılarak yazılmıştır.

GAN'lar nelerdir?

Üretken Düşman Ağları (GAN'lar), günümüzde bilgisayar bilimindeki en ilginç fikirlerden biridir. İki model, çekişmeli bir süreçle aynı anda eğitilir. Bir üretici ("sanatçı") gerçek görünen görüntüler yaratmayı öğrenirken, bir ayrımcı ("sanat eleştirmeni") gerçek görüntüleri sahte olanlardan ayırmayı öğrenir.

Bir jeneratör ve ayrımcının bir diyagramı

Eğitim sırasında, üretici gerçek görünen görüntüler yaratmada giderek daha iyi hale gelirken, ayrımcı bunları ayırt etmede daha iyi hale gelir. Ayrımcı artık gerçek görüntüleri sahtelerden ayırt edemediğinde süreç dengeye ulaşır.

Bir jeneratör ve ayırıcının ikinci bir diyagramı

Bu not defteri, bu işlemi MNIST veri kümesinde gösterir. Aşağıdaki animasyon, jeneratör tarafından 50 dönem boyunca eğitilirken üretilen bir dizi görüntüyü göstermektedir. Görüntüler rastgele gürültü olarak başlar ve zamanla giderek artan şekilde elle yazılmış rakamlara benzer.

örnek çıktı

GAN'lar hakkında daha fazla bilgi edinmek için MIT'nin Derin Öğrenmeye Giriş kursuna bakın.

Kurulum

import tensorflow as tf
tf.__version__
'2.5.0'
# To generate GIFs
pip install imageio
pip install git+https://github.com/tensorflow/docs
import glob
import imageio
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
from tensorflow.keras import layers
import time

from IPython import display

Veri kümesini yükleyin ve hazırlayın

Üreteci ve ayrımcıyı eğitmek için MNIST veri setini kullanacaksınız. Jeneratör, MNIST verilerine benzeyen el yazısı rakamları üretecektir.

(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5  # Normalize the images to [-1, 1]
BUFFER_SIZE = 60000
BATCH_SIZE = 256
# Batch and shuffle the data
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)

Modelleri oluşturun

Hem oluşturucu hem de ayırıcı, Keras Sıralı API'si kullanılarak tanımlanır.

Jeneratör

Oluşturucu, bir tohumdan (rastgele gürültü) bir görüntü üretmek için tf.keras.layers.Conv2DTranspose (üst örnekleme) katmanlarını kullanır. Bu çekirdeği girdi olarak alan bir Dense katmanla başlayın, ardından istediğiniz 28x28x1 görüntü boyutuna ulaşana kadar birkaç kez üst örnek alın. tf.keras.layers.LeakyReLU kullanan çıktı katmanı dışında her katman için tf.keras.layers.LeakyReLU aktivasyonuna dikkat edin.

def make_generator_model():
    model = tf.keras.Sequential()
    model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Reshape((7, 7, 256)))
    assert model.output_shape == (None, 7, 7, 256)  # Note: None is the batch size

    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
    assert model.output_shape == (None, 7, 7, 128)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    assert model.output_shape == (None, 14, 14, 64)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
    assert model.output_shape == (None, 28, 28, 1)

    return model

Bir görüntü oluşturmak için (henüz eğitimsiz) oluşturucuyu kullanın.

generator = make_generator_model()

noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)

plt.imshow(generated_image[0, :, :, 0], cmap='gray')
<matplotlib.image.AxesImage at 0x7f7322b54fd0>

png

Ayrımcı

Ayrımcı, CNN tabanlı bir görüntü sınıflandırıcıdır.

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

Oluşturulan görüntüleri gerçek veya sahte olarak sınıflandırmak için (henüz eğitimsiz) ayırıcıyı kullanın. Model, gerçek görüntüler için pozitif değerler ve sahte görüntüler için negatif değerler verecek şekilde eğitilecektir.

discriminator = make_discriminator_model()
decision = discriminator(generated_image)
print (decision)
tf.Tensor([[-0.00085865]], shape=(1, 1), dtype=float32)

Kaybı ve optimize edicileri tanımlayın

Her iki model için de kayıp fonksiyonlarını ve optimize edicileri tanımlayın.

# This method returns a helper function to compute cross entropy loss
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

Ayrımcı kaybı

Bu yöntem, ayırıcının gerçek görüntüleri sahtelerden ne kadar iyi ayırt edebildiğini ölçer. Ayırt edicinin gerçek görüntüler üzerindeki tahminlerini bir dizi 1'lerle ve ayırt edicinin sahte (oluşturulan) görüntüler üzerindeki tahminlerini bir dizi 0 ile karşılaştırır.

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

Jeneratör kaybı

Jeneratörün kaybı, ayrımcıyı ne kadar iyi kandırabildiğini ölçer. Sezgisel olarak, jeneratör iyi performans gösteriyorsa, ayrımcı sahte görüntüleri gerçek (veya 1) olarak sınıflandırır. Burada, oluşturulan görüntüler üzerindeki ayrımcı kararlarını 1s dizisiyle karşılaştırın.

def generator_loss(fake_output):
    return cross_entropy(tf.ones_like(fake_output), fake_output)

Ayrımcı ve jeneratör optimize ediciler, iki ağı ayrı ayrı eğiteceğiniz için farklıdır.

generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)

Kontrol noktalarını kaydet

Bu not defteri ayrıca, uzun süredir devam eden bir eğitim görevinin kesintiye uğraması durumunda yardımcı olabilecek modellerin nasıl kaydedileceğini ve geri yükleneceğini de gösterir.

checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
                                 discriminator_optimizer=discriminator_optimizer,
                                 generator=generator,
                                 discriminator=discriminator)

Eğitim döngüsünü tanımlayın

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])

Eğitim döngüsü, jeneratörün girdi olarak rastgele bir tohum almasıyla başlar. Bu tohum bir görüntü üretmek için kullanılır. Ayırıcı daha sonra gerçek görüntüleri (eğitim setinden çekilmiş) ve sahte görüntüleri (jeneratör tarafından üretilen) sınıflandırmak için kullanılır. Bu modellerin her biri için kayıp hesaplanır ve gradyanlar, üreteci ve ayırıcıyı güncellemek için kullanılır.

# Notice the use of `tf.function`
# This annotation causes the function to be "compiled".
@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))
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)

Görüntüler oluşturun ve kaydedin

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()

Modeli eğit

Üreteci ve ayırıcıyı aynı anda eğitmek için yukarıda tanımlanan train() yöntemini çağırın. GAN'ların eğitiminin zor olabileceğini unutmayın. Jeneratör ve ayrımcının birbirine üstün gelmemesi önemlidir (örneğin, benzer bir hızda antrenman yapmaları).

Eğitimin başında oluşturulan görüntüler rastgele gürültü gibi görünür. Eğitim ilerledikçe, oluşturulan rakamlar giderek daha gerçekçi görünecek. Yaklaşık 50 çağdan sonra MNIST rakamlarına benziyorlar. Bu, Colab'daki varsayılan ayarlarla yaklaşık bir dakika/dönem alabilir.

train(train_dataset, EPOCHS)

png

En son kontrol noktasını geri yükleyin.

checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f72983d2bd0>

GIF oluştur

# Display a single image using the epoch number
def display_image(epoch_no):
  return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))
display_image(EPOCHS)

png

Eğitim sırasında kaydedilen görüntüleri kullanarak hareketli bir gif oluşturmak için imageio kullanın.

anim_file = 'dcgan.gif'

with imageio.get_writer(anim_file, mode='I') as writer:
  filenames = glob.glob('image*.png')
  filenames = sorted(filenames)
  for filename in filenames:
    image = imageio.imread(filename)
    writer.append_data(image)
  image = imageio.imread(filename)
  writer.append_data(image)
import tensorflow_docs.vis.embed as embed
embed.embed_file(anim_file)

gif

Sonraki adımlar

Bu öğretici, bir GAN yazmak ve eğitmek için gereken kodun tamamını göstermiştir. Bir sonraki adım olarak, farklı bir veri kümesi deneme isteyebilirsiniz, örneğin büyük ölçekli Celeb Nitelikler (CelebA) veri kümesini Faces Kaggle geçerli . GAN'lar hakkında daha fazla bilgi edinmek için NIPS 2016 Eğitimi: Üretken Düşman Ağları'na bakın .