资讯详情

GAN 并不是你所需要的全部:从AE到VAE的自编码器全面总结

说到计算机生成的图像,你肯定会想到它deep fake:把马变成斑马或生成不存在的猫。在图像生成方面GAN它似乎已经成为主流,但尽管这些模型在生成现实图像方面取得了巨大的成功,但它们的缺陷也非常明显,并不是所有的图像。自编码器(autoencoder)传统的图像模型还没有过时和发展,所以别忘了自编码器!

GAN 不是你所需要的一切

当谈到计算机视觉的生成和建模时,几乎总是提到GAN 。使用GAN它开发了许多惊人的应用程序,并且可以在这些应用程序中生成高保真图像。GAN缺点也很明显:

1、训练不稳定,经常会出现梯度消失、模式崩溃问题(会生成相同的图像),这使得我们需要做大量的额外工作来为数据找到合适的架构。

2、GAN 很难逆转(不可逆转),这意味着没有简单的方法将图像从生成的图像推回到产生图像的噪声输入。例如,如果使用可逆生成模型来增强生成的图像,您可以直接获得生成图像的特定输入,然后在正确的方向上稍微干扰它,以获得非常相似的图像,但GAN这样做很麻烦。

3、GAN 不提供密度估计。也就是说,它可以生成图像,但不知道出现特定特征的可能性。例如,如果密度估计对异常检测至关重要,如果有生成模型可以告诉我们一只可能的猫和一只不太可能的猫,我们可以将这些密度估计传递给下游的异常检测任务,但GAN不能提供这样的估计。

自编码器 (AE) 这是一种替代方案。它们相对快速,易于训练,可逆且有概率。AE 生成的图像的保真度可能还没有 GAN 但这不是不使用它们的原因!

自编码器还没有过时

有人说:一旦 GAN 自编码器已经过时度上是正确的, 但时代在进步GAN自编码器的出现给了自编码器更多的动力。经过仔细研究,人们已经意识到 GAN 缺点并不总是最适合接受它们的模型。因此,自编码器继续进一步研究。

例如,自编码器称为矢量变分 (Vector Quantized Variational AutoEncoder / VQ-VAE ) 的自回归 AE 声称可以生成和 GAN 与质量匹配的图像不会同时出现 GAN 模式崩溃、缺乏多样性等已知缺点。

731f39ac22bef12bb3403e662d0d5647.png

使用 VQ-VAE-2 生成多样化的高保真图像(链接:arXiv:1906.00446)

在论文中,作者通过生成渔民图像将他们 AE 模型与 DeepMind 的 BigGAN 比较。你可以看到。 AE 图像之间有多少变化。

此外,自编码器领域的另一个令人兴奋的研究例子是 VAE / GAN。使用这种混合模型 GAN 在典型的对抗训练中,识别器学到了提高知识的知识 AE 产生能力。

“Autoencoding beyond pixels using a learned similarity metric”(arXiv:1512.09300)

在上图中,作者用他们的模型从学习表达中重建了一组图像,这是 GAN 因为GAN缺乏上述可逆性。从图片上看,重建看起来很好。

虽然GAN然而,自编码器仍然以某种方式在图像生成中发挥作用( 自编码器可能还没被完全的开发),熟悉它们肯定是件好事。

在本文的下自编码器的工作原理、不同类型的自编码器以及如何使用它们。最后,我会提供一些 TensorFlow 的代码。

用自编码器表示学习

自编码器是关于如何有效地表示数据的。他们的工作是找到一个高维输入的低维表示,重建原始输入而不损失内容。

如下图所示quickdraw 数据集中获取斧头。图像为 28x28 灰度,这意味着它是由来的 784 个像素组成。自编码器会找到这个 784 维空间到 2D 空间的映射压缩后 ax 图像将只描述两个数字:地图上的图像 X 和 Y 坐标。接下来,只知道 X-Y 坐标,自编码器将这两个值重建原始坐标 784 个像素。

学习自编码器输入的低维表示。

重建当然不是完美的,因为一些信息在压缩过程中不可避免地会丢失,但我们的目标是让它足以识别原始图像。在我们的例子中,地图是有效地表示数据的潜在空间。虽然我们使用它 2D 但事实上,潜在空间通常更大,但仍然比输入图像小得多。

自编码器的工作是创建一个低维表示重建原始输入。这确保了这个潜在的空间压缩了最相关的输入特征,而且没有噪音和重建输入不重要的特征。

要点:自编码器的潜在空间压缩了现在相关的输入特征,并且没有噪声和冗余特征。

这一特点使它在许多方面都很有吸引力。自编码器可用于降维或提取特征(可构建数学等同于主要成分分析或 PCA 对于自编码器,我们以前有一篇相应的文章。如果您感兴趣,可以搜索参考)。因此,高维数据可以在任何数据管道中用自编码器学习的低维表示替换。

自编码器还有许多其他应用程序。它们可以用来去除图像噪声:只要输入一个噪声图像,自编码器就会重建原始的无噪声图像。它们也可以用于自我监督和预训练,其中模型从大量未标记的数据中学习图像特征,然后微调标记数据的一小部分监督任务。最后,自编码器可以用作生成模型,这将是本文的重点。

要点:自编码器可用于降维、特征提取、图像去噪、自监督学习和生成模型。

传统的自编码器 AE

这里使用 Google 游戏“Quick, Draw!玩家制作的手绘形状 quickdraw 构建简单的自编码器的数据集。为便于演示, 我们只使用三种图像:狗、猫和树。这是图像的例子。

如何构建自编码器?它需要由两部分组成:编码器接收输入图像并将其压缩成低维表示器和解码器。它做相反的事情:从潜在表示中生成原始大小的图像.

让我们从编码器开始。在网络中使用卷积层是因为它处理图像。该模型通过卷积层和最大池化层依次输入图像,将其压缩成低维表示。

encoder = tf.keras.models.Sequential([    tf.keras.layers.Reshape([28, 28, 1], input_shape=[28, 28]),    tf.keras.layers.Conv2D(      16, kernel_size=3, padding="same", activation="selu"    ),    tf.keras.layers.MaxPool2D(pool_size=2),    tf.keras.layers.Conv2D(      32, kernel_size=3, padding="same", activation="selu"    ),    tf.keras.layers.MaxPool2D(pool_size=2),    tf.keras.layers.Conv2D(      64, kernel_size=3, padding="same", activation="selu"    ),    tf.keras.layers.MaxPool2D(pool_size=2) ])

基于这种特殊的结构 Aurélien Géron 用于他的书 FashionMNIST 数据集的架构(见底部来源)。在这里使用 SELU 激活而不是 ReLU,因为他比较新,效果好

最终输出编码器 64 每个特征图的大小为 3x3.这是数据的低维表示。这里需要一个解码器将这些表示处理成原始大小的图像。这里使用转移卷积(可视为与常规卷积相反的操作)。转移卷积放大图像,增加其高度和宽度,减少其深度或特征图数量。

decoder = tf.keras.models.Sequential([    tf.keras.layers.Conv2DTranspose(      32, kernel_size=3, strides=2, padding="valid", activation="selu", input_shape=[3, 3, 64]    ),    tf.keras.layers.Conv2DTranspose(      16, kernel_size=3, strides=2, padding="same", activation="selu"    ),    tf.keras.layers.Conv2DTranspose(      1, kernel_size=3, strides=2, padding="same", activation="sigmoid"    ),    tf.keras.layers.Reshape([28, 28]) ])

剩下的就是把编码器和解码器连接起来,作为一个完整的自编码器进行联合训练。模型采用二元交叉熵损失 20 个 epoch 代码如下:

ae = tf.keras.models.Sequential([encoder, decoder])

ae.compile(
    loss="binary_crossentropy", 
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
    metrics=["accuracy"]
)

history = ae.fit(
    X_train, X_train, 
    epochs=20, 
    validation_data=(X_val, X_val)
)

损失函数选择来说:二元交叉熵和RMSE都可以被用作损失函数, 两者的主要区别在于二元交叉熵对大误差的惩罚更强,这可以将重建图像的像素值推入平均幅度,但是这反过来又会使重建的图像不那么生动。因为这个数据集是灰度图像,所以损失函数的选择不会产生任何有意义的差异。

下面看一下测试集中的一些图像,以及自编码器重建它们的效果如何。

测试集的原始图像(上)与它们的重建图像(下)。

看起来不错,但是一些细节模糊(这是自编码器的缺陷,也是GAN的优势),但整体重建精度似乎相当不错。另一种可视化自编码器所学内容的方法是将一些测试图像仅传递给编码器。这将产生它们的潜在表示,本例(3, 3, 64) 。然后使用降维算法(例如 t-SNE)将它们映射到二维并绘制散点图,通过它们的标签(猫、狗或树)为点着色,如下图所示:

可以清楚地看到,树与其他图像分离良好而猫和狗则有点混杂。注意底部的大蓝色区域,这些是带有胡须的猫头的图像这些并没有与狗混淆。但是在图的的上半部分都是从动物的侧面,这使得区分猫和狗变得更加困难。这里一个非常值得关注的事情是,自编码器在没有给出标签的情况下了解了多少图像类别!(上面说到的自监督学习)

要点:自编码器可以在没有标签的情况下学习很多关于图像分类的知识。

传统的自编码器模型似乎已经学会了数据的有意义的潜在表示。下面让我们回到本文的主题:它可以作为生成模型吗?

传统自编码器作为生成模型

首先明确一下我们对生成模型的期望:希望能够选择潜在空间中的任何随机点,将其通过解码器获得逼真的图像。最重要的是,在潜在空间中选择不同的随机点应该会产生不同的生成图像,这些图像应该涵盖模型看到的所有类型的数据:猫、狗和树。

从潜在空间采样

当我们在潜在空间中选择一个随机点时,第一个问题就出现了:在这种情况下,“随机”是什么意思?它应该来自正态分布还是均匀分布?分布应该如何参数化?下图显示了对测试数据样本进行编码后潜在空间值的概率密度。

除此以外,我还计算了一些汇总统计数据:最小潜在空间值为 -1.76,最大值为 22.35。对于随机点采样,让潜在空间以零为中心对称中心化会容易得多,或者说至少以某种方式是有界的,需要一个最大值和最小值。

要点:潜在空间值形成不规则的、无界的分布,会使随机点采样变得困难。

图像多样性

另一个问题涉及潜在空间中各个类别的代表区域,这会影响生成图像的多样性。

模型的潜在空间是 3x3x64,它是 576 维的无法可视化。为了便于解释可以尝试对一个维度进行 3D 切片,其形状为 3x1x1。只考虑此切片时,每个图像在潜在空间中由 3D 矢量表示可以将其可视化为散点图。这是测试数据样本的图:

蓝色点云分布在比红色和绿色云小得多的体积上。这意味着如果要从这个空间中随机抽取一个点,最终得到猫或狗的可能性要比得到树的可能性大得多。在极端情况下,考虑到潜在空间的所有 576 个维度,可能永远不会对树进行采样,这违背了对生成模型能够覆盖它所看到的数据的整个空间的要求。

要点:不同图像类别的潜在表示可能在大小上有所不同,导致模型生成某些类别的频率比其他类别高得多。

红色和绿色点云中向上突出的尖峰。在这个尖峰内部存在一些图像的潜在表示。但如果从那里向旁边移动,在尖刺旁边的正上方一个点取样呢?能得出真实的图像吗?

潜在空间中的有意义区域

在潜在空间的 3D 子空间中,图像嵌入通常是良好聚类的——可能除了点云顶部的红绿 尖峰 之外。但是随着我们添加更多的维度,嵌入式图像之间会出现更多的空白空间。这使得整个 3x3x64 的潜在空间充满了真空。当从其中随机采样一个点时,很可能会从任何特定图像中得到一个远离(在现在的维度上)的点。如果通过解码器传递这些随机选择的点,我们会得到什么?答案是得不到任何的形状。

猫和狗之间的采样不应该产生一个耳朵和胡须松软的生物吗?

传统自编码器学习的潜在空间不是连续的,所以该空间中的点之间的含义没有平滑的过渡。并且即使是一个小的扰动点也可能会致垃圾输出。

要点:传统的自编码器学习的潜在空间不是连续的。

使用传统自编码器作为生成模型存在三个问题:不知道如何从一个不规则的、无界的空间中采样,一些类可能在潜空间中被过度表示,学习空间是不连续的,这使得很难找到一个点将解码成一个良好的图像。所以这时候变分自编码器出现了。

变分自编码器 VAE

变分自编码器(Variational autoencoder)或称 VAE,通过引入随机性和约束潜在空间以便更容易从中采样来解决上面讨论的问题。

要点:变分自编码器将随机性引入模型并限制潜在空间。

要将传统自编码器转换为变分自编码器,只需要调整编码器部分和损失函数。让我们从第一步开始。

变分编码器

变分编码器不是将输入图像映射到潜在空间中的一个点,而是将其映射到一个分布中,准确地说是多元正态分布(multivariate normal distribution)。

多元正态分布是将单变量正态分布扩展到更多维度。就像单变量正态分布由两个参数描述:均值和方差,多元正态分布由两个参数向量描述,每个参数的长度等于维数。例如,2D 法线将有一个包含两个均值的向量和一个包含两个方差的向量。如果分布的许多维度是相关的,则会出现额外的协方差参数,但在 VAE 中,假设所有维度都是独立的,这样所有协方差为零。

为了将输入图像编码为潜在空间中的低维度表示,将从多元正态分布中对其进行采样,其参数(均值和方差)将由编码器学习。

这样潜在空间将用两个向量来描述:均值向量和方差向量。本文的例子中将这两个向量都设为 576 维,以匹配之前构建的编码器,后者编码为 3x3x64 = 576 维空间。实际上可以重用上面的编码器代码。只需展平它的输出并将两个向量附加到它上面。

vanilla_encoder = tf.keras.models.clone_model(encoder)
encoder_inputs = tf.keras.layers.Input(shape=[28, 28])
z = vanilla_encoder(encoder_inputs)
z = tf.keras.layers.Flatten()(z)

codings_mean = tf.keras.layers.Dense(576)(z)
codings_log_var = tf.keras.layers.Dense(576)(z)
codings = Sampling()([codings_mean, codings_log_var])

var_encoder = tf.keras.models.Model(
    inputs=[encoder_inputs], 
    outputs=[codings_mean, codings_log_var, codings]
)

这里只有两件事需要详细说明:

1、正如可能从变量名称中猜到的那样,使用方差的对数来描述正态分布,而不是按原样描述方差。这是因为方差需要为正,而对数方差可以是任何值。

2、编码器使用自定义采样层,该层根据均值和对数变量从多元法线中采样一个点。下面就是代码:

class Sampling(tf.keras.layers.Layer):
    def call(self, inputs):
        mean, log_var = inputs
        epsilon = K.random_normal(tf.shape(log_var))
        return mean + K.exp(log_var / 2) * epsilon

为什么变分编码器可以工作

与传统编码器相比,VAE不将输入映射到一个确定性点,而将其映射到某个空间中的一个随机点。为什么这个更好呢?

对于一个相同的图像,每次都会在潜在空间中得到一个稍微不同的点(尽管它们都在均值附近)。这使得 VAE 了解该邻域中的所有点在解码时都应该产生类似的输出。这确保了潜在空间是连续的!

要点:编码器中的随机化迫使潜在空间是连续的。

变分解码器

VAE 的解码器不需要太多更改,直接可以重用以前的代码。

vanilla_decoder = tf.keras.models.clone_model(decoder)

decoder_inputs = tf.keras.layers.Input(shape=[576])
x = tf.keras.layers.Reshape([3, 3, 64])(decoder_inputs)
decoder_outputs = vanilla_decoder(x)

var_decoder = tf.keras.models.Model(
    inputs=[decoder_inputs], 
    outputs=[decoder_outputs]
)

唯一的区别是现在编码器的输出或潜在空间是一维向量而不是 3D 张量。所以只需添加一个重塑层就可以了。现在可以将变分编码器和解码器组合到 VAE 模型中。

_, _, codings = var_encoder(encoder_inputs)
reconstructions = var_decoder(codings)
vae = tf.keras.models.Model(
    inputs=[encoder_inputs], 
    outputs=[reconstructions]
)

变分损失函数

在传统自编码器中,使用了二元交叉熵损失,并提到均方根误差可能是一种替代方法。在 VAE 中损失函数是需要扩展得,因为交叉熵或 RMSE 损失是一种重建损失——它会惩罚模型以产生与原始输入不同的重建。

在 VAE 中在损失函数中增加了KL 散度,惩罚模型学习与标准正态有很大不同的多元正态分布。KL 散度是衡量两个分布差异程度的指标,在此可以衡量标准正态分布与模型学习的分布之间的差异。也就是说:如果均值不为零且方差不为 1,则会产生损失。

latent_loss = -0.5 * \
    K.sum(1 + codings_log_var - K.exp(codings_log_var) - \
          K.square(codings_mean),
          axis=-1)
vae.add_loss(K.mean(latent_loss) / (28 * 28))
vae.compile(
    loss="binary_crossentropy", 
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), 
    metrics=["accuracy"]
)

latent_loss 的公式就是 KL-divergence 公式,并且在这种特殊情况下得到简化:目标分布是标准正态分布并且两者都没有零协方差。另外就是需要将其缩放到输入图像的大小,以确保它与重建损失具有相似的比例并且不会占主导地位。既然不是主导地位,为什么我们要把这个 KL 部分加到损失中呢?

1、它使潜在空间中的随机点采样变得简单。我们可以从标准法线中取样,并确保该空间对模型有意义。

2、由于标准法线是圆形的并且围绕其平均值对称,因此潜在空间中存在间隙的风险较小,也就是说解码器产生无效的图像的概率会小。

通过以上方式,VAE克服了传统自编码器在图像生成方面的所有三个缺点。现在训练一下看看效果。

history = vae.fit(
    X_train, X_train, 
    epochs=100, 
    batch_size=128, 
    validation_data=(X_val, X_val),
)

变分自编码器的分析

原始图像和它们的重建图像。

后者可能看更模糊,这是意料之中的,毕竟我们调整了损失函数:不仅关注重建精度,还关注产生有意义的潜在空间。

图像之间的变形

先来验证变分自编码器学习到的潜在空间确实是连续的、行为良好且有意义的,那就是选择两个图像并在它们之间变形。让我们以这只猫和这棵树为例。

对它们进行编码以获得它们的隐藏表示,并在它们之间进行线性插值。然后将沿插值线的每个点传递给解码器,这样可以在猫和树之间生成图像。

cat = var_encoder(X_train[5930, :, :].reshape(1, 28, 28))[0].numpy()
tree = var_encoder(X_train[17397, :, :].reshape(1, 28, 28))[0].numpy()

linfit = interp1d([1, 10], np.vstack([cat, tree]), axis=0)

将两个潜在表示堆叠在一个形状为 2x576 的矩阵中,并应用 scipy 的线性插值函数,如果需要调整,可以修改 linfit ([i + 1 for i in range (10)]) 来获得中间插值。

仔细看看猫的嘴是如何变成树干的。以类似的方式,还可以将另猫变成狗。注意猫的尖耳朵是如何逐渐变成狗的松软耳朵的。

这个有趣的实验表明,变分自编码器学习的潜在空间是连续的,并确保点之间的平滑过渡。

要点:VAE 潜在空间是连续的,允许在图像之间生成有意义的插值。

如果潜在空间是连续且有意义的,我们应该能够对图像进行算术运算。

考虑这两只猫(图片是重建而不是原始图像)。

如果从左边有胡须的猫中减去右边的无胡须猫,我们会得到什么?减法必须发生在潜在空间中。

cat_1 = var_encoder(X_train[19015, :, :].reshape(1, 28, 28))[0].numpy()
cat_2 = var_encoder(X_train[7685, :, :].reshape(1, 28, 28))[0].numpy()
result = var_decoder(cat_1 - cat_2)

结果类似于胡须?还真有点像

总结

本文中已经介绍了自编码器如何学习数据的低维表示,以及这些潜在表示对于新图像的生成是如何不完美的,至少在传统自编码器的情况下:它们学习的空间难以采样且不连续。

还介绍了变分自编码器如何通过向编码器引入随机性并增强损失函数来强制学习连续且有意义的潜在空间来缓解这些问题,从而允许在图像之间进行算术和变形。

上面讨论的示例是在具有现成架构的简单数据集上训练的。想象一下实际应用得时候变分自编码器有多么强大!

引用:

  • Geron A., 2019, 2nd edition, Hands-On Machine Learning with Scikit-Learn and  TensorFlow: Concepts, Tools, and Techniques to Build Intelligent  Systems, O’Reilly

  • Foster D., 2019, Generative Deep Learning. Teaching Machines to Paint, Write, Compose and Play, O’Reilly

本文作者:Michał Oleszak

推荐阅读:

我的2022届互联网校招分享

我的2021总结

浅谈算法岗和开发岗的区别

互联网校招研发薪资汇总

对于时间序列,你所能做的一切.

什么是时空序列问题?这类问题主要应用了哪些模型?主要应用在哪些领域?

保持谦逊、保持自律、保持进步

发送【蜗牛】获取一份《手把手AI项目》(AI蜗牛车著)

发送【1222】获取一份不错的leetcode刷题笔记

标签: 连接器真空管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台