Tensorflow下VAE(变分自动编码器)在MNIST数据集下的实验

2024-03-01 03:58

本文主要是介绍Tensorflow下VAE(变分自动编码器)在MNIST数据集下的实验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先简单介绍一下AE和VAE然后在完成代码实践

一、什么是自编码器(Auto-encoder)

自动编码器是一种数据的压缩算法,其中数据的压缩和解压缩函数是数据相关的、有损的、从样本中自动学习的。在大部分提到自动编码器的场合,压缩和解压缩的函数是通过神经网络实现的。

这种算法的大致思想是:将神经网络的隐含层看成是一个编码器和解码器,输入数据经过隐含层的编码和解码,到达输出层时,确保输出的结果尽量与输入数据保持一致。也就是说,隐含层是尽量保证输出数据等于输入数据的。  这样做的一个好处是,隐含层能够抓住输入数据的特点,使其特征保持不变。例如,假设输入层有100个神经元,隐含层只有50个神经元,输出层有100个神经元,通过自动编码器算法,我们只用隐含层的50个神经元就找到了100个输入层数据的特点,能够保证输出数据和输入数据大致一致,就大大降低了隐含层的维度。

既然隐含层的任务是尽量找输入数据的特征,也就是说,尽量用最少的维度来代表输入数据,因此,我们可以想象,隐含层各层之间的参数构成的参数矩阵,应该尽量是个稀疏矩阵,即各层之间有越多的参数为0就越好。

1)自动编码器是数据相关的(data-specific 或 data-dependent),这意味着自动编码器只能压缩那些与训练数据类似的数据。比如,使用人脸训练出来的自动编码器在压缩别的图片,比如树木时性能很差,因为它学习到的特征是与人脸相关的。

2)自动编码器是有损的,意思是解压缩的输出与原来的输入相比是退化的,MP3,JPEG等压缩算法也是如此。这与无损压缩算法不同。

3)自动编码器是从数据样本中自动学习的,这意味着很容易对指定类的输入训练出一种特定的编码器,而不需要完成任何新工作。

搭建一个自动编码器需要完成下面三样工作:搭建编码器,搭建解码器,设定一个损失函数,用以衡量由于压缩而损失掉的信息。编码器和解码器一般都是参数化的方程,并关于损失函数可导,典型情况是使用神经网络。编码器和解码器的参数可以通过最小化损失函数而优化,例如SGD。

自编码器是一个自监督的算法,并不是一个无监督算法。自监督学习是监督学习的一个实例,其标签产生自输入数据。要获得一个自监督的模型,你需要一个靠谱的目标跟一个损失函数,仅仅把目标设定为重构输入可能不是正确的选项。基本上,要求模型在像素级上精确重构输入不是机器学习的兴趣所在,学习到高级的抽象特征才是。事实上,当主要任务是分类、定位之类的任务时,那些对这类任务而言的最好的特征基本上都是重构输入时的最差的那种特征。

目前自编码器的应用主要有两个方面,第一是数据去噪,第二是为进行可视化而降维。配合适当的维度和稀疏约束,自编码器可以学习到比PCA等技术更有意思的数据投影。

二、什么是变分自编码器(Variational Auto-Encoder)

VAEs简介

变分自编码器(Variational auto-encoder,VAE)是一类重要的生成模型(generative model),它于2013年由Diederik P.Kingma和Max Welling提出[1]。2016年Carl Doersch写了一篇VAEs的tutorial[2],对VAEs做了更详细的介绍,比文献[1]更易懂。这篇读书笔记基于文献[1]。

除了VAEs,还有一类重要的生成模型GANs(对GANs感兴趣可以去我的微信公众号看介绍文章:学术兴趣小组)。

我们来看一下VAE是怎样设计的。



上图是VAE的图模型。我们能观测到的数据是\text{x},而\text{x}由隐变量\text{z}产生,由\text{z}\rightarrow \text{x}是生成模型p_{\theta}(\text{x}|\text{z}),从自编码器(auto-encoder)的角度来看,就是解码器;而由\text{x}\rightarrow \text{z}是识别模型(recognition model)q_{\phi}(\text{z}|\text{x}),类似于自编码器的编码器。

VAEs现在广泛地用于生成图像,当生成模型p_{\theta}(\text{x}|\text{z})训练好了以后,我们就可以用它来生成图像了。与GANs不同的是,我们是知道图像的密度函数(PDF)的(或者说,是我们设定的),而GANs我们并不知道图像的分布。


VAEs模型的理论推导

以下的推导参考了文献[1]和[3],文献[3]是变分推理的课件。

首先,假定所有的数据都是独立同分布的(i.i.d),两个观测不会相互影响。我们要对生成模型p_{\theta}(\text{x}|\text{z})做参数估计,利用对数最大似然法,就是要最大化下面的对数似然函数:

\log p_{\theta}(\text{x}^{(1)},\text{x}^{(2)},\cdots,\text{x}^{(N)})=\sum_{i=1}^N \log p_{\theta}(\text{x}^{(i)})

VAEs用识别模型q_{\phi}(\text{z}|\text{x}^{(i)})去逼近真实的后验概率p_{\theta}(\text{z}|\text{x}^{(i)}),衡量两个分布的相似程度,我们一般采用KL散度,即

\begin{align} KL(q_{\phi}(\text{z}|\text{x}^{(i)})||p_{\theta}(\text{z}|\text{x}^{(i)}))&=\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log \frac{q_{\phi}(\text{z}|\text{x}^{(i)})}{p_{\theta}(\text{z}|\text{x}^{(i)})}\\ &=\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log \frac{q_{\phi}(\text{z}|\text{x}^{(i)})p_{\theta}(\text{x}^{(i)})}{p_{\theta}(\text{z}|\text{x}^{(i)})p_{\theta}(\text{x}^{(i)})}\\ &=\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log \frac{q_{\phi}(\text{z}|\text{x}^{(i)})}{p_{\theta}(\text{z},\text{x}^{(i)})}+\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log p_{\theta}(\text{x}^{(i)})\\ &=\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log \frac{q_{\phi}(\text{z}|\text{x}^{(i)})}{p_{\theta}(\text{z},\text{x}^{(i)})}+\log p_{\theta}(\text{x}^{(i)}) \end{align}

于是

\log p_{\theta}(\text{x}^{(i)})=KL(q_{\phi}(\text{z}|\text{x}^{(i)}), p_{\theta}(\text{z}|\text{x}^{(i)}))+\mathcal{L}(\theta,\phi;\text{x}^{(i)})

其中,

\begin{align} \mathcal{L}(\theta,\phi;\text{x}^{(i)})& = -\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log \frac{q_{\phi}(\text{z}|\text{x}^{(i)})}{p_{\theta}(\text{z},\text{x}^{(i)})}\\ &=\mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log p_{\theta}(\text{z}, \text{x}^{(i)}) - \mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log q_{\phi}(\text{z}|\text{x}^{(i)}) \end{align}

由于KL散度非负,当两个分布一致时(允许在一个零测集上不一致),KL散度为0。于是\log p_{\theta}(\text{x}^{(i)}) \geq \mathcal{L}(\theta,\phi;\text{x}^{(i)})\mathcal{L}(\theta,\phi;\text{x}^{(i)})称为对数似然函数的变分下界。

直接优化\log p_{\theta}(\text{x}^{(i)})是不可行的,因此一般转而优化它的下界\mathcal{L}(\theta,\phi;\text{x}^{(i)})。对应的,优化对数似然函数转化为优化\mathcal{L}(\theta,\phi;\text{X})=\sum_{i=1}^N \mathcal{L}(\theta,\phi;\text{x}^{(i)})

作者指出,\mathcal{L}(\theta,\phi;\text{x}^{(i)})\phi的梯度方差很大,不适于用于数值计算。为了解决这个问题,假定识别模型q_{\phi}(\text{z}|\text{x})可以写成可微函数g_{\phi}(\epsilon, \text{x}),其中,\epsilon为噪声,\epsilon \sim p(\epsilon)。于是,\mathcal{L}(\theta,\phi;\text{x}^{(i)})可以做如下估计(利用蒙特卡罗方法估计期望):

\mathcal{\tilde{L}}^A(\theta,\phi;\text{x}^{(i)})=\frac{1}{L}\sum_{l=1}^L [\log p_{\theta}(\text{x}^{(i)}, \text{z}^{(i,l)}) - \log q_{\phi}(\text{z}^{(i,l)}|\text{x}^{(i)})]

其中,\text{z}^{(i,l)}=g_{\phi}(\epsilon^{(i,l)}, \text{x}^{(i)}), \quad \epsilon^{(i,l)} \sim p(\epsilon)

此外,\mathcal{L}(\theta,\phi;\text{x}^{(i)})还可以改写为

\mathcal{L}(\theta,\phi;\text{x}^{(i)})=-KL(q_{\phi}(\text{z}|\text{x}^{(i)})||p_{\theta}(\text{z})) + \mathbb{E}_{q_{\phi}(\text{z}|\text{x}^{(i)})} \log p_{\theta}(\text{x}^{(i)}|\text{z})

由此可以得到另外一个估计

\mathcal{\tilde{L}}^B(\theta, \phi; \text{x}^{(i)})=-KL(q_{\phi}(\text{z}|\text{x}^{(i)})||p_{\theta}(\text{z})) +\frac{1}{L} \sum_{l=1}^L \log p_{\theta}(\text{x}^{(i)}|\text{z}^{(i,l)})

其中,\text{z}^{(i,l)}=g_{\phi}(\epsilon^{(i,l)}, \text{x}^{(i)}), \quad \epsilon^{(i,l)} \sim p(\epsilon)

实际试验时,如果样本量N很大,我们一般采用minibatch的方法进行学习,对数似然函数的下界可以通过minibatch来估计:

\mathcal{L}(\theta,\phi;\text{X})\simeq \mathcal{\tilde{L}}^M (\theta,\phi;\text{X}^M)=\frac{N}{M}\sum_{i=1}^M \mathcal{\tilde{L}}(\theta,\phi;\text{x}^{(i)})

可以看到,为了计算\mathcal{L}(\theta,\phi;\text{X}),我们用了两层估计。当M较大时,内层估计可以由外层估计来完成,也就是说,取L=1即可。实际计算中,作者取M=100,L=1。由上述推导得到AEVB算法:

VAEs模型

上面给的AEVB算法是一个算法框架,只有给定了\epsilon, p_{\theta}(\text{x}|\text{z}), q_{\phi}(\text{z}|\text{x}), p_{\theta}(\text{z})分布的形式以及g_{\phi}(\epsilon, \text{x}),我们才能启动算法。实际应用中,作者取

\begin{align} p(\epsilon) &= \mathcal{N}(\epsilon; 0,\text{I})\\ q_{\phi}(\text{z}|\text{x}^{(i)}) &= \mathcal{N}(\text{z}; {\mu}^{(i)}, {\sigma}^{2(i)}\text{I})\\ p_{\theta}(\text{z})&=\mathcal{N}(\text{z}; 0,\text{I})\\ g_{\phi}(\epsilon^{(l)}, \text{x}^{(i)}) &= {\mu}^{(i)}+{\sigma}^{(i)}\odot \epsilon^{(l)} \end{align}

p_{\theta}(\text{x}|\text{z})根据样本是实值还是二元数据进行选择,若样本为二元数据,则选择

p_{\theta}(x_i|\text{z})=\mathcal{B}(x_i;1,y_i)=y_i^{x_i}\cdot (1-y_i)^{1-x_i}, \quad i=1,2,\cdots,D_{\text x}(D_{\text x}=\dim(\text{x}))

若样本是实值数据,则选择

p_{\theta}(\text{x}^{(i)}|\text{z})=\mathcal{N}(\text{x}^{(i)}; \mu'^{(i)},\sigma'^{2(i)}\text{I})

实验中,作者选择多层感知器(MLP)对p_{\theta}(\text{x}|\text{z}), q_{\phi}(\text{z}|\text{x})进行拟合,具体来说,

p_{\theta}(\text{x}|\text{z}),参数为\theta=(\mu', \sigma'),若样本为二元数据,则

\begin{align} \log p(\text{x}|\text{z}) &= \sum_{i=1}^{D_\text x} x_i \log y_i + (1-x_i)\cdot \log (1-y_i)\\ \text{y}&=\text{sigmoid}(\text W_2 \tanh(\text W_1\text{z} + \text b_1) + \text b_2) \end{align}

若样本为实值数据,则

\begin{align} \mu' &= \text{W}_4\text{h}'+\text{b}_4 \\ \sigma' &= \text W_5\text{h}' + \text{b}_5\\ \text{h}' &= \tanh(\text W_3 \text{z} + \text b_3) \end{align}

q_{\phi}(\text{z}|\text{x}),参数为\phi=(\mu, \sigma)

\begin{align} \mu &= \text{W}_7\text{h}+\text{b}_7 \\ \sigma &= \text W_8\text{h} + \text{b}_8\\ \text{h} &= \tanh(\text W_6 \text{x} + \text b_6) \end{align}

根据以上假设的分布,不难计算

\mathcal{L}(\theta,\phi;\text{x}^{(i)}) \simeq \frac{1}{2}\sum_{j=1}^{D_\text z}(1 + \log ((\sigma_j^{(i)})^2) - (\mu_j^{(i)})^2 - (\sigma_j^{(i)})^2) + \frac{1}{L}\sum_{l=1}^L \log p_{\theta}(\text{x}^{(i)} | \text{z}^{(i,l)})

其中,\text{z}^{(i,l)}=\mu^{(i)}+\sigma^{(i)} \odot\epsilon^{(l)}, \quad \epsilon^{(l)} \sim p(\epsilon)

最后,我们从auto-encoder的角度来理解VAE,下图给出了VAE训练的时候的网络结构(以实值样本为例,注意下面两个图中的\epsilon节点并不是bias!而是噪声变量,它的维数与\text z相同。):

训练好了以后,生成样本采用下面的网络结构:

VAE实验效果

作者在Frey face数据集和MNIST数据集上进行实验,实验得到的数据流形分布如下图所示,可以看出,VAE能够捕捉到图像的结构变化(倾斜角度、圈的位置、形状变化、表情变化等)。这也是VAE的一个好处,它有显式的分布,能够容易地可视化图像的分布。GANs虽然不具有显式的图像分布,但是可以通过对隐变量的插值变化来可视化图像的分布(参见DCGAN)。


VAE在不同维数的隐变量空间(\text z)下生成手写数字的效果如下:

可以看出,采用MLP也能产生效果还不错的数字,有趣的是,隐变量维数较低时,生成的图像笔画清晰,但是带有较大的噪声(模糊);隐变量维数高时,生成的数字部分笔画不清晰,但噪声小。

三、VAE在Tensorflow下的代码实现

第一种代码
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data')

tf.reset_default_graph()

batch_size = 64
X_in = tf.placeholder(dtype=tf.float32, shape=[None, 28, 28], name='X')
Y = tf.placeholder(dtype=tf.float32, shape=[None, 28, 28], name='Y')
Y_flat = tf.reshape(Y, shape=[-1, 28 * 28])
keep_prob = tf.placeholder(dtype=tf.float32, shape=(), name='keep_prob')

dec_in_channels = 1
n_latent = 8
reshaped_dim = [-1, 7, 7, dec_in_channels]
inputs_decoder = 49 * dec_in_channels / 2


def lrelu(x, alpha=0.3):
    return tf.maximum(x, tf.multiply(x, alpha))


def encoder(X_in, keep_prob):
    activation = lreluwith tf.variable_scope("encoder", reuse=None):
        X = tf.reshape(X_in, shape=[-1, 28, 28, 1])
        x = tf.layers.conv2d(X, filters=64, kernel_size=4, strides=2, padding='same', activation=activation)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d(x, filters=64, kernel_size=4, strides=2, padding='same', activation=activation)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d(x, filters=64, kernel_size=4, strides=1, padding='same', activation=activation)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.contrib.layers.flatten(x)
        mn = tf.layers.dense(x, units=n_latent)
        sd       = 0.5 * tf.layers.dense(x, units=n_latent)
        epsilon = tf.random_normal(tf.stack([tf.shape(x)[0], n_latent]))
        z  = mn + tf.multiply(epsilon, tf.exp(sd))
        return z, mn, sddef decoder(sampled_z, keep_prob):
    with tf.variable_scope("decoder", reuse=None):
        x = tf.layers.dense(sampled_z, units=inputs_decoder, activation=lrelu)
        x = tf.layers.dense(x, units=inputs_decoder * 2 + 1, activation=lrelu)
        x = tf.reshape(x, reshaped_dim)
        x = tf.layers.conv2d_transpose(x, filters=64, kernel_size=4, strides=2, padding='same', activation=tf.nn.relu)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d_transpose(x, filters=64, kernel_size=4, strides=1, padding='same', activation=tf.nn.relu)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d_transpose(x, filters=64, kernel_size=4, strides=1, padding='same', activation=tf.nn.relu)

        x = tf.contrib.layers.flatten(x)
        x = tf.layers.dense(x, units=28 * 28, activation=tf.nn.sigmoid)
        img = tf.reshape(x, shape=[-1, 28, 28])
        return imgsampled, mn, sd = encoder(X_in, keep_prob)
dec = decoder(sampled, keep_prob)

unreshaped = tf.reshape(dec, [-1, 28*28])
img_loss = tf.reduce_sum(tf.squared_difference(unreshaped, Y_flat), 1)
latent_loss = -0.5 * tf.reduce_sum(1.0 + 2.0 * sd - tf.square(mn) - tf.exp(2.0 * sd), 1)
loss = tf.reduce_mean(img_loss + latent_loss)
optimizer = tf.train.AdamOptimizer(0.0005).minimize(loss)
sess = tf.Session()
sess.run(tf.global_variables_initializer())

for i in range(30000):
    batch = [np.reshape(b, [28, 28]) for b in mnist.train.next_batch(batch_size=batch_size)[0]]
    sess.run(optimizer, feed_dict = {X_in: batch, Y: batch, keep_prob: 0.8})
    if not i % 200:
        ls, d, i_ls, d_ls, mu, sigm = sess.run([loss, dec, img_loss, dst_loss, mn, sd], feed_dict = {X_in: batch, Y: batch, keep_prob: 1.0})
        plt.imshow(np.reshape(batch[0], [28, 28]), cmap='gray')
        plt.show()
        plt.imshow(d[0], cmap='gray')
        plt.show()
        print(i, ls, np.mean(i_ls), np.mean(d_ls))

randoms = [np.random.normal(0, 1, n_latent) for _ in range(10)]
imgs = sess.run(dec, feed_dict = {sampled: randoms, keep_prob: 1.0})
imgs = [np.reshape(imgs[i], [28, 28]) for i in range(len(imgs))]
for img in imgs:
    plt.figure(figsize=(1,1))
    plt.axis('off')
    plt.imshow(img, cmap='gray')

第一步加载训练数据


首先我们来执行一些基本的导入操作。TensorFlow 具有非常便利的函数来让我们能够很容易地访问 MNIST 数据集。

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data')
定义输入数据和输出数据

MNIST 图像的维度是 28*28 像素,只有单色通道。我们的输入数据 X_in 是一批一批的 MNIST 字符,网络会学习如何重建它们。然后在一个占位符 Y 中输出它们,输出和输入具有相同的维度。

Y_flat 将会在后面计算损失函数的时候用到,keep_prob 将会在应用 dropout 的时候用到(作为一种正则化的方法)。在训练的过程中,它的值会设为 0.8,当生成新数据的时候,我们不使用 dropout,所以它的值会变成 1。

lrelu 函数需要自及定义,因为 TensorFlow 中并没有预定义一个 Leaky ReLU 函数

tf.reset_default_graph()

batch_size = 64
X_in = tf.placeholder(dtype=tf.float32, shape=[None, 28, 28], name='X')
Y = tf.placeholder(dtype=tf.float32, shape=[None, 28, 28], name='Y')
Y_flat = tf.reshape(Y, shape=[-1, 28 * 28])
keep_prob = tf.placeholder(dtype=tf.float32, shape=(), name='keep_prob')

dec_in_channels = 1
n_latent = 8
reshaped_dim = [-1, 7, 7, dec_in_channels]
inputs_decoder = 49 * dec_in_channels / 2


def lrelu(x, alpha=0.3):
    return tf.maximum(x, tf.multiply(x, alpha))

定义编码器


因为我们的输入是图像,所以使用一些卷积变换会更加合理。最值得注意的是我们在编码器中创建了两个向量,因为编码器应该创建服从高斯分布的对象。

  • 一个是均值向量
  • 一个是标准差向量

在后面你会看到,我们是如何「强制」编码器来保证它确实生成 了服从正态分布的数据点,我们可以把将会被输入到解码器中的编码值表示为 z。在计算损失函数的时候,我们会需要我们所选分布的均值和标准差

def encoder(X_in, keep_prob):
    activation = lreluwith tf.variable_scope("encoder", reuse=None):
        X = tf.reshape(X_in, shape=[-1, 28, 28, 1])
        x = tf.layers.conv2d(X, filters=64, kernel_size=4, strides=2, padding='same', activation=activation)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d(x, filters=64, kernel_size=4, strides=2, padding='same', activation=activation)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d(x, filters=64, kernel_size=4, strides=1, padding='same', activation=activation)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.contrib.layers.flatten(x)
        mn = tf.layers.dense(x, units=n_latent)
        sd       = 0.5 * tf.layers.dense(x, units=n_latent)
        epsilon = tf.random_normal(tf.stack([tf.shape(x)[0], n_latent]))
        z  = mn + tf.multiply(epsilon, tf.exp(sd))
        return z, mn, sd

定义解码器


解码器不会关心输入值是不是从我们定义的某个特定分布中采样得到的。它仅仅会尝试重建输入图像。最后,我们使用了一系列的转置卷积(transpose convolution)。

def decoder(sampled_z, keep_prob):
    with tf.variable_scope("decoder", reuse=None):
        x = tf.layers.dense(sampled_z, units=inputs_decoder, activation=lrelu)
        x = tf.layers.dense(x, units=inputs_decoder * 2 + 1, activation=lrelu)
        x = tf.reshape(x, reshaped_dim)
        x = tf.layers.conv2d_transpose(x, filters=64, kernel_size=4, strides=2, padding='same', activation=tf.nn.relu)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d_transpose(x, filters=64, kernel_size=4, strides=1, padding='same', activation=tf.nn.relu)
        x = tf.nn.dropout(x, keep_prob)
        x = tf.layers.conv2d_transpose(x, filters=64, kernel_size=4, strides=1, padding='same', activation=tf.nn.relu)

        x = tf.contrib.layers.flatten(x)
        x = tf.layers.dense(x, units=28 * 28, activation=tf.nn.sigmoid)
        img = tf.reshape(x, shape=[-1, 28, 28])
        return img

现在,我们将两部分连在一起。

sampled, mn, sd = encoder(X_in, keep_prob)
dec = decoder(sampled, keep_prob)

计算损失函数,并隐藏一个分布

为了计算图像重构的损失函数,我们简单地使用了平方差(这有时候会使图像变得有些模糊)。这个损失函数还结合了 KL 散度,这确保了我们的隐藏值将会从一个标准分布中采样。关于这个主题,如果想要了解更多,可以看一下这篇文章(https://jaan.io/what-is-variational-autoencoder-vae-tutorial/)。

unreshaped = tf.reshape(dec, [-1, 28*28])
img_loss = tf.reduce_sum(tf.squared_difference(unreshaped, Y_flat), 1)
latent_loss = -0.5 * tf.reduce_sum(1.0 + 2.0 * sd - tf.square(mn) - tf.exp(2.0 * sd), 1)
loss = tf.reduce_mean(img_loss + latent_loss)
optimizer = tf.train.AdamOptimizer(0.0005).minimize(loss)
sess = tf.Session()
sess.run(tf.global_variables_initializer())

训练网络


现在我们终于可以训练我们的 VAE 了!


每隔 200 步,我们会看一下当前的重建是什么样子的。大约在处理了 2000 次迭代后,大多数重建看上去是挺合理的。

 
for i in range(30000):
    batch = [np.reshape(b, [28, 28]) for b in mnist.train.next_batch(batch_size=batch_size)[0]]
    sess.run(optimizer, feed_dict = {X_in: batch, Y: batch, keep_prob: 0.8})
    if not i % 200:
        ls, d, i_ls, d_ls, mu, sigm = sess.run([loss, dec, img_loss, dst_loss, mn, sd], feed_dict = {X_in: batch, Y: batch, keep_prob: 1.0})
        plt.imshow(np.reshape(batch[0], [28, 28]), cmap='gray')
        plt.show()
        plt.imshow(d[0], cmap='gray')
        plt.show()
        print(i, ls, np.mean(i_ls), np.mean(d_ls)


生成新数据


最惊人的是我们现在可以生成新的字符了。最后,我们仅仅是从一个单位正态分布里面采集了一个值,输入到解码器。生成的大多数字符都和人类手写的是一样的。

randoms = [np.random.normal(0, 1, n_latent) for _ in range(10)]
imgs = sess.run(dec, feed_dict = {sampled: randoms, keep_prob: 1.0})
imgs = [np.reshape(imgs[i], [28, 28]) for i in range(len(imgs))]
for img in imgs:
    plt.figure(figsize=(1,1))
    plt.axis('off')
    plt.imshow(img, cmap='gray')

26139image%20(3).png

一些自动生成的字符。


第二种方法不再详细讲解,直接附上代码

from __future__ import division
from __future__ import print_function
import os.pathimport tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_datamnist = input_data.read_data_sets('MNIST')

input_dim = 784
hidden_encoder_dim = 400
hidden_decoder_dim = 400
latent_dim = 20
lam = 0

def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.001)
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.constant(0., shape=shape)
  return tf.Variable(initial)

x = tf.placeholder("float", shape=[None, input_dim])
l2_loss = tf.constant(0.0)

W_encoder_input_hidden = weight_variable([input_dim,hidden_encoder_dim])
b_encoder_input_hidden = bias_variable([hidden_encoder_dim])
l2_loss += tf.nn.l2_loss(W_encoder_input_hidden)

# Hidden layer encoder
hidden_encoder = tf.nn.relu(tf.matmul(x, W_encoder_input_hidden) + b_encoder_input_hidden)

W_encoder_hidden_mu = weight_variable([hidden_encoder_dim,latent_dim])
b_encoder_hidden_mu = bias_variable([latent_dim])
l2_loss += tf.nn.l2_loss(W_encoder_hidden_mu)

# Mu encoder
mu_encoder = tf.matmul(hidden_encoder, W_encoder_hidden_mu) + b_encoder_hidden_muW_encoder_hidden_logvar = weight_variable([hidden_encoder_dim,latent_dim])
b_encoder_hidden_logvar = bias_variable([latent_dim])
l2_loss += tf.nn.l2_loss(W_encoder_hidden_logvar)

# Sigma encoder
logvar_encoder = tf.matmul(hidden_encoder, W_encoder_hidden_logvar) + b_encoder_hidden_logvar# Sample epsilon
epsilon = tf.random_normal(tf.shape(logvar_encoder), name='epsilon')

# Sample latent variable
std_encoder = tf.exp(0.5 * logvar_encoder)
z = mu_encoder + tf.multiply(std_encoder, epsilon)

W_decoder_z_hidden = weight_variable([latent_dim,hidden_decoder_dim])
b_decoder_z_hidden = bias_variable([hidden_decoder_dim])
l2_loss += tf.nn.l2_loss(W_decoder_z_hidden)

# Hidden layer decoder
hidden_decoder = tf.nn.relu(tf.matmul(z, W_decoder_z_hidden) + b_decoder_z_hidden)

W_decoder_hidden_reconstruction = weight_variable([hidden_decoder_dim, input_dim])
b_decoder_hidden_reconstruction = bias_variable([input_dim])
l2_loss += tf.nn.l2_loss(W_decoder_hidden_reconstruction)

KLD = -0.5 * tf.reduce_sum(1 + logvar_encoder - tf.pow(mu_encoder, 2) - tf.exp(logvar_encoder), reduction_indices=1)

x_hat = tf.matmul(hidden_decoder, W_decoder_hidden_reconstruction) + b_decoder_hidden_reconstruction
BCE = tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(logits=x_hat, labels=x), reduction_indices=1)

loss = tf.reduce_mean(BCE + KLD)

regularized_loss = loss + lam * l2_lossloss_summ = tf.summary.scalar("lowerbound", loss)
train_step = tf.train.AdamOptimizer(0.01).minimize(regularized_loss)

# add op for merging summary
summary_op = tf.summary.merge_all()

# add Saver ops
saver = tf.train.Saver()

n_steps = int(1e6)
batch_size = 100

with tf.Session() as sess:
  summary_writer = tf.summary.FileWriter('experiment',
                                          graph=sess.graph)
  if os.path.isfile("save/model.ckpt"):
    print("Restoring saved parameters")
    saver.restore(sess, "save/model.ckpt")
  else:
    print("Initializing parameters")
    sess.run(tf.global_variables_initializer())

  for step in range(1, n_steps):
    batch = mnist.train.next_batch(batch_size)
    feed_dict = {x: batch[0]}
    _, cur_loss, summary_str = sess.run([train_step, loss, summary_op], feed_dict=feed_dict)
    summary_writer.add_summary(summary_str, step)

    if step % 50 == 0:
      save_path = saver.save(sess, "save/model.ckpt")
      print("Step {0} | Loss: {1}".format(step, cur_loss))




这篇关于Tensorflow下VAE(变分自动编码器)在MNIST数据集下的实验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/761142

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

烟火目标检测数据集 7800张 烟火检测 带标注 voc yolo

一个包含7800张带标注图像的数据集,专门用于烟火目标检测,是一个非常有价值的资源,尤其对于那些致力于公共安全、事件管理和烟花表演监控等领域的人士而言。下面是对此数据集的一个详细介绍: 数据集名称:烟火目标检测数据集 数据集规模: 图片数量:7800张类别:主要包含烟火类目标,可能还包括其他相关类别,如烟火发射装置、背景等。格式:图像文件通常为JPEG或PNG格式;标注文件可能为X

pandas数据过滤

Pandas 数据过滤方法 Pandas 提供了多种方法来过滤数据,可以根据不同的条件进行筛选。以下是一些常见的 Pandas 数据过滤方法,结合实例进行讲解,希望能帮你快速理解。 1. 基于条件筛选行 可以使用布尔索引来根据条件过滤行。 import pandas as pd# 创建示例数据data = {'Name': ['Alice', 'Bob', 'Charlie', 'Dav

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者