使用变分自编码器(VAE)控制人脸属性生成人脸图片

「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

使用VAE生成人脸图片

变分自编码器(VAE)的基础知识参考博文《变分自编码器(VAE)详解与实现(采用TensorFlow2实现)》。作为VAE的应用,我们将使用VAE生成一些可控制属性的人脸图片。可用的人脸数据集包括:

  1. Celeb A: 这是在学术界很流行的数据集,因为它包含面部特征的注释,不能用于商业用途。
  2. Flickr-Faces-HQ Dataset, FFHQ: 该数据集可免费用于商业用途,并包含高分辨率图像。

网络架构

网络的输入形状为(112,112,3),可以在数据预处理时调整图片尺寸。由于数据集较复杂,可以增加滤波器数量以增加网络容量。因此,编码器中的卷积层如下:

a) Conv2D(filters = 32, kernel_size=(3,3), strides = 2)
b) Conv2D(filters = 32, kernel_size=(3,3), strides = 2)
c) Conv2D(filters = 64, kernel_size=(3,3), strides = 2)
d) Conv2D(filters = 64, kernel_size=(3,3), strides = 2)
复制代码

人脸重建

让我们先看一下VAE的重构图像效果:

重建图片

尽管重建的图片并不完美,但它们至少看起来不错。 VAE设法从输入图像中学习了一些特征,并使用它们来绘制新的面孔。可以看出,VAE可以更好地重建女性面孔。这是由于Celeb_A数据集中女性的比例较高。这也就是为什么男性的肤色更趋向年轻、女性化。

观察图像背景,由于图像背景的多样性,因此编码器无法将每个细节编码至低维度,因此我们可以看到VAE对背景颜色进行编码,而解码器则基于这些颜色创建模糊的背景。

生成新面孔

为了生成新图像,我们从标准的高斯分布中采样随机数,并将传递给解码器:

z_samples = np.random.normal(loc=0., scale=1, size=(image_num, z_dim))
images = vae.decoder(z_samples.astype(np.float32))
复制代码

但,某些生成的面孔看起来太恐怖了!

生成新面孔

我们可以使用采样技巧来提高图像保真度。

采样技巧

可以看到,训练后的VAE可以很好地重建人脸。但,随机抽样潜变量生成的图像中存在问题。为了调试该问题,将数据集中图像输入到VAE解码器中,以获取潜在空间的均值和方差。然后,绘制了每个潜在空间变量的均值:

分布情况

从理论上讲,它们应该以0为均值且方差为1,但随机采样的样本并不总是与解码器期望的分布匹配。这是采样技巧技巧的地方,收集潜在变量的平均标准差(一个标量值),该标准差用于生成正态分布的样本(200维)。然后,在其中添加了平均均值(200个维度)。

yep,现在生成的图片看起来好多了!

生成图片

接下来,将介绍如何进行面部属性编辑,而不是生成随机的面孔。

控制人脸属性

潜在空间

本质上,潜在空间意味着潜在变量的每个可能值。在我们的VAE中,它是200个维度的向量(或者称200个变量)。我们希望每个变量都包含独特的语义,例如z[0]代表眼睛,z[1]代表眼睛的颜色,依此类推,事情从来没有那么简单。假设信息是在所有潜在向量中编码的,就可以使用向量算术探索潜在空间。

属性控制

使用一个二维示例解释属性控制的原理。假设现在在地图上的(0,0)点,而目的地位于(x, y)。因此,朝目的地的方向是(x-0, y-0)除以(x, y)的L2范数,可以将方向表示为(x_dot, y_dot)。因此,每次移动(x_dot, y_dot)时,都在朝着目的地移动。每次移动(-2 * x_dot, -2 * y_dot)时,将以两倍的步幅远离目的地。

类似的,如果我们知道了微笑属性的方向向量,则可以将其添加到潜在变量中以使人脸附加微笑属性:

new_z_samples = z_samples +  smiling_magnitude*smiling_vector
复制代码

smile_magnitude是我们设置的标量值,因此下一步是找出获取smile_vector的方法。

查找属性向量

Celeb A数据集附带每个图像的面部属性注释。标签是二进制的,指示图像中是否存在特定属性。我们将使用标签和编码的潜在变量来找到我们的方向向量:

  1. 使用测试数据集或训练数据集中的样本,并使用VAE解码器生成潜矢量。
  2. 将潜在向量分为两组:具有(正向量)或不具有(负向量)的我们感兴趣的一个属性。
  3. 分别计算正向量和负向量的平均值。
  4. 通过从平均正向量中减去平均负向量来获取属性方向向量。

在预处理函数中,返回我们感兴趣的属性的标签。然后,使用lambda函数映射到数据管道:

def preprocess_attrib(sample, attribute):
    image = sample['image']
    image = tf.image.resize(image, [112,112])
    image = tf.cast(image, tf.float32)/255.
    return image, sample['attributes'][attribute]
ds = ds.map(lambda x: preprocess_attrib(x, attribute))
复制代码

人脸属性编辑

提取属性向量后,进行以下操作:

  1. 首先,我们从数据集中获取图像,将其放在首位,作为对比。
  2. 将人脸编码为潜在变量,然后对其进行解码以生成新人脸,并将其放置在中间。
  3. 然后,我们向右逐渐增加属性向量。
  4. 同样,我们向左逐渐减少属性向量。

下图显示了通过内插潜在向量生成的图像:

生成图片

生成图片

生成图片

接下来,我们可以尝试一起更改多个面部属性。在下图中,左侧的图像是随机生成的,并用作基准。右侧是经过一些潜在空间运算后的新图像:

小部件修改多个面部属性

该小部件可在Jupyter notebook中使用。

相关链接

《变分自编码器(VAE)详解与实现(采用TensorFlow2实现)》

おすすめ

転載: juejin.im/post/7033951810125725732
おすすめ