机器学习算法-VAE

1. VAE模型推导

1.1 算法引入

​ 在EM算法中,隐变量的最优分布\(q^{\star}(\mathbf{z})\)是在观测数据给定时的条件分布\(p(\mathbf{z}|\mathbf{x})\),此时对应的证据下界与似然函数相等。但是在实际中,后验概率可能很难计算甚至不能计算,这时EM算法中的E-step便无法进行。VAE的思路是用一个新的分布\(q(\mathbf{z}|\mathbf{x})\)进行估计:

\[q^{\star}(\mathbf{z}|\mathbf{x}) = \arg\min_q \mathrm{KL}(q(\mathbf{z}|\mathbf{x})|| p(\mathbf{z}|\mathbf{x}))
\]

1.2 模型推导

​ 将KL散度的计算公式进行变形得到:

\[\begin{align}
\mathrm{KL}(q(\mathbf{z}|\mathbf{x})||p(\mathbf{z}|\mathbf{x})) &= -\int_\mathbf{z} q(\mathbf{z}|\mathbf{x})\log\frac{p(\mathbf{z}|\mathbf{x})}{q(\mathbf{z}|\mathbf{x})}d\mathbf{z}\\
&= -\int_{\mathbf{z}}q(\mathbf{z}|\mathbf{x})\left[\frac{p(\mathbf{x},\mathbf{z})}{q(\mathbf{z}|\mathbf{x})}\right]d\mathbf{z} + \log p(\mathbf{x})\\
\Longrightarrow &\log p(\mathbf{x}) = \int_{\mathbf{z}}q(\mathbf{z}|\mathbf{x})\left[\frac{p(\mathbf{x},\mathbf{z})}{q(\mathbf{z}|\mathbf{x})}\right]d\mathbf{z} + \mathrm{KL}(q(\mathbf{z}|\mathbf{x})||p(\mathbf{z}|\mathbf{x})) \geq \mathcal{L}_{\mathrm{ELBO}}
\end{align}
\]

​ 通过对KL散度的改写,我们找到了似然函数的一个下界,对这个下界优化可以近似得到似然函数的最优值。为了优化变分下界\(\mathcal{L}_{\mathrm{ELBO}}\),将其形式进行变换:

\[\begin{aligned}
\mathcal{L}_{\mathrm{ELBO}}(\mathbf{x}) &=\mathbb{E}_{\mathbf{z} \sim q(\mathbf{z} \mid \mathbf{x})} \left[\log \frac{p(\mathbf{x}, \mathbf{z})}{q(\mathbf{z}\mid \mathbf{x})}\right] \\
&=\mathbb{E}_{\mathbf{z} \sim q(\mathbf{z} \mid \mathbf{x})} \left[\log \frac{p(\mathbf{x} \mid \mathbf{z}) p(\mathbf{z})}{q(\mathbf{z} \mid \mathbf{x})}\right] \\
&=\int q(\mathbf{z} \mid \mathbf{x})(\log p(\mathbf{z})-\log q(\mathbf{z} \mid \mathbf{x})+\log p(\mathbf{x} \mid \mathbf{z})) d \mathbf{z} \\
&\left.=-\int q(\mathbf{z} \mid \mathbf{x})\left(\log \frac{q(\mathbf{z} \mid \mathbf{x})}{p(\mathbf{z})}\right) d \mathbf{z}+\int q(\mathbf{z} \mid \mathbf{x}) \log p(\mathbf{x} \mid \mathbf{z})\right) d \mathbf{z} \\
&=-\mathrm{KL}(q(\mathbf{z} \mid \mathbf{x}) \| p(\mathbf{z}))+\mathbb{E}_{\mathbf{z} \sim q(\mathbf{z} \mid \mathbf{x})}\left[\log p(\mathbf{x} \mid \mathbf{z})\right]
\end{aligned}
\]

​ 变分下界中出现了三个概率分布,为了方便求解,并对分布的类型和参数做了假设:

  • \(q(\mathbf{z}|\mathbf{x})\):编码器,根据样本\(\mathbf{x}\)生成对应的隐变量\(\mathbf{z}\)

    \[q(\mathbf{z}|\mathbf{x})=\mathcal{N}(\mu_1, \sigma_1^2)\\
    [\mu_1, \log\sigma_1^2] = f_{\theta}(\mathbf{x})
    \]
  • \(p(\mathbf{x}|\mathbf{z})\):解码器,根据隐变量\(\mathbf{z}\)生成样本\(\mathbf{x}\)

    \[p(\mathbf{x}|\mathbf{z})=\mathcal{N}(\mu_2, \mathbf{I})\\
    \mu_2 = g_{\phi}(\mathbf{z})
    \]
  • \(p(\mathbf{z})\):隐变量的先验分布

    \[p(\mathbf{z})=\mathcal{N}(\mathbf{0}, \mathbf{I})
    \]

其中\(f_{\theta}(\cdot)\)和\(g_{\phi}(\cdot)\)分别是参数为\(\theta\)和\(\phi\)的神经网络。

1.3 损失函数

​ 在变分下界中存在两项:重构误差和KL散度项。重构误差实现了让解码出来的样本和真实样本尽可能接近,KL散度项对隐变量分布进行了限制,起到了正则化的作用。原始的重构误差可以通过蒙特卡洛采样计算得到:

\[\mathbb{E}_{\mathbf{z}\sim q(\mathbf{z}|\mathbf{x})}\simeq \frac{1}{L}\sum_{l=1}^L \log p(\mathbf{x}|\mathbf{z}_l), \quad \mathbf{z}_l\sim q(\mathbf{z}|\mathbf{x})
\]

事实上,将\(p(\mathbf{x}|\mathbf{z}_l)\)的表达式代入到公式中可以发现,重构误差本质上是原始样本\(\mathbf{x}\)和重构样本\(\mathbf{x}_l\)之间的欧氏距离。更一般地,可以将该项换为其他的损失函数。

​ KL散度项直接代入高斯分布的KL散度计算公式可以得到:

\[\mathrm{KL}(q(\mathbf{z} \mid \mathbf{x}) \| p(\mathbf{z})) = -\frac{1}{2} \sum_{j=1}^{J}\left(1+\log \sigma_{1j}^{2}-\mu_{1j}^{2}-\sigma_{1j}^{2}\right)
\]

1.4 重参数技巧

​ 在解码过程需要用到样本的编码\(\mathbf{z}\),编码\(\mathbf{z}\)是从分布\(\mathcal{N}(\mu_1, \sigma_1^2)\)中采样得到,而采样过程是一个“不可微分”的过程,对后续的反向传播带来了困难。重参数技巧运用了一个基本的定理:

\[if \quad \mathbf{z}\sim\mathcal{N}(\mathbf{0}, \mathbf{I}), \quad then \quad \Sigma^{\frac{1}{2}}\mathbf{z}+\mu \sim \mathcal{N}(\mu, \Sigma)
\]

重参数的过程为:先从标准正态分布中生成一个样本\(\mathbf{z}_0\),然后乘上标准差再加上均值。

2. 实现

2.1 模型定义

本文采用了Pytorch实现了VAE模型,并在MNIST数据集上进行了实验。神经网络选用了全连接网络,事实上也可以用其他网络,如CNN、RNN等

class VAE(nn.Module):
# 使用全链接网络
def __init__(self, encoder_structure, decoder_structure, hidden_num):
super(VAE, self).__init__()
self.encoder = nn.Sequential()
for i in range(1, len(encoder_structure)):
self.encoder.add_module("linear"+str(i), nn.Linear(encoder_structure[i-1], encoder_structure[i]))
self.encoder.add_module("relu"+str(i), nn.ReLU()) self.z_layer = nn.Linear(encoder_structure[-1], hidden_num)
self.log_var_layer = nn.Linear(encoder_structure[-1], hidden_num) self.decoder = nn.Sequential()
for i in range(1, len(decoder_structure)):
self.decoder.add_module("linear"+str(i), nn.Linear(decoder_structure[i-1], decoder_structure[i]))
if(i < len(decoder_structure)-1): self.decoder.add_module("relu"+str(i), nn.ReLU()) def forward(self, x):
self.z_mean, self.z_log_var = self.encode(x)
z = self._reparameters(self.z_mean, self.z_log_var)
self.x_mean = self.decode(z)
return self.z_mean, self.z_log_var, z, self.x_mean def encode(self, x):
code = self.encoder(x)
z_mean = self.z_layer(code)
z_log_var = self.log_var_layer(code)
return z_mean, z_log_var def decode(self, z):
x_mean = self.decoder(z)
return x_mean def loss(self, x, recon_func):
KL_loss = -0.5 * torch.sum(1 + self.z_log_var - self.z_mean.pow(2) - self.z_log_var.exp())
recon_loss = recon_func(self.x_mean, x)
return KL_loss + recon_loss def _reparameters(self, z_mean, z_log_var):
z0 = torch.randn_like(z_mean)
return z_mean + z0 * torch.exp(0.5*z_log_var) def train(self, net, dataIter, recon_loss, optimizer, epoches):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("training on %s" %(device))
net = net.to(device)
train_loss = [0.]*epoches
for epoch in range(epoches):
cnt = 0
for batch_idx, (data, label) in enumerate(dataIter):
# 前向
data = data.view(data.size(0), -1).to(device)
z_mean, z_log_var, z, x_mean = net(data)
loss = net.loss(data, recon_loss)
# 反向
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss[epoch] += loss.cpu().item()
if((batch_idx+1) % 100 == 0):
print("epoch : {0} | #batch : {1} | batch average loss: {2}"
.format(epoch, batch_idx, loss.cpu().item()/len(data)))
# train_loss[epoch] /= len(dataIter.dataset)
print("Epoch : {0} | epoch average loss : {1}"
.format(epoch, train_loss[epoch] / len(dataIter.dataset)))

2.2 实验

导入相关包

#%% 导入包
from AutoEncoder import *
import torch
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import MNIST
from torchvision.utils import save_image
import matplotlib.pyplot as plt

下载数据集,并查看前六张图片

#%%
img_transform = transforms.Compose([
transforms.ToTensor()])
path = "../../dataset"
batch_size = 128
dataset = MNIST(path, transform=img_transform, train = True, download=False)
dataIter = DataLoader(dataset, batch_size=batch_size, shuffle=True)
imgs = dataset.data[:6].numpy()
labels = dataset.targets[:6].numpy() _, axes = plt.subplots(2, 3)
for i in range(2):
for j in range(3):
axes[i][j].imshow(imgs[i*3 + j], cmap='gray')
axes[i][j].set_title("True: " + str(labels[i*3+j]))
axes[i][j].get_xaxis().set_visible(False)
axes[i][j].get_yaxis().set_visible(False)
plt.show()

定义模型并训练

#%%
encoder_structure = [784, 512, 64]
decoder_structure = [20, 64, 512, 784]
model = VAE(encoder_structure, decoder_structure, 20)
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
print(model)
model.train(model, dataIter, nn.MSELoss(size_average=False), opt, 50)

随机采样几个编码,并生成样本

shape = (6, 20)
z_mean = torch.rand(shape, device='cuda') rand_z = torch.randn(shape,device='cuda') + z_mean
gen_x = model.decode(rand_z).cpu()
rand_img = to_image(gen_x).detach().numpy()
# rand_img = (rand_img * 255 / (rand_img.max() - rand_img.min())).astype(np.uint8)
_ ,axes = plt.subplots(2, 3)
for i in range(2):
for j in range(3):
axes[i][j].imshow(rand_img[i*3+j], cmap='gray')
plt.show()

高糊。。。。。

【机器学习】VAE的更多相关文章

  1. VAE (variational autoencoder)

    https://www.zhihu.com/question/41490383/answer/103006793 自编码是一种表示学习的技术,是deep learning的核心问题 让输入等于输出,取 ...

  2. 【机器学习】无监督学习Autoencoder和VAE

    众所周知,机器学习的训练数据之所以非常昂贵,是因为需要大量人工标注数据. autoencoder可以输入数据和输出数据维度相同,这样测试数据匹配时和训练数据的输出端直接匹配,从而实现无监督训练的效果. ...

  3. 【GAN与NLP】GAN的原理 —— 与VAE对比及JS散度出发

    0. introduction GAN模型最早由Ian Goodfellow et al于2014年提出,之后主要用于signal processing和natural document proces ...

  4. AIOps探索:基于VAE模型的周期性KPI异常检测方法——VAE异常检测

    AIOps探索:基于VAE模型的周期性KPI异常检测方法 from:jinjinlin.com   作者:林锦进 前言 在智能运维领域中,由于缺少异常样本,有监督方法的使用场景受限.因此,如何利用无监 ...

  5. GAN与VAE

    经典算法·GAN与VAE Generative Adversarial Networks 及其变体 生成对抗网络是近几年最为经典的生成模型的代表工作,Goodfellow的经典工作.通过两个神经网络结 ...

  6. 机器学习中的 7 大损失函数实战总结(附Python演练)

    介绍 想象一下-你已经在给定的数据集上训练了机器学习模型,并准备好将它交付给客户.但是,你如何确定该模型能够提供最佳结果?是否有指标或技术可以帮助你快速评估数据集上的模型? 当然是有的,简而言之,机器 ...

  7. 推荐|近期热点机器学习git项目

    No1: InterpretML by Microsoft--Machine Learning Interpretability github地址:https://github.com/microso ...

  8. VAE变分自编码器

    我在学习VAE的时候遇到了很多问题,很多博客写的不太好理解,因此将很多内容重新进行了整合. 我自己的学习路线是先学EM算法再看的变分推断,最后学VAE,自我感觉这个线路比较好理解. 一.首先我们来宏观 ...

  9. 深度学习与CV教程(2) | 图像分类与机器学习基础

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...

随机推荐

  1. CF41C Email address 题解

    Content 有一个字符串 \(s\),它满足以下要求: 只包含 ..@ 和小写字母. 不以 . 为开头或者结尾. 不以 @ 为开头或者结尾,并只能包含一个 @. 请将其进行如下操作,使得这个字符串 ...

  2. Ribbon——负载均衡

    一.什么是Ribbon Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起.Ribbon客户端组件提供一系列完善的配置项如连接超 ...

  3. rinted端口转发

    https://www.cnblogs.com/linuxk/p/10075803.html 阿里云Redis外网转发访问   1.前提条件 如果您需要从本地 PC 端访问 Redis 实例进行数据操 ...

  4. libevent源码学习(8):event_signal_map解析

    目录event_signal_map结构体向event_signal_map中添加event激活event_signal_map中的event删除event_signal_map中的event以下源码 ...

  5. Springcloud(二) feign

    Feign Spring Cloud Feign对 Ribbon 负载均衡.Hystrix 服务熔断进行简化,在其基础上进行了进一步的封装,不仅在配置上大大简化了开发工作,同时还提供了一种声明式的 W ...

  6. 解决Centos7误删Python问题

    1.前言 昨天安装Python3.6的时候.不小心把原来的Python全删了.不知道咋办了.后面参考一篇博客.重新安装了一下.相关的包全回来了.所以还是得注意root模式下.慎用rm -rf命令.(笑 ...

  7. git clone报错: Out of memory, malloc failed (tried to allocate 524288000 bytes)

    IDEA 拉取项目报错:Out of memory, malloc failed (tried to allocate 524288000 bytes) 执行 git config --global ...

  8. 【LeetCode】1410. 实体解析器 HTML Entity Parser HTML

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 替换 日期 题目地址:https://leetcode ...

  9. 【LeetCode】659. Split Array into Consecutive Subsequences 解题报告(Python)

    [LeetCode]659. Split Array into Consecutive Subsequences 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id ...

  10. 一个网关服务性能问题的Dump分析

    本篇文章分为三部分,首先简单介绍一下分析的工具Windbg,其次针对一个网关服务性能问题进行逐步刨析,最后针对性能问题的分析总结. 一 Windbg介绍 1.Windbg是个非常强大的调试器,它设计了 ...