شبكة الخصومة التلافيفية التوليفية العميقة

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يوضح هذا البرنامج التعليمي كيفية إنشاء صور للأرقام المكتوبة بخط اليد باستخدام شبكة خصومة توليفية عميقة التلافيف (DCGAN). تمت كتابة الكود باستخدام Keras Sequential API مع حلقة تدريب tf.GradientTape .

ما هي شبكات GAN؟

تعد شبكات الخصومة التوليدية (GANs) واحدة من أكثر الأفكار إثارة للاهتمام في علوم الكمبيوتر اليوم. يتم تدريب نموذجين في وقت واحد من خلال عملية عدائية. يتعلم المولد ("الفنان") إنشاء صور تبدو حقيقية ، بينما يتعلم المميّز ("الناقد الفني") تمييز الصور الحقيقية بصرف النظر عن الصور المقلدة.

رسم تخطيطي لمولد ومميز

أثناء التدريب ، يصبح المولد بشكل تدريجي أفضل في إنشاء صور تبدو حقيقية ، بينما يصبح أداة التمييز أفضل في التمييز بينها. تصل العملية إلى حالة التوازن عندما لا يستطيع المُميِّز التمييز بين الصور الحقيقية والمزيفة.

رسم تخطيطي ثان لمولد ومميز

يوضح هذا الكمبيوتر الدفتري هذه العملية على مجموعة بيانات MNIST. تُظهر الرسوم المتحركة التالية سلسلة من الصور التي أنتجها المولد حيث تم تدريبه على مدى 50 حقبة. تبدأ الصور كضوضاء عشوائية ، وتشبه بشكل متزايد الأرقام المكتوبة بخط اليد بمرور الوقت.

إخراج العينة

لمعرفة المزيد حول شبكات GAN ، راجع دورة Intro to Deep Learning الخاصة بمعهد ماساتشوستس للتكنولوجيا.

يثبت

import tensorflow as tf
tf.__version__
'2.8.0-rc1'
# 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

قم بتحميل مجموعة البيانات وتحضيرها

ستستخدم مجموعة بيانات MNIST لتدريب المولد والمميز. سيقوم المولد بإنشاء أرقام مكتوبة بخط اليد تشبه بيانات MNIST.

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

قم بإنشاء النماذج

يتم تعريف كل من المولد والمميز باستخدام Keras Sequential API .

المولد

يستخدم المولد tf.keras.layers.Conv2DTranspose الطبقات (اختزال) لإنتاج صورة من بذرة (ضوضاء عشوائية). ابدأ بطبقة Dense تأخذ هذه البذرة كمدخل ، ثم قم بتجميعها عدة مرات حتى تصل إلى حجم الصورة المطلوب 28 × 28 × 1. لاحظ تنشيط tf.keras.layers.LeakyReLU لكل طبقة ، باستثناء طبقة الإخراج التي تستخدم tanh.

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

استخدم المولد (الذي لم يتم تدريبه بعد) لإنشاء صورة.

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 0x7f6fe7a04b90>

بي إن جي

التمييز

المُميِّز هو مُصنِّف صور قائم على CNN.

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

استخدم أداة التمييز (التي لم يتم تدريبها بعد) لتصنيف الصور التي تم إنشاؤها على أنها حقيقية أو مزيفة. سيتم تدريب النموذج على إخراج القيم الإيجابية للصور الحقيقية والقيم السلبية للصور المزيفة.

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

تحديد الخسارة والمحسِّنون

تحديد وظائف الخسارة والمحسّنات لكلا الطرازين.

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

الخسارة المميزة

تحدد هذه الطريقة مدى قدرة أداة التمييز على التمييز بين الصور الحقيقية والمزيفة. يقارن تنبؤات أداة التمييز على الصور الحقيقية بمصفوفة من 1 ثانية ، وتنبؤات أداة التمييز على الصور المزيفة (المُنشأة) بمصفوفة من 0 ثانية.

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

خسارة المولد

تحدد خسارة المولد مدى قدرته على خداع أداة التمييز. حدسيًا ، إذا كان أداء المولد جيدًا ، فسيقوم المميّز بتصنيف الصور المزيفة على أنها حقيقية (أو 1). هنا ، قارن قرارات أدوات التمييز على الصور التي تم إنشاؤها بمصفوفة من 1 ثانية.

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

يختلف المُميِّز ومُحسِّن المولد لأنك ستقوم بتدريب شبكتين بشكل منفصل.

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

حفظ نقاط التفتيش

يوضح هذا الكمبيوتر الدفتري أيضًا كيفية حفظ النماذج واستعادتها ، مما قد يكون مفيدًا في حالة مقاطعة مهمة تدريب طويلة الأمد.

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)

تحديد حلقة التدريب

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

تبدأ حلقة التدريب بمولد يتلقى بذرة عشوائية كمدخل. تستخدم تلك البذرة لإنتاج صورة. ثم يتم استخدام أداة التمييز لتصنيف الصور الحقيقية (المستمدة من مجموعة التدريب) والصور المزيفة (التي ينتجها المولد). يتم حساب الخسارة لكل نموذج من هذه النماذج ، ويتم استخدام التدرجات لتحديث المولد والمميز.

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

توليد وحفظ الصور

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

تدريب النموذج

قم باستدعاء طريقة train() المحددة أعلاه لتدريب المولد والمميز في وقت واحد. لاحظ أن تدريب شبكات GAN يمكن أن يكون صعبًا. من المهم ألا يتغلب المولد والمميز على بعضهما البعض (على سبيل المثال ، أن يتدربوا بمعدل مماثل).

في بداية التدريب ، تبدو الصور التي تم إنشاؤها وكأنها ضوضاء عشوائية. مع تقدم التدريب ، ستبدو الأرقام الناتجة حقيقية بشكل متزايد. بعد حوالي 50 حقبة ، أصبحت تشبه أرقام MNIST. قد يستغرق هذا حوالي دقيقة واحدة / فترة مع الإعدادات الافتراضية على Colab.

train(train_dataset, EPOCHS)

بي إن جي

استعادة أحدث نقطة تفتيش.

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

قم بإنشاء ملف GIF

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

بي إن جي

استخدم imageio لإنشاء صورة gif متحركة باستخدام الصور المحفوظة أثناء التدريب.

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

الخطوات التالية

أظهر هذا البرنامج التعليمي الكود الكامل اللازم لكتابة وتدريب GAN. كخطوة تالية ، قد ترغب في تجربة مجموعة بيانات مختلفة ، على سبيل المثال مجموعة بيانات سمات وجوه المشاهير واسعة النطاق (CelebA) المتوفرة على Kaggle . لمعرفة المزيد حول شبكات GAN ، راجع البرنامج التعليمي NIPS 2016: شبكات الخصومة التوليدية .