JavaScript 深度学习(四)(2)

简介: JavaScript 深度学习(四)

JavaScript 深度学习(四)(1)https://developer.aliyun.com/article/1516976

10.3. 使用 GANs 进行图像生成

自从 Ian Goodfellow 和他的同事在 2014 年引入了 GANs^([16]) 这项技术以来,它的兴趣和复杂程度迅速增长。如今,GANs 已经成为生成图像和其他数据模态的强大工具。它们能够输出高分辨率图像,有些情况下,这些图像在人类眼中几乎无法与真实图像区分开来。查看 NVIDIA 的 StyleGANs 生成的人脸图像,如 图 10.9^([17]) 所示。如果不是人脸上偶尔出现的瑕疵点和背景中不自然的场景,人类观察者几乎无法将这些生成的图像与真实图像区分开来。

¹⁶

Ian Goodfellow 等人,“生成对抗网络”,NIPS 会议论文集,2014 年,mng.bz/4ePv

¹⁷

thispersondoesnotexist.com 的网站。有关学术论文,请参阅 Tero Karras,Samuli Laine 和 Timo Aila,“用于生成对抗网络的基于样式的生成器架构”,于 2018 年 12 月 12 日提交,arxiv.org/abs/1812.04948

图 10.9. NVIDIA 的 StyleGAN 生成的示例人脸图像,从 thispersondoesnotexist.com 中采样于 2019 年 4 月

除了“从蓝天中生成引人注目的图像”之外,GAN 生成的图像还可以根据某些输入数据或参数进行条件约束,这带来了更多的特定任务和有用的应用。例如,GAN 可以用于从低分辨率输入(图像超分辨率)生成更高分辨率的图像,填补图像的缺失部分(图像修复),将黑白图像转换为彩色图像(图像着色),根据文本描述生成图像以及根据输入图像中同一人采取的姿势生成该人的图像。此外,已经开发了新类型的 GAN 用于生成非图像输出,例如音乐。^([18]) 除了在艺术、音乐制作和游戏设计等领域中生成无限量的逼真材料的明显价值之外,GAN 还有其他应用,例如通过在获取此类样本代价高昂的情况下生成训练示例来辅助深度学习。例如,GAN 正被用于为训练自动驾驶神经网络生成逼真的街景图像。^([19])

¹⁸

请参阅 Hao-Wen Dong 等人的 MuseGAN 项目:salu133445.github.io/musegan/

¹⁹

James Vincent,《NVIDIA 使用 AI 让永远阳光的街道下雪》,The Verge,2017 年 12 月 5 日,mng.bz/Q0oQ

虽然 VAE 和 GAN 都是生成模型,但它们基于不同的思想。VAE 通过使用原始输入和解码器输出之间的均方误差损失来确保生成的示例的质量,而 GAN 则通过使用鉴别器来确保其输出逼真,我们很快就会解释。此外,GAN 的许多变体允许输入不仅包含潜空间向量,还包括条件输入,例如所需的图像类别。我们将要探索的 ACGAN 就是这方面的一个很好的例子。在这种具有混合输入的 GAN 类型中,潜空间不再与网络输入具有连续性。

在这个部分,我们将深入研究一种相对简单的 GAN 类型。具体而言,我们将在熟悉的 MNIST 手写数字数据集上训练一个辅助分类器 GAN (ACGAN)^([20])。这将给我们一个能够生成与真实 MNIST 数字完全相似的数字图像的模型。同时,由于 ACGAN 的“辅助分类器”部分,我们将能够控制每个生成图像所属的数字类别(0 到 9)。为了理解 ACGAN 的工作原理,让我们一步一步来。首先,我们将解释 ACGAN 的基本“GAN”部分如何工作。然后,我们将描述 ACGAN 通过额外的机制如何使类别标识具有可控性。

²⁰

Augustus Odena、Christopher Olah 和 Jonathon Shlens,“带辅助分类器 GAN 的条件图像合成”,2016 年 10 月 30 日提交,arxiv.org/abs/1610.09585

10.3.1. GANs 背后的基本思想

生成对抗网络(GAN)是如何学习生成逼真图片的?它通过其包含的两个子部分之间的相互作用来实现这一点:一个生成器和一个鉴别器。把生成器想象成一个伪造者,其目标是创建高质量的假毕加索画作;而鉴别器则像是一位艺术品经销商,其工作是将假的毕加索画作与真实的区分开来。伪造者(生成器)努力创建越来越好的假画作以欺骗艺术品经销商(鉴别器),而艺术品经销商的工作是成为对画作的评判者越来越好,从而不被伪造者欺骗。我们两个角色之间的这种对抗是“GAN”名称中“对抗性”部分的原因。有趣的是,伪造者和艺术品经销商最终互相帮助变得更好,尽管表面上是对手。

起初,伪造者(生成器)在创建逼真的毕加索画作方面表现糟糕,因为其权重是随机初始化的。结果,艺术品经销商(鉴别器)很快就学会了区分真假毕加索画作。这里是所有这些工作的重要部分:每次伪造者给艺术品经销商带来一幅新画作时,他们都会得到详细的反馈(来自艺术品经销商),指出画作的哪些部分看起来不对劲,以及如何改变画作使其看起来更真实。伪造者学习并记住这一点,以便下次他们来到艺术品经销商那里时,他们的画作看起来会稍微好一些。这个过程重复多次。结果发现,如果所有参数都设置正确,我们最终会得到一个技艺精湛的伪造者(生成器)。当然,我们也会得到一个技艺精湛的鉴别器(艺术品经销商),但通常在 GAN 训练完成后我们只需要生成器。

图 10.10 更详细地描述了如何训练通用 GAN 模型的判别器部分。为了训练判别器,我们需要一批生成的图像和一批真实图像。生成的图像由生成器生成。但生成器无法从空气中制作图像。相反,它需要作为输入的随机向量。潜在向量在概念上类似于我们在第 10.2 节中用于 VAE 的向量。对于生成器生成的每个图像,潜在向量是形状为[latentSize]的一维张量。但像本书中大多数训练过程一样,我们一次对一批图像执行步骤。因此,潜在向量的形状为[batchSize, latentSize]。真实图像直接从实际 MNIST 数据集中提取。为了对称起见,我们在每个训练步骤中绘制与生成的图像完全相同数量的batchSize真实图像。

图 10.10. 示出 GAN 判别器部分训练算法的示意图。请注意,为简单起见,该图省略了 ACGAN 的数字类部分。有关 ACGAN 生成器训练的完整图表,请参见图 10.13。

生成的图像和真实图像随后被连接成一批图像,表示为形状为[2 * batchSize, 28, 28, 1]的张量。判别器在这批合并图像上执行,输出每个图像是真实的预测概率分数。这些概率分数可以轻松地通过二元交叉熵损失函数与基准真值进行测试(我们知道哪些是真实的,哪些是生成的!)。然后,熟悉的反向传播算法发挥作用,借助优化器(图中未显示)更新判别器的权重参数。这一步使判别器略微朝着正确的预测方向推进。请注意,生成器仅通过提供生成的样本参与此训练步骤,但它不会通过反向传播过程进行更新。下一步训练将更新生成器(图 10.11)。

图 10.11. 示出 GAN 生成器部分训练算法的示意图。请注意,为简单起见,该图省略了 ACGAN 的数字类部分。有关 ACGAN 生成器训练过程的完整图表,请参见图 10.14。

图 10.11 说明了生成器的训练步骤。我们让生成器生成另一批生成图像。但与鉴别器的训练步骤不同,我们不需要任何真实的 MNIST 图像。鉴别器被赋予了这批生成图像以及一批二进制真实性标签。我们假装这些生成图像都是真实的,将真实性标签设置为全部为 1。静下心来思考一下:这是 GAN 训练中最重要的技巧。当然,这些图像都是生成的(并非真实的),但我们让真实性标签表明它们是真实的。鉴别器可能(正确地)对一些或所有的输入图像分配较低的真实性概率。但是如果这样做,由于虚假的真实性标签,二进制交叉熵损失将得到较大的值。这将导致反向传播更新生成器,以使鉴别器的真实性得分稍微增加。请注意,反向传播只更新生成器,不对鉴别器进行任何更改。这是另一个重要的技巧:它确保生成器最终产生的图像看起来更真实一些,而不是降低鉴别器对真实性的要求。这是通过冻结模型的鉴别器部分实现的,这是我们在第五章中用于迁移学习的一种操作。

总结生成器训练步骤:冻结鉴别器并向其提供全是 1 的真实性标签,尽管它得到的是由生成器生成的生成图像。由于这样,对生成器的权重更新将导致其生成的图像在鉴别器中看起来稍微更真实。只有当鉴别器相当擅长区分真实和生成的图像时,这种训练生成器的方式才会奏效。我们如何确保这一点?答案是我们已经讨论过的鉴别器训练步骤。因此,你可以看到这两个训练步骤形成了一种复杂的阴阳动态,其中 GAN 的两个部分相互抵触并互相帮助。

这就是对通用 GAN 训练的高级概览。在下一节中,我们将介绍鉴别器和生成器的内部架构以及它们如何融入有关图像类别的信息。

10.3.2. ACGAN 的构建模块

清单 10.9 显示了创建 MNIST ACGAN 判别器部分的 TensorFlow.js 代码(摘自 mnist-acgan/gan.js)。在判别器的核心是一种类似于我们在第四章中看到的深度卷积网络。其输入具有 MNIST 图像的经典形状,即 [28, 28, 1]。输入图像通过四个 2D 的卷积(conv2d)层,然后被展平并经过两个全连接层处理。其中一个全连接层为输入图像的真实性二进制预测输出,另一个输出 10 个数字类别的 softmax 概率。判别器是一个有两个全连接层输出的函数模型。图 10.12 的面板 A 提供了判别器的一个输入-两个输出拓扑结构的示意图。

图 10.12。ACGAN 的判别器(面板 A)和生成器(面板 B)部分的内部拓扑示意图。为简洁起见,省略了某些细节(例如判别器中的 dropout 层)。有关详细的代码,请参见清单 10.9 和 10.10。

清单 10.9。创建 ACGAN 的判别器部分。
function buildDiscriminator() {
  const cnn = tf.sequential();
  cnn.add(tf.layers.conv2d({
    filters: 32,
    kernelSize: 3,
    padding: 'same',
    strides: 2,
    inputShape: [IMAGE_SIZE, IMAGE_SIZE, 1]                                ***1***
  }));
  cnn.add(tf.layers.leakyReLU({alpha: 0.2}));
  cnn.add(tf.layers.dropout({rate: 0.3}));                                 ***2***
  cnn.add(tf.layers.conv2d(
      {filters: 64, kernelSize: 3, padding: 'same', strides: 1}));
  cnn.add(tf.layers.leakyReLU({alpha: 0.2}));
  cnn.add(tf.layers.dropout({rate: 0.3}));
  cnn.add(tf.layers.conv2d(
      {filters: 128, kernelSize: 3, padding: 'same', strides: 2}));
  cnn.add(tf.layers.leakyReLU({alpha: 0.2}));
  cnn.add(tf.layers.dropout({rate: 0.3}));
  cnn.add(tf.layers.conv2d(
      {filters: 256, kernelSize: 3, padding: 'same', strides: 1}));
  cnn.add(tf.layers.leakyReLU({alpha: 0.2}));
  cnn.add(tf.layers.dropout({rate: 0.3}));
  cnn.add(tf.layers.flatten());
  const image = tf.input({shape: [IMAGE_SIZE, IMAGE_SIZE, 1]});
  const features = cnn.apply(image);
  const realnessScore =                                                    ***3***
      tf.layers.dense({units: 1, activation: 'sigmoid'}).apply(features);  ***3***
  const aux = tf.layers.dense({units: NUM_CLASSES, activation: 'softmax'}) ***4***
                  .apply(features);                                        ***4***
  return tf.model({inputs: image, outputs: [realnessScore, aux]});
}
  • 1 判别器只接受 MNIST 格式的图像作为输入。
  • 2 使用 Dropout 层来对抗过拟合。
  • 3 判别器的两个输出之一是二进制真实性分类的概率分数。
  • 4 第二个输出是 10 个 MNIST 数字类别的 softmax 概率。

清单 10.10 中的代码负责创建 ACGAN 生成器。正如我们之前暗示的那样,生成器的生成过程需要一个叫做潜在向量(代码中称为 latent)的输入。这体现在其第一个全连接层的 inputShape 参数中。然而,如果你仔细检查代码,就会发现生成器实际上接受两个输入。这在 图 10.12 的面板 B 中有描述。除了潜在向量外,也就是一个形状为 [latentSize] 的一维张量,生成器需要一个额外的输入,名为 imageClass,形状简单,为 [1]。这是告诉模型要生成哪个 MNIST 数字(0 到 9)的方式。例如,如果我们想要模型生成数字 8 的图像,我们应该将形状为 tf.tensor2d([[8]]) 的张量值输入到第二个输入(请记住,即使只有一个示例,模型也始终期望批量张量)。同样,如果我们想要模型生成两个图像,一个是数字 8,另一个是数字 9,则馈送的张量应为 tf.tensor2d([[8], [9]])

一旦 imageClass 输入进入生成器,嵌入层将其转换为与 latent 相同形状的张量 ([latentSize])。这一步在数学上类似于我们在 第九章 中用于情感分析和日期转换模型的嵌入查找过程。期望的数字类别是一个整数量,类似于情感分析数据中的单词索引和日期转换数据中的字符索引。它被转换为与单词和字符索引转换为 1D 向量的方式相同的 1D 向量。然而,我们在这里对 imageClass 使用嵌入查找是为了不同的目的:将其与 latent 向量合并并形成一个单一的组合向量(在 清单 10.10 中命名为 h)。这个合并是通过一个 multiply 层完成的,该层在两个相同形状的向量之间执行逐元素相乘。结果张量的形状与输入相同 ([latentSize]),并传入生成器的后续部分。

生成器立即在合并的潜在向量 (h) 上应用一个密集层,并将其重塑为 3D 形状 [3, 3, 384]。这种重塑产生了一个类似图像的张量,随后可以由生成器的后续部分转换为具有标准 MNIST 形状 ([28, 28, 1]) 的图像。

生成器不使用熟悉的 conv2d 层来转换输入,而是使用 conv2dTranspose 层来转换其图像张量。粗略地说,conv2dTranspose 执行与 conv2d 的逆操作(有时称为反卷积)。conv2d 层的输出通常比其输入具有更小的高度和宽度(除了 kernelSize 为 1 的情况之外),如您在 第四章 中的 convnets 中所见。然而,conv2dTranspose 层的输出通常比其输入具有更大的高度和宽度。换句话说,虽然 conv2d 层通常缩小其输入的维度,但典型的 conv2dTranspose 层扩展它们。这就是为什么在生成器中,第一个 conv2dTranspose 层接受高度为 3 和宽度为 3 的输入,但最后一个 conv2dTranspose 层输出高度为 28 和宽度为 28 的原因。这就是生成器将输入潜在向量和数字索引转换为标准 MNIST 图像尺寸的图像的方式。以下清单中的代码摘录自 mnist-acgan/gan.js; 为了清晰起见,删除了一些错误检查代码。

清单 10.10. 创建 ACGAN 的生成器部分
function buildGenerator(latentSize) {
  const cnn = tf.sequential();
  cnn.add(tf.layers.dense({
    units: 3 * 3 * 384,                                ***1***
    inputShape: [latentSize],
    activation: 'relu'
  }));
  cnn.add(tf.layers.reshape({targetShape: [3, 3, 384]}));
  cnn.add(tf.layers.conv2dTranspose({                  ***2***
    filters: 192,
    kernelSize: 5,
    strides: 1,
    padding: 'valid',
    activation: 'relu',
    kernelInitializer: 'glorotNormal'
  }));
  cnn.add(tf.layers.batchNormalization());
  cnn.add(tf.layers.conv2dTranspose({                  ***3***
    filters: 96,
    kernelSize: 5,
    strides: 2,
    padding: 'same',
    activation: 'relu',
    kernelInitializer: 'glorotNormal'
  }));
  cnn.add(tf.layers.batchNormalization());
  cnn.add(tf.layers.conv2dTranspose({                  ***4***
    filters: 1,
    kernelSize: 5,
    strides: 2,
    padding: 'same',
    activation: 'tanh',
    kernelInitializer: 'glorotNormal'
  }));
  const latent = tf.input({shape: [latentSize]});      ***5***
  const imageClass = tf.input({shape: [1]});           ***6***
  const classEmbedding = tf.layers.embedding({         ***7***
    inputDim: NUM_CLASSES,
    outputDim: latentSize,
    embeddingsInitializer: 'glorotNormal'
  }).apply(imageClass);
  const h = tf.layers.multiply().apply(                ***8***
      [latent, classEmbedding]);                       ***8***
  const fakeImage = cnn.apply(h);
  return tf.model({                                    ***9***
   inputs: [latent, imageClass],                       ***9***
   outputs: fakeImage                                  ***9***
  });                                                  ***9***
}
  • 1 单元的数量被选择为当输出被重塑并通过后续的 conv2dTranspose 层时,最终输出的张量的形状与 MNIST 图像完全匹配 ([28, 28, 1])。
  • 2 从 [3, 3, …] 上采样至 [7, 7, …]
  • 3 上采样至 [14, 14, …]
  • 4 上采样至 [28, 28, …]
  • 5 这是生成器的两个输入之一:作为伪图像生成的“种子”的潜在(z-空间)向量。
  • 6 生成器的第二个输入:控制生成的图像属于哪个 MNIST 数字类别的类标签
  • 7 通过嵌入查找将期望标签转换为长度为 latentSize 的向量
  • 8 通过乘法将潜在向量和类别条件嵌入组合起来
  • 9 最终创建模型,以顺序卷积网络为核心。
10.3.3. 更深入地了解 ACGAN 的训练

最后一节应该让你更好地理解了 ACGAN 的鉴别器和生成器的内部结构,以及它们如何整合数字类别信息(ACGAN 名字中的“AC”部分)。有了这些知识,我们就可以扩展 figures 10.10 和 10.11,以全面了解 ACGAN 的训练方式。

Figure 10.13 是 figure 10.10 的扩展版本。它展示了 ACGAN 的鉴别器部分的训练。与之前相比,这一训练步骤不仅提高了鉴别器区分真实和生成(伪造)图像的能力,还磨练了其确定给定图像(包括真实和生成的图像)属于哪个数字类别的能力。为了更容易与之前的简单图表进行比较,我们将已在 figure 10.10 中看到的部分灰暗显示,并突出显示新的部分。首先,注意到生成器现在有了一个额外的输入(数字类别),这使得指定生成器应该生成什么数字成为可能。此外,鉴别器不仅输出真实性预测,还输出数字类别预测。因此,鉴别器的两个输出头都需要进行训练。对于真实性预测的训练与之前相同(figure 10.10);类别预测部分的训练依赖于我们知道生成和真实图像属于哪些数字类别。模型的两个头部编译了不同的损失函数,反映了两种预测的不同性质。对于真实性预测,我们使用二元交叉熵损失,但对于数字类别预测,我们使用了稀疏分类交叉熵损失。你可以在 mnist-acgan/gan.js 的下一行中看到这一点:

discriminator.compile({
    optimizer: tf.train.adam(args.learningRate, args.adamBeta1),
    loss: ['binaryCrossentropy', 'sparseCategoricalCrossentropy']
  });
图 10.13. 说明 ACGAN 的鉴别器部分是如何训练的示意图。这个图表在 figure 10.10 的基础上添加了与数字类别相关的部分。图表的其余部分已经在 figure 10.10 中出现,并且被灰暗显示。

如 图 10.13 中的两条弯曲箭头所示,当更新鉴别器的权重时,通过反向传播的梯度会相互叠加。图 10.14 是 图 10.11 的扩展版本,提供了 ACGAN 生成器部分训练的详细示意图。该图显示了生成器学习如何根据指定的数字类别生成正确的图像,以及学习如何生成真实的图像。与 图 10.13 类似,新添加的部分被突出显示,而已经存在于 图 10.11 的部分则被隐藏。从突出显示的部分中,可以看到我们在训练步骤中输入的标签现在不仅包括真实性标签,还包括数字类别标签。与以前一样,真实性标签都是故意虚假的。但是新添加的数字类别标签更加真实,因为我们确实将这些类别标签给了生成器。

图 10.14. 示意图,说明 ACGAN 的生成器部分是如何训练的。这个图是 图 10.11 的扩展,显示了与数字类别相关的部分。图的其余部分已经在图 10.11 中出现,已被隐藏。

先前,我们看到虚假真实标签与鉴别器的真实概率输出之间的任何差异会被用来更新 ACGAN 的生成器,使其在“欺骗”鉴别器方面更加优秀。在这里,鉴别器的数字分类预测发挥了类似的作用。例如,如果我们告诉生成器生成一个数字 8 的图像,但是鉴别器将图像分类为 9,则稀疏分类交叉熵的值将较高,并且与之关联的梯度将有较大的幅度。因此,生成器权重的更新将导致生成器生成一个更像数字 8 的图像(根据鉴别器的判断)。显然,只有当鉴别器在将图像分类为 10 个 MNIST 数字类别方面足够好时,这种训练生成器的方法才会起作用。这就是前一个鉴别器训练步骤所帮助确保的。再次强调,在 ACGAN 的训练过程中,我们看到了鉴别器和生成器部分之间的阴阳动力学。

GAN 训练:一大堆诡计

训练和调整 GAN 的过程众所周知地困难。您在 mnist-acgan 示例中看到的训练脚本是研究人员大量试错的结晶。像深度学习中的大多数事物一样,这更像是一种艺术而不是精确科学:这些技巧是启发式的,没有系统理论的支持。它们得到了对手头现象的直觉理解,并且在经验上被证明效果良好,尽管不一定在每种情况下都有效。

以下是本节中 ACGAN 中使用的一些值得注意的技巧列表:

  • 我们在生成器的最后一个 conv2dTranspose 层中使用 tanh 作为激活函数。在其他类型的模型中,tanh 激活函数出现得较少。
  • 随机性有助于诱导鲁棒性。因为 GAN 的训练可能导致动态平衡,所以 GAN 很容易陷入各种各样的困境中。在训练过程中引入随机性有助于防止这种情况发生。我们通过两种方式引入随机性:在鉴别器中使用 dropout,以及为鉴别器的真实标签使用“soft one”值(0.95)。
  • 稀疏梯度(许多值为零的梯度)可能会妨碍 GAN 的训练。在其他类型的深度学习中,稀疏性通常是一种理想的特性,但在 GAN 中不是这样。梯度中的稀疏性可能由两个因素引起:最大池化操作和 relu 激活函数。建议使用步幅卷积进行下采样,而不是最大池化,这正是生成器创建代码中所示的内容。建议使用 leakyReLU 激活函数,其中负部分具有小的负值,而不是严格的零。这也在清单 10.10 中显示。
10.3.4. 查看 MNIST ACGAN 训练和生成

mnist-acgan 示例可以通过以下命令检出和准备:

git clone https://github.com/tensorflow/tfjs-examples.git
cd tfjs-examples/mnist-acganyarn

运行示例涉及两个阶段:在 Node.js 中进行训练,然后在浏览器中进行生成。要启动训练过程,只需使用以下命令:

yarn train

训练默认使用 tfjs-node。然而,像我们之前见过的涉及卷积神经网络的示例一样,使用 tfjs-node-gpu 可以显著提高训练速度。如果您的计算机上正确设置了支持 CUDA 的 GPU,您可以在yarn train命令中追加--gpu标志来实现。训练 ACGAN 至少需要几个小时。对于这个长时间运行的训练任务,您可以使用--logDir标志通过 TensorBoard 监控进度:

yarn train --logDir /tmp/mnist-acgan-logs

一旦在单独的终端中使用以下命令启动了 TensorBoard 进程,

tensorboard --logdir /tmp/mnist-acgan-logs

您可以在浏览器中导航到 TensorBoard URL(由 TensorBoard 服务器进程打印)以查看损失曲线。图 10.15 显示了训练过程中的一些示例损失曲线。GAN 训练的损失曲线的一个显著特征是,它们并不总是像大多数其他类型的神经网络的损失曲线那样趋向于下降。相反,判别器的损失(图中的 dLoss)和生成器的损失(图中的 gLoss)都以非单调方式变化,并相互交织形成复杂的舞蹈。

图 10.15. ACGAN 训练作业中的样本损失曲线。dLoss 是判别器训练步骤的损失。具体来说,它是真实性预测的二元交叉熵和数字类别预测的稀疏分类交叉熵的总和。gLoss 是生成器训练步骤的损失。与 dLoss 类似,gLoss 是来自二元真实性分类和多类数字分类的损失的总和。

在训练接近结束时,两者的损失都不会接近零。相反,它们只是趋于平稳(收敛)。此时,训练过程结束并将模型的生成器部分保存到磁盘上,以便在浏览器内生成步骤中进行服务:

await generator.save(saveURL);

要运行浏览器内生成演示,请使用命令 yarn watch。它将编译 mnist-acgan/index.js 和相关的 HTML 和 CSS 资源,然后会在您的浏览器中打开一个标签页并显示演示页面。^([21])

²¹

您还可以完全跳过训练和构建步骤,直接导航到托管的演示页面,网址为 mng.bz/4eGw

演示页面加载了从前一阶段保存的训练好的 ACGAN 生成器。由于判别器在此演示阶段并不真正有用,因此它既不保存也不加载。有了生成器加载后,我们可以构建一批潜在向量,以及一批期望的数字类别索引,并调用生成器的 predict()。执行此操作的代码位于 mnist-acgan/index.js 中:

const latentVectors = getLatentVectors(10);
    const sampledLabels = tf.tensor2d(
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 1]);
    const generatedImages =
        generator.predict([latentVectors, sampledLabels]).add(1).div(2);

我们的数字类别标签批次始终是一个有序的 10 元素向量,从 0 到 9。这就是为什么生成的图像批次总是一个从 0 到 9 的有序图像数组。这些图像使用 tf.concat() 函数拼接在一起,并在页面上的 div 元素中呈现(参见图 10.16 中的顶部图像)。与随机抽样的真实 MNIST 图像(参见图 10.16 中的底部图像)相比,这些 ACGAN 生成的图像看起来就像真实的一样。此外,它们的数字类别身份看起来是正确的。这表明我们的 ACGAN 训练是成功的。如果您想查看 ACGAN 生成器的更多输出,请点击页面上的 Generator 按钮。每次点击按钮,都会生成并显示页面上的新批次包含 10 张假图像。您可以玩一下,直观地感受图像生成的质量。

图 10.16. ACGAN 训练模型的生成器部分生成的样本图片(顶部的 10 x 1 面板)。底部的面板展示了一个 10 x 10 的真实 MNIST 图像网格,以进行比较。点击“显示 Z-向量滑块”按钮,您可以打开一个填满了 100 个滑块的区域。这些滑块允许您改变潜在向量(z-向量)的元素,并观察其对生成的 MNIST 图像的影响。请注意,如果您逐个更改滑块,大多数滑块对图像的影响都很微小且不易察觉。但偶尔,您会发现一个具有更大且更明显影响的滑块。

进一步阅读材料

  • Ian Goodfellow、Yoshua Bengio 和 Aaron Courville,“深度生成模型”,深度学习,第二十章,麻省理工学院出版社,2017 年。
  • Jakub Langr 和 Vladimir Bok,《GAN 行动中:生成对抗网络的深度学习》,Manning 出版社,2019 年。
  • Andrej Karpathy,“循环神经网络的不合理有效性”,博客,2015 年 5 月 21 日,karpathy.github.io/2015/05/21/rnn-effectiveness/
  • Jonathan Hui,“GAN—什么是生成对抗网络 GAN?” Medium,2018 年 6 月 19 日,mng.bz/Q0N6
  • GAN 实验室,一个用 TensorFlow.js 构建的交互式网络环境,用于理解和探索 GAN 的工作原理:Minsuk Kahng 等人,poloclub.github.io/ganlab/

练习

  1. 除了莎士比亚文本语料库外,lstm-text-generation 示例还配置了其他几个文本数据集,并准备好供您探索。运行它们的训练,并观察其效果。例如,使用未压缩的 TensorFlow.js 代码作为训练数据集。在模型训练期间和之后,观察生成的文本是否表现出以下 JavaScript 源代码的模式以及温度参数如何影响这些模式:
  1. 较短程模式,例如关键字(例如,“for”和“function”)
  2. 中程模式,例如代码的逐行组织
  3. 较长程模式,例如括号和方括号的配对,以及每个“function”关键字后必须跟着一对括号和一对花括号
  1. 在 fashion-mnist-vae 示例中,如果您将 VAE 的自定义损失中的 KL 散度项删除会发生什么?通过修改 fashion-mnist-vae/model.js 中的 vaeLoss() 函数(清单 10.7)来测试。从潜在空间采样的图像是否仍然看起来像 Fashion-MNIST 图像?空间是否仍然展现出可解释的模式?
  2. 在 mnist-acgan 示例中,尝试将 10 个数字类别合并为 5 个(0 和 1 将成为第一类,2 和 3 将成为第二类,依此类推),并观察在训练后这如何改变 ACGAN 的输出。当您指定第一类时,您期望看到生成的图像是什么?例如,当您指定第一类时,您期望 ACGAN 生成什么?提示:要进行此更改,您需要修改 mnist-acgan/data.js 中的 loadLabels() 函数。需要相应修改 gan.js 中的常量 NUM_CLASSES。此外,generateAnd-VisualizeImages() 函数(位于 index.js 中)中的 sampledLabels 变量也需要修改。

总结

  • 生成模型与我们在本书早期章节中学习的判别模型不同,因为它们旨在模拟训练数据集的生成过程,以及它们的统计分布。由于这种设计,它们能够生成符合分布并且看起来类似于真实训练数据的新样本。
  • 我们介绍了一种模拟文本数据集结构的方法:下一个字符预测。LSTM 可以用来以迭代方式执行此任务,以生成任意长度的文本。温度参数控制生成文本的随机性(多么随机和不可预测)。
  • 自动编码器是一种由编码器和解码器组成的生成模型。首先,编码器将输入数据压缩为称为潜在向量或 z-向量的简明表示。然后,解码器尝试仅使用潜在向量来重构输入数据。通过训练过程,编码器变成了一个高效的数据摘要生成器,解码器则具有对示例的统计分布的知识。VAE 对潜在向量添加了一些额外的统计约束,使得在 VAE 训练后组成这些向量的潜在空间显示出连续变化和可解释的结构。
  • GAN 基于鉴别器和生成器之间的竞争和合作的想法。鉴别器试图区分真实数据示例和生成的数据示例,而生成器旨在生成“欺骗”鉴别器的虚假示例。通过联合训练,生成器部分最终将能够生成逼真的示例。ACGAN 在基本 GAN 架构中添加了类信息,以便指定要生成的示例的类别。

JavaScript 深度学习(四)(3)https://developer.aliyun.com/article/1516981

相关文章
|
23天前
|
机器学习/深度学习 JavaScript 前端开发
JavaScript 深度学习(五)(5)
JavaScript 深度学习(五)
8 0
|
23天前
|
机器学习/深度学习 JavaScript 前端开发
JavaScript 深度学习(五)(4)
JavaScript 深度学习(五)
24 0
|
23天前
|
存储 机器学习/深度学习 JavaScript
JavaScript 深度学习(五)(3)
JavaScript 深度学习(五)
22 0
|
23天前
|
机器学习/深度学习 并行计算 JavaScript
JavaScript 深度学习(五)(2)
JavaScript 深度学习(五)
29 0
|
23天前
|
机器学习/深度学习 人工智能 JavaScript
JavaScript 深度学习(五)(1)
JavaScript 深度学习(五)
25 0
|
23天前
|
机器学习/深度学习 前端开发 JavaScript
JavaScript 深度学习(四)(5)
JavaScript 深度学习(四)
12 1
|
23天前
|
机器学习/深度学习 存储 算法
JavaScript 深度学习(四)(4)
JavaScript 深度学习(四)
22 1
|
23天前
|
机器学习/深度学习 算法 JavaScript
JavaScript 深度学习(四)(3)
JavaScript 深度学习(四)
26 1
|
23天前
|
机器学习/深度学习 自然语言处理 JavaScript
JavaScript 深度学习(四)(1)
JavaScript 深度学习(四)
12 2
|
3天前
|
机器学习/深度学习 数据采集 算法
未来研究将深入探索深度学习的应用及数据质量与安全问题
【6月更文挑战第13天】本文探讨了使用Python和机器学习预测股票价格的方法,包括数据收集与预处理(填充缺失值、处理异常值、标准化)、特征选择(技术指标、基本面指标、市场情绪)、模型选择与训练(线性回归、SVM、神经网络等)、模型评估与调优。尽管股票价格受多重因素影响,通过不断优化,可构建预测模型。未来研究将深入探索深度学习的应用及数据质量与安全问题。
19 5