หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

เครือข่ายผู้ให้คำปรึกษาเชิงลึกเชิงลึก

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

บทช่วยสอนนี้สาธิตวิธีสร้างภาพของตัวเลขที่เขียนด้วยลายมือโดยใช้ Deep Convolutional Generative Adversarial Network (DCGAN) โค้ดถูกเขียนโดยใช้ Keras Sequential API พร้อมกับลูปการฝึกอบรม tf.GradientTape

GAN คืออะไร

Generative Adversarial Networks (GANs) เป็นหนึ่งในแนวคิดที่น่าสนใจที่สุดในวิทยาการคอมพิวเตอร์ในปัจจุบัน สองรุ่นได้รับการฝึกฝนพร้อมกันโดยกระบวนการที่ไม่เป็นมิตร เครื่องกำเนิดไฟฟ้า ("ศิลปิน") เรียนรู้ที่จะสร้างภาพที่ดูสมจริงในขณะที่ผู้ เลือกปฏิบัติ ("นักวิจารณ์ศิลปะ") เรียนรู้ที่จะบอกภาพที่แท้จริงนอกเหนือจากของปลอม

แผนภาพของเครื่องกำเนิดไฟฟ้าและตัวแยกส่วน

ในระหว่างการฝึกอบรม เครื่องกำเนิด จะก้าวหน้าในการสร้างภาพที่ดูสมจริงในขณะที่ผู้ เลือกปฏิบัติ จะ แยกแยะภาพได้ ดีขึ้น กระบวนการถึงจุดสมดุลเมื่อ discriminator ไม่สามารถแยกแยะภาพจริงจากการปลอมได้อีกต่อไป

ไดอะแกรมที่สองของเครื่องกำเนิดไฟฟ้าและ discriminator

สมุดบันทึกนี้สาธิตกระบวนการนี้ในชุดข้อมูล MNIST แอนิเมชันต่อไปนี้แสดงชุดภาพที่สร้างโดย เครื่องกำเนิดไฟฟ้า เนื่องจากได้รับการฝึกฝนมาแล้ว 50 ยุค ภาพจะเริ่มเป็นจุดรบกวนแบบสุ่มและคล้ายกับตัวเลขที่เขียนด้วยมือมากขึ้นเมื่อเวลาผ่านไป

ตัวอย่างผลลัพธ์

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ GAN เราขอแนะนำหลักสูตร Introduction to Deep Learning ของ MIT

นำเข้า TensorFlow และห้องสมุดอื่น ๆ

 import tensorflow as tf
 
 tf.__version__
 
'2.2.0'
 # To generate GIFs
!pip install -q imageio
 
 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 (upsampling) เพื่อสร้างภาพจากเมล็ด (สัญญาณรบกวนแบบสุ่ม) เริ่มต้นด้วยเลเยอร์ 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 0x7f57b8444a90>

PNG

Discriminator

discriminator เป็นตัวแยกประเภทภาพตาม 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 (ที่ยังไม่ผ่านการฝึกอบรม) เพื่อจัดประเภทรูปภาพที่สร้างเป็นของจริงหรือของปลอม แบบจำลองนี้จะได้รับการฝึกฝนให้ส่งออกค่าบวกสำหรับภาพจริงและค่าลบสำหรับภาพปลอม

 discriminator = make_discriminator_model()
decision = discriminator(generated_image)
print (decision)
 
tf.Tensor([[0.00012482]], 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)
 

การสูญเสีย Discriminator

วิธีการนี้จะวัดว่าผู้จำแนกสามารถจำแนกภาพจริงจากการปลอมได้ดีเพียงใด มันเปรียบเทียบการคาดการณ์ของ discriminator ในภาพจริงกับอาร์เรย์ 1s และการคาดการณ์ของ discriminator ในภาพปลอม (สร้างขึ้น) กับอาร์เรย์ 0s

 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) ที่นี่เราจะเปรียบเทียบการตัดสินใจของผู้เลือกปฏิบัติในภาพที่สร้างขึ้นกับอาร์เรย์ 1s

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

discriminator และเครื่องมือเพิ่มประสิทธิภาพตัวสร้างนั้นแตกต่างกันเนื่องจากเราจะฝึกอบรมสองเครือข่ายแยกกัน

 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

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

ลูปการฝึกอบรมเริ่มต้นด้วยตัวสร้างที่รับเมล็ดสุ่มเป็นอินพุต เมล็ดนั้นใช้สำหรับสร้างภาพ discriminator นั้นจะใช้ในการจำแนกภาพจริง (วาดจากชุดฝึกอบรม) และปลอมภาพ (ผลิตโดยเครื่องกำเนิดไฟฟ้า) การสูญเสียจะถูกคำนวณสำหรับแต่ละรุ่นเหล่านี้และการไล่ระดับสีจะใช้ในการปรับปรุงเครื่องกำเนิดและตัวเลือก

 # 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 we 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() กำหนดไว้ด้านบนเพื่อฝึกเครื่องกำเนิดไฟฟ้าและตัวแยกสัญญาณพร้อมกัน หมายเหตุการฝึกฝน GANs อาจเป็นเรื่องยุ่งยาก สิ่งสำคัญคือเครื่องกำเนิดไฟฟ้าและเครื่องแยกประเภทไม่สามารถเอาชนะซึ่งกันและกันได้ (เช่นฝึกในอัตราที่ใกล้เคียงกัน)

ที่จุดเริ่มต้นของการฝึกอบรมภาพที่สร้างขึ้นจะมีจุดรบกวนแบบสุ่ม เมื่อการฝึกดำเนินไปตัวเลขที่สร้างขึ้นจะดูสมจริงมากขึ้น หลังจากผ่านไปประมาณ 50 ยุคพวกมันคล้ายกับตัวเลข MNIST อาจใช้เวลาประมาณหนึ่งนาที / ยุคเมื่อมีการตั้งค่าเริ่มต้นใน Colab

 train(train_dataset, EPOCHS)
 

PNG

คืนค่าจุดตรวจสอบล่าสุด

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

สร้าง 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 เพื่อสร้างภาพเคลื่อนไหว gif โดยใช้ภาพที่บันทึกไว้ในระหว่างการฝึก

 anim_file = 'dcgan.gif'

with imageio.get_writer(anim_file, mode='I') as writer:
  filenames = glob.glob('image*.png')
  filenames = sorted(filenames)
  last = -1
  for i,filename in enumerate(filenames):
    frame = 2*(i**0.5)
    if round(frame) > round(last):
      last = frame
    else:
      continue
    image = imageio.imread(filename)
    writer.append_data(image)
  image = imageio.imread(filename)
  writer.append_data(image)

import IPython
if IPython.version_info > (6,2,0,''):
  display.Image(filename=anim_file)
 

หากคุณทำงานใน Colab คุณสามารถดาวน์โหลดภาพเคลื่อนไหวด้วยรหัสด้านล่าง:

 try:
  from google.colab import files
except ImportError:
   pass
else:
  files.download(anim_file)
 

ขั้นตอนถัดไป

บทช่วยสอนนี้แสดงรหัสสมบูรณ์ที่จำเป็นสำหรับการเขียนและฝึกอบรม GAN ในขั้นตอนถัดไปคุณอาจต้องการทดสอบกับชุดข้อมูลอื่นตัวอย่างเช่นชุดข้อมูล Celeb Faces Attributes (CelebA) ขนาดใหญ่ที่ มีอยู่ใน Kaggle หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ GAN เราขอแนะนำ บทเรียน NIPS 2016: เครือข่ายผู้ ไม่หวังดี