当前位置:   article > 正文

CVAE-GAN——生成0-9数字图像(Pytorch+mnist)

CVAE-GAN——生成0-9数字图像(Pytorch+mnist)

1、简介

  • CVAE-GAN(Conditional Variational Autoencoder Generative Adversarial Network)是一种混合型生成模型,结合了条件变分自编码器(CVAE)和生成对抗网络(GAN)的思想。
  • 在CVAE-GAN中,CVAE的编码器和解码器被结合在一起,作为GAN的生成器,用于学习数据的潜在表示和生成数据。同时,GAN的判别器帮助训练生成器生成更逼真的数据。
  • CVAE-GAN的工作方式如下:
    • 编码器(Encoder):接收输入数据和条件信息,并将其映射到潜在空间中的潜在表示。
    • 解码器(Decoder/Generator):接收从潜在空间中采样的潜在表示和条件信息,并生成与条件信息相关的数据样本。
    • 判别器(Discriminator):接收真实样本和生成样本(包括条件信息),并尝试将它们区分开来。
    • 损失函数:CVAE-GAN的损失函数通常包括两部分:一部分是CVAE的重构损失,用于确保生成数据与输入数据相似;另一部分是GAN的对抗损失,用于鼓励生成数据与真实数据分布相匹配。
  • CVAE-GAN通过结合CVAE和GAN的优点,既可以学习数据的潜在表示,又可以生成高质量的数据样本,并且可以通过条件信息控制生成过程。这种结合使得CVAE-GAN在诸如图像生成、图像编辑等任务中具有很好的表现。
  • 本文利用CVAE-GAN,输入数字图像和对应的标签。训练后,生成0-9数字图像。
    • (epoch=10)
    • (epoch=20)
    • (epoch=30)
    • (epoch=40)
    • (epoch=50)

2、代码

2.1、生成器(Genration.py)

    1. import torch
    2. import torch.nn as nn
    3. import torch.nn.functional as F
    4. # 变分自编码器
    5. class CVAE(nn.Module):
    6. def __init__(self, input_size, num_classes):
    7. super(CVAE, self).__init__()
    8. self.num_classes = num_classes # 标签数量
    9. self.input_size = input_size
    10. self.potential_size = 64 # 潜在空间大小
    11. # 编码器层
    12. self.fc1 = nn.Linear(self.input_size + self.num_classes, 512) # 编码器输入层
    13. self.fc2 = nn.Linear(512, self.potential_size)
    14. self.fc3 = nn.Linear(512, self.potential_size)
    15. # 解码器层
    16. self.fc4 = nn.Linear(self.potential_size + self.num_classes, 512) # 解码器输入层
    17. self.fc5 = nn.Linear(512, self.input_size) # 解码器输出层
    18. # 编码器部分
    19. def encode(self, x):
    20. x = F.relu(self.fc1(x)) # 编码器的隐藏表示
    21. mu = self.fc2(x) # 潜在空间均值
    22. log_var = self.fc3(x) # 潜在空间对数方差
    23. return mu, log_var
    24. # 重参数化技巧
    25. def reparameterize(self, mu, log_var): # 从编码器输出的均值和对数方差中采样得到潜在变量z
    26. std = torch.exp(0.5 * log_var) # 计算标准差
    27. eps = torch.randn_like(std) # 从标准正态分布中采样得到随机噪声
    28. return mu + eps * std # 根据重参数化公式计算潜在变量z
    29. # 解码器部分
    30. def decode(self, z):
    31. z = F.relu(self.fc4(z)) # 将潜在变量 z 解码为重构图像
    32. return torch.sigmoid(self.fc5(z)) # 将隐藏表示映射回输入图像大小,并应用 sigmoid 激活函数,以产生重构图像
    33. # 前向传播
    34. def forward(self, x, y): # 输入图像 x,标签 y 通过编码器和解码器,得到重构图像和潜在变量的均值和对数方差
    35. x = torch.cat([x, y], dim=1)
    36. mu, log_var = self.encode(x)
    37. z = self.reparameterize(mu, log_var)
    38. z = torch.cat([z, y], dim=1)
    39. return self.decode(z), mu, log_var
    40. # 使用重构损失和 KL 散度作为损失函数
    41. def generator_loss(self, recon_x, x, mu, log_var): # 参数:重构的图像、原始图像、潜在变量的均值、潜在变量的对数方差
    42. MSE = F.mse_loss(recon_x, x.view(-1, self.input_size), reduction='sum') # 计算重构图像 recon_x 和原始图像 x 之间的均方误差
    43. KLD = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp()) # 计算潜在变量的KL散度
    44. return MSE + KLD # 返回二进制交叉熵损失和 KLD 损失的总和作为最终的损失值

2.2、判别器(Discriminator.py)

    1. import torch
    2. import torch.nn as nn
    3. import torch.nn.functional as F
    4. # 定义判别器类
    5. class Discriminator(nn.Module):
    6. def __init__(self, input_size, num_classes):
    7. super(Discriminator, self).__init__()
    8. self.num_classes = num_classes # 标签数量
    9. self.input_size = input_size
    10. # 判别器层
    11. self.fc1 = nn.Linear(self.input_size + self.num_classes, 512)
    12. self.fc2 = nn.Linear(512, 256)
    13. self.fc3 = nn.Linear(256, 1)
    14. def forward(self, x):
    15. x = F.relu(self.fc1(x))
    16. x = F.relu(self.fc2(x))
    17. return torch.sigmoid(self.fc3(x))

2.3、训练(train.py)

    1. from Discriminator import Discriminator
    2. from Genration import CVAE
    3. import torch
    4. import torch.optim as optim
    5. import torchvision
    6. import torch.nn.functional as F
    7. from torchvision.utils import save_image
    8. # 生成0-9数字
    9. def sample_images(epoch):
    10. with torch.no_grad(): # 上下文管理器,确保在该上下文中不会进行梯度计算。因为在这里只是生成样本而不需要梯度
    11. number = 10
    12. # 生成标签
    13. sample_labels = torch.arange(10).long().to(device) # 0-9的标签
    14. sample_labels_onehot = F.one_hot(sample_labels, num_classes=10).float()
    15. # 生成随机噪声
    16. sample = torch.randn(number, latent_size).to(device) # 生成一个形状为 (64, latent_size) 的张量,其中包含从标准正态分布中采样的随机数
    17. sample = torch.cat([sample, sample_labels_onehot], dim=1) # 连接图片和标签
    18. sample = cvae_model.decode(sample).cpu() # 将随机样本输入到解码器中,解码器将其映射为图像
    19. save_image(sample.view(number, 1, 28, 28), f'sample{epoch}.png', nrow=int(number / 2)) # 将生成的图像保存为文件
    20. def generator_loss(recon_x, x, mu, log_var, discriminator_output):
    21. mse_loss = F.mse_loss(recon_x, x.view(-1, input_size), reduction='sum') # 计算重构图像 recon_x 和原始图像 x 之间的均方误差
    22. kld_loss = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp())
    23. gan_loss = F.binary_cross_entropy(discriminator_output, torch.ones_like(discriminator_output))
    24. return mse_loss + kld_loss + gan_loss
    25. def discriminator_loss_acc(real_output, fake_output):
    26. # 损失
    27. real_loss = F.binary_cross_entropy(real_output, torch.ones_like(real_output))
    28. fake_loss = F.binary_cross_entropy(fake_output, torch.zeros_like(fake_output))
    29. total_loss = real_loss + fake_loss
    30. # 精度
    31. real_pred = torch.round(real_output)
    32. fake_pred = torch.round(fake_output)
    33. real_acc = (real_pred == 1).sum().item() / real_output.numel()
    34. fake_acc = (fake_pred == 0).sum().item() / fake_output.numel()
    35. total_acc = (real_acc + fake_acc) / 2
    36. return total_loss, total_acc
    37. if __name__ == '__main__':
    38. batch_size = 512 # 批次大小
    39. epochs = 50 # 学习周期
    40. sample_interval = 10 # 保存结果的周期
    41. learning_rate = 0.001 # 学习率
    42. input_size = 784 # 输入大小
    43. num_classes = 10 # 标签数量
    44. latent_size = 64 # 噪声大小
    45. # 载入 MNIST 数据集中的图片进行训练
    46. transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()]) # 将图像转换为张量
    47. train_dataset = torchvision.datasets.MNIST(
    48. root="~/torch_datasets", train=True, transform=transform, download=True
    49. ) # 加载 MNIST 数据集的训练集,设置路径、转换和下载为 True
    50. train_loader = torch.utils.data.DataLoader(
    51. train_dataset, batch_size=batch_size, shuffle=True
    52. ) # 创建一个数据加载器,用于加载训练数据,设置批处理大小和是否随机打乱数据
    53. # 配置要在哪个设备上运行
    54. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    55. cvae_model = CVAE(input_size, num_classes).to(device)
    56. dis_model = Discriminator(input_size, num_classes).to(device)
    57. optimizer_cvae = optim.Adam(cvae_model.parameters(), lr=learning_rate)
    58. optimizer_dis = optim.Adam(dis_model.parameters(), lr=learning_rate)
    59. for epoch in range(epochs):
    60. generator_loss_total = 0
    61. discriminator_loss_total = 0
    62. discriminator_acc_total = 0
    63. for batch_idx, (data, labels) in enumerate(train_loader):
    64. data = data.to(device)
    65. data = data.view(-1, input_size)
    66. labels = F.one_hot(labels, num_classes).float().to(device)
    67. # 更新判别器
    68. optimizer_dis.zero_grad()
    69. recon_batch, _, _ = cvae_model(data, labels) # 生成虚假数据
    70. fake_data = torch.cat([recon_batch, labels], dim=1)
    71. real_data = torch.cat([data, labels], dim=1)
    72. fake_output = dis_model(fake_data)
    73. real_output = dis_model(real_data)
    74. d_loss, d_acc = discriminator_loss_acc(real_output, fake_output) # 计算判别器损失和精度
    75. d_loss.backward()
    76. optimizer_dis.step() # 更新模型参数
    77. # 更新生成器
    78. optimizer_cvae.zero_grad()
    79. recon_batch, mu, log_var = cvae_model(data, labels)
    80. fake_data = torch.cat([recon_batch, labels], dim=1)
    81. fake_output = dis_model(fake_data)
    82. g_loss = generator_loss(recon_batch, data, mu, log_var, fake_output)
    83. g_loss.backward()
    84. optimizer_cvae.step()
    85. generator_loss_total += g_loss.item()
    86. discriminator_loss_total += d_loss.item()
    87. discriminator_acc_total += d_acc
    88. generator_loss_avg = generator_loss_total / len(train_loader)
    89. discriminator_loss_avg = discriminator_loss_total / len(train_loader)
    90. discriminator_acc_avg = discriminator_acc_total / len(train_loader)
    91. print('Epoch [{}/{}], Generator Loss: {:.3f}, Discriminator Loss: {:.3f}, Discriminator Acc: {:.2f}%'.format(
    92. epoch + 1, epochs, generator_loss_avg, discriminator_loss_avg, discriminator_acc_avg * 100))
    93. if (epoch + 1) % sample_interval == 0:
    94. sample_images(epoch + 1)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/359156
推荐阅读
相关标签
  

闽ICP备14008679号