0%

DL笔记(15)Unsupervised Learning-Generative Model

关于Generative Model推荐一篇很好的文章,来自OpenAI的Generative Models。文章的开头引用了Richard Feynman的话,“What I cannot create, I do not understand”,我无法创造的东西,我也无法真正理解,机器可以做猫狗分类,但却不一定知道“猫”和“狗”的概念,但如果机器能自己画出“猫”来,它或许才真正理解了“猫”这个概念,这也是Generative Model想要让machine做的事。

下面将简要介绍:PixelRNN、VAE和GAN这三种Generative model的方法。

1. PixelRNN

RNN可以处理长度可变的input,它的基本思想是可以根据过去发生的状态去推测下一个状态。PixelRNN的基本思想是每次只画一个pixel来生成一个image,这个pixel是由过去所有已产生的pixel共同决定的。例如一个$3\times 3$的Image,第一次给一个橙色的pixel,输入到NN中,output得到一个蓝色的pixel;然后再将上一步得到的橙色和蓝色的pixel一起输入到NN中得到一个浅蓝色的pixel;再下一步将这三个pixel输入到NN中得到一个灰色的pixel,以此类推就可以得到一个$3\times 3$的image。这种方法的精髓在于根据过去预测未来,画出来的图一般都是比较清晰的

(Reference[1]: Oord A, Kalchbrenner N, Kavukcuoglu K. Pixel recurrent neural networks[J]. arXiv preprint arXiv:1601.06759, 2016.

这个方法也适用于语音生成,可以用前面一段的语音去预测接下来生成的语音信号。也可以用在影像上,用前面一段video来预测后面的video。

(Reference[2]: Oord A, Dieleman S, Zen H, et al. Wavenet: A generative model for raw audio[J]. arXiv preprint arXiv:1609.03499, 2016.

Reference[3]: Kalchbrenner N, Oord A, Simonyan K, et al. Video pixel networks[C]//International Conference on Machine Learning. 2017: 1771-1779.

pokemon creation

下面这个小例子是给machine792个pekemon的image,想让machine学会去生成pekeon的Image。

在使用Generative model去生成宝可梦之前,有几个tips需要注意:

  • 为了减少运算量,将40×40的图像截取成20×20

  • 如果将每个pixel都以[R, G, B]的vector表示的话,生成的图像都是灰蒙蒙的,原因如下:

    • 亮度比较高的图像,一般都是RGB值差距特别大而形成的,如果各个维度的值大小比较接近,则生成的图像偏向于灰色;

    • 如果用sigmoid function,最终生成的RGB往往都是在0.5左右(归一化之后),导致色彩度不鲜艳;

    • 解决方案:将所有色彩集合成一个1-of-N encoding,由于色彩种类比较多,因此这里先对相近的颜色做clustering聚类表示为一种颜色,最终获得了167种色彩组成的向量。我们用这样的向量去表示每个pixel,可以让生成的色彩比较鲜艳。

相关数据连接如下:

  • 原始图像(40*40)数据的链接
  • 裁剪后的图像(20*20)数据链接
  • 数值与色彩(RGB)的映射关系链接

使用PixelRNN训练好模型之后,给它看没有被放在训练集中的3张图像的一部分,分别遮住原图的50%和75%,得到的原图如下:

训练好的pixel RNN预测到的图片如下:

做这种Generation的task有一个难点是,设计上的好坏较难去evaluate。接下来我们让训练好的model凭空去画,但如果什么都不给machine让它从头开始画的话,它得到的每一个image可能都是一样的,因此我们要故意加一些random,即machine在画下一个pixel的时候不一定是选几率最高的颜色,也有几率选几率比较低的颜色,这样它每次画出来的图才会有点不一样。

2. VAE

上一篇笔记中介绍过Auto-encoder,如果我们拿出其中的Decoder,给它随机的code,就可以生成对应的图像。但普通的Decoder生成效果并不好,VAE(Variational Auto-encoder,可变自动编码器)可以得到更好的效果。

在VAE中,code不再直接等于Encoder的输出,这里假设目标降维空间为3维,那我们使Encoder分别输出$m_1,m_2,m_3$和$\sigma_1,\sigma_2,\sigma_3$,此外我们从正态分布中随机取出三个点$e_1,e_2,e_3$,将下式作为最终的编码结果:

再将$c_i$输入到Decoder里面得到output,我们希望MInimize reconstruction error。但此时,我们的训练目标不仅要最小化input和output之间的差距,还要同时最小化下式(比较“神妙”):

2.1. Pokemon Creation

与PixelRNN不同的是,VAE画出的图一般都是不太清晰的,但在某种程度上我们可以控制生成的image。假设我们现在把VAE用在Pokemon creation上面,在Trainig的时候我们input一个pokemon,然后reconstruct一个同样的pokemon,learn出来的code设为10维。Learn好这个VAE之后,我们把Decoder的部分拿出来。我们在给Decoder输入10维的vector时可以固定其中的8维,只选2维出来,我们可以在2维平面上sample一系列的点,加上我们固定的8维后Input到Decoder中看输出的image是什么样的。这样我们就可以解读code的每一个dimension是代表什么含义,然后去控制VAE去生成一些image。

下面是固定code中的8维,让剩下的2维变化得到的image,发现image的变化确实是有些规律的。

2.2. Writing Poerty

VAE还可以用来写诗,将input和output都换成是sentence,我们只需要得到某两句话对应的code,然后在降维后的空间中得到这两个code所在点的连线,从中间等间隔地取一些点,把这些点输入给Decoder,得到还原后的句子,就可以得到类似下图中的效果。

2.3. Why VAE?

VAE和传统的Auto-encoder相比,有什么优势呢?事实上,VAE就是加了噪声noise的Autoencoder,它的抗干扰能力更强,过渡生成能力也更强。

对原先的Autoencoder来说,假设我们得到了满月和弦月的code,从两者连线中随机获取一个点并映射回原来的空间,得到的图像很可能是完全不一样的东西,因为code和output得到的image是一一对应的。

而对VAE来说,它要保证在降维后的code空间中,加了noise的一段范围内的所有点都能够映射到目标图像,如下图所示,当某个点既被要求映射到满月、又被要求映射到弦月,则它最终映射出来的结果就很有可能是两者之间的过渡图像。

再回过来头看VAE的结构,其中:

  • $m_i$其实就代表原来的code

  • $c_i$则代表加了noise以后的code

  • $\sigma_i$代表了noise的variance,描述了noise的大小,这是由NN学习到的参数

    (注:使用$\exp(\sigma_i)$的目的是保证variance是正的)

  • $e_i$是正态分布中随机采样的点

注意到,损失函数仅仅让input和output差距最小是不够的,因为variance是由机器自己决定的,如果不加以约束,它自然会去让variance=0,这就跟普通的Autoencoder没有区别了。

额外加的限制函数解释如下:

下图中,蓝线表示$e^{\sigma_i}$,红线表示$1+\sigma_i$,两者相减得到绿线。绿线的最低点$\sigma_i=0$,则variance $e^{\sigma_i}=1$,此时loss最低,而$(m_i)^2$项则是对code的L2 regularization,让它比较sparse,不容易过拟合。

上面是比较直观的理由,以下是paper上比较常见的解释。我们回归到我们要做的事情是什么,假设要machine generate pokemon的image,那每一张pokemon的图都可以想成是高维空间中的一个点。假设它是20*20的image,在高维空间中也就是400维的点,在图上我们用一维描述它,但其实是一个高维的空间。那现在要做的就是estimate p(x)的分布,它表示一张图片像宝可梦的几率,然后就可以根据p(x)高的地方去sample出一张像宝可梦的图。

Estimate the probability distributon可以用Gaussion mixture model。Guassion mixture model:现在有一个distribution(黑色的线),这个黑色的distribution其实是很多的Gaussion(青蓝色)用不同的weight叠合起来的结果。如果你的gaussion数目够多,你就可以产生很复杂的distribution,公式为 $p(x)=\sum_{m}p(m)p(x|m)$ 。

这样每一个$x$并不属于某一个class或者cluster,而是有一个vector来描述它不同面向的disstribution,描述它不同面向的特性,VAE其实就是Gaussian Mixture Model的Distributed representation的版本

假设我们要sample一个vector $z$,$z$是从normal distribution中sample出来的,$z$的每一个dimension都代表了某种attribute(特质,特性)。由$z$可以决定Gaussian的mean $\mu$和variance $\sigma$,由于$z$是continous的,所有它有无穷的可能,那mean和variance也有无穷多的可能。

$P(x)$的曲线是这样产生的:$z$上的每一个点都有可能被sample到,当sample出一个点后它就会对应到一个Gaussian,把它们mixture起来就得到了$P(x)$,即$P(x)=\int \limits_{z}P(z)P(x|z)dz $(注意因为$z$是continous的,所以这里要用积分,而不是sum)。

2.4. Maximizing Likelihood

那给出一个$z$怎么找出mean和variance呢,假设mean和variance都来自一个function,这个function就可以是一个NN。当然$z$的分布不一定是Gaussian,可以自己设定。那训练这个NN的目标就是要Maximiza the likelihood of the observed $x$,即使下式最大:

我们要做的就是调NN里的参数weight和bias,使得likelihood最大。然后我们需要引入另一个distribution $q(z|x)$,它是given $x$来决定在$z$的space上的mean和variance,还需要有另外一个NN’,input $x$之后会output对应的$z$的mean $\mu’(x)$和variance $\sigma’(x)$,即决定$z$要从什么样的mean和variance中被sample出来。

上图中上面的NN就相当于是VAE中的Decoder,下面的NN就相当于是VAE中的ENcoder。

下面是对$L=\sum \limits_{x}\log P(x)$的表达式的具体的推导:

推导$\log P(x)=L_b+KL(q(z|x)||P(z|x))$:

我们本来要做的是找使得$L$最大的$P(x|z)$,现在转换为求使$L_b$最大的$P(x|z)$和$q(z|x)$。

如果我们只找$p(x∣z)$ 这一项的话,然后去maximizing $L_b$ ,当增加$L_b$的时候,有可能会增加likehood,但不知道likehood跟lower bound之间到底有还差多少距离。你希望你做到的是:当lower bound上升的时候,likehood也跟着上升。但是有可能会遇到糟糕的事情是:lower bound上升的时候,likehood反而下降,因为不知道它们之间的差距是多少。

引入$q(z|x)$这一项就是为了解决这一问题。如上图中蓝色的是likehood, $\log P(x)=L_b+KL$,如果你今天调 $q(z|x)$来maximizing $L_b$,会发现$q(z|x)$跟$\log P(x)$是没有关系的,即ikelihood不变,那maximizing $L_b$的同时也在minimize KL divergence,即让lower bound($L_b$)跟likehood越来越接近。如果固定住 $p(x|z)$这一项,去调 $q(z|x)$这一项的话,会让$L_b$ 一直上升,直到KL divergence会完全不见。由于likehood一定要比lower bound大,这时你再调$p(x|z)$和$q(z|x)$来maximizing $L_b$的话,就可以保证likehood会一定增大。

这样做也会得到一个副产物,当maximize $L_b$这一项的时候,会让KL divergence越来越小,意味着会让 $q(z|x)$ 跟 $p(z|x)$越来越接近。所以接下来做的事情就是找$p(x|z)$跟$q(z|x)$,可以让$L_b$越大越好,就等同于让likehood越来越大,最后顺便会得到$q(z|x)$可以去approximation $p(z|x)$。

而$q$是个neural network,要去minimize $KL(q(z|x)||P(z))$就是去调NN‘让它产生的distribution和normal distribution越接近越好,而loss function就是之前讲过的那个式子$\sum \limits^{3} \limits_{i=1}(\exp(\sigma_i)-(1+\sigma_i)+(m_i)^2)$(这部分的推导可以参考VAE的原始论文)。

另外一项是转化为$\log P(x|z))$的期望值:

可以理解是我们从$q(z|x)$中去sample data,要让$\log P(x|z)$越大越好,这其实就是Auto-encoder在做的事情。你可以把$x$输入到NN’中得到mean $\mu’(x)$和variance $\sigma’(x)$,根据这个mean和variance可以sample出一个$z$;接下来把z输入到NN,得到mean $\mu(x)$和variance $\sigma(x)$,我们的目标是让这个mean和variance代表的distribution是$x$的几率越大越好,在实际使用中往往不考虑variance,那我们就是让最后输出的mean和$x$越接近越好,这不就是Auto-encoder在做的事情。

Conditional VAE

还有一种方法叫Conditional VAE,举个例子,如果你让VAE可以产生手写的数字,给它一个digit,然后它把这个digit的特性抽取出来(笔画的粗细等等),然后丢进encoder的时候一方面给它有关这个数字特性的distribution,另外一方面告诉decoder它是什么数字。那你就可以根据这一个digit,generate跟它style相近的digit。Conditional VAE可以根据某一个digit画出跟它style相近的数字。

Problems of VAE

VAE有一个缺点,它只是在努力做到让生成的图像与数据集里的图像尽可能相似,却从来没有想过怎么样真的产生一张新的图像,因此由VAE生成的图像大多是数据集中图像的线性变化,而很难自主生成全新的图像。假设Decoder output跟真正的image之间有一个pixel的差距,那有时不同的pixel落在不同的位置会得到非常不一样的结果,如下图中的两个数字“7”,人很容易发现其区别:左边的像是真的数字,而右边明显是fake。但对VAE来说,它们只是有一个pixel的差异,两张image都是一样好或者不好的。VAE做到的只是模仿,而不是创造,GAN的诞生,就是为了创造。

3. GAN

Generative Adversarial Network(GAN,对抗生成网络),基本思想类似天敌之间相互竞争,相互进步。

GAN由生成器(Generator)和判别器(Discriminator)组成:

  • 对判别器的训练:把生成器产生的图像标记为0,真实图像标记为1,丢给判别器训练分类,希望它能分辨real image和fake image;
  • 对生成器的训练:调整生成器的参数,使产生的图像(fake image)能够“骗过”判别器,即判别器输出越接近1越好;
  • 每次训练GAN时生成器和判别器要分开训练:先Fix住生成器,训练判别器的参数;再Fix 判别器,训练生成器的参数,如此反复。

(PS:李老师后面会有专门介绍GAN的课程,之后再做详细记录吧。)