使用渐进式GAN生成人工面部的实践指南

416 阅读5分钟

如今,生成对抗网络,简称GAN,是为基于计算机视觉的应用训练数据的有效方法。此外,它还可以用来创建一个合成数据集以满足要求。一个典型的GAN架构使用两个模型进行训练。生成器和鉴别器;生成器用于输出合成图像。鉴别器模型用于根据生成器模型训练的鉴别器输出来检查图像是真的还是假的。为了达到适当的平衡,两个模型都以对抗的方式进行训练。

如果你是GAN的新手,我推荐这篇文章,以便对GAN有一个正确的理解。

当涉及到具有较高像素值的大型数据集时,GAN生成的图像具有尖锐的像素,虽然使训练不稳定,但看起来很清晰。生成高分辨率的图像是一项具有挑战性的任务,因为生成器必须知道图像中涉及的细节和结构。高分辨率的图像会引起任何问题,而判别器可以很容易地发现这些问题;因此,整个训练过程会失败。

GAN的这种行为的解决方案是通过逐步增加层来训练网络。渐进式GAN是标准GAN的扩展,在处理大图像时将生成器保持在稳定模式,以达到更好的稳定性能。方法包括从非常小的图像开始,如4×4像素的图像,并陆续增加层块,将图像的大小增加到8×8、64×64,直到所需的大小,如下图所示。这使得渐进式GAN能够生成高像素的图像,如1024 x 1024的图像。

图像来源

从上图中我们可以看到,在训练过程中,新的卷积层被添加到生成器和判别器模型中;这使得整个模型能够有效地同时学习像素的深层细节和更细的层次像素。

今天在这篇文章中,我们将使用TensorFlow实现Progressive GAN模型,并看看这个模型如何被用来生成人工脸。

渐进式GAN的实现。

下面的演示使用了基于GAN的Tensorflow模型,它将N维Latent空间向量映射到RGB图像中。

下面的代码显示了潜空间向量与图像的映射,并使用梯度下降法生成目标图像,从而得到潜向量。

在这里,潜向量只是压缩数据的一个表示,其中类似的数据点在空间中彼此更接近。这一点很重要,因为GAN模型从潜向量中获取点,并在此基础上生成一个图像。

下面的代码是Tensorflow中渐进式GAN的官方实现的一部分。

安装并导入所有的依赖项。

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

from absl import logging
import imageio
import PIL.Image
import PIL.Image
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
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

帮助函数。

该模型使用潜伏维度为512的倍数。在本节中,我们创建了一个用于显示图像的函数,通过动画来查看图像的变化,以及用于创建新像素的插值函数。

latent_dim = 512
def interpolating_vectors(v1, v2, num_steps):
  v1_n = tf.norm(v1)
  v2_n = tf.norm(v2)
  v2_normal = v2 * (v1_n / v2_n)
  vect = []
  for step in range(num_steps):
    interpol = v1 + (v2_normal - v1) * step / (num_steps - 1)
    interpol_norm = tf.norm(interpol)
    interpol_normal = interpol * (v1_n / interpol_norm)
    vect.append(interpol_normal)
  return tf.stack(vect)

def image_display(img):
  img = tf.constant(img)
  img = tf.image.convert_image_dtype(img, dtype=tf.uint8)
  return PIL.Image.fromarray(img.numpy())

def animation(img):
  img = np.array(img)
  converted_images = np.clip(img * 255, 0, 255).astype(np.uint8)
  imageio.mimsave('./animation.gif', converted_images)
  return embed.embed_file('./animation.gif')  

潜伏空间插值。

在这里,我们使用随机向量进行插值;为此,我们使用TF-hub模块,该模块由Progressive GAN的预训练模型组成。

progan = hub.load('https://tfhub.dev/google/progan-128/1').signatures['default']
def interpolating_between_vect():
  v1 = tf.random.normal([latent_dim])
  v2 = tf.random.normal([latent_dim])
  vect = interpolating_vectors(v1, v2, 150)
  interpolated_images = progan(vect)['default']
  return interpolated_images

interpolated_images = interpolating_between_vect()
animation(interpolated_images)

请看Progressive GAN通过随机生成的向量生成的图像。

在潜伏空间中寻找壁橱矢量。

在这里,我们尝试使用潜空间向量生成目标图像;我们也可以通过将image_from_module_space改为False来上传我们的图像。

inside_image = True  
def from_module_space():
  vector = tf.random.normal([1, 512])
  images = progan(vector)['default'][0]
  return images

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

if inside_image:
  target_image = from_module_space()
else:
  target_image = upload_image()

image_display(target_image)

由于我选择了使用来自模块的图像,这里是我们的目标图像。

下面,我们正在生成我们的起始图像,基于该模型试图收敛到目标图像。我们需要定义目标图像和潜在空间图像之间的损失函数。然后,我们可以使用梯度下降法来寻找使损失最小的变量。

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

下面是我们的起始图像。

现在得到目标图像的最接近的潜点。

def closest_vetor(initial_vector, num_optimization_,
                               steps_per_img):
  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_):
    if (step % 100)==0:
      print()
    print('.', end='')

    with tf.GradientTape() as tape:
      image = progan(vector.read_value())['default'][0]
      if (step % steps_per_img) == 0:
        images.append(image.numpy())

      target_image_difference = loss_fn(image, target_image[:,:,:3])
      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_=200
steps_per_img=5
images, loss = closest_vetor(initial_vector, num_optimization_, steps_per_img)

结果。

请看动画图片,它显示了模型是如何收敛到我们的目标图像的;并请看侧面的结果,左边是生成的图像,右边是目标图像。

animate(np.stack(images))

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

结论。

从这篇文章中,我们已经看到了标准GAN架构在处理大像素值时是如何滞后的。但是,渐进式GAN来完成这个任务;我们看到逐步增加层数是如何帮助生成器函数在整个操作过程中保持稳定并生成一个合理的图像。

参考资料。

The postHands-On Guide to Generating Artificial Faces Using Progressive GANappeared first onAnalytics India Magazine.