Tạo khuôn mặt nhân tạo với mô hình GAN tiến bộ CelebA

Xem trên TensorFlow.org Chạy trong Google Colab Xem trên GitHub Tải xuống sổ ghi chép Xem mô hình TF Hub

Colab này trình bày việc sử dụng mô-đun TF Hub dựa trên mạng đối thủ chung (GAN). Mô-đun ánh xạ từ vectơ N chiều, được gọi là không gian tiềm ẩn, sang hình ảnh RGB.

Hai ví dụ được cung cấp:

  • Lập bản đồ từ vũ trụ tiềm ẩn đến hình ảnh, và
  • Cho một hình ảnh mục tiêu, sử dụng gradient descent để tìm một vector tiềm ẩn mà tạo ra một hình ảnh tương tự như hình ảnh mục tiêu.

Điều kiện tiên quyết tùy chọn

Nhiều mô hình hơn

Ở đây bạn có thể tìm thấy tất cả các mẫu hiện đang được lưu trữ trên tfhub.dev có thể tạo ra hình ảnh.

Thành lập

# Install imageio for creating animations.
pip -q install imageio
pip -q install scikit-image
pip install git+https://github.com/tensorflow/docs

Nhập khẩu và định nghĩa chức năng

from absl import logging

import imageio
import PIL.Image
import matplotlib.pyplot as plt
import numpy as np

import tensorflow as tf
tf.random.set_seed(0)

import tensorflow_hub as hub
from tensorflow_docs.vis import embed
import time

try:
  from google.colab import files
except ImportError:
  pass

from IPython import display
from skimage import transform

# We could retrieve this value from module.get_input_shapes() if we didn't know
# beforehand which module we will be using.
latent_dim = 512


# Interpolates between two vectors that are non-zero and don't both lie on a
# line going through origin. First normalizes v2 to have the same norm as v1. 
# Then interpolates between the two vectors on the hypersphere.
def interpolate_hypersphere(v1, v2, num_steps):
  v1_norm = tf.norm(v1)
  v2_norm = tf.norm(v2)
  v2_normalized = v2 * (v1_norm / v2_norm)

  vectors = []
  for step in range(num_steps):
    interpolated = v1 + (v2_normalized - v1) * step / (num_steps - 1)
    interpolated_norm = tf.norm(interpolated)
    interpolated_normalized = interpolated * (v1_norm / interpolated_norm)
    vectors.append(interpolated_normalized)
  return tf.stack(vectors)

# Simple way to display an image.
def display_image(image):
  image = tf.constant(image)
  image = tf.image.convert_image_dtype(image, tf.uint8)
  return PIL.Image.fromarray(image.numpy())

# Given a set of images, show an animation.
def animate(images):
  images = np.array(images)
  converted_images = np.clip(images * 255, 0, 255).astype(np.uint8)
  imageio.mimsave('./animation.gif', converted_images)
  return embed.embed_file('./animation.gif')

logging.set_verbosity(logging.ERROR)

Nội suy không gian tiềm ẩn

Vectơ ngẫu nhiên

Nội suy không gian tiềm ẩn giữa hai vectơ được khởi tạo ngẫu nhiên. Chúng tôi sẽ sử dụng một mô-đun TF Hub progan-128 có chứa một pre-đào tạo Progressive GAN.

progan = hub.load("https://tfhub.dev/google/progan-128/1").signatures['default']
def interpolate_between_vectors():
  v1 = tf.random.normal([latent_dim])
  v2 = tf.random.normal([latent_dim])

  # Creates a tensor with 25 steps of interpolation between v1 and v2.
  vectors = interpolate_hypersphere(v1, v2, 50)

  # Uses module to generate images from the latent space.
  interpolated_images = progan(vectors)['default']

  return interpolated_images

interpolated_images = interpolate_between_vectors()
animate(interpolated_images)

gif

Tìm vectơ gần nhất trong không gian tiềm ẩn

Sửa một hình ảnh mục tiêu. Ví dụ, sử dụng hình ảnh được tạo từ mô-đun hoặc tải lên của riêng bạn.

image_from_module_space = True  # @param { isTemplate:true, type:"boolean" }

def get_module_space_image():
  vector = tf.random.normal([1, latent_dim])
  images = progan(vector)['default'][0]
  return images

def upload_image():
  uploaded = files.upload()
  image = imageio.imread(uploaded[list(uploaded.keys())[0]])
  return transform.resize(image, [128, 128])

if image_from_module_space:
  target_image = get_module_space_image()
else:
  target_image = upload_image()

display_image(target_image)

png

Sau khi xác định một hàm mất mát giữa hình ảnh đích và hình ảnh được tạo ra bởi một biến không gian tiềm ẩn, chúng ta có thể sử dụng gradient descent để tìm các giá trị biến giảm thiểu tổn thất.

tf.random.set_seed(42)
initial_vector = tf.random.normal([1, latent_dim])
display_image(progan(initial_vector)['default'][0])

png

def find_closest_latent_vector(initial_vector, num_optimization_steps,
                               steps_per_image):
  images = []
  losses = []

  vector = tf.Variable(initial_vector)  
  optimizer = tf.optimizers.Adam(learning_rate=0.01)
  loss_fn = tf.losses.MeanAbsoluteError(reduction="sum")

  for step in range(num_optimization_steps):
    if (step % 100)==0:
      print()
    print('.', end='')
    with tf.GradientTape() as tape:
      image = progan(vector.read_value())['default'][0]
      if (step % steps_per_image) == 0:
        images.append(image.numpy())
      target_image_difference = loss_fn(image, target_image[:,:,:3])
      # The latent vectors were sampled from a normal distribution. We can get
      # more realistic images if we regularize the length of the latent vector to 
      # the average length of vector from this distribution.
      regularizer = tf.abs(tf.norm(vector) - np.sqrt(latent_dim))

      loss = target_image_difference + regularizer
      losses.append(loss.numpy())
    grads = tape.gradient(loss, [vector])
    optimizer.apply_gradients(zip(grads, [vector]))

  return images, losses


num_optimization_steps=200
steps_per_image=5
images, loss = find_closest_latent_vector(initial_vector, num_optimization_steps, steps_per_image)
....................................................................................................
....................................................................................................
plt.plot(loss)
plt.ylim([0,max(plt.ylim())])
(0.0, 6696.301751708985)

png

animate(np.stack(images))

gif

So sánh kết quả với mục tiêu:

display_image(np.concatenate([images[-1], target_image], axis=1))

png

Chơi với ví dụ trên

Nếu hình ảnh từ không gian mô-đun, quá trình giảm xuống nhanh chóng và hội tụ đến một mẫu hợp lý. Thử giảm dần đến một hình ảnh mà không phảitừ không gian mô-đun. Phần gốc sẽ chỉ hội tụ nếu hình ảnh ở gần một cách hợp lý với không gian của hình ảnh huấn luyện.

Làm thế nào để làm cho nó giảm xuống nhanh hơn và hình ảnh thực tế hơn? Người ta có thể thử:

  • sử dụng sự mất mát khác nhau trên sự khác biệt hình ảnh, ví dụ: bậc hai,
  • sử dụng bộ điều chỉnh khác nhau trên vectơ tiềm ẩn,
  • khởi tạo từ một vectơ ngẫu nhiên trong nhiều lần chạy,
  • Vân vân.