变分自编码器(VAE) 直观推导
最近一周系统的看下概率论的东西,把公式都看了下,这次重新对VAE
做一个直观的推导,我这里就不说VAE
为什么要这么做(水平不够),只说他是怎么做的。
编码器网络结构
一般的编码器网络,是通过构造隐变量z,学习从x→z的编码器,以及从z→˜x的解码器。但是他们的损失函数只是简单的(x−˜x)2或者p(x)logp(˜x),最终缺乏一个生成的效果。
变分自编码器结构
VAE
的思想说白了就是为了得到生成的效果,给隐变量z制造不确定性,然后就使用到了概率论的方案。让z成为一种概率分布,那么训练完成之后,只要给出不同的z就可以得到不同的˜x,增加了生成性。
下面就是VAE
的公式。使用神经网络拟合编码器p(z|x)和解码器p(˜x|z),用KL散度
使隐变量z的分布接近于标准正态分布,用交叉熵使生成样本相似与原始样本。这里其实很巧妙,如果z只有均值且为0,那也就是和以前的编码器一样,没有生成效果,但是z还有方差项,可以提供噪声来保证生成能力。
p(z)=p(z|x)p(x) Encoderp(˜x)=p(˜x|z)p(z) Decoder∵
损失函数计算
重构损失
\mathcal{L}_{re}计算很简单,直接使用tf.nn.sigmoid_cross_entropy_with_logits
即可。
KL损失
这个需要好好推导: \begin{aligned} &KL(p({z}|x)\| N(0,1))=\int p({z}|x)\ \log\frac{p({z}|x)}{N(0,1)}\ dz \\ &=\int p({z}|x)\ \log \frac{\frac{1}{\sqrt{2\pi \sigma^2}}e^{-\frac{(z-\mu)^2}{2\sigma^2}}}{\frac{1}{\sqrt{2\pi}}e^{-\frac{z^2}{2}}}\ dz \\ &=\int p({z}|x)\ [\log\frac{1}{\sqrt{\sigma^2}}+\log e^{\frac{1}{2}(z^2-\frac{(z-\mu)^2}{\sigma^2})}]\ dz\\ &=\int p({z}|x)\ [-\frac{1}{2}\log \sigma^2+\frac{1}{2}(z^2-\frac{(z-\mu)^2}{\sigma^2})]\ dz \\ &=\int p({z}|x)\ [-\frac{1}{2}\log \sigma^2+\frac{1}{2}(z^2-\frac{(z-\mu)^2}{\sigma^2})]\ dz \\ &=\frac{1}{2}[-\int p({z}|x)\ \log \sigma^2 \ dz +\int p({z}|x)\ z^2\ dz-\int p({z}|x)\ \frac{(z-\mu)^2}{\sigma^2}\ dz] \\ &=\frac{1}{2}[-\log\sigma^2+E(z^2)-\frac{D(z)}{\sigma^2}] \\ &=\frac{1}{2}(-\log\sigma^2+\mu^2+\sigma^2-1) \end{aligned}
注意: 上面的p({z}|x)其实就是正态分布的概率,所以\int p({z}|x)\ dz=1。后面两个就是求z^2的期望,和z的方差。
重参数技巧
这个其实和算法没多大关系,就是因为随机采样的操作无法求导,只能对采样出来的值求导。所以就使用如下技巧:
从N(\mu,\sigma^2)中采样一个z,相当于从N(0,1)中采样一个\epsilon,然后让z=\mu+\epsilon\times\sigma。
这样就可以直接对值求导即可,概念图如下
代码
代码运行环境为Tensorflow 1.14
:
import tensorflow.python as tf |