VAE ( Variational Autoencoders,变分自编码) 模型组合了神经网络和贝叶斯推理这两种最好的方法,是最酷的神经网络,已经成为无监督学习的流行方法之一。(摘自《TensorFlow 深度学习实战》 安东尼奥-古利;阿米塔-卡普尔。机械工业出版社)。
在上一篇博客 VAE 模型基本原理简单介绍 已经介绍基本原理,这里记录一下 VAE 模型的实现过程(必须声明:)
本次实验采用的是 notebook,可以是自己电脑上安装的 jupyter notebook,也可以使用自己云服务器安装的,也可以考虑使用谷歌提供的 Colaboratory。
注:本节内容绝大部分是参考 (推荐阅读英文版),只是对一些部分做了一些补充解释
推荐直接下载这个文件,下载地址。下载后请去除后面的 .txt
,因为这是一个 ipynb 文件。
如果方便的话,可以直接访问 VAE demo,使用 谷歌提供的免费 GPU 。
不能访问的话直接下载到本地,再运行。
tensorflow 2.x
!pip show tensorflow
如果当前安装的不是 tensorflow 2.x 的话,请输入以下命令安装:!pip install tensorflow==2.3.0 -i
imageio
!pip install imageio
import tensorflow as tfimport os
import time
import numpy as np
import glob
import matplotlib.pyplot as plt
import PIL
import imageiofrom IPython import display(train_images, _), (test_images, _) = tf.ist.load_data()train_images = shape(train_images.shape[0], 28, 28, 1).astype('float32')
test_images = shape(test_images.shape[0], 28, 28, 1).astype('float32')# 标准化图片到区间 [0., 1.] 内
train_images /= 255.
test_images /= 255.# 二值化
train_images[train_images >= .5] = 1.
train_images[train_images < .5] = 0.
test_images[test_images >= .5] = 1.
test_images[test_images < .5] = 0.# 使用 tf.data 来将数据分批和打乱
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(TRAIN_BUF).batch(BATCH_SIZE)
test_dataset = tf.data.Dataset.from_tensor_slices(test_images).shuffle(TEST_BUF).batch(BATCH_SIZE)
准备工作做完了后,这里正式开始编写实现VAE模型。
关于 VAE 模型的基本原理请参考一下 上一篇博客。
这里简单回顾一下, VAE 模型可以理解为 三部分
推断网络
。该 NN 用来生成隐变量的参数(隐变量由多个高斯分布组成)。对于隐变量 z z z,首先初始化时可以是标准高斯分布,然后通过这个 NN,通过不断计算后验概率 q ( z ∣ x ) q(z|x) q(z∣x) 来逐步确定高斯分布的参数(均值和方差)。生成网络
。该 NN 用于根据隐变量生成数据,我们希望它既有能力还原 encoder 的数据,同时还能根据数据特征生成一些输入样本中不包含的数据。卷积
这里使用两个小卷积层分别用于 Encoder 和 Decoder 。在文献中,这些网络也分别被称为 推断网络或识别网络 (inference/recognition)和生成模型。
重参数化技巧 (Reparameterization Trick)
其中 μ mu μ 和 σ sigma σ 表示高斯分布中的均值 和 标准差。它们可以从 decoder 输出中导出。可以认为 ε varepsilon ε 是用来保持 z z z 随机性的随机噪声。我们从标准正态分布生成。
网络结构 (Network architecture)
对于 VAE 模型构建,
注意,在训练VAE时,通常避免使用批次标准化,因为使用小批量的额外随机性可能会加剧抽样随机性之外的不稳定性。
class CVAE(tf.keras.Model):"""Convolutional variational autoencoder."""def __init__(self, latent_dim):super(CVAE, self).__init__()self.latent_dim = der = tf.keras.Sequential([tf.keras.layers.InputLayer(input_shape=(28, 28, 1)),tf.keras.layers.Conv2D(filters=32, kernel_size=3, strides=(2, 2), activation='relu'),tf.keras.layers.Conv2D(filters=64, kernel_size=3, strides=(2, 2), activation='relu'),tf.keras.layers.Flatten(),# No activationtf.keras.layers.Dense(latent_dim + latent_dim),])self.decoder = tf.keras.Sequential([tf.keras.layers.InputLayer(input_shape=(latent_dim,)),tf.keras.layers.Dense(units=7*7*32, activationlu),tf.keras.layers.Reshape(target_shape=(7, 7, 32)),tf.keras.layers.Conv2DTranspose(filters=64, kernel_size=3, strides=2, padding='same',activation='relu'),tf.keras.layers.Conv2DTranspose(filters=32, kernel_size=3, strides=2, padding='same',activation='relu'),# No activationtf.keras.layers.Conv2DTranspose(filters=1, kernel_size=3, strides=1, padding='same'),])@tf.functiondef sample(self, eps=None):if eps is None:eps = al(shape=(100, self.latent_dim))return self.decode(eps, apply_sigmoid=True)def encode(self, x):mean, logvar = tf.der(x), num_or_size_splits=2, axis=1)return mean, logvardef reparameterize(self, mean, logvar):eps = al(shape=mean.shape)return eps * tf.exp(logvar * .5) + meandef decode(self, z, apply_sigmoid=False):logits = self.decoder(z)if apply_sigmoid:probs = tf.sigmoid(logits)return probsreturn logits
上篇博客中提到过,VAE 通过 log p ( x ) log p(x) logp(x) 极大似然 ELBO ( the evidence lower bound) 进行训练:
log p ( x ) ≥ ELBO = E q ( z ∣ x ) [ log p ( x , z ) q ( z ∣ x ) ] . log p(x) ge text{ELBO} = mathbb{E}_{q(z|x)}left[log frac{p(x, z)}{q(z|x)}right]. logp(x)≥ELBO=Eq(z∣x)[logq(z∣x)p(x,z)].
实际操作中,我们优化了这种单样本蒙特卡罗估计:
log p ( x ∣ z ) + log p ( z ) − log q ( z ∣ x ) , log p(x| z) + log p(z) - log q(z|x), logp(x∣z)+logp(z)−logq(z∣x),
其中 z z z 从 q ( z ∣ x ) q(z|x) q(z∣x) 中采样。
关于 log_normal_pdf 的计算方法可以参考 。
对该表达式求对数值,可得
log_normal_pdf ( x ) = log 1 σ 2 π e − 1 2 ( x − μ σ ) 2 = − log ( σ 2 π ) − 1 2 ( x − μ σ ) 2 = − log σ − 1 2 log 2 π − 1 2 ( x − μ σ ) 2 = − 1 2 ( log σ 2 + log 2 π + ( x − μ ) 2 σ 2 ) text{log_normal_pdf} (x) = log frac{1}{sigmasqrt{2pi}} e^{-frac{1}{2}(frac{x-mu}{sigma})^2} \ = - log (sigma sqrt{2pi}) - {frac{1}{2}(frac{x-mu}{sigma})^2} \ = - log sigma - frac{1}{2} log 2pi - {frac{1}{2}(frac{x-mu}{sigma})^2} \ = -frac{1}{2}bigl(log sigma^2 + log 2pi + frac{(x-mu)^2}{sigma^2}bigr) log_normal_pdf (x)=logσ2π 1 e−21(σx−μ)2=−log (σ2π )−21(σx−μ)2=−logσ−21log2π−21(σx−μ)2=−21(logσ2+log2π+σ2(x−μ)2)
optimizer = tf.keras.optimizers.Adam(1e-4)# 计算概率分布密度的 log 值
def log_normal_pdf(sample, mean, logvar, raxis=1):log2pi = tf.math.log(2. * np.pi)duce_sum(-.5 * ((sample - mean) ** 2. * tf.exp(-logvar) + logvar + log2pi),axis=raxis)def compute_loss(model, x):mean, logvar = de(x)z = parameterize(mean, logvar)x_logit = model.decode(z)cross_ent = tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=x)# 计算 log p(x|z)logpx_z = -tf.reduce_sum(cross_ent, axis=[1, 2, 3])# 计算 log p(z)logpz = log_normal_pdf(z, 0., 0.)# 计算 log q(z|x)logqz_x = log_normal_pdf(z, mean, logvar)return -tf.reduce_mean(logpx_z + logpz - logqz_x)@tf.function
def train_step(model, x, optimizer):"""Executes one training step and returns the loss.This function computes the loss and gradients, and uses the latter toupdate the model's parameters."""with tf.GradientTape() as tape:loss = compute_loss(model, x)gradients = adient(loss, ainable_variables)optimizer.apply_gradients(zip(gradients, ainable_variables))
训练
关于 logits 的 解释
生成图片
epochs = 100
# set the dimensionality of the latent space to a plane for visualization later
latent_dim = 2
num_examples_to_generate = 16# keeping the random vector constant for generation (prediction) so
# it will be easier to see the improvement.
random_vector_for_generation = al(shape=[num_examples_to_generate, latent_dim])
model = CVAE(latent_dim)def generate_and_save_images(model, epoch, test_sample):mean, logvar = de(test_sample)z = parameterize(mean, logvar)predictions = model.sample(z)fig = plt.figure(figsize=(4, 4))for i in range(predictions.shape[0]):plt.subplot(4, 4, i + 1)plt.imshow(predictions[i, :, :, 0], cmap='gray')plt.axis('off')# tight_layout minimizes the overlap between 2 sub-plotsplt.savefig('image_at_epoch_{:04d}.png'.format(epoch))plt.show()# Pick a sample of the test set for generating output images
assert batch_size >= num_examples_to_generate
for test_batch in test_dataset.take(1):test_sample = test_batch[0:num_examples_to_generate, :, :, :]
generate_and_save_images(model, 0, test_sample)for epoch in range(1, epochs + 1):start_time = time.time()for train_x in train_dataset:train_step(model, train_x, optimizer)end_time = time.time()loss = ics.Mean()for test_x in test_dataset:loss(compute_loss(model, test_x))elbo = -sult()display.clear_output(wait=False)print('Epoch: {}, Test set ELBO: {}, time elapse for current epoch: {}'.format(epoch, elbo, end_time - start_time))generate_and_save_images(model, epoch, test_sample)
一百次循环后,生成的图片如下(感觉还可以):
同时可以生化 gif 图片来方便查看生成过程。
anim_file = 'cvae.gif'_writer(anim_file, mode='I') as writer:filenames = glob.glob('image*.png')filenames = sorted(filenames)for filename in filenames:image = imageio.imread(filename)writer.append_data(image)image = imageio.imread(filename)writer.append_data(image)
展示 gif 图片
import tensorflow_bed as embed
bed_file(anim_file)
隐变量
以上内容绝大部分是摘录于tensorflow 官网的 tutorials ,这里实现的是卷积 VAE 模型,简称(cVAE)。根据官网该文章, Next Step
Conv2D
和 Conv2DTranspose
的层数调成 512等等 ;这里只是介绍一下如何快速体验一下 VAE 模型,模型的实现也是 tensorflow 官网提供的,对原来的文章做了简单的翻译工作,并且也加了一些自己的理解和补充。如果有更多好玩的实现demo,也将补充在这里。
Smileyan
2020.10.10 15:31
本文发布于:2024-01-28 14:21:04,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17064228658037.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |