前段时间研究了一段Zero Shot Learning
,研究了一些生成模型,总结一下备忘。
关于生成模型有一个很好的github仓库,而且作者的博客写的也非常精彩。
所谓生成模型,即,给定一些数据,用一个向量$x$表示,每一个datapoint对应一张图片或者一句话,生成模型的目标是学会$P(x)$,有了$P(x)$就可以从中sample,从而生成一些数据。
本文主要总结下GAN和VAE两种生成模型。
GAN
简介
GAN的原始paper是大神Goodfellow的《Generative Adversarial Nets》
先简介下模型的思想:
GAN包含:一个生成模型$G$和一个判别模型$D$,$G$的输入是一个随机扰动$z$,输出是生成的数据$\hat x$;$D$的输入是原始数据$x$或者$G$生成的数据$\hat x$,输出是输入来自训练数据的概率。
如果将$G$看作假币生产者,$D$看作警察,那么作假者的任务是尽量生产可以以假换真的假币,警察的任务是尽可能区分假币和真币,二者将不断地竞争直到生产者生产的假币完全以假乱真。
目标函数
$$\underset{G}{min}\underset{D}{max};V(D, G) = E_{x∼p_{data}(x)}[log
D(x)] + E_{z∼p_z(z)}[log(1 − D(G(z)))]$$
上面的目标函数修改自交叉熵损失函数。
算法流程
很多初次接触GAN的同学都会对GAN的训练感到迷惑,看下面的训练算法:
- 每轮训练迭代:
- 前k步,训练分类器:
- 从先验噪声:$p_g(z)$中采样m个噪声作为minibatch:${z^{(1)}, …, z^{(m)}}$。
- 从数据生成分布中$p_{data}(x)$采样m个真实数据作为minibatch:${x^{(1)}, …, x^{(m)}}$。
- 前k步,训练分类器:
- 使用随机梯度上升更新判别器的参数:
- $\bigtriangledown_{\theta_d}\frac{1}{m}\sum_{i=1}^m[logD(x^{(i)})+log(D(G(z^{(i)})))]$
- 从先验噪声:$p_g(z)$中采样m个噪声作为minibatch:${z^{(1)}, …, z^{(m)}}$。
- 使用随机梯度下降更新生成器的参数:
- $\bigtriangledown_{\theta_g}\frac{1}{m}\sum_{i=1}^mlog(D(G(z^{(i)})))$
虽然是同一个损失函数按照相反的方向训练,但训练判别器和生成器时针对的参数不同(上面算法中$\theta_d$和$\theta_g$),因此并不会出现南辕北辙的情况。
优缺点
- 缺点:没有明确的$p_g(x)$的表示;$D$的训练需要和$G$同步。
- 优点:采样时无需Markov chain;训练时无需inference,模型设计的范围更广。
代码
下面代码来自https://github.com/wiseodd/generative-models ,使用python3.5。
1 | %matplotlib inline |
1 | Extracting data/train-images-idx3-ubyte.gz |
生成效果
VAE
简介
关于VAE可以直接看这篇《Tutorial on Variational Autoencoders》。
先来看看普通的Auto-Encoder长什么样:
也就是$x$作为输入,要努力使输出$\tilde x$接近$x$,最终使用的是中间的隐层向量,作为一个输入$x$的特征。
VAE是结合了隐变量(latent variable)的Auto-Encoder。
先考虑隐变量,假设输入的是一张猫的图片,隐变量可以是“腿的个数”/“耳朵的大小”等。
那么原来的生成模型可以转换成:$P(X) = \int P(X \vert z) P(z) dz$,$z$是隐变量(向量)。
但是手动去设计$z$向量显然是不可操作的,于是VAE假设,$z$的某个维度并不能简单地解释,并且$z\sim N(0,I)$。即便有这样的先验假设,只要映射函数足够复杂($P(X|z)$),最终的$P(X)$也可以足够复杂。
但是这里还有一个问题,即,对于大多数的$z$,$P(X|z)=0$,直接去做积分是一个很没有必要的劳动。
于是有人提出,学一个$Q(z|X)$,这个$Q$可以根据某个$X$,返回可能的$z$的分布。我们希望这里的$z$是比较可能产生$X$的,即$P(X|z)>0$,并且这里的$z$的空间比一开始$z$的空间要小。这样的话,计算$E_{z\sim Q}P(X|z)$去代替上面的积分,会更简单。
下面计算$Q(z|X)$和$P(z|X)$的KL散度:
$$D_{KL}[Q(z \vert X)
\Vert P(z \vert X)] = \sum_z Q(z \vert X) , \log \frac{Q(z \vert X)}{P(z \vert
X)} $$
$$= E_{z\sim Q} \left[ \log \frac{Q(z \vert
X)}{P(z \vert X)} \right]$$
$$= E_{z\sim Q}[\log Q(z
\vert X) - \log P(z \vert X)]$$
$$= E_{z\sim Q} \left[
\log Q(z \vert X) - \log \frac{P(X \vert z) P(z)}{P(X)} \right]$$
$$= E_{z\sim Q}[\log Q(z \vert X) - (\log P(X \vert z) + \log P(z) - \log P(X))]$$
$$= E_{z\sim Q}[\log Q(z \vert X) - \log P(X \vert z) - \log P(z) + \log P(X)]$$
把$\log P(X)$移到左边:
$$\log P(X) - D_{KL}[Q(z \vert
X) \Vert P(z \vert X)] $$
$$= E_{z\sim Q}[\log P(X \vert z) - (\log Q(z \vert X) - \log P(z))]$$
$$= E_{z\sim Q}[\log P(X \vert z)] - E[\log Q(z \vert X) - \log P(z)] $$
$$= E_{z\sim Q}[\log P(X \vert z)] - D_{KL}[Q(z \vert X) \Vert P(z)]$$
于是我们得到了VAE的核心方程(目标函数):
$$\log P(X) - D_{KL}[Q(z \vert X) \Vert P(z \vert X)]$$
$$= E_{z\sim Q}[\log P(X \vert z)] - D_{KL}[Q(z \vert X) \Vert P(z)]$$
等式的左边:
- $\log P(X)$是对数似然函数,使我们要最大化的对象;
- $- D_{KL}[Q(z \vert X) \Vert P(z \vert X)]$,使用$Q(z \vert X)$代替$ P(z \vert X)$时的产生的误差项。于是左边可以理解成:我们要找一个$\log P(X)$的下界函数。
回顾下一开始介绍的Auto-Encoder,我们可以:
- 把$Q(z \vert X)$当做Encoder;
- 把z当做隐层;
- 把$P(X \vert z)$当做Decoder。
等式的右边:这部分是我们可以用随机梯度下降去优化的。
- $E_{z\sim Q}[\log P(X \vert z)]$是Decoder的目标函数,即如果把z理解成输入,X是输出,则$E_{z\sim Q}[\log P(X \vert z)]$是极大似然函数;
- $-D_{KL}[Q(z \vert X) \Vert P(z)]$是Encoder的目标函数,要训练一个使$Q(z \vert X)$尽可能接近$P(z)$的Encoder。
之前我们已经限定$z\sim N(0, I)$,现在再限定$Q(z \vert X)$服从参数是$\mu(X)$和$\Sigma(X)$的高斯分布。这时,右边第二项是有准确表达式的:
$$D_{KL}[N(\mu(X),\Sigma(X)) \Vert N(0, 1)] = \frac{1}{2} \sum_k \left( \exp(\Sigma(X)) + \mu^2(X) - 1 - \Sigma(X) \right)$$
至于右边第一个表达式,我们可以使用二次损失函数替代。
这里为了训练的方便,使用了重参数技巧(reparameterization trick),即,引入一个随机性变量:$\epsilon \sim N(0,1)$,$z = \mu(X) + \Sigma^{\frac{1}{2}}(X) , \epsilon$,这样使得网络反向传播时没有涉及到随机变量。
代码
1 | import torch |
1 | Extracting data/train-images-idx3-ubyte.gz |