赞
踩
本文为365天深度学习训练营 中的学习记录博客
原作者:K同学啊
深度卷积对抗网络(Deep Convolutional Generative Adversarial Networks,简称DCGAN)是一种深度学习模型,由生成器(Generator)和判别器(Discriminator)两个神经网络组成。DCGAN结合了卷积神经网络(Convolutional Neural Networks,简称CNN)和生成对抗网络(Generative Adversarial Networks,简称GAN)的思想,用于生成逼真的图像。
基础任务:
- 学习DCGAN的基本原理
- 了解DCGAN与GAN的区别
- 绘制DCGAN网络结构图
- 学习本文DCGAN代码,并跑通代码
进阶任务:
调用训练好的模型生成新图像
一、理论基础
判别器模型使用卷积步长取代了空间池化,生成器模型中使用反卷积操作扩大数据维度。
除了生成器模型的输出层和判别器模型的输入层,在整个对抗网络的其它层上都使用了Batch Normalization,原因是Batch Normalization可以稳定学习,有助于优化初始化参数值不良而导致的训练问题。
整个网络去除了全连接层,直接使用卷积层连接生成器和判别器的输入层以及输出层。
在生成器的输出层使用Tanh激活函数以控制输出范围,而在其它层中均使用了ReLU激活函数;在判别器上使用Leaky ReLU激活函数。
图1:DCGAN结构图:
图1所示了一种常见的 DCGAN 结构。主要包含了左边生成网络(Generator)和右边判别网络(Discriminator),其各有四个转置卷积层(DeConv)和四个卷积层(Conv)。其中 4 * 4 * 512 代表这一层共有512个大小为4*4的特征图,括号内的BN和ReLU分别表示在卷积层之前和之后分别使用了批归一化和修正线性单元激活两种处理方法。Tanh 和 LeakyReLU 分别表示双切正切激活和弱修正线性激活。
图2:DCGAN基本结构示意图:
二、前期准备
我的环境:
语言环境:Python3.10.11
编译器:Jupyter Notebook
深度学习框架:Pytorch 2.2.2+cpu
数据集:头像数据集
import torch, random, random, os import torch.nn as nn import torch.nn.parallel import torch.optim as optim import torch.utils.data import torchvision.datasets as dset import torchvision.transforms as transforms import torchvision.utils as vutils import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from IPython.display import HTML manualSeed = 999 # 随机种子 print("Random Seed: ", manualSeed) random.seed(manualSeed) torch.manual_seed(manualSeed) torch.use_deterministic_algorithms(True) # Needed for reproducible results
输出结果:
Random Seed: 999
dataroot = "E:/365-jinjieying/GAN rumenshizhan/G2/" # 数据路径
batch_size = 128 # 训练过程中的批次大小
image_size = 64 # 图像的尺寸(宽度和高度)
nz = 100 # z潜在向量的大小(生成器输入的尺寸)
ngf = 64 # 生成器中的特征图大小
ndf = 64 # 判别器中的特征图大小
num_epochs = 50 # 训练的总轮数,如果你显卡不太行,可调小,但是生成效果会随之降低
lr = 0.0002 # 学习率
beta1 = 0.5 # Adam优化器的Beta1超参数
# 我们可以按照我们设置的方式使用图像文件夹数据集。 # 创建数据集 dataset = dset.ImageFolder(root=dataroot, transform=transforms.Compose([ transforms.Resize(image_size), # 调整图像大小 transforms.CenterCrop(image_size), # 中心裁剪图像 transforms.ToTensor(), # 将图像转换为张量 transforms.Normalize((0.5, 0.5, 0.5), # 标准化图像张量 (0.5, 0.5, 0.5)), ])) # 创建数据加载器 dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, # 批量大小 shuffle=True, # 是否打乱数据集 num_workers=5 # 使用多个线程加载数据的工作进程数 ) # 选择要在哪个设备上运行代码 device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu") print("使用的设备是:",device) # 绘制一些训练图像 real_batch = next(iter(dataloader)) plt.figure(figsize=(8,8)) plt.axis("off") plt.title("Training Images") plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:24], padding=2, normalize=True).cpu(),(1,2,0)))
输出结果(包括后面的图片):
使用的设备是: cpu
三、定义模型
# 自定义权重初始化函数,作用于netG和netD
def weights_init(m):
# 获取当前层的类名
classname = m.__class__.__name__
# 如果类名中包含'Conv',即当前层是卷积层
if classname.find('Conv') != -1:
# 使用正态分布初始化权重数据,均值为0,标准差为0.02
nn.init.normal_(m.weight.data, 0.0, 0.02)
# 如果类名中包含'BatchNorm',即当前层是批归一化层
elif classname.find('BatchNorm') != -1:
# 使用正态分布初始化权重数据,均值为1,标准差为0.02
nn.init.normal_(m.weight.data, 1.0, 0.02)
# 使用常数初始化偏置项数据,值为0
nn.init.constant_(m.bias.data, 0)
class Generator(nn.Module): def __init__(self): super(Generator, self).__init__() self.main = nn.Sequential( # 输入为Z,经过一个转置卷积层 nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False), nn.BatchNorm2d(ngf * 8), # 批归一化层,用于加速收敛和稳定训练过程 nn.ReLU(True), # ReLU激活函数 # 输出尺寸:(ngf*8) x 4 x 4 nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False), nn.BatchNorm2d(ngf * 4), nn.ReLU(True), # 输出尺寸:(ngf*4) x 8 x 8 nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False), nn.BatchNorm2d(ngf * 2), nn.ReLU(True), # 输出尺寸:(ngf*2) x 16 x 16 nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False), nn.BatchNorm2d(ngf), nn.ReLU(True), # 输出尺寸:(ngf) x 32 x 32 nn.ConvTranspose2d(ngf, 3, 4, 2, 1, bias=False), nn.Tanh() # Tanh激活函数 # 输出尺寸:3 x 64 x 64 ) def forward(self, input): return self.main(input)
# 创建生成器
netG = Generator().to(device)
# 使用 "weights_init" 函数对所有权重进行随机初始化,
# 平均值(mean)设置为0,标准差(stdev)设置为0.02。
netG.apply(weights_init)
# 打印生成器模型
print(netG)
输出结果:
Generator( (main): Sequential( (0): ConvTranspose2d(100, 512, kernel_size=(4, 4), stride=(1, 1), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU(inplace=True) (3): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (5): ReLU(inplace=True) (6): ConvTranspose2d(256, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (7): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (8): ReLU(inplace=True) (9): ConvTranspose2d(128, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (10): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (11): ReLU(inplace=True) (12): ConvTranspose2d(64, 3, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (13): Tanh() ) )
class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() # 定义判别器的主要结构,使用Sequential容器将多个层按顺序组合在一起 self.main = nn.Sequential( # 输入大小为3 x 64 x 64 nn.Conv2d(3, ndf, 4, 2, 1, bias=False), nn.LeakyReLU(0.2, inplace=True), # 输出大小为(ndf) x 32 x 32 nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False), nn.BatchNorm2d(ndf * 2), nn.LeakyReLU(0.2, inplace=True), # 输出大小为(ndf*2) x 16 x 16 nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False), nn.BatchNorm2d(ndf * 4), nn.LeakyReLU(0.2, inplace=True), # 输出大小为(ndf*4) x 8 x 8 nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False), nn.BatchNorm2d(ndf * 8), nn.LeakyReLU(0.2, inplace=True), # 输出大小为(ndf*8) x 4 x 4 nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False), nn.Sigmoid() ) def forward(self, input): # 将输入通过判别器的主要结构进行前向传播 return self.main(input)
判别器的主要结构是一系列的卷积层、批标准化层、激活函数层的组合。通过这些层的组合,输入的图像逐渐被降维并提取特征,最终输出一个标量值,表示输入图像被判别为真实图像的概率。
在前向传播方法(forward)中,输入数据通过判别器的主要结构进行前向传播,并返回输出结果。
# 创建判别器模型
netD = Discriminator().to(device)
# 应用 "weights_init" 函数来随机初始化所有权重
# 使用 mean=0, stdev=0.2 的方式进行初始化
netD.apply(weights_init)
# 打印模型
print(netD)
输出结果:
Discriminator( (main): Sequential( (0): Conv2d(3, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (1): LeakyReLU(negative_slope=0.2, inplace=True) (2): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (3): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (4): LeakyReLU(negative_slope=0.2, inplace=True) (5): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (6): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (7): LeakyReLU(negative_slope=0.2, inplace=True) (8): Conv2d(256, 512, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False) (9): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (10): LeakyReLU(negative_slope=0.2, inplace=True) (11): Conv2d(512, 1, kernel_size=(4, 4), stride=(1, 1), bias=False) (12): Sigmoid() ) )
四、训练模型
# 初始化“BCELoss”损失函数
criterion = nn.BCELoss()
# 创建用于可视化生成器进程的潜在向量批次
fixed_noise = torch.randn(64, nz, 1, 1, device=device)
real_label = 1.
fake_label = 0.
# 为生成器(G)和判别器(D)设置Adam优化器
optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
这段代码是一个典型的GAN训练循环。在训练过程中,首先更新判别器网络,然后更新生成器网络。在每个epoch的每个batch中,会进行以下操作:
(1)更新判别器网络:通过训练真实图像样本和生成图像样本,最大化判别器的损失。具体步骤如下:
(2)更新生成器网络:通过最大化生成器的损失,迫使生成器产生更逼真的图像样本。具体步骤如下:
(3)输出训练统计信息:每隔一定的步数,输出当前训练的epoch、batch以及判别器和生成器的损失值等信息。
(4)保存损失值:将生成器和判别器的损失值存储到相应的列表中,以便后续绘图和分析。
(5)检查生成器的性能:每隔一定的步数或者在训练结束时,通过将固定的噪声输入生成器,生成一批图像样本,并保存到img_list列表中。这样可以观察生成器在训练过程中生成的图像质量的变化。
(6)更新迭代次数:每完成一个batch的训练,将迭代次数iters加1。
总体来说,这段代码实现了GAN的训练过程,通过交替更新判别器和生成器的参数,目标是使生成器生成逼真的图像样本,同时判别器能够准确区分真实图像样本和生成图像样本。
img_list = [] # 用于存储生成的图像列表 G_losses = [] # 用于存储生成器的损失列表 D_losses = [] # 用于存储判别器的损失列表 iters = 0 # 迭代次数 print("Starting Training Loop...") # 输出训练开始的提示信息 # 对于每个epoch(训练周期) for epoch in range(num_epochs): # 对于dataloader中的每个batch for i, data in enumerate(dataloader, 0): ############################ # (1) 更新判别器网络:最大化 log(D(x)) + log(1 - D(G(z))) ########################### ## 使用真实图像样本训练 netD.zero_grad() # 清除判别器网络的梯度 # 准备真实图像的数据 real_cpu = data[0].to(device) b_size = real_cpu.size(0) label = torch.full((b_size,), real_label, dtype=torch.float, device=device) # 创建一个全是真实标签的张量 # 将真实图像样本输入判别器,进行前向传播 output = netD(real_cpu).view(-1) # 计算真实图像样本的损失 errD_real = criterion(output, label) # 通过反向传播计算判别器的梯度 errD_real.backward() D_x = output.mean().item() # 计算判别器对真实图像样本的输出的平均值 ## 使用生成图像样本训练 # 生成一批潜在向量 noise = torch.randn(b_size, nz, 1, 1, device=device) # 使用生成器生成一批假图像样本 fake = netG(noise) label.fill_(fake_label) # 创建一个全是假标签的张量 # 将所有生成的图像样本输入判别器,进行前向传播 output = netD(fake.detach()).view(-1) # 计算判别器对生成图像样本的损失 errD_fake = criterion(output, label) # 通过反向传播计算判别器的梯度 errD_fake.backward() D_G_z1 = output.mean().item() # 计算判别器对生成图像样本的输出的平均值 # 计算判别器的总损失,包括真实图像样本和生成图像样本的损失之和 errD = errD_real + errD_fake # 更新判别器的参数 optimizerD.step() ############################ # (2) 更新生成器网络:最大化 log(D(G(z))) ########################### netG.zero_grad() # 清除生成器网络的梯度 label.fill_(real_label) # 对于生成器成本而言,将假标签视为真实标签 # 由于刚刚更新了判别器,再次将所有生成的图像样本输入判别器,进行前向传播 output = netD(fake).view(-1) # 根据判别器的输出计算生成器的损失 errG = criterion(output, label) # 通过反向传播计算生成器的梯度 errG.backward() D_G_z2 = output.mean().item() # 计算判别器对生成器输出的平均值 # 更新生成器的参数 optimizerG.step() # 输出训练统计信息 if i % 400 == 0: print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f' % (epoch, num_epochs, i, len(dataloader), errD.item(), errG.item(), D_x, D_G_z1, D_G_z2)) # 保存损失值以便后续绘图 G_losses.append(errG.item()) D_losses.append(errD.item()) # 通过保存生成器在固定噪声上的输出来检查生成器的性能 if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)): with torch.no_grad(): fake = netG(fixed_noise).detach().cpu() img_list.append(vutils.make_grid(fake, padding=2, normalize=True)) iters += 1
输出结果:
Starting Training Loop... [0/50][0/36] Loss_D: 1.7236 Loss_G: 5.1258 D(x): 0.5475 D(G(z)): 0.5840 / 0.0090 [1/50][0/36] Loss_D: 0.1670 Loss_G: 7.0507 D(x): 0.9398 D(G(z)): 0.0032 / 0.0017 [2/50][0/36] Loss_D: 0.0205 Loss_G: 39.1438 D(x): 0.9833 D(G(z)): 0.0000 / 0.0000 [3/50][0/36] Loss_D: 3.7439 Loss_G: 21.9752 D(x): 0.2176 D(G(z)): 0.0000 / 0.0000 [4/50][0/36] Loss_D: 0.8738 Loss_G: 2.7060 D(x): 0.5909 D(G(z)): 0.0302 / 0.1410 [5/50][0/36] Loss_D: 0.4764 Loss_G: 5.3485 D(x): 0.8807 D(G(z)): 0.2351 / 0.0084 [6/50][0/36] Loss_D: 0.8423 Loss_G: 3.8487 D(x): 0.5749 D(G(z)): 0.0297 / 0.0340 [7/50][0/36] Loss_D: 0.6693 Loss_G: 7.5742 D(x): 0.8020 D(G(z)): 0.2691 / 0.0011 [8/50][0/36] Loss_D: 0.4876 Loss_G: 5.2915 D(x): 0.8403 D(G(z)): 0.2169 / 0.0091 [9/50][0/36] Loss_D: 0.4631 Loss_G: 3.5658 D(x): 0.8549 D(G(z)): 0.2022 / 0.0541 [10/50][0/36] Loss_D: 0.6300 Loss_G: 5.3001 D(x): 0.8657 D(G(z)): 0.3255 / 0.0147 [11/50][0/36] Loss_D: 0.7151 Loss_G: 7.5457 D(x): 0.9666 D(G(z)): 0.4073 / 0.0035 [12/50][0/36] Loss_D: 0.6189 Loss_G: 2.9570 D(x): 0.6931 D(G(z)): 0.0855 / 0.0890 [13/50][0/36] Loss_D: 0.4698 Loss_G: 3.7897 D(x): 0.8271 D(G(z)): 0.1884 / 0.0390 [14/50][0/36] Loss_D: 0.7368 Loss_G: 3.5642 D(x): 0.7721 D(G(z)): 0.2447 / 0.0633 [15/50][0/36] Loss_D: 0.3868 Loss_G: 5.2894 D(x): 0.8856 D(G(z)): 0.1969 / 0.0093 [16/50][0/36] Loss_D: 0.7691 Loss_G: 6.2546 D(x): 0.9263 D(G(z)): 0.3821 / 0.0040 [17/50][0/36] Loss_D: 0.3868 Loss_G: 3.6160 D(x): 0.8224 D(G(z)): 0.1242 / 0.0459 [18/50][0/36] Loss_D: 0.3331 Loss_G: 5.0577 D(x): 0.9683 D(G(z)): 0.2289 / 0.0147 [19/50][0/36] Loss_D: 0.3890 Loss_G: 4.7641 D(x): 0.8990 D(G(z)): 0.2028 / 0.0158 [20/50][0/36] Loss_D: 0.3035 Loss_G: 4.2962 D(x): 0.8438 D(G(z)): 0.0687 / 0.0200 [21/50][0/36] Loss_D: 1.8367 Loss_G: 0.6489 D(x): 0.3455 D(G(z)): 0.0036 / 0.6609 [22/50][0/36] Loss_D: 0.4665 Loss_G: 5.6370 D(x): 0.7161 D(G(z)): 0.0196 / 0.0103 [23/50][0/36] Loss_D: 0.5452 Loss_G: 4.1468 D(x): 0.7789 D(G(z)): 0.1878 / 0.0299 [24/50][0/36] Loss_D: 0.6071 Loss_G: 6.2045 D(x): 0.8965 D(G(z)): 0.3422 / 0.0036 [25/50][0/36] Loss_D: 0.2952 Loss_G: 6.0639 D(x): 0.9489 D(G(z)): 0.1862 / 0.0055 [26/50][0/36] Loss_D: 0.4313 Loss_G: 5.2921 D(x): 0.9108 D(G(z)): 0.2455 / 0.0094 [27/50][0/36] Loss_D: 0.4372 Loss_G: 5.1677 D(x): 0.8730 D(G(z)): 0.1964 / 0.0152 [28/50][0/36] Loss_D: 1.2932 Loss_G: 4.0526 D(x): 0.4024 D(G(z)): 0.0048 / 0.0522 [29/50][0/36] Loss_D: 0.7796 Loss_G: 2.4516 D(x): 0.6147 D(G(z)): 0.0800 / 0.1393 [30/50][0/36] Loss_D: 0.6354 Loss_G: 4.8091 D(x): 0.8551 D(G(z)): 0.2941 / 0.0179 [31/50][0/36] Loss_D: 0.3797 Loss_G: 4.9613 D(x): 0.9307 D(G(z)): 0.2284 / 0.0136 [32/50][0/36] Loss_D: 0.2428 Loss_G: 4.6010 D(x): 0.9156 D(G(z)): 0.1098 / 0.0186 [33/50][0/36] Loss_D: 0.5072 Loss_G: 4.3122 D(x): 0.8620 D(G(z)): 0.2500 / 0.0245 [34/50][0/36] Loss_D: 0.5032 Loss_G: 5.1097 D(x): 0.9554 D(G(z)): 0.3119 / 0.0129 [35/50][0/36] Loss_D: 0.6527 Loss_G: 5.7284 D(x): 0.8805 D(G(z)): 0.3481 / 0.0080 [36/50][0/36] Loss_D: 0.3805 Loss_G: 3.2829 D(x): 0.7557 D(G(z)): 0.0538 / 0.0582 [37/50][0/36] Loss_D: 0.5703 Loss_G: 2.7791 D(x): 0.7273 D(G(z)): 0.1099 / 0.1194 [38/50][0/36] Loss_D: 0.5249 Loss_G: 4.2585 D(x): 0.8005 D(G(z)): 0.1861 / 0.0291 [39/50][0/36] Loss_D: 0.3888 Loss_G: 4.3294 D(x): 0.9441 D(G(z)): 0.2450 / 0.0244 [40/50][0/36] Loss_D: 0.3919 Loss_G: 3.8665 D(x): 0.8714 D(G(z)): 0.1923 / 0.0338 [41/50][0/36] Loss_D: 0.4774 Loss_G: 3.8450 D(x): 0.7290 D(G(z)): 0.0859 / 0.0435 [42/50][0/36] Loss_D: 0.8533 Loss_G: 2.1504 D(x): 0.5499 D(G(z)): 0.0590 / 0.1885 [43/50][0/36] Loss_D: 0.2122 Loss_G: 3.7659 D(x): 0.9077 D(G(z)): 0.0971 / 0.0384 [44/50][0/36] Loss_D: 4.0494 Loss_G: 9.0735 D(x): 0.9972 D(G(z)): 0.9550 / 0.0009 [45/50][0/36] Loss_D: 0.7066 Loss_G: 5.1055 D(x): 0.9402 D(G(z)): 0.4092 / 0.0093 [46/50][0/36] Loss_D: 0.4008 Loss_G: 4.8491 D(x): 0.9110 D(G(z)): 0.2363 / 0.0117 [47/50][0/36] Loss_D: 0.3970 Loss_G: 3.0069 D(x): 0.7535 D(G(z)): 0.0492 / 0.0796 [48/50][0/36] Loss_D: 0.3218 Loss_G: 3.5868 D(x): 0.8389 D(G(z)): 0.1082 / 0.0438 [49/50][0/36] Loss_D: 1.4430 Loss_G: 5.6858 D(x): 0.9269 D(G(z)): 0.6618 / 0.0078
plt.figure(figsize=(10,5))
plt.title("Generator and Discriminator Loss During Training")
plt.plot(G_losses,label="G")
plt.plot(D_losses,label="D")
plt.xlabel("iterations")
plt.ylabel("Loss")
plt.legend()
plt.show()
输出结果:
# 创建一个大小为8x8的图形对象
fig = plt.figure(figsize=(8, 8))
# 不显示坐标轴
plt.axis("off")
# 将图像列表img_list中的图像转置并创建一个包含每个图像的单个列表ims
ims = [[plt.imshow(np.transpose(i, (1, 2, 0)), animated=True)] for i in img_list]
# 使用图形对象、图像列表ims以及其他参数创建一个动画对象ani
ani = animation.ArtistAnimation(fig, ims, interval=1000, repeat_delay=1000, blit=True)
# 将动画以HTML形式呈现
HTML(ani.to_jshtml())
输出结果:
animation.ArtistAnimation类的作用是基于一组Artist对象来创建动画。
animation.ArtistAnimation类是matplotlib库中的一个动画类,它主要用于制作动画。这个类的核心功能是将一系列的Artist对象(如图像、曲线等)按顺序展示,从而形成动画效果。每一帧的Artist对象在展示后会被擦除,以便下一帧的对象展示。具体来说,animation.ArtistAnimation类的构造方法接收以下参数:
●fig:表示动画所在的画布,通常使用plt.figure()生成。
●artists:一个列表,每个元素代表一帧的Artist对象集合。
●interval:帧间隔时间,单位为毫秒。
●repeat_delay:动画重复播放之间的延迟时间,单位为毫秒。
●repeat:是否重复播放动画。
●blit:是否使用双缓冲技术优化绘图性能。
总的来说,animation.ArtistAnimation类提供了一种灵活的方式来创建动画,尤其是当需要展示一系列静态图像或者matplotlib绘图对象时。通过调整参数,可以控制动画的播放速度、是否循环播放以及绘图性能等。
# 从数据加载器中获取一批真实图像 real_batch = next(iter(dataloader)) # 绘制真实图像 plt.figure(figsize=(15,15)) plt.subplot(1,2,1) plt.axis("off") plt.title("Real Images") plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=5, normalize=True).cpu(),(1,2,0))) # 绘制上一个时期生成的假图像 plt.subplot(1,2,2) plt.axis("off") plt.title("Fake Images") plt.imshow(np.transpose(img_list[-1],(1,2,0))) plt.show()
输出结果:
五、总结
DCGAN的生成器中使用了反卷积操作,它能放大特征图,从而改变尺寸。而判别器中则使用卷积步长取代空间池化。经过训练,生成的图像已经有部分接近真实图像了。
不过训练效果还是比较差的,人眼一看就知道比较失真,需要对模型进行调试和修改。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。