变分自编码器——pytorch实现VAE

介绍实现代码:

介绍

变分自编码器(Variational Autoencoder,VAE)是一种生成模型,结合了自编码器和变分推断的思想。它可以用于学习数据的潜在表示,并用于生成新的样本。

VAE的目标是学习一个潜在空间(latent space),使得原始数据可以通过从该潜在空间中采样并解码重构出来。为了实现这一目标,VAE引入了两个关键概念:编码器(encoder)和解码器(decoder)。

编码器将输入数据映射到潜在空间中的一组潜在变量(latent variables)。编码器将输入数据

x

x

x映射到潜在变量

z

z

z的概率分布

q

(

z

x

)

q(z|x)

q(z∣x)。通常假设这个概率分布服从高斯分布,其均值和方差由编码器输出的参数控制。

解码器则将潜在变量

z

z

z映射回原始数据空间,以重构输入数据。解码器将潜在变量

z

z

z映射到重构数据

x

x

x的条件概率分布

p

(

x

z

)

p(x|z)

p(x∣z)。

VAE的训练过程包括两个阶段:推断(inference)和生成(generation)。

在推断阶段,我们通过最大化潜在变量的后验概率

p

(

z

x

)

p(z|x)

p(z∣x)来估计潜在变量

z

z

z。然而,由于后验概率的计算是困难的,VAE使用变分推断的方法,通过近似后验概率

q

(

z

x

)

q(z|x)

q(z∣x)来近似

p

(

z

x

)

p(z|x)

p(z∣x)。这里引入了一个重要的技巧,也就是重参数化技巧(reparameterization trick)。它允许将潜在变量

z

z

z表示为编码器输出的均值

μ

\mu

μ和方差

σ

2

\sigma^2

σ2之间的线性变换:

z

=

μ

+

ϵ

σ

z = \mu + \epsilon \cdot \sigma

z=μ+ϵ⋅σ,其中

ϵ

\epsilon

ϵ是从标准正态分布中采样的噪声。

在生成阶段,我们从先验概率

p

(

z

)

p(z)

p(z)中采样一个潜在变量

z

z

z,然后通过解码器

p

(

x

z

)

p(x|z)

p(x∣z)将其映射回原始数据空间,从而生成新的样本。

VAE的训练目标是最大化观测数据

x

x

x和潜在变量

z

z

z的联合概率

p

(

x

,

z

)

p(x,z)

p(x,z)的下界,也称为变分下界(variational lower bound)或ELBO(Evidence Lower BOund):

ELBO

=

E

q

(

z

x

)

[

log

p

(

x

z

)

]

KL

(

q

(

z

x

)

p

(

z

)

)

\text{ELBO} = \mathbb{E}_{q(z|x)}[\log p(x|z)] - \text{KL}(q(z|x) || p(z))

ELBO=Eq(z∣x)​[logp(x∣z)]−KL(q(z∣x)∣∣p(z))

其中第一项是重构损失(reconstruction loss),用于衡量重构数据

x

x

x和原始数据

x

x

x之间的差异。第二项是KL散度(Kullback-Leibler divergence),用于衡量近似后验概率

q

(

z

x

)

q(z|x)

q(z∣x)和先验概率

p

(

z

)

p(z)

p(z)之间的差异。

通过最大化ELBO,VAE可以学习到一个能够将输入数据

x

x

x映射到潜在空间

z

z

z中,并从中重构出输入数据的编码器和解码器。

总结起来,VAE通过引入编码器和解码器,并使用变分推断的思想,可以学习数据的潜在表示,并用于生成新的样本。通过最大化ELBO,VAE能够在训练过程中同时优化重构损失和KL散度,从而实现了潜在空间的学习和生成。

实现代码:

以下是使用PyTorch实现变分自编码器(VAE)的示例代码:

import torch

import torch.nn as nn

import torch.optim as optim

from torch.autograd import Variable

# 定义变分自编码器(VAE)模型

class VAE(nn.Module):

def __init__(self, input_dim, hidden_dim, latent_dim):

super(VAE, self).__init__()

# 编码器

self.encoder = nn.Sequential(

nn.Linear(input_dim, hidden_dim),

nn.ReLU(),

nn.Linear(hidden_dim, latent_dim * 2) # 输出均值和方差

)

# 解码器

self.decoder = nn.Sequential(

nn.Linear(latent_dim, hidden_dim),

nn.ReLU(),

nn.Linear(hidden_dim, input_dim),

nn.Sigmoid()

)

def reparameterize(self, mu, logvar):

std = torch.exp(0.5 * logvar) # 计算标准差

eps = torch.randn_like(std) # 从标准正态分布中采样噪声

z = mu + eps * std # 重参数化技巧

return z

def forward(self, x):

# 编码

encoded = self.encoder(x)

mu, logvar = torch.chunk(encoded, 2, dim=1) # 将输出分割为均值和方差

z = self.reparameterize(mu, logvar) # 重参数化

# 解码

decoded = self.decoder(z)

return decoded, mu, logvar

# 定义训练函数

def train_vae(model, train_loader, num_epochs, learning_rate):

criterion = nn.BCELoss() # 二元交叉熵损失函数

optimizer = optim.Adam(model.parameters(), lr=learning_rate) # Adam优化器

model.train() # 设置模型为训练模式

for epoch in range(num_epochs):

total_loss = 0.0

for data in train_loader:

images, _ = data

images = images.view(images.size(0), -1) # 展平输入图像

optimizer.zero_grad()

# 前向传播

outputs, mu, logvar = model(images)

# 计算重构损失和KL散度

reconstruction_loss = criterion(outputs, images)

kl_divergence = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())

# 计算总损失

loss = reconstruction_loss + kl_divergence

# 反向传播和优化

loss.backward()

optimizer.step()

total_loss += loss.item()

# 输出当前训练轮次的损失

print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, total_loss / len(train_loader)))

print('Training finished.')

# 示例用法

if __name__ == '__main__':

# 设置超参数

input_dim = 784 # 输入维度(MNIST图像的大小为28x28,展平后为784)

hidden_dim = 256 # 隐层维度

latent_dim = 64 # 潜在空间维度

num_epochs = 10 # 训练轮次

learning_rate = 0.001 # 学习率

# 加载MNIST数据集

train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True)

train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=100, shuffle=True)

# 创建VAE模型

model = VAE(input_dim, hidden_dim, latent_dim)

# 训练VAE模型

train_vae(model, train_loader, num_epochs, learning_rate)

这段代码实现了一个简单的变分自编码器(VAE)模型,并使用MNIST数据集进行训练。在模型定义中,编码器和解码器分别使用全连接层构建,激活函数使用ReLU和Sigmoid。训练函数中使用了二元交叉熵损失函数和Adam优化器进行训练。在训练过程中,每个训练批次都会计算重构损失和KL散度,并将二者相加作为总损失进行优化。

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: