VAE——生成数字(Pytorch+mnist)

news/2024/5/4 6:00:10

1、简介

  • VAE(变分自编码器)同样由编码器和解码器组成,但与AE不同的是,VAE通过引入隐变量并利用概率分布来学习潜在表示。
  • VAE的编码器学习将输入数据映射到潜在空间的概率分布的参数,而不是直接映射到确定性的潜在表示。
  • VAE的解码器则通过从编码器学得的概率分布中采样,从而生成样本。
  • VAE的训练目标既包括最小化重构误差,也包括最大化编码器输出的潜在空间与单位高斯分布之间的KL散度,以促使学得的潜在表示更接近于标准正态分布。
  • VAE可以生成更连续、更具表现力的样本,并且具有更强的概率建模能力。
  • 本文利用VAE,输入数字图像。训练后,生成新的数字图像。
    • (100个epochs的结果)
  • 【注】本文案例输出的是随机的64个数字。

2、代码

  • import torch
    import torch.nn as nn
    import torch.optim as optim
    import torchvision
    import torch.nn.functional as F
    from torchvision.utils import save_image# 变分自编码器
    class VAE(nn.Module):def __init__(self):super(VAE, self).__init__()# 编码器层self.fc1 = nn.Linear(input_size, 512)  # 编码器输入层self.fc2 = nn.Linear(512, latent_size)self.fc3 = nn.Linear(512, latent_size)# 解码器层self.fc4 = nn.Linear(latent_size, 512)  # 解码器输入层self.fc5 = nn.Linear(512, input_size)  # 解码器输出层# 编码器部分def encode(self, x):x = F.relu(self.fc1(x))  # 编码器的隐藏表示mu = self.fc2(x)  # 潜在空间均值log_var = self.fc3(x)  # 潜在空间对数方差return mu, log_var# 重参数化技巧def reparameterize(self, mu, log_var):  # 从编码器输出的均值和对数方差中采样得到潜在变量zstd = torch.exp(0.5 * log_var)  # 计算标准差eps = torch.randn_like(std)  # 从标准正态分布中采样得到随机噪声return mu + eps * std  # 根据重参数化公式计算潜在变量z# 解码器部分def decode(self, z):z = F.relu(self.fc4(z))  # 将潜在变量 z 解码为重构图像return torch.sigmoid(self.fc5(z))  # 将隐藏表示映射回输入图像大小,并应用 sigmoid 激活函数,以产生重构图像# 前向传播def forward(self, x):  # 输入图像 x 通过编码器和解码器,得到重构图像和潜在变量的均值和对数方差mu, log_var = self.encode(x.view(-1, input_size))z = self.reparameterize(mu, log_var)return self.decode(z), mu, log_var# 使用重构损失和 KL 散度作为损失函数
    def loss_function(recon_x, x, mu, log_var):  # 参数:重构的图像、原始图像、潜在变量的均值、潜在变量的对数方差MSE = F.mse_loss(recon_x, x.view(-1, input_size), reduction='sum')  # 计算重构图像 recon_x 和原始图像 x 之间的均方误差KLD = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp())  # 计算潜在变量的KL散度return MSE + KLD  # 返回二进制交叉熵损失和 KLD 损失的总和作为最终的损失值def sample_images(epoch):with torch.no_grad():  # 上下文管理器,确保在该上下文中不会进行梯度计算。因为在这里只是生成样本而不需要梯度number = 64sample = torch.randn(number, latent_size).to(device)  # 生成一个形状为 (64, latent_size) 的张量,其中包含从标准正态分布中采样的随机数sample = model.decode(sample).cpu()  # 将随机样本输入到解码器中,解码器将其映射为图像save_image(sample.view(number, 1, 28, 28), f'sample{epoch}.png')  # 将生成的图像保存为文件if __name__ == '__main__':batch_size = 512  # 批次大小epochs = 100  # 学习周期sample_interval = 10  # 保存结果的周期learning_rate = 0.001  # 学习率input_size = 784  # 输入大小latent_size = 64  # 噪声大小# 载入 MNIST 数据集中的图片进行训练transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])  # 将图像转换为张量train_dataset = torchvision.datasets.MNIST(root="~/torch_datasets", train=True, transform=transform, download=True)  # 加载 MNIST 数据集的训练集,设置路径、转换和下载为 Truetrain_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)  # 创建一个数据加载器,用于加载训练数据,设置批处理大小和是否随机打乱数据# 在使用定义的 AE 类之前,有以下事情要做:# 配置要在哪个设备上运行device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 建立 VAE 模型并载入到 CPU 设备model = VAE().to(device)# Adam 优化器,学习率optimizer = optim.Adam(model.parameters(), lr=learning_rate)# 训练for epoch in range(epochs):train_loss = 0for batch_idx, (data, _) in enumerate(train_loader):data = data.to(device)  # 将输入数据移动到设备(GPU 或 CPU)上optimizer.zero_grad()  # 进行反向传播之前,需要将优化器中的梯度清零,以避免梯度的累积# 重构图像 recon_batch、潜在变量的均值 mu 和对数方差 log_varrecon_batch, mu, log_var = model(data)loss = loss_function(recon_batch, data, mu, log_var)  # 计算损失loss.backward()  # 计算损失相对于模型参数的梯度train_loss += loss.item()optimizer.step()  # 更新模型参数train_loss = train_loss / len(train_loader)  # # 计算每个周期的训练损失print('Epoch [{}/{}], Loss: {:.3f}'.format(epoch + 1, epochs, train_loss))# 每10次保存图像if (epoch + 1) % sample_interval == 0:sample_images(epoch + 1)# 每训练10次保存模型if (epoch + 1) % sample_interval == 0:torch.save(model.state_dict(), f'vae{epoch + 1}.pth')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.cpky.cn/p/11305.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

STM32——USART

一、通信 1.1通信是什么; 通信是将一个设备的数据发送到另一个设备中,从而实现硬件的扩展; 1.2通信的目的是什么; 实现硬件的扩展-在STM32中集成了很多功能,例如PWM输出,AD采集,定时器等&am…

傻瓜式启动关闭重启docker容器的脚本

运行脚本后,界面如下: 选择对应的编号后,会列举所有关闭的容器或者所有开启的容器列表,当我要启动一个容器 时输入1,就会出现下面的页面。 然后输入指定的编号后,就会启动对应的容器。 脚本代码如下&#…

Redis入门三(主从复制、Redis哨兵、Redis集群、缓存更新策略、缓存穿透、缓存击穿、缓存雪崩)

文章目录 一、主从复制1.单例redis存在的问题2.主从复制是什么?3.主从复制的原理4.主从搭建1)准备工作2)方式一3)方式二 5.python中操作1)原生操作2)Django的缓存操作 二、Redis哨兵(Redis-Sent…

Nuxt2 渲染时html比css加载快,导致闪屏/CSS样式迟滞/抖动问题记录

问题场景: 最近在用Nuxt2重写公司官网,但因为笔者不是专业前端,之前虽然也用vue2来写前端,但是用nuxt2来写项目还是第一次。在开发过程中虽然也磕磕碰碰,但因为开发的是官网,偏CMS型的网站,所以…

大数据主要组件HDFS Iceberg Hadoop spark介绍

HDFSIceberghadoopspark HDFS 面向PB级数据存储的分布式文件系统,可以存储任意类型与格式的数据文件,包括结构化的数据以及非结构化的数据。HDFS将导入的大数据文件切割成小数据块,均匀分布到服务器集群中的各个节点,并且每个数据…

windows系统安装RabbitMQ

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客…