如何使用插值和向量算术来探索GAN的潜在空间。
生成对抗网络(GAN)是一种用于训练生成模型(例如用于生成图像的深度卷积神经网络)的架构。
GAN架构中的生成模型学习将潜在空间中的点映射到生成的图像。潜在空间除了通过生成模型赋予的含义外,本身没有其他意义。然而,潜在空间具有可以探索的结构,例如通过在点之间进行插值以及在潜在空间中的点之间执行向量算术,这些操作对生成的图像具有有意义的、有针对性的影响。
在本教程中,您将学习如何开发用于人脸生成的生成对抗网络,并探索潜在空间的结构以及对生成人脸的影响。
完成本教程后,您将了解:
- 如何开发用于生成人脸的生成对抗网络。
- 如何对潜在空间中的点进行插值,并生成从一张脸到另一张脸的变形图像。
- 如何在潜在空间中执行向量算术,并在结果生成的面部中实现有针对性的效果。
用我新书《Python生成对抗网络》开启您的项目,其中包括分步教程以及所有示例的Python源代码文件。
让我们开始吧。
- 2019 年 11 月更新: 已更新至 TensorFlow v2.0 和 MTCNN v0.1.0。

如何使用生成对抗网络对人脸进行插值和向量算术。
照片由Intermountain Forest Service拍摄,部分权利保留。
教程概述
本教程分为五个部分;它们是:
- 潜在空间中的向量算术
- 大规模CelebFaces数据集(CelebA)
- 如何准备CelebA人脸数据集
- 如何开发生成对抗网络
- 如何探索生成人脸的潜在空间
潜在空间中的向量算术
GAN架构中的生成器模型以潜在空间中的一个点作为输入,并生成一张新图像。
潜在空间本身没有意义。通常,它是一个100维的超球体,每个变量从均值为零、标准差为一的高斯分布中抽取。通过训练,生成器学会将潜在空间中的点映射到特定的输出图像,并且这种映射在模型每次训练时都会有所不同。
当被生成器模型解释时,潜在空间具有结构,并且对于给定的模型,这种结构可以被查询和导航。
通常,使用潜在空间中的随机点来生成新图像。更进一步,可以构造潜在空间中的点(例如,全0、全0.5或全1),并将其用作输入或查询以生成特定图像。
可以在潜在空间中两个点之间的线性路径上创建一系列点,例如两个生成的图像。这些点可用于生成一系列图像,显示两个生成图像之间的过渡。
最后,可以保留潜在空间中的点,并用于简单的向量算术以创建新的潜在空间点,这些点又可用于生成图像。这是一个有趣的想法,因为它允许直观且有针对性地生成图像。
Alec Radford等人在2015年发表的重要论文“使用深度卷积生成对抗网络进行无监督表示学习”介绍了一种稳定的模型配置,用于在GAN架构中训练深度卷积神经网络模型。
在论文中,作者们探索了在多个不同训练数据集上训练的GAN的潜在空间,尤其是名人脸部数据集。他们展示了两个有趣的方面。
第一个是人脸的向量算术。例如,一张微笑女性的脸减去一张中性女性的脸再加上一张中性男性的脸,结果是一张微笑男性的脸。
1 |
微笑女性 - 中性女性 + 中性男性 = 微笑男性 |
具体来说,算术运算是针对生成人脸的潜在空间中的点进行的。实际上是对具有给定特征的多个面部的平均值进行运算,以提供更稳健的结果。

使用GAN生成人脸的潜在空间点向量算术示例。
摘自“使用深度卷积生成对抗网络进行无监督表示学习”。
第二个演示是两个生成人脸之间的过渡,具体方法是创建生成两个人脸的点之间的潜在维度线性路径,然后生成路径上所有点的所有面部。

两个GAN生成人脸之间路径上人脸示例。
摘自“使用深度卷积生成对抗网络进行无监督表示学习”。
探索GAN模型的潜在空间结构,对于问题领域来说既有趣,也有助于培养对生成器模型所学内容的直观理解。
在本教程中,我们将开发一个用于生成人脸照片的GAN,然后通过向量算术来探索模型的潜在空间。
想从零开始开发GAN吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
大规模CelebFaces数据集(CelebA)
第一步是选择一个面部数据集。
在本教程中,我们将使用大规模CelebFaces属性数据集,简称CelebA。该数据集由Ziwei Liu等人为其2015年论文“从面部部件响应到人脸检测:深度学习方法”开发和发布。
该数据集提供了约20万张名人面部照片,以及对照片中出现的物品的标注,例如眼镜、脸型、帽子、发型等。作为数据集的一部分,作者提供了每张照片的居中人脸版本,并裁剪为肖像,尺寸在150像素宽和200像素高之间。我们将以此为基础来开发我们的GAN模型。
可以从Kaggle网页轻松下载该数据集。注意:这可能需要Kaggle账户。
具体来说,下载文件“img_align_celeba.zip”,该文件大小约为1.3 GB。为此,请在Kaggle网站上点击文件名,然后点击下载图标。
下载速度可能需要一些时间,具体取决于您的互联网连接速度。
下载完成后,解压存档。
这将创建一个名为“img_align_celeba”的新目录,其中包含所有图像,文件名类似202599.jpg和202598.jpg。
接下来,我们可以准备原始图像以供建模。
如何准备CelebA人脸数据集
第一步是开发加载图像的代码。
我们可以使用Pillow库加载给定的图像文件,将其转换为RGB格式(如果需要),然后返回像素数据数组。下面的load_image()函数实现了这一点。
1 2 3 4 5 6 7 8 9 |
# 将图像加载为rgb numpy数组 def load_image(filename): # 从文件加载图像 image = Image.open(filename) # 如果需要,转换为 RGB image = image.convert('RGB') # 转换为数组 pixels = asarray(image) return pixels |
接下来,我们可以枚举图像目录,依次将每个图像加载为像素数组,然后返回包含所有图像的数组。
数据集中有20万张图像,这可能比我们需要的多,所以我们也可以通过参数限制加载的图像数量。下面的load_faces()函数实现了这一点。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 加载图像并提取目录中所有图像的人脸 def load_faces(directory, n_faces): faces = list() # 枚举文件 for filename in listdir(directory): # 加载图像 pixels = load_image(directory + filename) # 存储 faces.append(pixels) # 一旦我们有足够数量就停止 if len(faces) >= n_faces: break return asarray(faces) |
最后,一旦图像被加载,我们就可以使用matplotlib库的imshow()函数来绘制它们。
下面的plot_faces()函数执行此操作,将图像绘制成正方形排列。
1 2 3 4 5 6 7 8 9 10 |
# 绘制加载的人脸列表 def plot_faces(faces, n): for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(faces[i]) pyplot.show() |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# 加载并绘制人脸 from os import listdir from numpy import asarray from PIL import Image from matplotlib import pyplot # 将图像加载为rgb numpy数组 def load_image(filename): # 从文件加载图像 image = Image.open(filename) # 如果需要,转换为 RGB image = image.convert('RGB') # 转换为数组 pixels = asarray(image) return pixels # 加载图像并提取目录中所有图像的人脸 def load_faces(directory, n_faces): faces = list() # 枚举文件 for filename in listdir(directory): # 加载图像 pixels = load_image(directory + filename) # 存储 faces.append(pixels) # 一旦我们有足够数量就停止 if len(faces) >= n_faces: break return asarray(faces) # 绘制加载的人脸列表 def plot_faces(faces, n): for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(faces[i]) pyplot.show() # 包含所有图像的目录 directory = 'img_align_celeba/' # 加载并提取所有面部 faces = load_faces(directory, 25) print('已加载: ', faces.shape) # 绘制人脸 plot_faces(faces, 5) |
运行该示例将从目录加载总共25张图像,然后总结返回数组的大小。
1 |
已加载: (25, 218, 178, 3) |
最后,将25张图像绘制在5x5的方格中。

名人面部数据集的25张面部样本图
在使用GAN时,如果所有图像都小且呈方形,则更容易对数据集进行建模。
此外,由于我们只对照片中的人脸感兴趣,而不是背景,我们可以执行人脸检测,只提取人脸,然后再将结果缩放到固定大小。
人脸检测有很多方法。在这种情况下,我们将使用预训练的多任务级联卷积神经网络(MTCNN)。这是一个最先进的人脸检测深度学习模型,在2016年的论文“使用多任务级联卷积网络进行联合人脸检测和对齐”中有描述。
我们将使用Iván de Paz Centeno在ipazc/mtcnn项目中提供的实现。该库可以通过pip安装,如下所示
1 |
sudo pip install mtcnn |
我们可以通过导入库并打印版本来确认库是否正确安装;例如
1 2 3 4 |
# 确认 mtcnn 是否正确安装 import mtcnn # 打印版本 print(mtcnn.__version__) |
运行示例会打印库的当前版本。
1 |
0.1.0 |
MTCNN模型非常易于使用。
首先,创建一个MTCNN模型实例,然后可以调用detect_faces()函数,传入一张图像的像素数据。结果是检测到的人脸列表,其中包含以像素偏移值定义的边界框。
1 2 3 4 5 6 7 |
... # 准备模型 model = MTCNN() # 检测图像中的人脸 faces = model.detect_faces(pixels) # 提取人脸的详细信息 x1, y1, width, height = faces[0]['box'] |
我们可以更新我们的示例,从每张加载的照片中提取人脸,并将提取的人脸像素调整为固定大小。在这种情况下,我们将使用80x80像素的方形。
下面的extract_face()函数实现了这一点,它将MTCNN模型和单个照片的像素值作为参数,并返回一个80x80x3的像素值数组,其中只包含人脸,或者如果未检测到人脸则返回None(这种情况很少见)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 从加载的图像中提取人脸并进行缩放 def extract_face(model, pixels, required_size=(80, 80)): # 检测图像中的人脸 faces = model.detect_faces(pixels) # 跳过无法检测到人脸的情况 if len(faces) == 0: return None # 提取人脸的详细信息 x1, y1, width, height = faces[0]['box'] # 将检测到的像素值强制设为正数(bug修复) x1, y1 = abs(x1), abs(y1) # 转换为坐标 x2, y2 = x1 + width, y1 + height # 检索人脸像素 face_pixels = pixels[y1:y2, x1:x2] # 将像素调整到模型大小 image = Image.fromarray(face_pixels) image = image.resize(required_size) face_array = asarray(image) return face_array |
我们可以更新load_faces()函数,从加载的照片中提取人脸并将其存储在返回的面部列表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 加载图像并提取目录中所有图像的人脸 def load_faces(directory, n_faces): # 准备模型 model = MTCNN() faces = list() # 枚举文件 for filename in listdir(directory): # 加载图像 pixels = load_image(directory + filename) # 获取人脸 face = extract_face(model, pixels) if face is None: continue # 存储 faces.append(face) print(len(faces), face.shape) # 一旦我们有足够数量就停止 if len(faces) >= n_faces: break return asarray(faces) |
将这些结合起来,完整的示例列在下面。
在这种情况下,我们将加载的面部总数增加到50,000,为我们的GAN模型提供一个良好的训练数据集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# 提取和调整人脸以创建新数据集的示例 from os import listdir from numpy import asarray from numpy import savez_compressed from PIL import Image from mtcnn.mtcnn import MTCNN from matplotlib import pyplot # 将图像加载为rgb numpy数组 def load_image(filename): # 从文件加载图像 image = Image.open(filename) # 如果需要,转换为 RGB image = image.convert('RGB') # 转换为数组 pixels = asarray(image) return pixels # 从加载的图像中提取人脸并进行缩放 def extract_face(model, pixels, required_size=(80, 80)): # 检测图像中的人脸 faces = model.detect_faces(pixels) # 跳过无法检测到人脸的情况 if len(faces) == 0: return None # 提取人脸的详细信息 x1, y1, width, height = faces[0]['box'] # 将检测到的像素值强制设为正数(bug修复) x1, y1 = abs(x1), abs(y1) # 转换为坐标 x2, y2 = x1 + width, y1 + height # 检索人脸像素 face_pixels = pixels[y1:y2, x1:x2] # 将像素调整到模型大小 image = Image.fromarray(face_pixels) image = image.resize(required_size) face_array = asarray(image) return face_array # 加载图像并提取目录中所有图像的人脸 def load_faces(directory, n_faces): # 准备模型 model = MTCNN() faces = list() # 枚举文件 for filename in listdir(directory): # 加载图像 pixels = load_image(directory + filename) # 获取人脸 face = extract_face(model, pixels) if face is None: continue # 存储 faces.append(face) print(len(faces), face.shape) # 一旦我们有足够数量就停止 if len(faces) >= n_faces: break return asarray(faces) # 包含所有图像的目录 directory = 'img_align_celeba/' # 加载并提取所有面部 all_faces = load_faces(directory, 50000) print('已加载: ', all_faces.shape) # 以压缩格式保存 savez_compressed('img_align_celeba.npz', all_faces) |
运行该示例可能需要几分钟时间,因为需要加载的面部数量较多。
在运行结束时,提取和调整大小的人脸数组将保存为压缩的NumPy数组,文件名为“img_align_celeba.npz”。
准备好的数据集以后可以随时加载,如下所示。
1 2 3 4 5 6 |
# 加载准备好的数据集 from numpy import load # 加载人脸数据集 data = load('img_align_celeba.npz') faces = data['arr_0'] print('已加载: ', faces.shape) |
加载数据集将总结数组的形状,显示50K张80x80像素的图像和三个颜色通道。
1 |
已加载: (50000, 80, 80, 3) |
我们现在准备好开发一个GAN模型来使用此数据集生成人脸。
如何开发生成对抗网络
在本节中,我们将开发一个用于我们已准备好的面部数据集的GAN。
第一步是定义模型。
判别器模型以一个80x80的彩色图像作为输入,并输出一个二元预测,判断图像是真实的(class=1)还是伪造的(class=0)。它被实现为一个适度的卷积神经网络,使用了GAN设计的最佳实践,例如使用斜率为0.2的LeakyReLU激活函数,使用2x2步长进行下采样,以及使用学习率为0.0002、动量为0.5的Adam版本的随机梯度下降。
下面的define_discriminator()函数实现了这一点,它定义并编译了判别器模型并返回。图像的输入形状被参数化为默认函数参数,以防您以后想将该函数用于自己的图像数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# 定义独立的判别器模型 def define_discriminator(in_shape=(80,80,3)): model = Sequential() # 正常 model.add(Conv2D(128, (5,5), padding='same', input_shape=in_shape)) model.add(LeakyReLU(alpha=0.2)) # 下采样至40x40 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 下采样至20x30 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 下采样至10x10 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 下采样至5x5 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 分类器 model.add(Flatten()) model.add(Dropout(0.4)) model.add(Dense(1, activation='sigmoid')) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy']) return model |
生成器模型以潜在空间中的一个点作为输入,并输出一张80x80的彩色图像。
这是通过使用一个全连接层来解释潜在空间中的点,并提供足够的激活,可以将其重塑为许多副本(在此例中为128个)的输出图像的低分辨率版本(例如5x5)。然后,通过转置卷积层对其进行四次上采样,每次都将激活的大小加倍,区域扩大四倍。该模型使用了最佳实践,如LeakyReLU激活、核大小是步长大小的因子,以及输出层中的双曲正切(tanh)激活函数。
下面的define_generator()函数定义了生成器模型,但故意不编译它,因为它不直接训练,然后返回模型。潜在空间的大小被参数化为函数参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 定义独立的生成器模型 def define_generator(latent_dim): model = Sequential() # 5x5特征图的基础 n_nodes = 128 * 5 * 5 model.add(Dense(n_nodes, input_dim=latent_dim)) model.add(LeakyReLU(alpha=0.2)) model.add(Reshape((5, 5, 128))) # 上采样至10x10 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 上采样至20x20 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 上采样至40x40 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 上采样至80x80 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 输出层 80x80x3 model.add(Conv2D(3, (5,5), activation='tanh', padding='same')) return model |
接下来,可以定义一个GAN模型,它将生成器模型和判别器模型组合成一个更大的模型。这个更大的模型将用于训练生成器中的模型权重,使用判别器模型计算的输出和误差。判别器模型是单独训练的,因此,在这个更大的GAN模型中,模型权重被标记为不可训练,以确保只有生成器模型的权重被更新。这种对判别器权重可训练性的改变仅在训练组合GAN模型时生效,而在单独训练判别器时不生效。
这个更大的GAN模型以潜在空间中的一个点作为输入,使用生成器模型生成一张图像,然后将该图像作为输入提供给判别器模型,最后输出或分类为真实或伪造。
下面的 define_gan() 函数实现了这一点,它接收已经定义的生成器和判别器模型作为输入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 定义组合的生成器和判别器模型,用于更新生成器 def define_gan(g_model, d_model): # 使判别器中的权重不可训练 d_model.trainable = False # 连接它们 model = Sequential() # 添加生成器 model.add(g_model) # 添加判别器 model.add(d_model) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss='binary_crossentropy', optimizer=opt) return model |
现在我们已经定义了GAN模型,我们需要训练它。但是,在训练模型之前,我们需要输入数据。
第一步是加载和缩放预处理过的面部数据集。可以像上一节那样加载保存的NumPy数组,然后必须将像素值缩放到[-1,1]的范围,以匹配生成器模型的输出。
下面的 load_real_samples() 函数实现了这一点,它返回已加载和缩放的图像数据,为建模做好准备。
1 2 3 4 5 6 7 8 9 10 |
# 加载并准备训练图像 def load_real_samples(): # load the face dataset data = load('img_align_celeba.npz') X = data['arr_0'] # 将无符号整数转换为浮点数 X = X.astype('float32') # 从 [0,255] 缩放到 [-1,1] X = (X - 127.5) / 127.5 return X |
我们需要从数据集中获取一个批次(或半个批次)的真实图像来更新GAN模型。一种简单的方法是每次都从数据集中选择一个随机样本。
下面的 generate_real_samples() 函数实现了这一点,它接收准备好的数据集作为参数,选择并返回一个随机的面部图像样本及其对应的类别标签给判别器,特别是 class=1,表示它们是真实图像。
1 2 3 4 5 6 7 8 9 |
# 选择真实样本 def generate_real_samples(dataset, n_samples): # 选择随机实例 ix = randint(0, dataset.shape[0], n_samples) # 检索选定的图像 X = dataset[ix] # 生成“真实”类别标签 (1) y = ones((n_samples, 1)) return X, y |
接下来,我们需要为生成器模型提供输入。这些是来自潜在空间的随机点,具体来说是高斯分布的随机变量。
generate_latent_points() 函数实现了这一点,它接收潜在空间的维度作为参数,以及所需的点数,并返回它们作为生成器模型的输入样本批次。
1 2 3 4 5 6 7 |
# 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 x_input = x_input.reshape(n_samples, latent_dim) return x_input |
接下来,我们需要使用潜在空间中的点作为生成器的输入,以生成新的图像。
下面的 generate_fake_samples() 函数实现了这一点,它接收生成器模型和潜在空间的维度作为参数,然后生成潜在空间中的点并将它们用作生成器模型的输入。该函数返回生成的图像及其对应的类别标签给判别器模型,特别是 class=0,表示它们是伪造的或生成的。
1 2 3 4 5 6 7 8 9 |
# 使用生成器生成 n 个假示例,并带有类别标签 def generate_fake_samples(g_model, latent_dim, n_samples): # 在潜在空间中生成点 x_input = generate_latent_points(latent_dim, n_samples) # 预测输出 X = g_model.predict(x_input) # 创建“假”类别标签 (0) y = zeros((n_samples, 1)) return X, y |
我们现在准备好拟合GAN模型了。
该模型训练100个训练周期,这是任意设定的,因为模型可能在最初几个周期后就开始生成合理的图像。使用128个样本的批次大小,每个训练周期涉及50,000/128,即约390个真实和伪造样本的批次以及对模型的更新。
首先,判别器模型更新半批次的真实样本,然后是半批次的伪造样本,两者合在一起构成一个批次的权重更新。然后通过组合的GAN模型更新生成器。重要的是,伪造样本的类别标签设置为1(真实)。这会更新生成器,使其在下一批次中更好地生成真实样本。
下面的 train() 函数实现了这一点,它接收定义的模型、数据集和潜在空间的维度作为参数,并使用默认参数来设置周期数和批次大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 训练生成器和判别器 def train(g_model, d_model, gan_model, dataset, latent_dim, n_epochs=100, n_batch=128): bat_per_epo = int(dataset.shape[0] / n_batch) half_batch = int(n_batch / 2) # 手动枚举 epoch for i in range(n_epochs): # 枚举训练集中的批次 for j in range(bat_per_epo): # 获取随机选择的“真实”样本 X_real, y_real = generate_real_samples(dataset, half_batch) # 更新判别器模型权重 d_loss1, _ = d_model.train_on_batch(X_real, y_real) # 生成“假”示例 X_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch) # 更新判别器模型权重 d_loss2, _ = d_model.train_on_batch(X_fake, y_fake) # 准备潜在空间中的点作为生成器的输入 X_gan = generate_latent_points(latent_dim, n_batch) # 为伪样本创建反转标签 y_gan = ones((n_batch, 1)) # 通过判别器的误差更新生成器 g_loss = gan_model.train_on_batch(X_gan, y_gan) # 总结此批次的损失 print('>%d, %d/%d, d1=%.3f, d2=%.3f g=%.3f' % (i+1, j+1, bat_per_epo, d_loss1, d_loss2, g_loss) # 有时评估模型性能 if (i+1) % 10 == 0: summarize_performance(i, g_model, d_model, dataset, latent_dim) |
您会注意到,每训练10个周期,就会调用summarize_performance() 函数。
目前没有可靠的方法可以自动评估生成图像的质量。因此,我们必须在训练过程中定期生成图像,并在这些时候保存模型。这既提供了一个我们可以稍后加载并用于生成图像的检查点,也提供了一种防止训练过程失败的方法,因为训练过程可能会失败。
下面定义了summarize_performance() 和 save_plot() 函数。
summarize_performance() 函数生成样本并评估判别器在真实和伪造样本上的性能。报告分类准确率,这可能有助于了解模型性能。调用 save_plot() 来创建和保存生成图像的图,然后将模型保存到文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# 创建并保存生成的图像图 def save_plot(examples, epoch, n=10): # 将范围从[-1,1]缩放到[0,1] examples = (examples + 1) / 2.0 # 绘制图像 for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i]) # 保存图到文件 filename = 'generated_plot_e%03d.png' % (epoch+1) pyplot.savefig(filename) pyplot.close() # 评估判别器,绘制生成的图像,保存生成器模型 def summarize_performance(epoch, g_model, d_model, dataset, latent_dim, n_samples=100): # 准备真实样本 X_real, y_real = generate_real_samples(dataset, n_samples) # 评估判别器在真实示例上的表现 _, acc_real = d_model.evaluate(X_real, y_real, verbose=0) # 准备伪示例 x_fake, y_fake = generate_fake_samples(g_model, latent_dim, n_samples) # 评估判别器在伪示例上的表现 _, acc_fake = d_model.evaluate(x_fake, y_fake, verbose=0) # 总结判别器性能 print('>Accuracy real: %.0f%%, fake: %.0f%%' % (acc_real*100, acc_fake*100)) # 保存图 save_plot(x_fake, epoch) # 保存生成器模型到文件 filename = 'generator_model_%03d.h5' % (epoch+1) g_model.save(filename) |
然后,我们可以定义潜在空间的维度,定义所有三个模型,并在加载的人脸数据集上训练它们。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 潜在空间的大小 latent_dim = 100 # 创建判别器 d_model = define_discriminator() # 创建生成器 g_model = define_generator(latent_dim) # 创建GAN gan_model = define_gan(g_model, d_model) # 加载图像数据 dataset = load_real_samples() # 训练模型 train(g_model, d_model, gan_model, dataset, latent_dim) |
将所有这些联系在一起,完整的示例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# example of a gan for generating faces from numpy import load from numpy import zeros from numpy import ones from numpy.random import randn from numpy.random import randint from keras.optimizers import Adam from keras.models import Sequential from keras.layers import Dense from keras.layers import Reshape from keras.layers import Flatten 从 keras.layers 导入 Conv2D from keras.layers import Conv2DTranspose from keras.layers import LeakyReLU 从 keras.layers 导入 Dropout from matplotlib import pyplot # 定义独立的判别器模型 def define_discriminator(in_shape=(80,80,3)): model = Sequential() # 正常 model.add(Conv2D(128, (5,5), padding='same', input_shape=in_shape)) model.add(LeakyReLU(alpha=0.2)) # 下采样至40x40 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 下采样至20x30 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 下采样至10x10 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 下采样至5x5 model.add(Conv2D(128, (5,5), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 分类器 model.add(Flatten()) model.add(Dropout(0.4)) model.add(Dense(1, activation='sigmoid')) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy']) return model # 定义独立的生成器模型 def define_generator(latent_dim): model = Sequential() # 5x5特征图的基础 n_nodes = 128 * 5 * 5 model.add(Dense(n_nodes, input_dim=latent_dim)) model.add(LeakyReLU(alpha=0.2)) model.add(Reshape((5, 5, 128))) # 上采样至10x10 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 上采样至20x20 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 上采样至40x40 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 上采样至80x80 model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')) model.add(LeakyReLU(alpha=0.2)) # 输出层 80x80x3 model.add(Conv2D(3, (5,5), activation='tanh', padding='same')) return model # 定义组合的生成器和判别器模型,用于更新生成器 def define_gan(g_model, d_model): # 使判别器中的权重不可训练 d_model.trainable = False # 连接它们 model = Sequential() # 添加生成器 model.add(g_model) # 添加判别器 model.add(d_model) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss='binary_crossentropy', optimizer=opt) return model # 加载并准备训练图像 def load_real_samples(): # load the face dataset data = load('img_align_celeba.npz') X = data['arr_0'] # 将无符号整数转换为浮点数 X = X.astype('float32') # 从 [0,255] 缩放到 [-1,1] X = (X - 127.5) / 127.5 return X # 选择真实样本 def generate_real_samples(dataset, n_samples): # 选择随机实例 ix = randint(0, dataset.shape[0], n_samples) # 检索选定的图像 X = dataset[ix] # 生成“真实”类别标签 (1) y = ones((n_samples, 1)) 返回 X, y # 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 x_input = x_input.reshape(n_samples, latent_dim) return x_input # 使用生成器生成 n 个假示例,并带有类别标签 def generate_fake_samples(g_model, latent_dim, n_samples): # 在潜在空间中生成点 x_input = generate_latent_points(latent_dim, n_samples) # 预测输出 X = g_model.predict(x_input) # 创建“假”类别标签 (0) y = zeros((n_samples, 1)) 返回 X, y # 创建并保存生成的图像图 def save_plot(examples, epoch, n=10): # 将范围从[-1,1]缩放到[0,1] examples = (examples + 1) / 2.0 # 绘制图像 for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i]) # 保存图到文件 filename = 'generated_plot_e%03d.png' % (epoch+1) pyplot.savefig(filename) pyplot.close() # 评估判别器,绘制生成的图像,保存生成器模型 def summarize_performance(epoch, g_model, d_model, dataset, latent_dim, n_samples=100): # 准备真实样本 X_real, y_real = generate_real_samples(dataset, n_samples) # 评估判别器在真实示例上的表现 _, acc_real = d_model.evaluate(X_real, y_real, verbose=0) # 准备伪示例 x_fake, y_fake = generate_fake_samples(g_model, latent_dim, n_samples) # 评估判别器在伪示例上的表现 _, acc_fake = d_model.evaluate(x_fake, y_fake, verbose=0) # 总结判别器性能 print('>Accuracy real: %.0f%%, fake: %.0f%%' % (acc_real*100, acc_fake*100)) # 保存图 save_plot(x_fake, epoch) # 保存生成器模型到文件 filename = 'generator_model_%03d.h5' % (epoch+1) g_model.save(filename) # 训练生成器和判别器 def train(g_model, d_model, gan_model, dataset, latent_dim, n_epochs=100, n_batch=128): bat_per_epo = int(dataset.shape[0] / n_batch) half_batch = int(n_batch / 2) # 手动枚举 epoch for i in range(n_epochs): # 枚举训练集中的批次 for j in range(bat_per_epo): # 获取随机选择的“真实”样本 X_real, y_real = generate_real_samples(dataset, half_batch) # 更新判别器模型权重 d_loss1, _ = d_model.train_on_batch(X_real, y_real) # 生成“假”示例 X_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch) # 更新判别器模型权重 d_loss2, _ = d_model.train_on_batch(X_fake, y_fake) # 准备潜在空间中的点作为生成器的输入 X_gan = generate_latent_points(latent_dim, n_batch) # 为伪样本创建反转标签 y_gan = ones((n_batch, 1)) # 通过判别器的误差更新生成器 g_loss = gan_model.train_on_batch(X_gan, y_gan) # 总结此批次的损失 print('>%d, %d/%d, d1=%.3f, d2=%.3f g=%.3f' % (i+1, j+1, bat_per_epo, d_loss1, d_loss2, g_loss) # 有时评估模型性能 if (i+1) % 10 == 0: summarize_performance(i, g_model, d_model, dataset, latent_dim) # 潜在空间的大小 latent_dim = 100 # 创建判别器 d_model = define_discriminator() # 创建生成器 g_model = define_generator(latent_dim) # 创建GAN gan_model = define_gan(g_model, d_model) # 加载图像数据 dataset = load_real_samples() # 训练模型 train(g_model, d_model, gan_model, dataset, latent_dim) |
在适度的硬件上运行示例可能需要很长时间。
我建议在GPU硬件上运行该示例。如果您需要帮助,可以快速开始使用AWS EC2实例来训练模型。请参阅教程
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
每个批次之后都会报告判别器在真实和伪造样本上的损失,以及生成器上的损失。
1 2 3 4 5 6 |
>1, 1/390, d1=0.699, d2=0.696 g=0.692 >1, 2/390, d1=0.541, d2=0.702 g=0.686 >1, 3/390, d1=0.213, d2=0.742 g=0.656 >1, 4/390, d1=0.013, d2=0.806 g=0.656 >1, 5/390, d1=0.012, d2=0.772 g=0.682 ... |
判别器的损失可能会下降到真实和生成样本的0.0值。
如果发生这种情况,它是一个训练失败的例子,模型很可能无法恢复,您应该重新开始训练过程。
1 2 3 4 5 6 7 8 |
... >34, 130/390, d1=0.844, d2=8.434 g=3.450 >34, 131/390, d1=1.233, d2=12.021 g=3.541 >34, 132/390, d1=1.183, d2=15.759 g=0.000 >34, 133/390, d1=0.000, d2=15.942 g=0.006 >34, 134/390, d1=0.081, d2=15.942 g=0.000 >34, 135/390, d1=0.000, d2=15.942 g=0.000 ... |
检查生成的图,并根据图像质量最好的模型进行选择。
模型应该在训练大约30个周期后开始生成人脸。
这些人脸不完全清晰,但很明显它们是人脸,具有所有正确的特征(头发、眼睛、鼻子、嘴巴)大致在正确的位置。

生成对抗网络生成名人面部的示例
如何探索生成人脸的潜在空间
在本节中,我们将使用我们训练好的GAN模型作为探索潜在空间的基础。
如何加载模型并生成人脸
第一步是加载保存的模型,并确认它可以生成合理的人脸。
可以使用Keras API中的 load_model() 函数加载模型。然后,我们可以生成一些潜在空间中的随机点,并将它们作为输入提供给加载的模型以生成新的人脸。然后可以将这些面部绘制出来。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# 加载生成器模型并生成图像的示例 from numpy import asarray from numpy.random import randn from numpy.random import randint from keras.models import load_model from matplotlib import pyplot # 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples, n_classes=10): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 z_input = x_input.reshape(n_samples, latent_dim) return z_input # 创建生成图像的图 def plot_generated(examples, n): # 绘制图像 for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i, :, :]) pyplot.show() # 加载模型 model = load_model('generator_model_030.h5') # 生成图像 latent_points = generate_latent_points(100, 25) # 生成图像 X = model.predict(latent_points) # 将范围从[-1,1]缩放到[0,1] X = (X + 1) / 2.0 # 绘制结果 plot_generated(X, 5) |
运行示例首先加载保存的模型。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
然后,在100维潜在空间中创建25个随机点,将它们提供给生成器模型以创建25张人脸图像,然后将它们绘制在5x5的网格中。

使用加载的GAN模型随机生成人脸的图
如何对生成的人脸进行插值
接下来,我们可以创建潜在空间中两个点之间的插值路径,并沿该路径生成人脸。
我们可以使用的最简单的插值是线性或均匀插值,在潜在空间的两个点之间。我们可以使用 linspace() NumPy函数 来计算两个点贡献的比例,然后枚举这些比例并为每个比例构造一个向量。
下面的 interpolate_points() 函数实现了这一点,它返回潜在空间中两点之间的线性插值向量序列,包括第一点和最后一点。
1 2 3 4 5 6 7 8 9 10 |
# uniform interpolation between two points in latent space def interpolate_points(p1, p2, n_steps=10): # interpolate ratios between the points ratios = linspace(0, 1, num=n_steps) # linear interpolate vectors vectors = list() for ratio in ratios: v = (1.0 - ratio) * p1 + ratio * p2 vectors.append(v) return asarray(vectors) |
然后,我们可以生成潜在空间的两个点,进行插值,然后为每个插值向量生成一张图像。
结果将是一系列图像,这些图像在原始的两张图像之间进行过渡。下面的示例演示了两个人脸的插值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# example of interpolating between generated faces from numpy import asarray from numpy.random import randn from numpy.random import randint from numpy import linspace from keras.models import load_model from matplotlib import pyplot # 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples, n_classes=10): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 z_input = x_input.reshape(n_samples, latent_dim) return z_input # uniform interpolation between two points in latent space def interpolate_points(p1, p2, n_steps=10): # interpolate ratios between the points ratios = linspace(0, 1, num=n_steps) # linear interpolate vectors vectors = list() for ratio in ratios: v = (1.0 - ratio) * p1 + ratio * p2 vectors.append(v) return asarray(vectors) # 创建生成图像的图 def plot_generated(examples, n): # 绘制图像 for i in range(n): # 定义子图 pyplot.subplot(1, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i, :, :]) pyplot.show() # 加载模型 model = load_model('generator_model_030.h5') # generate points in latent space pts = generate_latent_points(100, 2) # interpolate points in latent space interpolated = interpolate_points(pts[0], pts[1]) # 生成图像 X = model.predict(interpolated) # 将范围从[-1,1]缩放到[0,1] X = (X + 1) / 2.0 # 绘制结果 plot_generated(X, len(interpolated)) |
运行示例会计算潜在空间中两点之间的插值路径,为每个点生成图像,并绘制结果。
您可以看到从左边的第一张脸到右边的最后一张脸,在十个步骤中有清晰的线性进展。

展示生成人脸之间线性插值的图
我们可以更新示例以多次重复此过程,以便在单个图中查看多个生成人脸之间的过渡。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# example of interpolating between generated faces from numpy import asarray from numpy import vstack from numpy.random import randn from numpy.random import randint from numpy import linspace from keras.models import load_model from matplotlib import pyplot # 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples, n_classes=10): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 z_input = x_input.reshape(n_samples, latent_dim) return z_input # uniform interpolation between two points in latent space def interpolate_points(p1, p2, n_steps=10): # interpolate ratios between the points ratios = linspace(0, 1, num=n_steps) # linear interpolate vectors vectors = list() for ratio in ratios: v = (1.0 - ratio) * p1 + ratio * p2 vectors.append(v) return asarray(vectors) # 创建生成图像的图 def plot_generated(examples, n): # 绘制图像 for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i, :, :]) pyplot.show() # 加载模型 model = load_model('generator_model_030.h5') # generate points in latent space n = 20 pts = generate_latent_points(100, n) # 插值对 results = None for i in range(0, n, 2): # 在潜在空间中插值点 interpolated = interpolate_points(pts[i], pts[i+1]) # 生成图像 X = model.predict(interpolated) # 将范围从[-1,1]缩放到[0,1] X = (X + 1) / 2.0 if results is None: results = X else: results = vstack((results, X)) # 绘制结果 plot_generated(results, 10) |
运行示例会生成10个不同的起始人脸和10个匹配的人脸终点,以及它们之间的线性插值。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。

展示生成人脸之间多重线性插值的图
在这些情况下,我们执行了线性插值,这假设潜在空间是均匀分布的超立方体。严格来说,我们选择的潜在空间是100维超球面或多模高斯分布。
有一个名为球面线性插值函数,或“Slerp”的数学函数,在插值这个空间时应该使用它,以确保考虑了空间的弯曲。更多详情,我推荐阅读Soumith Chintala的dcgan.torch项目中的“线性插值问题”。在该项目中,提供了Python的Slerp函数的实现,我们可以将其作为我们自己的Slerp函数的基础,如下所示:
1 2 3 4 5 6 7 8 |
# 球面线性插值(slerp) def slerp(val, low, high): omega = arccos(clip(dot(low/norm(low), high/norm(high)), -1, 1)) so = sin(omega) if so == 0: # 洛必达法则/LERP return (1.0-val) * low + val * high return sin((1.0-val)*omega) / so * low + sin(val*omega) / so * high |
我们可以从我们的*interpolate_points()*函数中调用此函数,而不是执行手动线性插值。
进行了此更改的完整示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# example of interpolating between generated faces from numpy import asarray from numpy import vstack from numpy.random import randn from numpy.random import randint from numpy import arccos 从 numpy 导入 clip 从 numpy 导入 dot from numpy import sin from numpy import linspace from numpy.linalg import norm from keras.models import load_model from matplotlib import pyplot # 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples, n_classes=10): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 z_input = x_input.reshape(n_samples, latent_dim) return z_input # 球面线性插值(slerp) def slerp(val, low, high): omega = arccos(clip(dot(low/norm(low), high/norm(high)), -1, 1)) so = sin(omega) if so == 0: # 洛必达法则/LERP return (1.0-val) * low + val * high return sin((1.0-val)*omega) / so * low + sin(val*omega) / so * high # uniform interpolation between two points in latent space def interpolate_points(p1, p2, n_steps=10): # interpolate ratios between the points ratios = linspace(0, 1, num=n_steps) # linear interpolate vectors vectors = list() for ratio in ratios: v = slerp(ratio, p1, p2) vectors.append(v) return asarray(vectors) # 创建生成图像的图 def plot_generated(examples, n): # 绘制图像 for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i, :, :]) pyplot.show() # 加载模型 model = load_model('generator_model_030.h5') # generate points in latent space n = 20 pts = generate_latent_points(100, n) # 插值对 results = None for i in range(0, n, 2): # 在潜在空间中插值点 interpolated = interpolate_points(pts[i], pts[i+1]) # 生成图像 X = model.predict(interpolated) # 将范围从[-1,1]缩放到[0,1] X = (X + 1) / 2.0 if results is None: results = X else: results = vstack((results, X)) # 绘制结果 plot_generated(results, 10) |
结果是又一个生成人脸之间的过渡,这次使用了正确的Slerp插值方法。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
差异很微妙,但视觉上更正确。

展示生成人脸之间多重球面线性插值的图
如何对人脸进行向量算术
最后,我们可以通过对生成的人脸进行向量算术来探索潜在空间。
首先,我们必须生成大量人脸,并保存人脸及其对应的潜在向量。然后,我们可以查看生成的人脸图,选择具有我们感兴趣特征的人脸,记下它们的索引(编号),并检索它们的潜在空间向量进行操作。
下面的示例将加载GAN模型并使用它来生成100张随机人脸。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# 加载生成器模型并生成图像的示例 from numpy import asarray from numpy.random import randn from numpy.random import randint from keras.models import load_model from matplotlib import pyplot from numpy import savez_compressed # 在潜在空间中生成点作为生成器的输入 def generate_latent_points(latent_dim, n_samples, n_classes=10): # 在潜在空间中生成点 x_input = randn(latent_dim * n_samples) # 重塑为网络的输入批次 z_input = x_input.reshape(n_samples, latent_dim) return z_input # 创建生成图像的图 def plot_generated(examples, n): # 绘制图像 for i in range(n * n): # 定义子图 pyplot.subplot(n, n, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i, :, :]) pyplot.savefig('generated_faces.png') pyplot.close() # 加载模型 model = load_model('generator_model_030.h5') # generate points in latent space latent_points = generate_latent_points(100, 100) # 保存点 savez_compressed('latent_points.npz', latent_points) # 生成图像 X = model.predict(latent_points) # 将范围从[-1,1]缩放到[0,1] X = (X + 1) / 2.0 # 保存图 plot_generated(X, 10) |
运行示例会加载模型,生成人脸,并保存潜在向量和生成的人脸。
潜在向量被保存到一个名为*latent_points.npz*的压缩NumPy数组中。生成的100张人脸绘制在10x10的网格中,并保存到名为*generated_faces.png*的文件中。
在这种情况下,我们有一组很好的人脸可供使用。每个人脸都有一个我们可以用来检索潜在向量的索引。例如,第一张人脸是1,它对应于保存数组中的第一个向量(索引0)。
我们将执行操作
1 |
微笑女性 - 中性女性 + 中性男性 = 微笑男性 |
因此,我们需要三张微笑女性、中性女性和中性男性的脸。
在这种情况下,我们将使用图像中的以下索引:
- 微笑女性:92, 98, 99
- 中性女性:9, 21, 79
- 中性男性:10, 30, 45

用作人脸向量算术基础的100张生成人脸图
现在我们有了可用的潜在向量和目标算术,我们就可以开始了。
首先,我们可以指定我们偏好的人脸图像并加载保存的潜在点NumPy数组。
1 2 3 4 5 6 7 |
# 检索特定点 smiling_woman_ix = [92, 98, 99] neutral_woman_ix = [9, 21, 79] neutral_man_ix = [10, 30, 45] # 加载已保存的潜在点 data = load('latent_points.npz') points = data['arr_0'] |
接下来,我们可以检索每个向量并计算每种向量类型(例如,微笑女性)的平均值。我们可以直接对单个图像执行向量算术,但如果我们处理的是具有所需属性的几个图像的平均值,则结果会更稳健。
下面的*average_points()*函数接受加载的潜在空间点数组,检索每个点,计算平均值,并返回所有向量。
1 2 3 4 5 6 7 8 9 10 11 |
# 平均潜在空间向量列表 def average_points(points, ix): # 转换为零偏移点 zero_ix = [i-1 for i in ix] # 检索所需的点 vectors = points[zero_ix] # 平均向量 avg_vector = mean(vectors, axis=0) # 合并原始向量和平均向量 all_vectors = vstack((vectors, avg_vector)) return all_vectors |
我们现在可以使用此函数来检索所有所需的潜在空间点并生成图像。
1 2 3 4 5 6 7 8 9 10 11 |
# 平均向量 smiling_woman = average_points(points, smiling_woman_ix) neutral_woman = average_points(points, neutral_woman_ix) neutral_man = average_points(points, neutral_man_ix) # 合并所有向量 all_vectors = vstack((smiling_woman, neutral_woman, neutral_man)) # 生成图像 images = model.predict(all_vectors) # 缩放像素值 images = (images + 1) / 2.0 plot_generated(images, 3, 4) |
最后,我们可以使用平均向量在潜在空间中进行向量算术并绘制结果。
1 2 3 4 5 6 7 8 9 |
# 微笑女性 - 中性女性 + 中性男性 = 微笑男性 result_vector = smiling_woman[-1] - neutral_woman[-1] + neutral_man[-1] # 生成图像 result_vector = expand_dims(result_vector, 0) result_image = model.predict(result_vector) # 缩放像素值 result_image = (result_image + 1) / 2.0 pyplot.imshow(result_image[0]) pyplot.show() |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# 加载生成器模型并生成图像的示例 from numpy import asarray from numpy.random import randn from numpy.random import randint from keras.models import load_model from matplotlib import pyplot from numpy import load from numpy import mean from numpy import vstack from numpy import expand_dims # 平均潜在空间向量列表 def average_points(points, ix): # 转换为零偏移点 zero_ix = [i-1 for i in ix] # 检索所需的点 vectors = points[zero_ix] # 平均向量 avg_vector = mean(vectors, axis=0) # 合并原始向量和平均向量 all_vectors = vstack((vectors, avg_vector)) return all_vectors # 创建生成图像的图 def plot_generated(examples, rows, cols): # 绘制图像 for i in range(rows * cols): # 定义子图 pyplot.subplot(rows, cols, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(examples[i, :, :]) pyplot.show() # 加载模型 model = load_model('generator_model_030.h5') # 检索特定点 smiling_woman_ix = [92, 98, 99] neutral_woman_ix = [9, 21, 79] neutral_man_ix = [10, 30, 45] # 加载已保存的潜在点 data = load('latent_points.npz') points = data['arr_0'] # 平均向量 smiling_woman = average_points(points, smiling_woman_ix) neutral_woman = average_points(points, neutral_woman_ix) neutral_man = average_points(points, neutral_man_ix) # 合并所有向量 all_vectors = vstack((smiling_woman, neutral_woman, neutral_man)) # 生成图像 images = model.predict(all_vectors) # 缩放像素值 images = (images + 1) / 2.0 plot_generated(images, 3, 4) # 微笑女性 - 中性女性 + 中性男性 = 微笑男性 result_vector = smiling_woman[-1] - neutral_woman[-1] + neutral_man[-1] # 生成图像 result_vector = expand_dims(result_vector, 0) result_image = model.predict(result_vector) # 缩放像素值 result_image = (result_image + 1) / 2.0 pyplot.imshow(result_image[0]) pyplot.show() |
运行示例首先加载我们特定图像的潜在空间点,计算点的平均值,并生成这些点的对应人脸。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
我们可以看到,确实,我们选择的人脸被正确检索,并且向量空间中点的平均值捕捉了我们想要的每条线上的突出特征(例如,微笑女性、中性女性等)。

绘制选定生成人脸和每行平均生成人脸的图
接下来,执行向量算术,结果是微笑的男性,正如我们所期望的那样。

基于潜在空间向量算术所得生成人脸的图
扩展
本节列出了一些您可能希望探索的扩展本教程的想法。
- 附加算术。尝试使用不同的人脸特征或不同的算术方法进行运算,并查看生成的人脸结果。
- 附加插值。尝试在潜在空间中插值三个或更多点,并查看生成的人脸结果。
- 调整模型。更新GAN模型的配置,使其训练更稳定,并能生成更高质量的人脸。
如果您探索了这些扩展中的任何一个,我很想知道。
请在下面的评论中发布您的发现。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 第20章。深度生成模型,深度学习,2016年。
- 第8章。生成深度学习,Python深度学习,2017年。
论文
- 生成对抗网络, 2014.
- 使用深度卷积生成对抗网络的无监督表征学习, 2015
- 教程:生成对抗网络,NIPS, 2016.
- 使用多任务级联卷积网络进行联合人脸检测和对齐, 2016.
API
- Keras 数据集 API.
- Keras 序列模型 API
- Keras卷积层API
- 我如何“冻结”Keras层?
- MatplotLib API
- NumPy 随机抽样 (numpy.random) API
- NumPy 数组操作例程
文章
- 大规模CelebFaces属性(CelebA)数据集。
- CelebFaces属性(CelebA)数据集,Kaggle.
- 使用GAN生成名人脸(Tensorflow实现),2018.
- MTCNN人脸检测项目,GitHub.
- 线性插值?,dcgan.torch项目,GitHub.
总结
在本教程中,您将学习如何开发用于人脸生成的生成对抗网络,并探索潜在空间的结构及其对生成人脸的影响。
具体来说,你学到了:
- 如何开发用于生成人脸的生成对抗网络。
- 如何对潜在空间中的点进行插值,并生成从一张脸到另一张脸的变形图像。
- 如何在潜在空间中执行向量算术,并在结果生成的面部中实现有针对性的效果。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
非常详细的帖子!热切期待您关于GAN的书。
谢谢!
是的,这真是一个有趣的教程!🙂
新书应该在一两周内准备好。我对此非常兴奋。
你好,Jason
在进行pip install之后,我在使用MTCNN时遇到了一些问题。我收到了消息“no module named cv2”。
我不得不通过输入来安装它:
python -m pip install opencv-python
祝好
感谢分享。
是的,需要OpenCV。
我得到这个值:
Loaded: (50000, 218, 178, 3)
可能是什么问题?
您是否跳过了某一步?
是的,那是一个愚蠢的错误,在一个函数里。现在没问题了。谢谢。
很高兴听到这个消息。
嗨!
我从您的教学中获得了灵感,并开发了一个用于花卉的GAN模型。我正在使用plotly的散点图在2D图表中绘制潜在点。但当我看到那些图像时,它们聚集在一起,但不是基于某个可识别的特征。有什么方法可以调整一些东西,使相似的图像聚集在一起,比如具有相似的颜色或形状等?
感谢您提供如此精彩的教程,我想知道如何对Cycle GAN做同样的事情来生成人脸,例如创建愤怒的人脸、微笑的人脸……
这可能对CycleGAN没有意义,因为它用于图像翻译,而不是生成新图像,比如人脸。
谢谢你。
我很难理解什么是潜在空间?我阅读了您的一些GAN教程和其他教程。我觉得我对潜在空间是什么以及为什么使用它有一个很好的经验性理解,但我不知道如何决定其形状。为什么在DCGAN中潜在空间是一个100维的向量?为什么不是50D、20D或1000D?
这也源于您另一篇教程中关于使用GAN网络进行简单1D函数建模的例子,其中您使用了5D潜在空间。我还读了您其他文章中的一些评论,您提到选择不同的潜在空间形状变得任意?我希望我在这里问对了问题。
如果您能给我一个ELI5(像5岁小孩一样解释)的版本,我将非常感激!谢谢!
大小/形状是任意的。更大可能提供更多自由度,过大或过小可能导致模型不稳定。
它不是一个真实的事物,而是模型学会赋予它意义——可以看作是从中汲取灵感,但以一种一致的方式,使其有序,并且空间的各个部分映射到不同的特定输出——尽管每次模型训练时,这种结构/映射都会不同。
这是一个难以理解的事情。你不是一个人🙂
我认为在这篇文章中我很好地介绍了“潜在空间”。
https://machinelearning.org.cn/what-are-generative-adversarial-networks-gans/
你好,我对这个领域还很新,您的文章非常有帮助。
但是,由于难以完全理解,我是否可以问几个具体的问题?
首先,我知道train on batch函数可以提高gan_model的性能。
但是,我不明白为什么生成latent_points总是使用randn函数来生成随机噪声。
我猜噪声的值并不重要,因为即使噪声总是随机的,GAN模型也会随着训练过程的进行生成更好的图像。
这是正确的吗?
第二,您能否简要告诉我,在加载npz文件(如points = data[‘arr_0’])的每个过程中,“arr_0”是什么意思?
第三,向量算术部分对我来说非常困难,我无法理解这部分在做什么。
”’
# 平均向量
smiling_woman = average_points(points, smiling_woman_ix)
neutral_woman = average_points(points, neutral_woman_ix)
neutral_man = average_points(points, neutral_man_ix)
# 合并所有向量
all_vectors = vstack((smiling_woman, neutral_woman, neutral_man))
# 微笑女性 – 中性女性 + 中性男性 = 微笑男性
result_vector = smiling_woman[-1] – neutral_woman[-1] + neutral_man[-1]
”’
您能否详细解释一下?我明白“average_points”是在不同图像的向量之间进行平均,但无法理解average_point函数的内部工作原理,以及为什么向量的最后一个元素构成了result_vector。
永远感谢您的文章。
我们会在潜在空间中生成随机点来生成新的合成图像。
生成器学会如何构建潜在空间。
npz文件格式保存了一个数组列表,我们只是访问保存的第一个数组。
也许可以从这个更简单的例子开始。
https://machinelearning.org.cn/how-to-develop-a-generative-adversarial-network-for-a-1-dimensional-function-from-scratch-in-keras/
为什么它不被归类为DCGAN,即使它在判别器和生成器模型中都包含CNN?
它是一种DCGAN。
您是否也探讨了降低潜在空间维度的可能性?这里是100维的,但对于某些应用来说,了解是否存在一个不能被跨越的下限,或者这对训练有什么影响,可能会很有用。
是的,我做过实验。小而非常小的空间总是导致模式崩溃。
我终于在Github上发布了描述和代码,使用了我从您的几篇精彩的在线教程中学到的知识,并且我想确保我恰当地引用了您的工作。我发布的是一个cGAN,带有嵌入,并对您的工作和Centeno的工作进行了修改,我还做了一些相关的处理,例如演示如何从中断处重新启动cGAN,自动纠正不太离谱的学习率,以及其他相关的处理。这些都是当人们对别人在互联网上发布的内容进行“小的”修改时可能想做的事情,然后发现他们的“小的”修改会导致收敛问题。它目前在https://github.com/tvtaerum/cGans_with_embedding—housekeeping 上是“私有的”,但我想确保我恰当地引用您和您的工作。我才刚刚开始发布东西,但我发现从一开始就把事情做对是值得的。如果我和其他人想做类似的事情,最合适的方法是什么?您是否碰巧有示例?
干得好。
这可以帮助你引用工作
https://machinelearning.org.cn/faq/single-faq/how-do-i-reference-or-cite-a-book-or-blog-post
0
我正在尝试使用以下命令保存一个 tensorflow kersa 模型,其摘要如下:Model: “sequential_2” 等。使用以下命令:model.save(‘my_model.h5’) 我遇到了以下错误:ImportError:save_modelrequires h5py。 所以,我使用 conda install h5py 安装了 h5py。安装后,我得到了 h5py 的版本:h5py.__version__ ‘2.8.0’ 仍然,我遇到了相同的错误。即使我手动导入了 h5py。
import h5py
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
filename = ‘g_model.h5’
model.save(filename)
**ImportError:**
save_model
需要 h5py。参见此处示例
https://machinelearning.org.cn/tensorflow-tutorial-deep-learning-with-tf-keras/
否则,也许可以在 Stack Overflow 上发布你的错误。
嗨!
再次感谢您提供的精彩材料。我从您的 CNN MNIST 教程开始,此后学到了很多东西。我有一个关于这个的问题
在您定义完整 GAN 的函数中,您将 d_model.trainable 设置为 False。但在代码的其他地方我找不到任何地方在训练循环中更新 d_model 权重之前将其切换回 True。我是否遗漏了什么?
祝好,
Alex
干得好。
我们不将其改回来。G 是通过 D 进行训练的,而 D 的权重不更新,在此处了解更多信息。
https://machinelearning.org.cn/how-to-code-the-generative-adversarial-network-training-algorithm-and-loss-functions/
嗨,Jason,
我买了您关于 GAN 的书,就在它出版后不久,我想说它(以及像这样的帖子)非常棒且非常有帮助。
不过,在整本书/帖子中,我开始注意到一些事情,想问一下
GAN 的所有常见应用都处理方形图像……我将如何处理矩形图像?
虽然我知道重塑和裁剪是解决这个问题的一个简单方法,但如果无法重塑图像怎么办?
我一直在计划一项关于在工作中处理的一些数据集中使用 GAN 进行数据增强的研究,但当我开始查看现有文献时,我注意到几乎所有人都使用方形矩阵表示的数据(无论是图像还是其他)。我目前正在为不同的应用处理不同类型的矩阵,并且并非所有矩阵都与图像相关,最重要的是它们不是方形的。在大多数情况下,我获得和处理的数据是具有许多行和仅三列的矩阵。在某些情况下,我会有更多的列,但总的来说,根据我正在处理的应用,我的数据集大约是 13×3、17×3 或 30×3。
您知道有处理矩形数据的论文或应用吗?或者您能否推荐一个起点,让我可以寻找一些灵感?
谢谢 Lloyd!
是的,我预计结果对于矩形图像也适用,但正如您所说,需要调整网络配置。
论文为了简单起见,处理的是方形图像。
抱歉,暂时没有。我建议您亲自尝试。
感谢您提供一本很棒的书,我玩得很开心。既然您征求反馈,如果我们探索扩展,那么……
我注意到 CelebA 的图像质量参差不齐。有很多图像是侧面或非常扭曲的面部。由于 MTCNN 还提供鼻子和眼睛的位置,我尝试对数据准备算法进行了一些小改动。我没有从 200K CelebA 图像中随机选择 50K 张,而是倾向于选择鼻子中心到眼睛中心之间的垂直距离较短的图像。这会选择人们看得更直(以及一些鼻子歪斜的 🙂)的图像。
让模型具有较少的变异性和较少的离群值姿势可以产生更一致的结果
谢谢。
做得好,谢谢分享!
感谢您提供如此详细的优秀教程。我有一个问题,正如您在文章中所提到的,“鉴别器的损失可能会骤降到真实样本和生成样本的 0.0 值”。我遇到了这个问题,但这是怎么发生的?您能否解释一下这种现象的原因?
是的,模型会变得不稳定并失败。
GAN 非常不稳定,这使得训练它们变得困难。
https://machinelearning.org.cn/practical-guide-to-gan-failure-modes/
我们还没有关于它们行为的充分理论,因此我们专注于经验方法——所谓的“GAN 技巧”或启发式方法,以防止它们失败。
https://machinelearning.org.cn/how-to-train-stable-generative-adversarial-networks/
因为我花了大约 3 天的时间来找出为什么 import cv2 不工作,所以我将分享我的解决方案,以防你们中有人可能遇到同样的问题。
对于 W10 N / pro N,您需要安装媒体功能包。
没有这个,任何安装和导入 cv2 的高级方法都将不起作用……
—
感谢 Jason 的这个了不起的博客,我正准备在我的机械工程博士工作中尝试使用 C-GAN,并且您为像我这样的非直接 AI/DL 研究人员澄清了许多关于此网络的秘密。
我计划下个月购买您的 GAN 书。
此致!
什么是“媒体功能包”?
我们在本教程中甚至没有使用 opencv!
是的,没有使用它,但……MTCNN 需要它才能工作:)
“媒体功能包”是 W10 N 系列操作系统的一些功能的集合。
抱歉,我从未听说过。
感谢您提供如此详细的优秀教程。像素值必须缩放到 [-1,1] 的范围才能匹配生成器模型的输出。为什么我们需要这样做??
生成器在生成范围在 -1 和 1 之间的像素时表现更好。
为了匹配生成器模型的输出,以便真实图像和生成的图像具有相同的比例。
你好,
在此示例中,当更新生成器的参数时,鉴别器的变量也会被更新,因为 train_on_batch 方法是在包含生成器和鉴别器的顺序模型上调用的。生成器的参数不应该独立于鉴别器的参数进行更新吗?
不完全是,我们将鉴别器设置为在其是生成器一部分时不可训练。
嗨,谢谢您的教程。
我有一个错误。请帮我
‘tensorflow.python.framework.errors_impl.ResourceExhaustedError
OOM 在为形状为[128,128,80,80]且类型为 float 的张量分配内存时出错,位于/job:localhost/replica:0/task:0/device:GPU:0,使用分配器 GPU_0_bfc
[[node gradients_1/sequential_1/conv2d_2/convolution_grad/Conv2DBackpropInput (定义于 \Users\HP\miniconda3\envs\Tensorflow_v2_py3\lib\site-packages\keras\backend\tensorflow_backend.py:3009) ]]’
看起来您内存不足。
尝试在具有更多 RAM 的 AWS EC2 实例上运行
https://machinelearning.org.cn/develop-evaluate-large-deep-learning-models-keras-amazon-web-services/
谢谢。我通过将 batch_size 减小到 64(之前是 128)解决了这个问题:)
干得好!
您的教程非常棒。我尝试使用 X 光胸部数据集。因为它不同于包含 RGB 的数据集,所以没有成功。您有什么建议可以这样做吗?提前谢谢您
谢谢!
好问题,我暂时没有好的建议。您可能需要尝试您的数据的 RGB 编码。
您好,先生!
我可以使用您的代码进行 InfoGAN 人脸生成吗?
或许可以试试看?
您好,先生,很棒的教程。
我正在为学术研究开发一个人类面部 InfoGAN 模型,我想知道是否可以将您的模型用作基础代码,因为它很稳定,并且可以生成良好的图像。
谢谢你。
试试看吧。
哦,我已经用了。效果很好。我正在撰写报告,我想确保我获得了使用您的代码和修改它的许可。
干得好!
是的,只要您清楚引用和链接到来源即可。
https://machinelearning.org.cn/faq/single-faq/can-i-use-your-code-in-my-own-project
当然!谢谢。
亲爱的 Jason,
感谢您的精彩书籍。这是我读过的您的第 7 本书,总是一次愉快的体验。我几乎花了我整个周末的时间来玩 GAN。这个周末我写了一篇关于您的 GAN 书的第三部分和第四部分的评论。
http://questioneurope.blogspot.com/2020/09/generative-adversarial-network-with.html
我也遇到了与社区成员在运行 cGAN 时类似的 the network failure,但在第二次运行时似乎有所改善。
诚挚的问候,
Dominique
谢谢!你取得的令人印象深刻的进展令人赞叹。
GAN 可能非常棘手。您必须通过构建/训练许多不同的模型来积累经验和信心——例如,书中采用的方法。
很棒的教程 Jason。但是,我如何对用于训练的 MNIST 数据进行插值?
我的意思是,而不是使用生成的数据。
由于代码不包含编码器,只包含作为解码器的生成器,我认为不行。
抱歉,我不明白您的问题。本教程是关于用于生成人脸图像的生成器,而不是 MNIST 数字,也不是其他图像的变换。
也许您对图像翻译 GAN(如 pix2pix)更感兴趣
https://machinelearning.org.cn/how-to-implement-pix2pix-gan-models-from-scratch-with-keras/
感谢您的明确教程。
而不是对生成数据进行潜在空间插值,我如何对原始 celebA 人脸进行插值?
我想我需要某种编码器,但不知道如何将原始人脸映射到潜在空间!
如果模型是在 celebA 数据集上训练的,那么对潜在空间进行插值就会对 celebA 数据集进行插值。
亲爱的Jason Brownlee,
感谢您分享您的优秀作品。您能否帮助我使用 Hinge 损失函数而不是二元交叉熵损失,在同一个模型中实现 80×80 图像尺寸的基本人脸生成?我尝试使用 Hinge 损失将数据缩放到 {-1, 1} 范围,但输出生成没有任何进展(只生成空白的灰色图像)。
祝好
也许可以尝试调整您模型的学习参数?
也许可以尝试调整您模型的网络架构?
你好!非常感谢您的文章。我有一个问题:我决定每 5 个训练周期输出一次鉴别器的准确率,针对 10,000 张图像(与文章中完全相同的方式)。也就是说,我每五个周期保存一次鉴别器,并针对 10,000 张图像运行其准确率。事实是,真实图像的准确率波动很大:第五个周期后——准确率是 99%,第 15 个周期后——75%,第 20 个周期后又是 92%。图像质量在每个周期后都会提高。您能否告诉我这种差异的原因?
real – 是 d1,fake – 是 d2。
{
‘descriminator_model_005.h5’: {‘real’: 99.98000264167786, ‘fake’: 100.0},
‘descriminator_model_010.h5’: {‘real’: 99.40000176429749, ‘fake’: 100.0},
‘descriminator_model_015.h5’: {‘real’: 75.51000118255615, ‘fake’: 90.85999727249146},
‘descriminator_model_020.h5’: {‘real’: 92.91999936103821, ‘fake’: 97.50000238418579},
‘descriminator_model_025.h5’: {‘real’: 94.0999984741211, ‘fake’: 97.97000288963318},
‘descriminator_model_030.h5’: {‘real’: 91.86000227928162, ‘fake’: 97.61999845504761},
‘descriminator_model_035.h5’: {‘real’: 84.42999720573425, ‘fake’: 98.83000254631042},
‘descriminator_model_040.h5’: {‘real’: 78.43999862670898, ‘fake’: 97.36999869346619},
‘descriminator_model_050.h5’: {‘real’: 86.82000041007996, ‘fake’: 100.0},
‘descriminator_model_055.h5’: {‘real’: 80.95999956130981, ‘fake’: 99.55999851226807},
‘descriminator_model_060.h5’: {‘real’: 82.56000280380249, ‘fake’: 100.0}
}
准确率是 GAN 的一个坏指标。它不可靠,请参见此链接。
https://machinelearning.org.cn/how-to-evaluate-generative-adversarial-networks/
您好,非常感谢您提供的信息性文章。我有一个关于自定义训练的问题。我能否不训练图像,而是使用一些单独的编码器网络生成的自定义“嵌入”来训练和测试 GAN?也就是说,我首先使用单独的网络在自定义数据集上生成一些嵌入,然后将这些向量馈送到 GAN。您有什么建议吗?谢谢!
不客气。
我暂时不确定,也许不行。您可能需要仔细考虑或甚至原型化以查看其是否可行。
这是一个很棒的教程,但当我尝试使用 load_faces 时,它会显示一些警告,如下所示
触发了 tf.function 重跟踪。重跟踪成本很高,而过多的重跟踪可能是由以下原因造成的:(1) 在循环中重复创建 @tf.function,(2) 传递形状不同的张量,(3) 传递 Python 对象而不是张量。对于 (1),请在循环外部定义您的 @tf.function。对于 (2),@tf.function 具有 experimental_relax_shapes=True 选项,可以放宽参数形状,从而避免不必要的重跟踪。对于 (3),请参考 https://tensorflowcn.cn/guide/function#controlling_retracing 和 https://tensorflowcn.cn/api_docs/python/tf/function 以获取更多详细信息。
您能告诉我如何解决这个问题吗?
谢谢。
也许您可以安全地忽略警告?
这是一个很棒的教程,我注意到在“如何对生成的人脸进行插值”标题下的两个代码块中。np.Linspace 支持向量之间的插值,因此您不需要
这两段代码
而是只需将您想要插值的向量输入 np.linspace,例如:
print(np.linspace([0,5],[1,6],num=10))
[[0. 5. ]
[0.11111111 5.11111111]
[0.22222222 5.22222222]
[0.33333333 5.33333333]
[0.44444444 5.44444444]
[0.55555556 5.55555556]
[0.66666667 5.66666667]
[0.77777778 5.77777778]
[0.88888889 5.88888889]
[1. 6. ]]
谢谢指出!
嗨 Jason!您的代码效果很好。
非常感谢您发布了优秀且具有启发性的 ML 博文……!
是的,训练可能需要数小时。我在 Google Colab 上只能训练模型 40 个周期(大约三个半小时)。
我已保存了三个模型,d_model、g_model 和 gan_model,但我无法继续训练。
有什么建议如何做到这一点吗?提前感谢!
Wilfredo,您非常受欢迎!请详细说明训练无法继续的原因。这将使我们能够更好地帮助您。
非常感谢您写这篇文章!您能否告诉我可视化潜在空间的最佳方法?我的意思是,例如 3D 中的点?
Jucris,您可能会发现以下资源很有用
https://towardsdatascience.com/understanding-latent-space-in-machine-learning-de5a7c687d8d
嗨!
我从您的教学中获得了灵感,并开发了一个用于花卉的GAN模型。我正在使用plotly的散点图在2D图表中绘制潜在点。但当我看到那些图像时,它们聚集在一起,但不是基于某个可识别的特征。有什么方法可以调整一些东西,使相似的图像聚集在一起,比如具有相似的颜色或形状等?
嗨 James,
您的文章易于理解,谢谢。我想了解更多关于潜在空间和向量以及它们在 GAN 中如何工作的信息。我期待理论和数学解释。希望您能帮我解决这个问题。谢谢……