百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

VAE 学习高维数据分布

toyiye 2024-06-21 12:39 13 浏览 0 评论

VAE: 学习高维数据分布


当前的内容是梳理《Transformer视觉系列遨游》系列过程中引申出来的。目前最近在AI作画这个领域 Transformer 火的一塌糊涂,AI画画效果从18年的 DeepDream[1] 噩梦中惊醒过来,开始从2022年 OpenAI 的 DALL·E 2[2] 引来插画效果和联想效果都达到惊人效果。虽然不懂,但是这个话题很吸引ZOMI,于是就着这个领域内容来看看有什么好玩的技术点。

但是要了解:Transformer 带来AI+艺术,从语言开始遇到多模态,碰撞艺术火花 这个主题,需要引申很多额外的知识点,可能跟 CV、NLP 等领域大力出奇迹的方式不同,AI+艺术会除了遇到 Transformer 结构以外,还会涉及到 VAE、ELBO、Diffusion Model等一系列跟数学相关的知识。

这里介绍的 AVE 是万里长征的第一步,VAE 希望训练一个生成模型 X=g(Z),这个模型能够将采样后的概率分布映射到训练集的概率分布

基础知识

之前天天听公司去年入职的搞 NLP 的同事说 VAE 怎么样怎么样,一直有留意但是没有注意,以为跟 AE 一样比较简单几层网络模型加一个 Reconstruct Loss。没想到看到论文《Auto-Encoding Variational Bayes》就蒙圈了,于是乎照样翻了网上很多资料,无一例外发现都很含糊,主要的感觉是公式写了一大通,老瓜子嗡嗡的,最后感觉自己搞明白了,但是看看代码实现发现怎么这么简单,刚才理解的公式都去哪里了?

这里面参考了很多文章,特别感谢博主 苏剑林 的图文讲解和 李宏毅 老师的视频。加上自己这段时间每天挂着个问号在脑袋旁边。感觉有点想明白了,于是赶紧积累下来,希望通过下面的文字把 VAE 梳理清楚。

下面列了几个跟 VAE 强相关的简单知识点,首先是 1)混合高斯模型主要是对高维的数据进行概率表示,2)而 KL 散度则是用来对比两个概率分布之间的差异,3)当AI具备了解数据的概率表示和数据的分布差异,那么是不是可以通过一个自动编码器来去模拟数据生成呢?

于是就有了下面三个基础知识点。

GMM 混合高斯模型

混合高斯模型(Gaussian Mixture Model,GMM) 指的是多个高斯分布函数的线性组合,理论上 GMM 可以拟合出任意类型的分布,通常用于解决同一集合下的数据包含多个不同的分布情况。

简单可以理解 GMM 是一种聚类算法,一般使用期望最大算法(Expectation Maximization,EM)进行估计。而 EM 算法通常用来估计参数的隐变量 z 的一种方法,它是一种迭代的方法,大致可以分为 E 步和 M 步:

  1. 期望 E 步:若参数 Θ 已知,则可根据训练数据推断出最优隐变量 z 的值;
  2. 最大化 M 步:若 z 的值已知,则可方便的对参数 Θ 做极大似然估计,求得参数 Θ 值;

KL 散度

KL散度(Kullback-Leibler divergence,KLD) 是对两个概率分布 P 和 Q 差别的非对称性的度量。一般来说 P 表示数据的真实分布,Q 表示数据的理论分布。如果两个分布属于同一概率分布(即它们相似), 那么KL散度越小;反之, 则越大。

DKL(P‖Q)=?∑iP(i)log?Q(i)P(i)

即按概率分布 P 求得的 P 和 Q 的对数商的平均值。

AE 自动编码器

自编码器(auto encoder, AE) 是一类在半监督学习和非监督学习中使用的人工神经网络 ANN,其功能是通过将输入信息作为学习目标,对输入信息进行表征学习(representation learning)。

AE 算法原理

自编码器比较好理解,直接看图就搞清楚了,主要是通常包括两部分:encoder(也称为识别网络)将输入转换成隐变量 z,decoder(也称为生成网络)将隐变量 z 表示转换成输出。

回到那一年,Auto-Encoder 自编码器是1986年由 Rumelhart 提出,可用于高维复杂数据的处理, 它促进了神经网络的发展。自编码神经网络是一种无监督学习算法(训练示例未标注),它使用了BP反向传播算法,致力于使输出与输入越接近越好,而输入和输出相同则是使用了 Reconstruct Error,即最小化输入和输出之间的重构误差。

根据图示,AE的算法描述为:

  1. Encoder:负责将输入数据进行压缩,n 维输入数据通过 Hidden layer 压缩成 m 维的数据(m << n),即通过编码器学习一组参数,得到一个 latent space;
  2. Decoder:负责还原数据,在需要用到的时候尽可能地以损失最小的方式恢复原始数据。

AE应用范围一般,但扩展能力很强,可以应用于机器学习中的数据降维、特征抽取和数据可视化分析,在CV领域的应用有文本检索、以图搜图、还可用于预训练,也可扩展并应用于生成模型。结构上在 NLP 领域衍生出了 Seq2Seq 架构,而 CV 领域则衍生出类似于 U-Net 架构啦。(虽然很基础,但是很有用哦)

AE 算法问题

AE 主要是表达一个自编码器模型,但还不是真正意义上的生成模型。对于一个特定的生成模型,它一般应该满足以下两点:

  1. 编码器和解码器是可以独立拆分表达(类比GAN的Generator和Discriminator)。
  2. 固定维度下任意采样出来的编码向量,都应该能通过解码器产生一个对应的数据。

下面参考李宏毅老师的解释,换成我最近比较喜欢的二哈。假设使用一张二哈和另外一张二哈去训练一个AE,经过训练,模型能够很好地还原出这两张图片。接下来,我们在 latent space 上中间一点,即两张图片编码空间中间处任取一点,将这点交给解码器进行解码,直觉上会得到一张二哈串串的图片。不过呢,实际 那这个点去 decode 的时候会发现 AE 还原出来的图片不仅模糊而且还是乱码的。

为什么会出现这种现象?一个直观上的解释是AE的 Encoder 和 Decoder 都使用了 DNN,DNN是一个非线性的变换过程,因此在latent space上点与点之间transform往往没有规律可循。

如何解决这个问题呢?一个思想就是引入噪声,扩大图片的编码区域,从而能够覆盖到失真的空白编码区。其实说白了就是通过增加输入的多样性从而增强输出的鲁棒性。当给输入图片进行编码之前引入一点噪声,那么就可以让每张图片的编码空间出现在绿色箭头范围内,这样一来所得到的 latent space 就能覆盖到更多的编码范围。此时再从中间点抽取去还原便可以得到一个比较理想的输出啦。

虽然在AE的基础上为输入数据增添了一些噪声使得 latent space 能够覆盖到比较多的区域,但是还是有不少地方没有被覆盖到,比如上图右边黄色的部分因为离得比较远所以就没编码到。

因此,是不是可以尝试利用更多的噪音,使得对于每一个输入样本,它的编码都能够覆盖到整个编码空间?也就是说,对每一个样本取得其编码空间的高斯分布,多个分布合起来变成一个混合高斯分布,latent space 的泛化能力就更强呢?

到这里已经不知不觉就引入了 VAE 变分自编码器的核心思想啦。

VAE 变分自编码器

通常意义上的编码器 Encoder 可以对数据进行压缩,降噪之类的一些处理,但是实际上不能来生成任意数据。上面不是已经简单用二哈举了个小例子, VAE 可以生成隐变量 z,并且 z 是及含有数据信息又含有噪声,除了还原输入的样本数据以外,还可以用于生成新的数据。

这么一说,感觉 VAE 其实也是个生成模型(功能和 GAN 相似),的确,GAN 和 VAE 两个的目标基本是一致的:希望构建一个从隐变量 z 生成目标数据 X 的模型,但是实现上有所不同。更准确地讲,它们是假设 z 服从某些常见的分布(比如正态分布),然后希望训练一个模型 X=g(Z),这个模型能够将原来的概率分布映射到训练集的概率分布,也就是说,目的都是进行分布之间的变换。

不过 VAE 采用的是概率的思想用神经网络迭代求解数据分布概率,GAN 直接用神经网络训练一个判别器。实际效果上,VAE 的鲁棒性比 GAN 更好,但是 GAN 在调优之后效果比 VAE更好,这也是 GAN 大火的原因。

ZOMI 先来学习 VAE 的数学模型结构(之所以称为数学模型结构而不是网络模型结构呢,是因为 VAE 是基于数学模型原理推导出来的,为了去计算或者逼近拟合后验概率 p(z|x) 从而引入的MLP)。

从图中可以看到,VAE就是在原本的AE结构上,给编码添加合适的噪声。VAE 的输入是样本 X 通过编码器 Encoder 求得平均值 μ 和标准差 σ 的向量;然后通过采样得到隐向量 z,接着通过解码器 Decoder 得到输出 x^ 。

Native VAE

VAE 看上去很简单,实际代码实心也很简单,Encoder 和 Decoder 都是用 MLP 来实现。难的在于为何要这么设计和计算。

假设现在我们手头上,有一批数据样本 x1,…,xn ,其整体用 x 来描述,本想根据 x1,…,xn 得到 x 的分布 p(x) 。如果能直接得到的话,那根据 p(x) 来采样,就可以得到所有可能的样本 x^ 。这是一个终极理想的生成模型,要是真的这么直接那就没 VAE 什么事情了。

由 VAE 的模型结构,可以看到编码空间 z 是由一个标准正态分布所产生的向量。实际上概率分布可以表示为:

(1)p(x)=∫zp(z)p(x∣z)dz

上面公式中的 p(x∣z) 英文是 p of x given z 描述一个由 z 来生成 x 的模型,假设 z 服从正态分布,也就是 p(z)=N(0,I) 。那么现在就可以从标准正太分布中采样一个 z,然后根据编码空间 z 计算出 x ,生成的 x 和真实样本 x 进行对比迭代优化求解,这就是最简单的生成模型。根据博主 苏剑林的理解,从新画了张图。

这里面有个问题,经过重新采样出来的 zk ,因为已经经过一个正态分布 N(0,I) 的压缩处理,压缩后的 zk 就不再是对应着原来的 xk 了(已经经过编码压缩),如果直接最小化 L(x^k,xk)2 是很不合理的。

Vanilla VAE

上面的 Native VAE 中并没有使用 p(z) 是正态分布的假设,而是假设后验分布 P(z∣x) 符合正态分布,也就是经过编码 Encoder 得到的分布。

具体来说,给定一个真实样本 xk ,假设存在一个专属于样本 xk 的后验分布 P(z∣xk) ,并进一步假设这个分布符合正态分布。之所以是后验分布,是因为 P(z∣xk) 与 样本 xk 相关,从分布中采样出来的 z 可以还原回 xk 中。

现在对 latency space 随机采样 m 个点,其中 m 服从多项式分布 p(x) ,每采样一个点 m,将其对应到一个高斯分布 N(μm,σm) ,于是一个多项式分布利用高斯混合模型 GMM 可以表示为:

(2)p(x)=∑mp(m)p(x∣m)=∫zp(z)p(x∣z)dz

我们知道正态分布有两组参数:分别是均值 μ 和方差 σ2,现在问题是怎么找到属于样本 xk 的正态分布 p(z∣xk) 的均值和方差呢?这个时候神经网络就来了,可以用 MLP 来进行拟合。于是在 Encoder 阶段构建两个神经网络来计算均值 μ 和方差 σ2 。

经过上面的推论,便可以将原先离散的、存在大量失真区域的编码方式,转换成连续有效的编码方式。根据 Z 来计算 X,即计算分布概率 p(x∣z) 。

下面就是 VAE 的 Encoder 和 Decoder 阶段的内容:

  1. Encoder:利用神经网络 MLP 来求解 μ(z) 和 σ(z) , 等价于求解 p(x∣z) 。
  2. Decoder:利用神经网络 MLP 来求解 q(z|x) ,q 为GMM的分布。

VAE 需要求解的目标为表达式 (2),原则上希望分布 p(x) 越大越好。根据最大似然估计,等价于求解:

(3)Maximum?L=∑xlog?p(x)

给定任意一个分布q,因为 ∫zq(z∣x)dz=1 ,所以可以推导出:

(4)log?p(x)=∫zq(z∣x)log?p(x)dz=∫zq(z∣x)log?(p(z,x)p(z∣x))dz=∫zq(z∣x)log?(p(z,x)q(z∣x)q(z∣x)p(z∣x))dz=∫zq(z∣x)log?(p(z,x)q(z∣x))dz+∫zq(z∣x)log?(q(z∣x)p(z∣x))dz=∫zq(z∣x)log?(p(z,x)q(z∣x))dz+KL(q(z∣x)‖p(z∣x))

上式右边一项为 q(z∣x) 和 p(z∣x) 这两个分布的 KL 散度,根据KL 散度公式的性质,可以知道右边 KL 项恒大于等于0,于是可以找到 logp(x) 的 对数似然的下界(Evidence Lower Bound,ELBO),此时可以把最大化对数似然转化为最大化 ELBO。即:

(5)log?p(x)≥∫zq(z∣x)log?(p(x∣z)p(z)q(z∣x))dz

这里把 ELBO 记作:

(6)Lb=∫zq(z∣x)log?(p(x∣z)p(z))q(z∣x))dz

代入式 (4),可写成:

(7)log?p(x)=Lb+KL(q(z∣x)‖p(z∣x))

有趣的地方来了,原本我们是求使得 logp(x) 最大化的 p(x∣z) , 现在转为同时求解 p(x∣z) 和 q(z∣x) 。为什么把一个简单的事情复杂化求解多个变量?下面我们观察下 logp(x) 和 Lb 之间的关系。

根据公式(2), Logp(x) 是固定的,如果调节 q(z∣x) 使得 $L_b$ 越大,那么 KL 散度就会越小。当两个分布 q(z∣x) 和 p(x∣z) 完全一致的时候,KL 散度为0,此时 ELBO Lb 就等于 Logp(x) 。

因为 Lb 是 Logp(x) 的下界,所以求解最大似然估计 Maximum?L 等价于求解:

(8)Maximum?Lb

而调节 p(x∣z) 就是训练 Decoder 的过程;调节 q(z∣x) 变成训练 Encoder 的过程。下面继续打开 ELBO:

(9)Lb=∫zq(z∣x)log?(P(z,x)P(z∣x))dz=∫zq(z∣x)log?(P(x∣z)P(z)q(z∣x))dz=∫zq(z∣x)log?(P(z)q(z∣x))dz+∫zq(z∣x)log?P(x∣z)dz=?KL(q(z∣x)‖P(z))+∫zq(z∣x)log?P(x∣z)dz

现在又把 Maximum?Lb 转化为求解上式左边项 KL 散度的最小值,以及右半部分的最大值。对于右半部分实际上很好理解:

(10)Maximum?∫zq(z∣x)log?p(x∣z)dz=Maximum?Eq(z∣x)[log?p(x∣z)]

E 为期望,表示期望 Encoder 输出 q(z∣x) 的情况下 Decoder 输出 p(x∣z) 尽可能的大。即需要从编码器 Encoder 得到的隐变量空间中采样隐变量 z,对采样得到的隐变量 z 进行解码 Decoder,使得解码得到的 x^ 分布中,对应是输入 x 的概率尽可能大。 对于左半部分,需要 KL(q(z∣x)|P(z)) 尽可能小,即需要编码器 Encoder 得到的隐变量概率分布与隐变量的先验分布尽可能接近。

重参数

最后是实现模型的一个技巧,英文名是 reparameterization trick,这里叫它做重参数吧。

其实很简单,就是要从 p(z|xk) 中采样一个 z_k 出来,尽管知道了 p(z|xk) 属于正态分布,但是均值方差都是靠 MLP 模型计算出来的,要靠这个过程反过来优化均值和方差的模型,但是对于“采样”这个操作是不可导的,而采样的结果却是可导。我们利用:

(11)12πσ2exp?(?(z?μ)22σ2)dz=12πexp?[?12(z?μσ)2]d(z?μσ)

这说明 (z?μ)/σ=ε 是服从均值为0、方差为1的标准正态分布,要同时把 dz 考虑进去,是因为乘上 dz 才算是概率,去掉 dz 是概率密度而不是概率。这时候我们得到:

从 N(μ,σ2) 中采样一个 z,相当于从 N(0,I) 中采样一个 ε,然后让 Z=μ+ε×σ 。

于是,从 N(μ,σ2) 采样变成了从 N(0,I) 中采样,然后通过参数变换得到从 N(μ,σ2) 中采样的结果。这样一来,“采样”这个操作就不用参与梯度下降了,改为采样的结果参与,使得整个模型变得可训练。

用更加通俗的话来说就是,当编码器和解码器输出的分布都是高斯分布,且要求隐变量先验分布为 N(0,I) 时,VAE的训练过程如下图上半部分所示。然而上半部分中的采样过程会导致训练过程中计算重构误差 |X?f(z)|2 。得到的梯度无法反向传播到编码器。因此将采样过程修改为下图下半部分的形式,从直接采样目标变量,变成采样目标变量到分布均值的差,这样使得反向传播过程中梯度可以传递到编码器。

代码解析

数学原理和公式都写了很多,如果跟我一样还没有被劝退,那么我们估计到这里已经满足不了求知的欲望,希望来点代码刺激刺激。毕竟原理推导很复杂,代码实现一行调包,就感觉自己懂了。

数据处理

数据集对于模型训练非常重要,好的数据集可以有效提高训练精度和效率。数据处理使用非常经典的 MNIST 手写字体识别,示例中用到的MNIST数据集是由10类28?28的灰度图片组成,训练数据集包含60000张图片,测试数据集包含10000张图片。

具体可以通过 pytroch 的 vision 套件来下载,比较简单,使用 torchvision.datasets 接口就可以啦。

import torch
from torchvision import datasets, transforms

bs = 100
# MNIST 手写字体下载
train_dataset = datasets.MNIST(root='./mnist_data/', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./mnist_data/', train=False, transform=transforms.ToTensor(), download=False)

# 数据加载
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=bs, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=bs, shuffle=False)

下载的数据集文件的目录结构如下:

./mnist/
├── test
│   ├── t10k-images-idx3-ubyte
│   └── t10k-labels-idx1-ubyte
└── train
    ├── train-images-idx3-ubyte
    └── train-labels-idx1-ubyte

AVE 模型结构

上面数据集虽然使用的是 MNIST 手写字体,但是 VAE 的网络模型结构不是使用 LeNet 网络模型,而是很简单的 Encoder 与 Decoder。

Encoder 阶段并没有什么秘密,正如上面原理介绍一样,2个 fc 层后接一个 fc 层用于计算均值 mu,另外一个 fc 层用于计算方差 var。

Decoder 阶段更加粗暴,直接使用3个 fc 层最后端到端的还原回输入 x 所相对应的向量大小。

class VAE(nn.Module):
    def __init__(self, x_dim, h_dim1, h_dim2, z_dim):
        super(VAE, self).__init__()

        # encoder part
        self.fc1 = nn.Linear(x_dim, h_dim1)
        self.fc2 = nn.Linear(h_dim1, h_dim2)
        self.fc31 = nn.Linear(h_dim2, z_dim)
        self.fc32 = nn.Linear(h_dim2, z_dim)

        # decoder part
        self.fc4 = nn.Linear(z_dim, h_dim2)
        self.fc5 = nn.Linear(h_dim2, h_dim1)
        self.fc6 = nn.Linear(h_dim1, x_dim)

    def encoder(self, x):
        h = F.relu(self.fc1(x))
        h = F.relu(self.fc2(h))
        return self.fc31(h), self.fc32(h) # mu, log_var

    def decoder(self, z):
        h = F.relu(self.fc4(z))
        h = F.relu(self.fc5(h))
        return F.sigmoid(self.fc6(h)) 

    def sampling(self, mu, log_var):
        std = torch.exp(0.5*log_var)
        eps = torch.randn_like(std)
        return eps.mul(std).add_(mu) # return z sample

    def forward(self, x):
          # where z = mu + sigma * torch.randn_like(mu)
        mu, log_var = self.encoder(x.view(-1, 784))
        z = self.sampling(mu, log_var)
        return self.decoder(z), mu, log_var

上面的代码其实很简单的啦,不过其中有一个 sampling 函数比较有意思,它的输入是 encoder 之后的均值mu和方差var,然后对var求解得到标准差 std。然后使用标准差求 eps,最后通过高斯采样获得隐变量 z。即对应公式:

z=μ+ε×σ

引用英语原文的意思,就是从均值为0、方差为1的正态分布中随机数的二维隐变量生成样本。

Generated samples from 2-D latent variable with random numbers from a normal distribution with mean 0 and variance 1.

最后就是构建 VAE 神经网络模型啦,输入是一个 1x784 的 tensor。为什么是 784?因为一张 MNIST 手写字体的小图片就是 28x28 = 784。z 设置得比较小,也就一个 1x2 的 tensor,所以看看最后生成什么样的效果图。

# build model
vae = VAE(x_dim=784, h_dim1= 512, h_dim2=256, z_dim=2)

优化器和损失函数

优化器没有特殊的地方,直接采用 Adam 优化器就好啦,这个比较通用。不过损失函数就有点不一样啦,因为损失函数除了 Reconstruct Error 以外,还有 KL 散度和 Lb 。

那么现在打开看看 损失函数,损失函数输入有点多,首先第一个 recon_x 是 VAE 网络模型生成输出(注意啦,不是预测输出),后面的三个入参就不用介绍啦。损失函数总体用下面这公式来表示:

L(x)≈1n∑i=1n[12∑j=1k[μ2(xi)+σ2(xi)?log?σ2(xi)?1]?∑l=1L[log?p(xi∣zi,l)]]

实际上根据公式(),目标是最小化KL散度:

Min KL(q(z∣x)|p(z∣x))=Min ∑(μ2+σ2?log?σ2)

而上面的式子右半部分可以理解为:

?log?p(xi∣zi)=?[∑j=1784xjlog?x^j+(1?xj)log?(1?x^j)]

上面这个公式其实认真看一看,发现其实就是Binary Cross Entropy loss。

optimizer = optim.Adam(vae.parameters())
# return reconstruction error + KL divergence losses

def loss_function(recon_x, x, mu, log_var):
    BCE = F.binary_cross_entropy(recon_x, x.view(-1, 784))
    KLD = 0.5 * torch.sum(mu.pow(2) + log_var.exp() - log_var - 1)
    return BCE + KLD

模型训练

训练代码就没啥好讲的啦,Pytorch 都一样:

def train(epoch):
    vae.train()
    train_loss = 0
    for batch_idx, (data, _) in enumerate(train_loader):
        optimizer.zero_grad()
        recon_data, mu, log_var = vae(data)
        loss = loss_function(recon_data, data, mu, log_var)

        loss.backward()
        train_loss += loss.item()
        optimizer.step()

        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item() / len(data)))
    print('====> Epoch: {} Average loss: {:.4f}'.format(epoch, train_loss / len(train_loader.dataset)))

输出结果示例:

rain Epoch: 1 [0/60000 (0%)]    Loss: 544.540078
Train Epoch: 1 [10000/60000 (17%)]    Loss: 184.232109
Train Epoch: 1 [20000/60000 (33%)]    Loss: 162.313955
Train Epoch: 1 [30000/60000 (50%)]    Loss: 165.958750
Train Epoch: 1 [40000/60000 (67%)]    Loss: 159.636836
Train Epoch: 1 [50000/60000 (83%)]    Loss: 157.480146
====> Epoch: 1 Average loss: 178.0764

VAE 结果

下面图中左边第一个是原始的MNIST 手写字体,那么后面的X-D 代表的是隐向量 z 的维度,可以看到啦维度越高能生成的数据越精确和清晰。

小结

VAE虽然也称是AE(AutoEncoder)的一种,但它的做法是别具一格的。在VAE中,它的Encoder有两个,一个用来计算均值,一个用来计算方差。很意外的是 Encoder 不是用来做编码压缩的,是用来算样本的均值和方差,这真是大新闻了,均值和方差不都是统计量吗,怎么是用神经网络来算的?

事实上,我觉得 VAE 从让普通人望而生畏的变分和贝叶斯理论出发,最后落地到一个具体的模型中,虽然走了比较长的一段路,但最终的模型其实是很接地气的:它本质上就是在常规的自编码器的基础上,对 Encoder 的结果加上了 “高斯噪声”,使得结果 Decoder 能够对噪声有鲁棒性;而额外的KL loss(目的是让均值为0,方差为1),事实上就是相当于对 Encoder 的一个正则项,希望 Encoder 出来的向量均有零均值。

那另外一个Encoder(对应着计算方差的网络)的作用呢?它是用来动态调节噪声的强度的。直觉上来想,当 Encoder 还没有训练好时(重构误差远大于KL loss),就会适当降低噪声(KL loss增加),使得拟合起来容易一些(重构误差开始下降);反之,如果 Decoder 训练得还不错时(重构误差小于KL loss),这时候噪声就会增加(KL loss减少),使得拟合更加困难了(重构误差又开始增加),这时候 Decoder 就要想办法提高它的生成能力了。

引用

[1] https://deepdreamgenerator.com/ Gkotzos, Konstantinos. "Google’s DeepDream: Algorithms on LSD."

[2] Marcus, Gary, Ernest Davis, and Scott Aaronson. "A very preliminary analysis of DALL-E 2." arXiv preprint arXiv:2204.13807 (2022).

[3] 变分自编码器|Scientific Spaces

[4] 无监督学习之VAE——变分自编码器详解

[5] From Autoencoder to Beta-VAE

[6] 李宏毅2022机器学习深度学习课程

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码