细水长flow之NICE:流模型的基本概念与实现

 等 

作者丨苏剑林

单位丨广州火焰信息科技有限公司

研究方向丨NLP,神经网络

个人主页丨kexue.fm

 

前言:自从在机器之心上看到了 Glow 模型之后(下一个GAN?OpenAI提出可逆生成模型Glow),我就一直对其念念不忘。现在机器学习模型层出不穷,我也经常关注一些新模型动态,但很少像 Glow 模型那样让我怦然心动,有种“就是它了”的感觉。更意外的是,这个效果看起来如此好的模型,居然是我以前完全没有听说过的。于是我翻来覆去阅读了好几天,越读越觉得有意思,感觉通过它能将我之前的很多想法都关联起来。在此,先来个阶段总结。

背景

本文主要是 NICE: Non-linear Independent Components Estimation 一文的介绍和实现。这篇文章也是 Glow 模型的基础文章之一,可以说它就是 Glow 的奠基石。

 

■ 论文 | NICE: Non-linear Independent Components Estimation

■ 链接 | 

■ 源码 | 

 

艰难的分布

众所周知,目前主流的生成模型包括 VAE 和 GAN,但事实上除了这两个之外,还有基于 flow 的模型(flow 可以直接翻译为“流”,它的概念我们后面再介绍)。

事实上 flow 的历史和 VAE、GAN 它们一样悠久,但是 flow 却鲜为人知。在我看来,大概原因是 flow 找不到像 GAN 一样的诸如“造假者-鉴别者”的直观解释吧,因为 flow 整体偏数学化,加上早期效果没有特别好但计算量又特别大,所以很难让人提起兴趣来。不过现在看来,OpenAI 的这个好得让人惊叹的、基于 flow 的 Glow 模型,估计会让更多的人投入到 flow 模型的改进中。

Glow模型生成的高清人脸

生成模型的本质,就是希望用一个我们知道的概率模型来拟合所给的数据样本,也就是说,我们得写出一个带参数 θ 的分布 qθ(x)。然而,我们的神经网络只是“万能函数拟合器”,却不是“万能分布拟合器”,也就是它原则上能拟合任意函数,但不能随意拟合一个概率分布,因为概率分布有“非负”和“归一化”的要求。这样一来,我们能直接写出来的只有离散型的分布,或者是连续型的高斯分布。

当然,从最严格的角度来看,图像应该是一个离散的分布,因为它是由有限个像素组成的,而每个像素的取值也是离散的、有限的,因此可以通过离散分布来描述。这个思路的成果就是 PixelRNN 一类的模型了,我们称之为“自回归流”,其特点就是无法并行,所以计算量特别大。所以,我们更希望用连续分布来描述图像。当然,图像只是一个场景,其他场景下我们也有很多连续型的数据,所以连续型的分布的研究是很有必要的。

各显神通

所以问题就来了,对于连续型的,我们也就只能写出高斯分布了,而且很多时候为了方便处理,我们只能写出各分量独立的高斯分布,这显然只是众多连续分布中极小的一部分,显然是不够用的。为了解决这个困境,我们通过积分来创造更多的分布:

这里 q(z) 一般是标准的高斯分布,而 qθ(x|z)=qθ(x|z) 可以选择任意的条件高斯分布或者狄拉克分布。这样的积分形式可以形成很多复杂的分布。理论上来讲,它能拟合任意分布。

现在分布形式有了,我们需要求出参数 θ,那一般就是最大似然,假设真实数据分布为 p̃(x),那么我们就需要最大化目标:

然而 qθ(x) 是积分形式的,能不能算下去很难说。

于是各路大神就“八仙过海,各显神通”了。其中,VAE 和 GAN 在不同方向上避开了这个困难。VAE 没有直接优化目标 (2),而是优化一个更强的上界,这使得它只能是一个近似模型,无法达到良好的生成效果。GAN 则是通过一个交替训练的方法绕开了这个困难,确实保留了模型的精确性,所以它才能有如此好的生成效果。但不管怎么样,GAN 也不能说处处让人满意了,所以探索别的解决方法是有意义的。

直面概率积分

flow 模型选择了一条“硬路”:直接把积分算出来。

具体来说,flow 模型选择 q(x|z) 为狄拉克分布 δ(x−g(z)),而且 g(z) 必须是可逆的,也就是说:

要从理论上(数学上)实现可逆,那么要求 z 和 x 的维度一样。假设 f,g 的形式都知道了,那么通过 (1) 算 q(x) 相当于是对 q(z) 做一个积分变换 z=f(x)。即本来是:

的标准高斯分布(D 是 z 的维度),现在要做一个变换 z=f(x)。注意概率密度函数的变量代换并不是简单地将 z 替换为 f(x) 就行了,还多出了一个“雅可比行列式”的绝对值,也就是:

这样,对 f 我们就有两个要求:

1. 可逆,并且易于求逆函数(它的逆 g 就是我们希望的生成模型);

2. 对应的雅可比行列式容易计算。

这样一来:

这个优化目标是可以求解的。并且由于 f 容易求逆,因此一旦训练完成,我们就可以随机采样一个 z,然后通过 f 的逆来生成一个样本 [公式] ,这就得到了生成模型。

flow

前面我们已经介绍了 flow 模型的特点和难点,下面我们来详细展示 flow 模型是如何针对难点来解决问题的。因为本文主要是介绍第一篇文章 NICE: Non-linear Independent Components Estimation 的工作,因此本文的模型也专称为 NICE。

分块耦合层

相对而言,行列式的计算要比函数求逆要困难,所以我们从“要求 2”出发思考。熟悉线性代数的朋友会知道,三角阵的行列式最容易计算:三角阵的行列式等于对角线元素之积。所以我们应该要想办法使得变换f的雅可比矩阵为三角阵。NICE 的做法很精巧,它将 D 维的 x 分为两部分 x1,x2,然后取下述变换:

其中 x1,x2 是 x 的某种划分,m 是 x1 的任意函数。也就是说,将 x 分为两部分,然后按照上述公式进行变换,得到新的变量 h,这个我们称为“加性耦合层”(Additive Coupling)。不失一般性,可以将 x 各个维度进行重排,使得 x1=x1:d 为前 d 个元素,x2=xd+1:D 为 d+1∼D 个元素。

不难看出,这个变换的雅可比矩阵 [公式] 是一个三角阵,而且对角线全部为 1,用分块矩阵表示为:

这样一来,这个变换的雅可比行列式为 1,其对数为 0,这样就解决了行列式的计算问题。

同时,(7) 式的变换也是可逆的,其逆变换为:

细水长flow

上面的变换让人十分惊喜:可逆,而且逆变换也很简单,并没有增加额外的计算量。尽管如此,我们可以留意到,变换 (7) 的第一部分是平凡的(恒等变换),因此单个变换不能达到非常强的非线性,所以我们需要多个简单变换的复合,以达到强非线性,增强拟合能力。

其中每个变换都是加性耦合层。这就好比流水一般,积少成多,细水长流,所以这样的一个流程成为一个“流(flow)”。也就是说,一个 flow 是多个加性耦合层的耦合。

由链式法则:

因为“矩阵的乘积的行列式等于矩阵的行列式的乘积”,而每一层都是加性耦合层,因此每一层的行列式为 1,所以结果就是:

考虑到下面的错位,行列式可能变为 -1,但绝对值依然为 1,所以我们依然不用考虑行列式。

交错中前进

要注意,如果耦合的顺序一直保持不变,即:

那么最后还是 z1=x1,第一部分依然是平凡的,如下图:

简单的耦合使得其中一部分仍然保持恒等,信息没有充分混合

为了得到不平凡的变换,我们可以考虑在每次进行加性耦合前,打乱或反转输入的各个维度的顺序,或者简单地直接交换这两部分的位置,使得信息可以充分混合,比如:

如下图:

通过交叉耦合,充分混合信息,达到更强的非线性

尺度变换层

在文章的前半部分我们已经指出过,flow 是基于可逆变换的,所以当模型训练完成之后,我们同时得到了一个生成模型和一个编码模型。但也正是因为可逆变换,随机变量 z 和输入样本 x 具有同一大小。

当我们指定 z 为高斯分布时,它是遍布整个 D 维空间的,D 也就是输入 x 的尺寸。但虽然 x 具有 D 维,但它未必就真正能遍布整个 D 维空间,比如 MNIST 图像虽然有 784 个像素,但有些像素不管在训练集还是测试集,都一直保持为 0,这说明它远远没有 784 维那么大。

也就是说,flow 这种基于可逆变换的模型,天生就存在比较严重的维度浪费问题:输入数据明明都不是 D 维流形,但却要编码为一个 D 维流形,这可行吗?

为了解决这个情况,NICE 引入了一个尺度变换层,它对最后编码出来的每个维度的特征都做了个尺度变换,也就是 [公式] 这样的形式,其中 s=(s1,s2,…,sD) 也是一个要优化的参数向量(各个元素非负)。这个 s 向量能识别该维度的重要程度(越小越重要,越大说明这个维度越不重要,接近可以忽略),起到压缩流形的作用。

注意这个尺度变换层的雅可比行列式就不再是 1 了,可以算得它的雅可比矩阵为对角阵:

所以它的行列式为 [公式] 。于是根据 (6) 式,我们有对数似然:

为什么这个尺度变换能识别特征的重要程度呢?其实这个尺度变换层可以换一种更加清晰的方式描述:我们开始设 z 的先验分布为标准正态分布,也就是各个方差都为 1。

事实上,我们可以将先验分布的方差也作为训练参数,这样训练完成后方差有大有小,方差越小,说明该特征的“弥散”越小,如果方差为 0,那么该特征就恒为均值 0,该维度的分布坍缩为一个点,于是这意味着流形减少了一维。

不同于 (4) 式,我们写出带方差的正态分布:

将流模型 z=f(x) 代入上式,然后取对数,类似 (6) 式,我们得到:

对比 (15) 式,其实就有 si=1/σi。所以尺度变换层等价于将先验分布的方差(标准差)也作为训练参数,如果方差足够小,我们就可以认为该维度所表示的流形坍缩为一个点,从而总体流形的维度减 1,暗含了降维的可能。

特征解耦

当我们将先验分布选为各分量独立的高斯分布时,除了采样上的方便,还能带来什么好处呢?

在 flow 模型中, [公式] 是生成模型,可以用来随机生成样本,那么 f 就是编码器。但是不同于普通神经网络中的自编码器“强迫低维重建高维来提取有效信息”的做法,flow 模型是完全可逆的,那么就不存在信息损失的问题,那么这个编码器还有什么价值呢?

这就涉及到了“什么是好的特征”的问题了。在现实生活中,我们经常抽象出一些维度来描述事物,比如“高矮”、“肥瘦”、“美丑”、“贫富”等,这些维度的特点是:“当我们说一个人高时,他不是必然会肥或会瘦,也不是必然会有钱或没钱”,也就是说这些特征之间没有多少必然联系,不然这些特征就有冗余了。所以,一个好的特征,理想情况下各个维度之间应该是相互独立的,这样实现了特征的解耦,使得每个维度都有自己独立的含义。

这样,我们就能理解“先验分布为各分量独立的高斯分布”的好处了,由于各分量的独立性,我们有理由说当我们用f对原始特征进行编码时,输出的编码特征 z=f(x) 的各个维度是解耦的。

NICE 的全称 Non-linear Independent Components Estimation,翻译为“非线性独立成分估计”,就是这个含义。反过来,由于 z 的每个维度的独立性,理论上我们控制改变单个维度时,就可以看出生成图像是如何随着该维度的改变而改变,从而发现该维度的含义。

类似地,我们也可以对两幅图像的编码进行插值(加权平均),得到过渡自然的生成样本,这些在后面发展起来的 Glow 模型中体现得很充分。不过,我们后面只做了 MNIST 实验,所以本文中就没有特别体现这一点。

实验

这里我们用 Keras 重现 NICE 一文中的 MNIST 实验。

模型细节

先来把 NICE 模型的各个部分汇总一下。NICE 模型是 flow 模型的一种,由多个加性耦合层组成,每个加性耦合层如 (7),它的逆是 (9)。在耦合之前,需要反转输入的维度,使得信息充分混合。最后一层需要加个尺度变换层,最后的 loss 是 (15) 式的相反数。

加性耦合层需要将输入分为两部分,NICE 采用交错分区,即下标为偶数的作为第一部分,下标为奇数的作为第二部分,而每个 m(x) 则简单地用多层全连接(5 个隐藏层,每个层 1000 节点,relu 激活)。在 NICE 中一共耦合了 4 个加性耦合层。

参考代码

这里是我用 Keras 实现的参考代码:

在我的实验中,20 个 epoch 内可以跑到最优,11s 一个 epoch(GTX1070 环境),最终的 loss 约为 -2200。

相比于原论文的实现,这里做了一些改动。对于加性耦合层,我用了 (9) 式作为前向,(7) 式作为其逆向。因为 m(x) 用 relu 激活,我们知道 relu 是非负的,因此两种选择是有点差别的。因为正向是编码器,而逆向是生成器,选用 (7) 式作为逆向,那么生成模型更倾向于生成正数,这跟我们要生成的图像是吻合的,因为我们需要生成的是像素值为 0~1 的图像。

NICE模型生成的数字样本

退火参数

虽然我们最终希望从标准正态分布中采样随机数来生成样本,但实际上对于训练好的模型,理想的采样方差并不一定是 1,而是在 1 上下波动,一般比 1 稍小。最终采样的正态分布的标准差,我们称之为退火参数。比如上面的参考实现中,我们的退火参数选为 0.8,目测在这时候生成模型的质量最优。

总结

NICE 的模型还是比较庞大的,按照上述模型,模型的参数量约为 [公式],也就是两千万的参数只为训练一个 MNIST 生成模型,也是夸张。

NICE 整体还是比较简单粗暴的,首先加性耦合本身比较简单,其次模型 m 部分只是简单地用到了庞大的全连接层,还没有结合卷积等玩法,因此探索空间还有很大,Real NVP 和 Glow 就是它们的两个改进版本,它们的故事我们后面再谈。

 

关于PaperWeekly

PaperWeekly 是一个推荐、解读、讨论、报道人工智能前沿论文成果的学术平台。如果你研究或从事 AI 领域,欢迎在公众号后台点击「交流群」,小助手将把你带入 PaperWeekly 的交流群里。

加入社区:

微信公众号:PaperWeekly

新浪微博:@PaperWeekly

编辑于 2018-08-13
机器学习
计算机视觉
深度学习(Deep Learning)
 

文章被以下专栏收录

PaperWeekly
PaperWeekly
PaperWeekly是一个推荐、解读、讨论和报道人工智能前沿论文成果的学术平台,致力于让国内外优秀科研工作得到更为广泛的传播和认可。

推荐阅读

简评 | 知识蒸馏(Knowledge Distillation)最新进展(一)

简评 | 知识蒸馏(Knowledge Distillation)最新进展(一)

PyTorch 中的 ModuleList 和 Sequential: 区别和使用场景

PyTorch 中有一些基础概念在构建网络的时候很重要,比如 nn.Module, nn.ModuleList, nn.Sequential,这些类我们称之为容器 (containers),因为我们可以添加模块 (module) 到它们之中。这些…

SRGAN With WGAN,让超分辨率算法训练更稳定

SRGAN With WGAN,让超分辨率算法训练更稳定

深度学习新星 | 图卷积神经网络(GCN)有多强大?

编译:集智翻译组 来源:THOMAS KIPF 综述 真实世界中,许多重要的数据集都是以图或者网络的形式存在的,比如社交网络,知识图谱,蛋白质相互作用网,世界贸易网等等。然而,迄今为止,却很…

6 条评论

  • 匿各用户
    匿各用户1 年前

    GLOW本质是构造将图像空间的分布(通过训练数据作为采样逼近)到一个固定的目标分布(高斯或者均匀分布)的变换。所用的代价函数可以理解为最大似然准则或者理解为分布变换的最小形变能量(Jacobian就有形变能量的物理意义),所以直观就是寻找一个平滑的形变能量小的变换完成图像到目标分布的变换。由于构造的变换是可逆的,所以其逆变换就是生成器。

    和GAN相比,这里只使用了训练数据采样,没有对更多的采样进行测试,而GAN是对新采样进行了测试,所以性能上应该还是GAN更好一些。

  • 写的真好。请问下什么时候也能写一篇关于RealNVP的解读吗?

  • 求知若渴
    求知若渴1 年前

    万分感谢,keras代码帮了大忙了,之前我自己写的生成图片很模糊。

    下面有两个小问题:

    1. 为什么生成时用的gaussian而不是logistic?
    2. 作者提到的dequantizer,说是防止log probability太大,想问下数学原理。
  • 店长
    店长1 年前
    保体积不一定是个好事情
  • SisaKu
    SisaKu11 个月前

    对于公式(2)提个问题:这里我是否可以理解为最小化KL散度,因为最大似然的公式不是这样算的吧?

  • mia1874
    mia18746 个月前

    inpainting怎么没说