1. FV(Fisher Vector)
FV的思想用一句话概括就是:用所有的聚类中心的线性组合去表示每个特征点
简单来说,假设样本各特征符合独立同分布(i.i.d)则样本的概率分布可以由各个特征维度的概率分布的乘积得到。对该式取对数的话,就可以将乘法运算转换为加法运算,即
Fisher vector就是对上式求偏导的结果归一化后构成的。具体数学推导过程请参考点击打开链接。fv的伪代码实现过程如下:
接下来是本文的重点部分,变分自编码器。
2. VAE(Variational Autoencoder)
VAE的基本思想是:为每个样本构造专属的正态分布,然后利用采样重构
由于我们若能了解一组数据的概率分布,就可以通过分布对其进行采样直接得到所有的可能数据,VAE就是采用了一种迂回的方式去实现这一思想。对于每个样本X,利用神经网络提取出该样本的正态分布参数(均值,方差)从而可以构建出每个样本的专属的正态分布P(X)再采样重构。然而直接对X采样是比较困难的,因此假设存在变量Z服从标准正态分布,就可以利用下式
先从标准正太分布中采样Z,然后根据Z来计算 X,即对于对应的采样变量Z,利用生成器生成样本X hat,即为X的重构值。
为了防止噪声对重构过程产生影响,同时还应保证该模型具有生成能力,需要使所有的P(Z|X)都接近标准正态分布,这是因为
此时满足Z是标准正态分布的先验条件,可以从标准正态分布中采样Z来重构X。为了实现这一点,VAE采用的方式是在重构误差项后面额外添加了误差项。因为当Z逼近标准正态分布时其均值应趋近于0,取对数后的方差也趋近于0(方差趋近于1),则二者对应的损失项应为各自值的范数的平方,利用KL散度合并二者得到该额外损失项的表达式为
至此,就是VAE的大体思路了。
接下来就是VAE另一非常重要的思想(技巧),即重参数技巧 Reparameterization Trick
由于采样操作的不可导,为了反向优化提取提取均值和方差的模型,根据下式
就可以通过参数变换的方式采样
采样操作将不需要直接参与梯度下降过程,而是改为采样结果参与,使模型可训练。
最终的变分自编码实现过程的伪代码为
最后补充说明几点:
1.当噪声为0时,变分自编码器将退化为普通自编码器,因此KL散度不应完全为0
2.该方法中使用的正态分布能够有效的保证KL散度在重构误差表达式中的正常应用,使用其他概率分布可能易出现kl散度接近无穷大的情况
3.variational(变分)体现在引入KL散度这一泛函的处理上
4.将label加入来辅助生成样本,可以实现通过控制均值来生成某类图像,该方法称为Conditional VAE,即CVAE
网上看到的VAE代码(来源github),较为简单易懂,如下所示:
import itertools
import matplotlib as mpl
import numpy as np
import os
import tensorflow as tf
import tensorflow.contrib.slim as slim
import time
import seaborn as sns
from matplotlib import pyplot as plt
from scipy.misc import imsave
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
# sns.set_style('whitegrid')
distributions = tf.distributions
flags = tf.app.flags
flags.DEFINE_string('data_dir', '/tmp/dat/', 'Directory for data')
flags.DEFINE_string('logdir', '/tmp/log/', 'Directory for logs')
flags.DEFINE_integer('latent_dim', 100, 'Latent dimensionality of model')
flags.DEFINE_integer('batch_size', 64, 'Minibatch size')
flags.DEFINE_integer('n_samples', 1, 'Number of samples to save')
flags.DEFINE_integer('print_every', 1000, 'Print every n iterations')
flags.DEFINE_integer('hidden_size', 200, 'Hidden size for neural networks')
flags.DEFINE_integer('n_iterations', 100000, 'number of iterations')
FLAGS = flags.FLAGS
# 提取mu和sigma的神经网络
def inference_network(x, latent_dim, hidden_size):
"""Construct an inference network parametrizing a Gaussian.
Args:
x: A batch of MNIST digits.
latent_dim: The latent dimensionality.
hidden_size: The size of the neural net hidden layers.
Returns:
mu: Mean parameters for the variational family Normal
sigma: Standard deviation parameters for the variational family Normal
"""
with slim.arg_scope([slim.fully_connected], activation_fn=tf.nn.relu):
net = slim.flatten(x)
net = slim.fully_connected(net, hidden_size)
net = slim.fully_connected(net, hidden_size)
gaussian_params = slim.fully_connected(
net, latent_dim * 2, activation_fn=None)
# The mean parameter is unconstrained
mu = gaussian_params[:, :latent_dim]
# The standard deviation must be positive. Parametrize with a softplus
sigma = tf.nn.softplus(gaussian_params[:, latent_dim:])
return mu, sigma
#利用采样的z重构x的生成器
#因为数据集是二值图像,因此采用伯努利分布为x|z的分布形式,从而可以利用神经网络(全连接层)得到生成模型q(x|z)
def generative_network(z, hidden_size):
"""Build a generative network parametrizing the likelihood of the data
Args:
z: Samples of latent variables
hidden_size: Size of the hidden state of the neural net
Returns:
bernoulli_logits: logits for the Bernoulli likelihood of the data
"""
with slim.arg_scope([slim.fully_connected], activation_fn=tf.nn.relu):
net = slim.fully_connected(z, hidden_size)
net = slim.fully_connected(net, hidden_size)
bernoulli_logits = slim.fully_connected(net, 784, activation_fn=None)
bernoulli_logits = tf.reshape(bernoulli_logits, [-1, 28, 28, 1])
return bernoulli_logits
def train():
# Train a Variational Autoencoder on MNIST
# Input placeholders
with tf.name_scope('data'):
x = tf.placeholder(tf.float32, [None, 28, 28, 1])
tf.summary.image('data', x)
with tf.variable_scope('variational'):
q_mu, q_sigma = inference_network(x=x,
latent_dim=FLAGS.latent_dim,
hidden_size=FLAGS.hidden_size)
# The variational distribution is a Normal with mean and standard
# deviation given by the inference network
q_z = distributions.Normal(loc=q_mu, scale=q_sigma)
assert q_z.reparameterization_type == distributions.FULLY_REPARAMETERIZED
with tf.variable_scope('model'):
# The likelihood is Bernoulli-distributed with logits given by the
# generative network
p_x_given_z_logits = generative_network(z=q_z.sample(),
hidden_size=FLAGS.hidden_size)
p_x_given_z = distributions.Bernoulli(logits=p_x_given_z_logits)
posterior_predictive_samples = p_x_given_z.sample()
tf.summary.image('posterior_predictive',
tf.cast(posterior_predictive_samples, tf.float32))
# Take samples from the prior
# 采样
with tf.variable_scope('model', reuse=True):
p_z = distributions.Normal(loc=np.zeros(FLAGS.latent_dim, dtype=np.float32),
scale=np.ones(FLAGS.latent_dim, dtype=np.float32))
p_z_sample = p_z.sample(FLAGS.n_samples)
p_x_given_z_logits = generative_network(z=p_z_sample,
hidden_size=FLAGS.hidden_size)
prior_predictive = distributions.Bernoulli(logits=p_x_given_z_logits)
prior_predictive_samples = prior_predictive.sample()
tf.summary.image('prior_predictive',
tf.cast(prior_predictive_samples, tf.float32))
# Take samples from the prior with a placeholder
with tf.variable_scope('model', reuse=True):
z_input = tf.placeholder(tf.float32, [None, FLAGS.latent_dim])
p_x_given_z_logits = generative_network(z=z_input,
hidden_size=FLAGS.hidden_size)
prior_predictive_inp = distributions.Bernoulli(logits=p_x_given_z_logits)
prior_predictive_inp_sample = prior_predictive_inp.sample()
# Build the evidence lower bound (ELBO) or the negative loss
kl = tf.reduce_sum(distributions.kl_divergence(q_z, p_z), 1) #按行求和
expected_log_likelihood = tf.reduce_sum(p_x_given_z.log_prob(x),
[1, 2, 3])#????
elbo = tf.reduce_sum(expected_log_likelihood - kl, 0)
optimizer = tf.train.RMSPropOptimizer(learning_rate=0.001)
train_op = optimizer.minimize(-elbo)
# Merge all the summaries
summary_op = tf.summary.merge_all()
init_op = tf.global_variables_initializer()
# Run training
sess = tf.InteractiveSession()
sess.run(init_op)
mnist = read_data_sets(FLAGS.data_dir, one_hot=True)
print('Saving TensorBoard summaries and images to: %s' % FLAGS.logdir)
train_writer = tf.summary.FileWriter(FLAGS.logdir, sess.graph)
# Get fixed MNIST digits for plotting posterior means during training
np_x_fixed, np_y = mnist.test.next_batch(5000)
np_x_fixed = np_x_fixed.reshape(5000, 28, 28, 1)
np_x_fixed = (np_x_fixed > 0.5).astype(np.float32)
t0 = time.time()
for i in range(FLAGS.n_iterations):
# Re-binarize the data at every batch; this improves results
np_x, _ = mnist.train.next_batch(FLAGS.batch_size)
np_x = np_x.reshape(FLAGS.batch_size, 28, 28, 1)
np_x = (np_x > 0.5).astype(np.float32)
sess.run(train_op, {x: np_x})
# Print progress and save samples every so often
if i % FLAGS.print_every == 0:
np_elbo, summary_str = sess.run([elbo, summary_op], {x: np_x})
train_writer.add_summary(summary_str, i)
print('Iteration: {0:d} ELBO: {1:.3f} s/iter: {2:.3e}'.format(
i,
np_elbo / FLAGS.batch_size,
(time.time() - t0) / FLAGS.print_every))
t0 = time.time()
# Save samples
np_posterior_samples, np_prior_samples = sess.run(
[posterior_predictive_samples, prior_predictive_samples], {x: np_x})
for k in range(FLAGS.n_samples):
f_name = os.path.join(
FLAGS.logdir, 'iter_%d_posterior_predictive_%d_data.jpg' % (i, k))
imsave(f_name, np_x[k, :, :, 0])
f_name = os.path.join(
FLAGS.logdir, 'iter_%d_posterior_predictive_%d_sample.jpg' % (i, k))
imsave(f_name, np_posterior_samples[k, :, :, 0])
f_name = os.path.join(
FLAGS.logdir, 'iter_%d_prior_predictive_%d.jpg' % (i, k))
imsave(f_name, np_prior_samples[k, :, :, 0])
# Plot the posterior predictive space
if FLAGS.latent_dim == 2:
np_q_mu = sess.run(q_mu, {x: np_x_fixed})
cmap = mpl.colors.ListedColormap(sns.color_palette("husl"))
f, ax = plt.subplots(1, figsize=(6 * 1.1618, 6))
im = ax.scatter(np_q_mu[:, 0], np_q_mu[:, 1], c=np.argmax(np_y, 1), cmap=cmap,
alpha=0.7)
ax.set_xlabel('First dimension of sampled latent variable $z_1$')
ax.set_ylabel('Second dimension of sampled latent variable mean $z_2$')
ax.set_xlim([-10., 10.])
ax.set_ylim([-10., 10.])
f.colorbar(im, ax=ax, label='Digit class')
plt.tight_layout()
plt.savefig(os.path.join(FLAGS.logdir,
'posterior_predictive_map_frame_%d.png' % i))
plt.close()
nx = ny = 20
x_values = np.linspace(-3, 3, nx)
y_values = np.linspace(-3, 3, ny)
canvas = np.empty((28 * ny, 28 * nx))
for ii, yi in enumerate(x_values):
for j, xi in enumerate(y_values):
np_z = np.array([[xi, yi]])
x_mean = sess.run(prior_predictive_inp_sample, {z_input: np_z})
canvas[(nx - ii - 1) * 28:(nx - ii) * 28, j *
28:(j + 1) * 28] = x_mean[0].reshape(28, 28)
imsave(os.path.join(FLAGS.logdir,
'prior_predictive_map_frame_%d.png' % i), canvas)
# plt.figure(figsize=(8, 10))
# Xi, Yi = np.meshgrid(x_values, y_values)
# plt.imshow(canvas, origin="upper")
# plt.tight_layout()
# plt.savefig()
# Make the gifs
if FLAGS.latent_dim == 2:
os.system(
'convert -delay 15 -loop 0 {0}/posterior_predictive_map_frame*png {0}/posterior_predictive.gif'
.format(FLAGS.logdir))
os.system(
'convert -delay 15 -loop 0 {0}/prior_predictive_map_frame*png {0}/prior_predictive.gif'
.format(FLAGS.logdir))
def main(_):
if tf.gfile.Exists(FLAGS.logdir):
tf.gfile.DeleteRecursively(FLAGS.logdir)
tf.gfile.MakeDirs(FLAGS.logdir)
train()
if __name__ == '__main__':
tf.app.run()
参考文献:https://spaces.ac.cn/archives/5253
———————————————————————————
第一次写博客,作为科研小白,才学疏浅,还请多多指教~