VAE是一个数据生成模型, 可以用于生成和训练数据相似的数据. 本文将会依照论文Auto-Encoding Variational Bayes的思路, 推导VAE的原理, 并使用VAE生成MNIST数据集中的手写数字图片.
MNIST数据集中的原始图片如下:
使用VAE生成的图片如下:
代码实现以及注释在VAE/main.ipynb中.
KL散度是衡量两个分布之间差异的度量。对于分布P和Q,KL散度定义为:
注意:
- KL散度不满足对称性,即
$D_{KL}(P||Q) \neq D_{KL}(Q||P)$ 。 - KL散度非负,即
$D_{KL}(P||Q) \geq 0$ 。 - 当且仅当
$P=Q$ 时,$D_{KL}(P||Q)=0$ 。
首先是问题定义,假设我们有一个数据集
假设这个数据集的每个样本是从一个随机过程中生成的,这个随机过程是这样的:
- 首先从隐变量分布
$p_{\theta}(z)$ 中选择一个隐变量$z^{(i)}$ ,我们假设这里的隐变量分布$p_{\theta}(z)$ 是一个连续分布。 - 然后根据隐变量
$z^{(i)}$ ,生成一个数据$x^{(i)}$ ,这个数据$x^{(i)}$ 服从一个条件分布$p_{\theta}(x|z=z^{(i)})$ 。
理论上根据前面的假设,那么我们可以计算出数据集中每个样例出现的概率为
为什么我们要求出模型的参数
$\theta$ 呢?因为求出$\theta$ 后,我们就可以重复上面的随机过程,不断从$p_{\theta}(z)$ 中采样隐变量$\hat{z}$ , 然后从$p_{\theta}(x|z=\hat{z})$ 采样出$\hat{x}$ , 从而不断生成新的数据(比如图片)。
但是实际上没有这么完美,VAE要解决的问题是
为此我们引入分布
在VAE中
$q_{\phi}(z|x)$ 扮演encoder的角色,$q_{\phi}(z|x)$ 将一个样例数据映射到一个隐变量的分布。而$p_{\theta}(x|z)$ 扮演decoder的角色,$p_{\theta}(x|z)$ 将一个隐变量映射回样例数据。在实际代码中,$q_{\phi}(z|x)$ 和$p_{\theta}(x|z)$ 都是神经网络。
根据上面的推导,既然
$$ \begin{aligned} D_{KL}(q_{\phi}(z|x^{(i)})||p_{\theta}(z|x^{(i)})) &= \int q_{\phi}(z|x^{(i)}) \log \frac{q_{\phi}(z|x^{(i)})}{p_{\theta}(z|x^{(i)})} dz \ &= \int q_{\phi}(z|x^{(i)}) \log q_{\phi}(z|x^{(i)}) dz - \int q_{\phi}(z|x^{(i)}) \log p_{\theta}(z|x^{(i)}) dz \ &= \int q_{\phi}(z|x^{(i)}) \log q_{\phi}(z|x^{(i)}) dz - \int q_{\phi}(z|x^{(i)}) \log \frac{p_{\theta}(z, x^{(i)})}{p_{\theta}(x^{(i)})} dz \ &= \int q_{\phi}(z|x^{(i)}) \log q_{\phi}(z|x^{(i)}) dz - \int q_{\phi}(z|x^{(i)}) \log p_{\theta}(z, x^{(i)}) dz + \int q_{\phi}(z|x^{(i)}) \log p_{\theta}(x^{(i)}) dz \ &= \int q_{\phi}(z|x^{(i)}) \log q_{\phi}(z|x^{(i)}) dz - \int q_{\phi}(z|x^{(i)}) \log p_{\theta}(z, x^{(i)}) dz + \log p_{\theta}(x^{(i)}) \ &= \int q_{\phi}(z|x^{(i)}) \log \frac{q_{\phi}(z|x^{(i)})}{p_{\theta}(z, x^{(i)})} dz + \log p_{\theta}(x^{(i)}) \
\text{移项可得:} \ \log p_{\theta}(x^{(i)}) &= D_{KL}(q_{\phi}(z|x^{(i)})||p_{\theta}(z|x^{(i)})) - \int q_{\phi}(z|x^{(i)}) \log \frac{q_{\phi}(z|x^{(i)})}{p_{\theta}(z, x^{(i)})} dz \ &= D_{KL}(q_{\phi}(z|x^{(i)})||p_{\theta}(z|x^{(i)})) + \int q_{\phi}(z|x^{(i)}) \log \frac{p_{\theta}(z, x^{(i)})}{q_{\phi}(z|x^{(i)})} dz \ &= D_{KL}(q_{\phi}(z|x^{(i)})||p_{\theta}(z|x^{(i)})) + \mathcal{L}(\theta, \phi; x^{(i)}) \
\text{其中:} \ \mathcal{L}(\theta, \phi; x^{(i)}) &= \int q_{\phi}(z|x^{(i)}) \log \frac{p_{\theta}(z, x^{(i)})}{q_{\phi}(z|x^{(i)})} dz \ &= \mathbb{E}{z \sim q{\phi}(z|x^{(i)})} [\log p_{\theta}(z, x^{(i)}) - \log q_{\phi}(z|x^{(i)})]
\end{aligned} $$
其中 $\mathbb{E}{z \sim q{\phi}(z|x^{(i)})} [\log p_{\theta}(z, x^{(i)}) - \log q_{\phi}(z|x^{(i)})]$ 的意思是
注意到上面公式中
而我们原本的目标是要最大化
到此我们已经推导出了VAE原论文中的公式(1)和(2)。
我们继续来看看
这里对应VAE原论文中的公式(3)。
现在我们暂时从VAE的推导中抽离出来,来看看如何最大化一个含有参数
情况一:
即:期望的导数等于导数的期望。
情况二:
还有一种情况三, 就是
现在我们尝试对
论文中提到,直接使用蒙特卡洛的方法对该梯度进行估计具有很大的方差,并不可行。
要解决这个问题,论文中提出了重参数化技巧。 思考一下期望 $\mathbb{E}{z \sim q{\phi}(z|x^{(i)})} f(z)$ 的计算过程,首先要使用参数
要实现这个操作,我们需要构造一个分布
这个部分搭配论文2.4章节的例子会更好理解,原论文放得太远了: 假设
$q_{\phi}(z|x^{(i)})$ 是高斯分布,也就是说使用参数$\phi$ ,对于输入$x^{(i)}$ 会计算出一个高斯分布的均值$\mu$ 和方差$\sigma^2$ ,原来的计算逻辑是从$\mathcal{N}(\mu, \sigma^2)$ 中采样$z$ ,然后计算$f(z)$ 的平均值。 现在,我们引入一个标准正态分布$p(\epsilon) = \mathcal{N}(0, 1)$ ,从这个分布中采样$\epsilon$ ,然后根据$z = \mu + \sigma \epsilon$ 计算出$z$ ,最后计算$f(z)$ 的平均值,可以证明这样算出来的期望和前一种算法的结果是一样的。 注意原本我们用$\phi$ 计算出$\mu$ 和$\sigma$ 之后,是需要从$\mathcal{N}(\mu, \sigma^2)$ 中采样,但是现在我们只是用$\mu$ 和$\sigma$ 参与一个表达式计算出$z$ ,采样被移动到和参数$\phi$ 无关的部分了。
现在,使用蒙特卡洛算法,我们可以这样计算期望:
论文中写蒙特卡洛算法显得比较高大上, 但是这里其实就是随机采样
$L$ 次, 然后用来估计$f(g_{\phi}(\epsilon^{(l)}, x^{(i)}))$ 的值.
现在让我们回到
带入重参数化技巧之后:
带入重参数化技巧之后:
借助这两个公式,我们都可以使用蒙特卡洛采样来估计
比如我们规定
$q_{\phi}(z|x^{(i)})$ 和$p_{\theta}(z)$ 都是高斯分布,那么我们就可以直接计算KL散度和梯度。
最后,如果我们要计算包含
这里实际的意思是, 我们可以从样例数据中随机采样一个batch来估计
$\mathcal{L}(\theta, \phi; X)$ 并且对它进行调优, 实际上是深度学习中很常用的一个技巧, 不过论文为了显得严谨, 啥都要列公式证明一下。
其中的
此外,如果我们采样的数据量
代码实现以及注释在VAE/main.ipynb中.
MNIST是著名的手写字识别数据集,数据集中的每个样例数据是一个28x28的灰度图,每个像素的取值范围是0到255。 这里我们为了方便处理,将每个像素除以255变成0到1之间的值。并且在之后的模型中会将每张图片处理成一个784维的一维向量。
我们在MNIST数据集上训练VAE会训练一个从图片到隐变量的encoder
训练VAE之前,首先我们需要做一些约定:
- 约定隐变量
$z$ 的维度为$J$ ,即$z \in \mathbb{R}^J$ 。并且隐变量$z$ 的分布$p_{\theta}(z)$ 是一个$J$ 维相互独立,均值为0方差为1的高斯分布,即$p_{\theta}(z) = \mathcal{N}(0, I)$ 。这个分布中并没有需要学习的参数,因此也可以直接写成$p(z) = \mathcal{N}(0, I)$ 。 - 对于encoder
$q_{\phi}(z|x)$ ,我们使用一个简单的两层神经网络来近似(一层全连接+激活函数+一层全连接)。我们约定$q_{\phi}(z|x)$ 也是一个高斯分布,因此encoder对于每张输入的图片会输出两个$J$ 维的向量,分别表示均值$\mu$ 和方差$\sigma^2$ ,描述一个高斯分布$\mathcal{N}(\mu, \sigma^2 I)$ ,不同维度之间相互独立。 - 对于decoder
$p_{\theta}(x|z)$ ,我们同样使用一个简单的两层神经网络来近似(一层全连接+激活函数+一层全连接+sigmoid)。最后的sigmoid激活函数保证了输出值在0到1之间,因此decoder输出的是一个784维的向量,表示每个像素是白色的概率。
由于
此外在代码中还有一些细节, 比如重参数化的代码实现, 比如由于

