本教程旨在通过实际案例,深入浅出地介绍DCGAN(深度卷积生成对抗网络)的概念与实现。我们将通过训练一个GAN模型,使其能够生成新的名人肖像,这一过程依赖于大量真实的名人照片作为训练数据。本教程所采用的代码主要基于PyTorch官方示例中的DCGAN实现,我们将对其进行详细解析,以阐明模型的运作机制和原理。尽管学习本文并不需要前置知识,但对于初学者而言,理解整个训练过程可能需要一些时间。此外,为了提高训练效率,我们推荐使用至少一个GPU。现在,让我们开始探索。
生成对抗网络概述
什么是GAN?
GAN,即生成对抗网络,是一种深度学习架构,旨在捕捉训练数据的分布特征,并据此生成新的数据样本。Ian Goodfellow于2014年首次提出这一概念。GAN包含两个核心组件:生成器(G)和判别器(D)。生成器负责创建逼真的“假”图像,而判别器则负责评估图像的真实性,即判断其是否来自真实的训练数据。在训练过程中,生成器不断优化其生成的图像,以提高其欺骗判别器的概率;同时,判别器也在努力提升其识别真实与伪造图像的能力。当生成器生成的图像达到足以迷惑判别器的程度,而判别器对图像真伪的判断准确率降至50%时,生成器与判别器之间的博弈达到一种动态平衡。
为了更好地理解,我们首先定义本教程中使用的符号。设 代表真实图像数据。 表示判别器网络,其输出为一个标量概率值,用于估计输入图像 来自真实数据集的概率。考虑到我们处理的是图像数据,D(x)的输入应是一个3x64x64的CHW格式图像。直观而言,若 来自真实数据集, 的值应较高;若 由生成器生成,其值则应较低。 可视为一个二分类器。
生成器的表示则设定为 ,它是一个从标准正态分布中抽取的潜向量。 表示生成器函数,负责将潜向量 映射至数据空间,以生成图像。生成器 的目标是学习训练数据的分布特征( ),从而能够依据此分布( )生成逼真的图像样本。
因此, 表示生成器 生成的图像被判别器判定为真实图像的概率。正如Goodfellow在其论文中所描述, 与 之间进行了一场极小极大博弈: 力求最大化其正确识别真伪图像的概率 ,而 则努力最小化 将其输出判定为伪造的概率 。根据论文,GAN的损失函数定义为:
理论上,这场博弈的解应在 时达成,即生成器生成的样本分布与真实数据分布一致,此时判别器对图像真伪的判断为随机选择。然而,GAN的收敛性理论仍在积极探讨中,实际训练过程中模型并不总能达到这一理想状态。
DCGAN定义
DCGAN,即深度卷积生成对抗网络,是GAN框架的一个扩展版本,其主要特点是判别器和生成器均采用卷积层和卷积转置层。这一模型最初由Radford等人提出,并在论文《Unsupervised Representation Learning With Deep Convolutional Generative Adversarial Networks》中进行了描述。判别器由步进卷积层、批量归一化层和ReLU激活函数构成,输入为3x64x64大小的图像,输出为一个标量概率值,用于判断输入图像是否源自真实数据分布。生成器则由卷积转置层、批量归一化层和ReLU激活函数组成,输入为从标准正态分布中抽取的潜向量 ,输出为3x64x64大小的RGB图像。步进卷积转置层的作用是将潜向量转换为与图像尺寸相匹配的三维体积。论文中还提供了一些关于优化器设置、损失函数计算及模型权重初始化的建议,这些建议将在后续章节中进行介绍。
#%matplotlib inline
import argparse # 导入命令行参数解析库
import os # 导入操作系统相关的库
import random # 导入随机数生成库
import torch # 导入PyTorch深度学习框架
import torch.nn as nn # 导入PyTorch的神经网络模块
import torch.nn.parallel # 导入PyTorch的并行计算模块
import torch.optim as optim # 导入PyTorch的优化算法模块
import torch.utils.data # 导入PyTorch的数据处理模块
import torchvision.datasets as dset # 导入PyTorch的图像数据集模块
import torchvision.transforms as transforms # 导入PyTorch的图像变换模块
import torchvision.utils as vutils # 导入PyTorch的图像处理工具模块
import numpy as np # 导入NumPy科学计算库
import matplotlib.pyplot as plt # 导入Matplotlib绘图库
import matplotlib.animation as animation # 导入Matplotlib动画库
from IPython.display import HTML # 导入IPython的HTML显示模块
# 设置随机种子以保证结果的可复现性
manualSeed = 999
# manualSeed = random.randint(1, 10000) # 如果需要新的结果,可以使用这行代码生成随机种子
print("Random Seed: ", manualSeed)
random.seed(manualSeed) # 设置Python的随机种子
torch.manual_seed(manualSeed) # 设置PyTorch的随机种子
torch.use_deterministic_algorithms(True) # 使用确定性算法以保证结果的可复现性
输入参数
接下来,我们将定义运行DCGAN所需的输入参数:
-
dataroot- 数据集文件夹根目录的路径。我们将在下一节中详细讨论数据集。 -
workers- 使用DataLoader加载数据时的工作线程数。 -
batch_size- 训练中使用的批量大小。DCGAN论文中使用的批量大小为128。 -
image_size- 用于训练的图像的空间大小。此实现默认为64x64。如果需要其他尺寸,必须更改D和G的结构。更多详情请参见这里。 -
nc- 输入图像中的颜色通道数。对于彩色图像,这是3。 -
nz- 潜在向量的长度。 -
ngf- 与通过生成器的特征图的深度有关。 -
ndf- 设置通过判别器传播的特征图的深度。 -
num_epochs- 要运行的训练周期数。更长时间的训练可能会带来更好的结果,但也会花费更多时间。 -
lr- 训练的学习率。如DCGAN论文所述,这个数字应该是0.0002。 -
beta1- Adam优化器的beta1超参数。如论文所述,这个数字应该是0.5。 -
ngpu- 可用的GPU数量。如果这是0,代码将在CPU模式下运行。如果这个数字大于0,它将在那个数量的GPU上运行。
dataroot = "data/celeba"
workers = 2
batch_size = 128
image_size = 64
nc = 3
nz = 100
ngf = 64
ndf = 64
num_epochs = 100
lr = 0.0002
beta1 = 0.5
ngpu = 1
数据
在本文中,我们使用Celeb-A Faces数据集。下载后,解压在data/celeba目录下。然后,将dataroot设置为data/celeba/(本文的.ipynb与data在同一路径)。确保目录结构如下所示:
data/celeba/
|-- img_align_celeba
| |-- 188242.jpg
| |-- 173822.jpg
| |-- ...
在进行实现阶段之前,确保数据集的组织结构满足ImageFolder数据集类的要求是至关重要的。接下来,我们将创建数据加载器、指定运行设备和可视化部分训练数据。在完成数据集的准备后,我们可以继续构建和训练DCGAN模型。
# 我们可以按照已经设置好的图像文件夹数据集的方式来使用。
# 创建数据集
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=workers) # 设置批量大小,是否打乱数据,以及使用的线程数
# 决定在哪个设备上运行
device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu") # 如果有可用的GPU并且ngpu大于0,则使用第一个GPU,否则使用CPU
# 绘制一些训练图像
real_batch = next(iter(dataloader)) # 获取数据加载器中的一个批次数据
plt.figure(figsize=(8,8)) # 设置图像大小
plt.axis("off") # 关闭坐标轴
plt.title("训练图像") # 设置图像标题
plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=2, normalize=True).cpu(),(1,2,0))) # 显示图像
plt.show() # 展示图像

在完成了输入参数的设置和数据集的准备之后,我们现在可以开始构建和训练DCGAN模型。这个过程将包括权重初始化、生成器和判别器的定义、损失函数的选择、优化器的配置,以及训练循环的编写。
权重初始化
按照DCGAN论文的建议,模型的权重应从均值为0,标准差为0.02的正态分布中随机初始化。weights_init函数接受一个已初始化的模型作为输入,并重新初始化所有卷积层、卷积转置层和批量归一化层的权重,以确保它们符合上述标准。这个函数在模型初始化后立即被应用于模型,以确保所有权重都被正确地初始化。
接下来,我们将详细讨论生成器和判别器的构建、损失函数的选择以及训练循环的编写,这些步骤将共同构成DCGAN模型的实现过程。
# 自定义权重初始化函数,应用于``netG``和``netD``
def weights_init(m):
classname = m.__class__.__name__
if classname.find('Conv') != -1: # 如果类名中包含'Conv',则进行以下操作
nn.init.normal_(m.weight.data, 0.0, 0.02) # 使用正态分布初始化权重
elif classname.find('BatchNorm') != -1: # 如果类名中包含'BatchNorm',则进行以下操作
nn.init.normal_(m.weight.data, 1.0, 0.02) # 使用正态分布初始化权重
nn.init.constant_(m.bias.data, 0) # 将偏置项初始化为0
权重初始化函数weights_init的作用是对模型中的权重进行初始化。该函数会检查每个模型组件的类名,并针对不同的组件类型应用不同的初始化策略。这个函数在模型初始化完成后立即被调用,以确保所有权重都被正确地初始化。
生成器
生成器(Generator)是DCGAN的核心组件之一,其目标是将随机潜向量 映射到图像数据空间,生成与训练数据集尺寸一致的RGB图像(即3x64x64)。为了实现这一目标,DCGAN采用了一种特定的网络架构,该架构包括一系列步长为2的二维卷积转置层,每个层都结合了二维批归一化层和ReLU激活函数。这种设计使得生成器能够逐步增加输出图像的尺寸,直至达到所需的3x64x64的RGB图像。

在生成器的输出部分,应用了tanh函数,以保证生成的图像值域在[-1,1]之间,这是训练深度卷积生成对抗网络时的推荐做法。此外,生成器架构中引入了批量归一化层,这一设计在DCGAN论文中被强调为有助于网络训练的稳定性,尤其是在处理高维数据(如图像)时。 在编写生成器的代码时,需要根据输入参数(如nz、ngf和nc)来确定网络的层数、每层的特征图大小以及输出图像的通道数。这些参数共同决定了生成器的结构,包括卷积转置层的布局和输出图像的最终尺寸。
class Generator(nn.Module):
def __init__(self, ngpu):
super(Generator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# 输入是潜在向量Z,通过一个卷积层
nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
# 状态尺寸 ``(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, nc, 4, 2, 1, bias=False),
nn.Tanh()
# 状态尺寸 ``(nc) x 64 x 64``
)
def forward(self, input):
return self.main(input)
接下来,我们将实例化生成器模型并应用权重初始化函数。完成这些步骤后,我们将打印生成器模型的结构,以便直观地了解其层次结构和配置。
创建生成器
netG = Generator(ngpu).to(device)
这里,我们创建了一个 Generator 实例并将其移动到了指定的设备(如GPU)上。
处理多GPU情况
if (device.type == 'cuda') and (ngpu > 1):
netG = nn.DataParallel(netG, list(range(ngpu)))
如果设备类型是CUDA(即GPU)并且 ngpu 大于1,我们使用 nn.DataParallel 来并行化生成器,这样可以在多个GPU上并行处理数据,加速训练过程。
应用权重初始化函数
netG.apply(weights_init)
使用 weights_init 函数来随机初始化所有权重,权重的均值为0,标准差为0.02。这是生成器训练前的一个重要步骤,因为合适的权重初始化可以帮助模型更快地收敛。
打印模型
print(netG)
打印出生成器模型的结构,这样可以直观地看到模型的层级结构和配置。
判别器
判别器(Discriminator)是DCGAN模型中的另一个关键组成部分,它负责对输入的图像进行二元分类,判断其是否为真实图像。在DCGAN架构中,判别器接收一个尺寸为3x64x64的输入图像,并经过一系列卷积层(Conv2d)、批量归一化层(BatchNorm2d)以及LeakyReLU激活函数的处理。这些层共同工作,逐步提取图像的特征,并最终通过Sigmoid激活函数输出一个标量概率值,表示输入图像为真实图像的概率。
判别器的架构可以根据需要进行扩展,增加更多的层以提高模型的性能。然而,DCGAN论文中提出的特定层的选择具有其特殊的重要性。使用步进卷积(步幅为2的卷积层)进行下采样,相较于传统的池化操作,能够使网络自身学习到更有效的特征提取方式。此外,批量归一化层和LeakyReLU激活函数的组合,有助于在训练过程中保持梯度的稳定性和传播的健康性,这对于生成器(Generator)和判别器(Discriminator)的协同学习至关重要。
class Discriminator(nn.Module):
def __init__(self, ngpu):
super(Discriminator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# 输入是 ``(nc) x 64 x 64`` 的图像
nn.Conv2d(nc, 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)
接下来,我们将按照与生成器类似的方法,创建判别器模型,并应用权重初始化函数。完成这些步骤后,我们将打印判别器模型的结构,以便直观地了解其层次结构和配置。
# 创建判别器
netD = Discriminator(ngpu).to(device)
# 如果需要,处理多GPU情况
if (device.type == 'cuda') and (ngpu > 1):
netD = nn.DataParallel(netD, list(range(ngpu)))
# 应用权重初始化函数,随机初始化所有权重,均值为0,标准差为0.2
netD.apply(weights_init)
# 打印模型结构
print(netD)
在配置了判别器(D)和生成器(G)之后,我们可以通过定义损失函数和优化器来指导它们的学习过程。我们将采用二元交叉熵损失(Binary Cross Entropy Loss, BCELoss)函数,它在PyTorch中定义如下:
这个函数包含两个对数组件,分别对应于真实标签和假标签的情况。在训练过程中,我们将根据需要选择使用BCE公式中的哪一部分。这里,我们将真实标签定义为1,假标签定义为0,这是原始GAN论文中的约定。 此外,我们将为判别器和生成器分别设置独立的优化器。根据DCGAN论文的建议,我们使用学习率为0.0002和Beta1参数为0.5的Adam优化器。为了跟踪生成器的学习进展,我们还会生成一批固定潜在向量(fixed_noise),并在训练循环中定期将其输入到生成器中。随着训练的进行,我们将观察到生成器从噪声中逐渐生成出更加真实的图像。
# 初始化二元交叉熵损失函数(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)架构的构建之后,我们即将着手进行模型的训练。训练GAN是一项复杂的任务,其中不恰当的超参数配置可能导致模型性能下降,这种现象被称为“模式崩溃”。尽管模式崩溃的原因可能难以明确,本教程将遵循Goodfellow等人在其论文中提出的算法,并结合GANhacks中推荐的最佳实践,以优化训练过程。
训练过程
判别器训练
在判别器训练阶段,我们的目标是最大化其准确区分真实图像与生成图像的概率。根据GAN的理论基础,我们通过更新判别器的参数来提升其性能。具体来说,我们的目标是最大化以下损失函数:
为了有效训练判别器,我们采取了两步训练策略。首先,从训练数据集中抽取一批真实图像并输入判别器,计算判别器输出的对数损失 并通过反向传播计算相应的梯度。其次,利用当前生成器生成一批虚假图像,同样输入判别器,并计算对数损失 ,然后在反向传播时将这些梯度累加到之前计算的梯度上。
通过这两步,我们累积了来自真实和虚假图像的梯度信息。最后,我们利用这些累积的梯度,通过判别器的优化器(例如Adam优化器)来更新判别器的参数。这一过程不断重复,直至判别器在区分真实图像和生成图像方面达到满意的性能水平。
生成器训练
根据原始论文的指导,我们的目标是通过最小化判别器对生成器输出的虚假判定概率 来训练生成器,以提高其生成逼真图像的能力。然而,这种方法在训练初期可能无法提供足够的梯度,导致生成器学习效果不佳。为了解决这一问题,我们采用了一种策略,即通过最大化判别器对生成器输出判定为真实的概率(log(D(G(z)))) 来训练生成器。
在实现中,我们首先将判别器应用于生成器的输出,将它们视为真实图像。然后,我们使用真实标签来计算生成器的损失,以衡量判别器错误地将生成器生成的图像判定为真实的情况。接下来,我们执行反向传播,计算生成器的梯度,并使用优化器更新其参数。尽管这种方法可能看起来违反直觉,但它允许我们利用BCELoss中的log(x)部分来指导生成器的训练,而不是使用log(1 - x)部分。 为了监测生成器的训练进展,我们在每个训练周期结束时使用一个预定义的固定噪声输入生成新图像。这种方法使我们能够可视化生成器的学习效果,并观察生成图像质量随时间的变化。 在生成器的训练过程中,为了监控其性能并可视化进展,我们执行了以下步骤:
-
统计报告:我们收集了一系列训练统计数据,这些数据帮助我们评估判别器和生成器的性能。 -
固定噪声批次通过生成器:在每个训练周期的末尾,我们使用一个预先定义的固定噪声批次作为输入传递给生成器。生成的图像被用于可视化生成器在每个周期中的学习效果。 -
训练统计信息: -
Loss_D:判别器的损失,它是真实和虚假批次损失的总和。具体来说,它是判别器对真实图像输出的损失 和判别器对生成器生成的图像输出的损失 的总和。 -
Loss_G:生成器的损失,它是判别器对生成器输出判定为真实图像的概率 的计算结果。 -
**D(x)**:判别器对全真实批次输出的平均值。在训练初期,这个值通常接近1,因为真实图像应该被准确地识别为真实。随着生成器性能的提升,这个值理论上会收敛到0.5,表示判别器无法准确判断生成器输出的图像是否真实。 -
**D(G(z))**:判别器对生成器输出的平均值。在判别器更新之前,这个值通常接近0,因为生成器生成的图像在开始时被判别器准确地识别为虚假。随着生成器性能的提升,这个值会逐渐增大,理想情况下应接近0.5,表示判别器无法准确判断生成图像的真实性。
这种动态的对抗过程是GAN训练的核心,也是其能够生成高质量图像的关键。
num_epochs = 40
# 用于跟踪进度的列表
img_list = []
G_losses = []
D_losses = []
iters = 0
print("开始训练循环...")
# 对于每一个时代
for epoch in range(num_epochs):
# 对于数据加载器中的每一个批次
for i, data in enumerate(dataloader, 0):
############################
# (1) 更新 D 网络:最大化 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)
# 将真实批次前向传播通过 D
output = netD(real_cpu).view(-1)
# 计算全部真实批次的损失
errD_real = criterion(output, label)
# 在后向传播中计算 D 的梯度
errD_real.backward()
D_x = output.mean().item()
## 用全部虚假批次训练
# 生成一批潜在向量
noise = torch.randn(b_size, nz, 1, 1, device=device)
# 使用 G 生成虚假图像批次
fake = netG(noise)
label.fill_(fake_label)
# 使用 D 对全部虚假批次进行分类
output = netD(fake.detach()).view(-1)
# 计算 D 在全部虚假批次上的损失
errD_fake = criterion(output, label)
# 计算这个批次的梯度,并与之前的梯度累加
errD_fake.backward()
D_G_z1 = output.mean().item()
# 计算 D 的错误作为虚假和真实批次的总和
errD = errD_real + errD_fake
# 更新 D
optimizerD.step()
############################
# (2) 更新 G 网络:最大化 log(D(G(z)))
###########################
netG.zero_grad()
label.fill_(real_label) # 对于生成器成本,虚假标签是真实的
# 由于我们刚刚更新了 D,对全部虚假批次再次执行前向传播通过 D
output = netD(fake).view(-1)
# 根据这个输出计算 G 的损失
errG = criterion(output, label)
# 计算 G 的梯度
errG.backward()
D_G_z2 = output.mean().item()
# 更新 G
optimizerG.step()
# 输出训练统计信息
if i % 50 == 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())
# 通过保存 G 在固定噪声上的输出来检查生成器的表现
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
评估训练成果
随着训练过程的完成,我们现在将对模型的表现进行评估。我们将从三个维度进行分析:首先,我们将追踪判别器(D)和生成器(G)的损失值随训练迭代的变化情况;其次,我们将通过可视化手段展示每个训练周期结束时,生成器在固定噪声输入下产出的图像;最后,我们将对比展示一批真实数据样本与生成器生成的图像样本。
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()

回顾训练过程,我们在每个训练周期结束时记录了生成器 G 对固定噪声的处理结果。现在,我们可以通过动画形式直观地展示生成器 G 训练过程中的变化。点击播放按钮,动画将展示生成器生成图像随着训练进展的演变。
fig = plt.figure(figsize=(8,8))
plt.axis("off")
ims = [[plt.imshow(np.transpose(i,(1,2,0)), animated=True)] for i in img_list]
ani = animation.ArtistAnimation(fig, ims, interval=1000, repeat_delay=1000, blit=True)
HTML(ani.to_jshtml())


# 从数据加载器中获取一批真实图像
real_batch = next(iter(dataloader))
# 绘制真实图像
plt.figure(figsize=(15,15))
plt.subplot(1,2,1)
plt.axis("off")
plt.title("True 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()

我们的探索之旅虽然已经结束,但您的学习之路还有许多方向可以继续:
-
延长训练周期:以观察模型性能的进一步提升。 -
调整模型参数:尝试应用于不同的数据集,并根据需要调整图像尺寸及模型结构。 -
探索更多GAN项目:发现更多创新的GAN应用。 -
尝试音乐生成GAN:探索GAN在音乐创作等新领域的应用潜力。
这些建议将为您在生成对抗网络领域的进一步学习和实践提供指导。
历史相关文章
原文链接:https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html

