شبکه متخاصم مولد Deep Convolutional

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

این آموزش نشان می دهد که چگونه می توان تصاویر ارقام دست نویس را با استفاده از یک شبکه متخاصم مولد عمیق (DCGAN) تولید کرد. کد با استفاده از Keras Sequential API با یک حلقه آموزشی tf.GradientTape نوشته شده است.

GAN چیست؟

شبکه‌های متخاصم مولد (GAN) یکی از جالب‌ترین ایده‌ها در علم کامپیوتر امروزی است. دو مدل به طور همزمان توسط یک فرآیند متخاصم آموزش داده می شوند. یک مولد ("هنرمند") یاد می گیرد که تصاویری را خلق کند که واقعی به نظر می رسند، در حالی که یک ممیز ("منتقد هنر") یاد می گیرد که تصاویر واقعی را جدا از تقلبی تشخیص دهد.

نمودار یک مولد و تفکیک کننده

در طول آموزش، مولد به تدریج در ایجاد تصاویری که واقعی به نظر می رسند بهتر می شود، در حالی که تمایز کننده در تشخیص آنها بهتر می شود. این فرآیند زمانی به تعادل می رسد که متمایز کننده دیگر نمی تواند تصاویر واقعی را از تقلبی تشخیص دهد.

نمودار دوم مولد و ممیز

این نوت بوک این فرآیند را در مجموعه داده MNIST نشان می دهد. انیمیشن زیر مجموعه ای از تصاویر را نشان می دهد که توسط ژنراتور به مدت 50 دوره آموزش داده شده است. تصاویر به صورت نویز تصادفی شروع می شوند و با گذشت زمان به طور فزاینده ای به ارقام دست نوشته شده شبیه می شوند.

خروجی نمونه

برای کسب اطلاعات بیشتر در مورد GAN ها، به دوره آموزشی آموزش عمیق MIT مراجعه کنید.

برپایی

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 شروع کنید که این دانه را به عنوان ورودی می گیرد، سپس چندین بار نمونه برداری کنید تا به اندازه تصویر دلخواه 28x28x1 برسید. به فعال سازی 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>

png

تبعیض کننده

تمایز کننده یک طبقه بندی کننده تصویر مبتنی بر 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)

ضرر تبعیض

این روش کمیت می‌کند که تمایزکننده چقدر می‌تواند تصاویر واقعی را از تقلبی تشخیص دهد. پیش‌بینی‌های تشخیص‌دهنده روی تصاویر واقعی را با آرایه‌ای از 1s و پیش‌بینی‌های تشخیص‌دهنده روی تصاویر جعلی (تولید شده) را با آرایه‌ای از 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)

png

آخرین پست بازرسی را بازیابی کنید.

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)

png

از imageio برای ایجاد یک گیف متحرک با استفاده از تصاویر ذخیره شده در طول آموزش استفاده کنید.

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 را نشان داده است. در مرحله بعدی، ممکن است بخواهید با مجموعه داده دیگری آزمایش کنید، به عنوان مثال مجموعه داده های Celeb Faces Attributes (CelebA) در مقیاس بزرگ که در Kaggle موجود است. برای کسب اطلاعات بیشتر در مورد GAN ها به آموزش NIPS 2016: Generative Adversarial Networks مراجعه کنید.