变分自编码器VAE详细解读

过去虽然没有细看,但印象里一直以为变分自编码器(Variational Auto-Encoder,VAE)是个好东西。趁着最近看几率图模型的三分钟热度,我决定也争取把 VAE 搞懂。git

因而乎照样翻了网上不少资料,无一例外发现都很含糊,主要的感受是公式写了一大通,仍是迷迷糊糊的,最后好不容易以为看懂了,再去看看实现的代码,又感受实现代码跟理论彻底不是一回事啊。github

终于,东拼西凑再加上我这段时间对几率模型的一些积累,并反复对比原论文 Auto-Encoding Variational Bayes,最后我以为我应该是想明白了。网络

其实真正的 VAE,跟不少教程说的的还真不大同样,不少教程写了一大通,都没有把模型的要点写出来。因而写了这篇东西,但愿经过下面的文字,能把 VAE 初步讲清楚。app

分布变换框架

一般咱们会拿 VAE 跟 GAN 比较,的确,它们两个的目标基本是一致的——但愿构建一个从隐变量 Z 生成目标数据 X 的模型,可是实现上有所不一样。函数

更准确地讲,它们是假设了服从某些常见的分布(好比正态分布或均匀分布),而后但愿训练一个模型 X=g(Z),这个模型可以将原来的几率分布映射到训练集的几率分布,也就是说,它们的目的都是进行分布之间的变换post

生成模型的难题就是判断生成分布与真实分布的类似度,由于咱们只知道二者的采样结果,不知道它们的分布表达式。学习

那如今假设服从标准的正态分布,那么我就能够从中采样获得若干个 Z1,Z2,…,Zn,而后对它作变换获得 X̂1=g(Z1),X̂2=g(Z2),…,X̂n=g(Zn),咱们怎么判断这个经过 f 构造出来的数据集,它的分布跟咱们目标的数据集分布是否是同样的呢?优化

有读者说不是有 KL 散度吗?固然不行,由于 KL 散度是根据两个几率分布的表达式来算它们的类似度的,然而目前咱们并不知道它们的几率分布的表达式。this

咱们只有一批从构造的分布采样而来的数据 {X̂1,X̂2,…,X̂n},还有一批从真实的分布采样而来的数据 {X1,X2,…,Xn}(也就是咱们但愿生成的训练集)。咱们只有样本自己,没有分布表达式,固然也就没有方法算 KL 散度。

虽然遇到困难,但仍是要想办法解决的。GAN 的思路很直接粗犷:既然没有合适的度量,那我干脆把这个度量也用神经网络训练出来吧

就这样,WGAN 就诞生了,详细过程请参考互怼的艺术:从零直达 WGAN-GP。而 VAE 则使用了一个精致迂回的技巧。

VAE慢谈

这一部分咱们先回顾通常教程是怎么介绍 VAE 的,而后再探究有什么问题,接着就天然地发现了 VAE 真正的面目。

首先咱们有一批数据样本 {X1,…,Xn},其总体用 X 来描述,咱们本想根据 {X1,…,Xn} 获得 X 的分布 p(X),若是能获得的话,那我直接根据 p(X) 来采样,就能够获得全部可能的 X 了(包括 {X1,…,Xn} 之外的),这是一个终极理想的生成模型了。

固然,这个理想很难实现,因而咱们将分布改一改:

这里咱们就不区分求和仍是求积分了,意思对了就行。此时 p(X|Z) 就描述了一个由 Z 来生成 X 的模型,而咱们假设 Z 服从标准正态分布,也就是 p(Z)=N(0,I)。若是这个理想能实现,那么咱们就能够先从标准正态分布中采样一个 Z,而后根据 Z 来算一个 X,也是一个很棒的生成模型

接下来就是结合自编码器来实现重构,保证有效信息没有丢失,再加上一系列的推导,最后把模型实现。框架的示意图以下:

▲ VAE的传统理解

看出了什么问题了吗?若是像这个图的话,咱们其实彻底不清楚:究竟通过从新采样出来的 Zk,是否是还对应着原来的 Xk,因此咱们若是直接最小化 D(X̂ k,Xk)^2(这里 D 表明某种距离函数)是很不科学的,而事实上你看代码也会发现根本不是这样实现的

也就是说,不少教程说了一大通头头是道的话,而后写代码时却不是按照所写的文字来写,但是他们也不以为这样会有矛盾。

VAE初现

其实,在整个 VAE 模型中,咱们并无去使用 p(Z)(先验分布)是正态分布的假设,咱们用的是假设 p(Z|X)(后验分布)是正态分布

具体来讲,给定一个真实样本 Xk,咱们假设存在一个专属于 Xk 的分布 p(Z|Xk)(学名叫后验分布),并进一步假设这个分布是(独立的、多元的)正态分布。

为何要强调“专属”呢?由于咱们后面要训练一个生成器 X=g(Z),但愿可以把从分布 p(Z|Xk) 采样出来的一个 Zk 还原为 Xk。

若是假设 p(Z) 是正态分布,而后从 p(Z) 中采样一个 Z,那么咱们怎么知道这个 Z 对应于哪一个真实的 X 呢?如今 p(Z|Xk) 专属于 Xk,咱们有理由说从这个分布采样出来的 Z 应该要还原到 Xk 中去

事实上,在论文 Auto-Encoding Variational Bayes的应用部分,也特别强调了这一点:

In this case, we can let the variational approximate posterior be a multivariate Gaussian with a diagonal covariance structure:

论文中的式 (9) 是实现整个模型的关键,不知道为何不少教程在介绍 VAE 时都没有把它凸显出来。尽管论文也提到 p(Z) 是标准正态分布,然而那其实并非本质重要的。

再次强调,这时候每个 Xk 都配上了一个专属的正态分布,才方便后面的生成器作还原。但这样有多少个 X 就有多少个正态分布了。咱们知道正态分布有两组参数:均值 μ 和方差 σ^2(多元的话,它们都是向量)。

那我怎么找出专属于 Xk 的正态分布 p(Z|Xk) 的均值和方差呢?好像并无什么直接的思路。

那好吧,我就用神经网络来拟合出来。这就是神经网络时代的哲学:难算的咱们都用神经网络来拟合,在 WGAN 那里咱们已经体验过一次了,如今再次体验到了。

因而咱们构建两个神经网络 μk=f1(Xk),logσ^2=f2(Xk) 来算它们了。咱们选择拟合 logσ^2 而不是直接拟合 σ^2,是由于 σ^2 老是非负的,须要加激活函数处理,而拟合 logσ^2 不须要加激活函数,由于它可正可负。

到这里,我能知道专属于 Xk 的均值和方差了,也就知道它的正态分布长什么样了,而后从这个专属分布中采样一个 Zk 出来,而后通过一个生成器获得 X̂k=g(Zk)。

如今咱们能够放心地最小化 D(X̂k,Xk)^2,由于 Zk 是从专属 Xk 的分布中采样出来的,这个生成器应该要把开始的 Xk 还原回来。因而能够画出 VAE 的示意图:

事实上,VAE 是为每一个样本构造专属的正态分布,而后采样来重构。

分布标准化

让咱们来思考一下,根据上图的训练过程,最终会获得什么结果。

首先,咱们但愿重构 X,也就是最小化 D(X̂k,Xk)^2,可是这个重构过程受到噪声的影响,由于 Zk 是经过从新采样过的,不是直接由 encoder 算出来的。

显然噪声会增长重构的难度,不过好在这个噪声强度(也就是方差)经过一个神经网络算出来的,因此最终模型为了重构得更好,确定会想尽办法让方差为0。

而方差为 0 的话,也就没有随机性了,因此无论怎么采样其实都只是获得肯定的结果(也就是均值),只拟合一个固然比拟合多个要容易,而均值是经过另一个神经网络算出来的。

说白了,模型会慢慢退化成普通的 AutoEncoder,噪声再也不起做用

这样不就白费力气了吗?说好的生成模型呢?

别急别急,其实 VAE 还让全部的 p(Z|X) 都向标准正态分布看齐,这样就防止了噪声为零,同时保证了模型具备生成能力。

怎么理解“保证了生成能力”呢?若是全部的 p(Z|X) 都很接近标准正态分布 N(0,I),那么根据定义:

这样咱们就能达到咱们的先验假设:p(Z) 是标准正态分布。而后咱们就能够放心地从 N(0,I) 中采样来生成图像了。

为了使模型具备生成能力,VAE 要求每一个 p(Z_X) 都向正态分布看齐。

那怎么让全部的 p(Z|X)都向 N(0,I) 看齐呢?若是没有外部知识的话,其实最直接的方法应该是在重构偏差的基础上中加入额外的 loss:

由于它们分别表明了均值 μk 和方差的对数 logσ^2,达到 N(0,I) 就是但愿两者尽可能接近于 0 了。不过,这又会面临着这两个损失的比例要怎么选取的问题,选取得很差,生成的图像会比较模糊。

因此,原论文直接算了通常(各份量独立的)正态分布与标准正态分布的 KL 散度 KL(N(μ,σ^2)‖N(0,I))做为这个额外的 loss,计算结果为:

这里的 d 是隐变量 Z 的维度,而 μ(i) 和 σ_{(i)}^{2} 分别表明通常正态分布的均值向量和方差向量的第 i 个份量。直接用这个式子作补充 loss,就不用考虑均值损失和方差损失的相对比例问题了。

显然,这个 loss 也能够分两部分理解:

推导

因为咱们考虑的是各份量独立的多元正态分布,所以只须要推导一元正态分布的情形便可,根据定义咱们能够写出:

整个结果分为三项积分,第一项实际上就是 −logσ^2 乘以几率密度的积分(也就是 1),因此结果是 −logσ^2;第二项实际是正态分布的二阶矩,熟悉正态分布的朋友应该都清楚正态分布的二阶矩为 μ^2+σ^2;而根据定义,第三项实际上就是“-方差除以方差=-1”。因此总结果就是:

重参数技巧

最后是实现模型的一个技巧,英文名是 Reparameterization Trick,我这里叫它作重参数吧。

▲ 重参数技巧

其实很简单,就是咱们要从 p(Z|Xk) 中采样一个 Zk 出来,尽管咱们知道了 p(Z|Xk) 是正态分布,可是均值方差都是靠模型算出来的,咱们要靠这个过程反过来优化均值方差的模型,可是“采样”这个操做是不可导的,而采样的结果是可导的,因而咱们利用了一个事实:

因此,咱们将从 N(μ,σ^2) 采样变成了从 N(μ,σ^2) 中采样,而后经过参数变换获得从 N(μ,σ^2) 中采样的结果。这样一来,“采样”这个操做就不用参与梯度降低了,改成采样的结果参与,使得整个模型可训练了。

具体怎么实现,你们把上述文字对照着代码看一下,一会儿就明白了。

后续分析

即使把上面的全部内容都搞清楚了,面对 VAE,咱们可能还存有不少疑问。

本质是什么

VAE 的本质是什么?VAE 虽然也称是 AE(AutoEncoder)的一种,但它的作法(或者说它对网络的诠释)是别具一格的。

在 VAE 中,它的 Encoder 有两个,一个用来计算均值,一个用来计算方差,这已经让人意外了:Encoder 不是用来 Encode 的,是用来算均值和方差的,这真是大新闻了,还有均值和方差不都是统计量吗,怎么是用神经网络来算的?

事实上,我以为 VAE 从让普通人望而生畏的变分和贝叶斯理论出发,最后落地到一个具体的模型中,虽然走了比较长的一段路,但最终的模型实际上是很接地气的。

它本质上就是在咱们常规的自编码器的基础上,对 encoder 的结果(在VAE中对应着计算均值的网络)加上了“高斯噪声”,使得结果 decoder 可以对噪声有鲁棒性;而那个额外的 KL loss(目的是让均值为 0,方差为 1),事实上就是至关于对 encoder 的一个正则项,但愿 encoder 出来的东西均有零均值。

那另一个 encoder(对应着计算方差的网络)的做用呢?它是用来动态调节噪声的强度的。

直觉上来想,当 decoder 尚未训练好时(重构偏差远大于 KL loss),就会适当下降噪声(KL loss 增长),使得拟合起来容易一些(重构偏差开始降低)

反之,若是 decoder 训练得还不错时(重构偏差小于 KL loss),这时候噪声就会增长(KL loss 减小),使得拟合更加困难了(重构偏差又开始增长),这时候 decoder 就要想办法提升它的生成能力了

▲ VAE的本质结构

说白了,重构的过程是但愿没噪声的,而 KL loss 则但愿有高斯噪声的,二者是对立的。因此,VAE 跟 GAN 同样,内部实际上是包含了一个对抗的过程,只不过它们二者是混合起来,共同进化的

从这个角度看,VAE 的思想彷佛还高明一些,由于在 GAN 中,造假者在进化时,鉴别者是安然不动的,反之亦然。固然,这只是一个侧面,不能说明 VAE 就比 GAN 好。

GAN 真正高明的地方是:它连度量都直接训练出来了,并且这个度量每每比咱们人工想的要好(然而 GAN 自己也有各类问题,这就不展开了)。

正态分布?

对于 p(Z|X) 的分布,读者可能会有疑惑:是否是必须选择正态分布?能够选择均匀分布吗?

首先,这个自己是一个实验问题,两种分布都试一下就知道了。可是从直觉上来说,正态分布要比均匀分布更加合理,由于正态分布有两组独立的参数:均值和方差,而均匀分布只有一组。

前面咱们说,在 VAE 中,重构跟噪声是相互对抗的,重构偏差跟噪声强度是两个相互对抗的指标,而在改变噪声强度时原则上须要有保持均值不变的能力,否则咱们很难肯定重构偏差增大了,到底是均值变化了(encoder的锅)仍是方差变大了(噪声的锅)

而均匀分布不能作到保持均值不变的状况下改变方差,因此正态分布应该更加合理。

变分在哪里

还有一个有意思(但不大重要)的问题是:VAE 叫作“变分自编码器”,它跟变分法有什么联系?在VAE 的论文和相关解读中,好像也没看到变分法的存在?

其实若是读者已经认可了 KL 散度的话,那 VAE 好像真的跟变分没多大关系了,由于 KL 散度的定义是:

若是是离散几率分布就要写成求和,咱们要证实:已几率分布 p(x)(或固定q(x))的状况下,对于任意的几率分布 q(x)(或 p(x)),都有 KLp(x)‖q(x))≥0,并且只有当p(x)=q(x)时才等于零

由于 KL(p(x)‖q(x))其实是一个泛函,要对泛函求极值就要用到变分法,固然,这里的变分法只是普通微积分的平行推广,还没涉及到真正复杂的变分法。而 VAE 的变分下界,是直接基于 KL 散度就获得的。因此直接认可了 KL 散度的话,就没有变分的什么事了。

一句话,VAE 的名字中“变分”,是由于它的推导过程用到了 KL 散度及其性质。

条件VAE

最后,由于目前的 VAE 是无监督训练的,所以很天然想到:若是有标签数据,那么能不能把标签信息加进去辅助生成样本呢?

这个问题的意图,每每是但愿可以实现控制某个变量来实现生成某一类图像。固然,这是确定能够的,咱们把这种状况叫作 Conditional VAE,或者叫 CVAE(相应地,在 GAN 中咱们也有个 CGAN)。

可是,CVAE 不是一个特定的模型,而是一类模型,总之就是把标签信息融入到 VAE 中的方式有不少,目的也不同。这里基于前面的讨论,给出一种很是简单的 VAE。

▲ 一个简单的CVAE结构

在前面的讨论中,咱们但愿 X 通过编码后,Z 的分布都具备零均值和单位方差,这个“但愿”是经过加入了 KL loss 来实现的。

若是如今多了类别信息 Y,咱们能够但愿同一个类的样本都有一个专属的均值 μ^Y(方差不变,仍是单位方差),这个 μ^Y 让模型本身训练出来

这样的话,有多少个类就有多少个正态分布,而在生成的时候,咱们就能够经过控制均值来控制生成图像的类别

事实上,这样可能也是在 VAE 的基础上加入最少的代码来实现 CVAE 的方案了,由于这个“新但愿”也只需经过修改 KL loss 实现:

下图显示这个简单的 CVAE 是有必定的效果的,不过由于 encoder 和 decoder 都比较简单(纯 MLP),因此控制生成的效果不尽完美。

用这个 CVAE 控制生成数字 9,能够发现生成了多种样式的 9,而且慢慢向 7 过渡,因此初步观察这种 CVAE 是有效的。

更完备的 CVAE 请读者自行学习了,最近还出来了 CVAE 与 GAN 结合的工做 CVAE-GAN: Fine-Grained Image Generation through Asymmetric Training,模型套路变幻无穷。

代码

我把 Keras 官方的 VAE 代码复制了一份,而后微调并根据前文内容添加了中文注释,也把最后说到的简单的 CVAE 实现了一下,供读者参考。

代码:https://github.com/bojone/vae

终点站

磕磕碰碰,又到了文章的终点了。不知道讲清楚了没,但愿你们多提点意见。

总的来讲,VAE 的思路仍是很漂亮的。倒不是说它提供了一个多么好的生成模型(由于事实上它生成的图像并不算好,偏模糊),而是它提供了一个将几率图跟深度学习结合起来的一个很是棒的案例,这个案例有诸多值得思考回味的地方。

 

本文引用:https://www.sohu.com/a/226209674_500659