DCGAN 教程

原文: https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html

注意

单击此处的下载完整的示例代码

作者Nathan Inkawhich

介绍

本教程将通过一个示例对 DCGAN 进行介绍。 在向其展示许多真实名人的照片后,我们将训练一个生成对抗网络(GAN)以产生新名人。 此处的大部分代码来自 pytorch / examples 中的 dcgan 实现,并且本文档将对该实现进行详尽的解释,并阐明此模型的工作方式和原因。 但是请放心,不需要 GAN 的先验知识,但这可能需要新手花一些时间来推理幕后实际发生的事情。 另外,为了节省时间,安装一两个 GPU 也将有所帮助。 让我们从头开始。

生成对抗网络

什么是 GAN?

GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们可以从同一分布中生成新数据。 GAN 由 Ian Goodfellow 于 2014 年发明,并首先在论文生成对抗网络中进行了描述。 它们由两个不同的模型组成:生成器鉴别器。 生成器的工作是生成看起来像训练图像的“假”图像。 鉴别器的工作是查看图像并从生成器输出它是真实的训练图像还是伪图像。 在训练过程中,生成器不断尝试通过生成越来越好的伪造品而使鉴别器的性能超过智者,而鉴别器正在努力成为更好的侦探并正确地对真实和伪造图像进行分类。 博弈的平衡点是当生成器生成的伪造品看起来像直接来自训练数据时,而鉴别器则总是猜测生成器输出是真品还是伪造品的 50%置信度。

现在,让我们从判别器开始定义一些在整个教程中使用的符号。 令DCGAN 教程 - 图1是表示图像的数据。 DCGAN 教程 - 图2是鉴别器网络,其输出DCGAN 教程 - 图3来自训练数据而非生成器的(标量)概率。 在这里,由于我们要处理图像,因此DCGAN 教程 - 图4的输入是 CHW 大小为 3x64x64 的图像。 直观地讲,当DCGAN 教程 - 图5来自训练数据时,DCGAN 教程 - 图6应该为高,而当DCGAN 教程 - 图7来自发生器时,则应为低。 DCGAN 教程 - 图8也可以被视为传统的二进制分类器。

对于发生器的表示法,将DCGAN 教程 - 图9设为从标准正态分布中采样的潜在空间矢量。 DCGAN 教程 - 图10表示将潜在矢量DCGAN 教程 - 图11映射到数据空间的生成器函数。 DCGAN 教程 - 图12的目标是估计训练数据来自的分布(DCGAN 教程 - 图13),以便它可以从该估计的分布(DCGAN 教程 - 图14)中生成假样本。

因此,DCGAN 教程 - 图15是发生器DCGAN 教程 - 图16的输出是真实图像的概率(标量)。 如所述,Goodfellow 的论文DCGAN 教程 - 图17DCGAN 教程 - 图18玩一个 minimax 游戏,其中DCGAN 教程 - 图19试图最大化其正确分类实物和假货(DCGAN 教程 - 图20)的概率,而DCGAN 教程 - 图21尝试 以最大程度地降低DCGAN 教程 - 图22预测其输出为假的可能性(DCGAN 教程 - 图23)。 从本文来看,GAN 损失函数为

DCGAN 教程 - 图24

从理论上讲,此 minimax 游戏的解决方案是DCGAN 教程 - 图25,判别器会随机猜测输入是真实的还是假的。 但是,GAN 的收敛理论仍在积极研究中,实际上,模型并不总是能达到这一目的。

什么是 DCGAN?

DCGAN 是上述 GAN 的直接扩展,不同之处在于 DCGAN 分别在鉴别器和生成器中分别使用卷积和卷积转置层。 它最初是由 Radford 等人描述的。 等 深度卷积生成对抗网络中的无监督表示学习。 鉴别器由分层的卷积层,批处理规范层和 LeakyReLU 激活组成。 输入是 3x64x64 的输入图像,输出是输入来自真实数据分布的标量概率。 生成器由卷积转置层,批处理规范层和 ReLU 激活组成。 输入是从标准正态分布中提取的潜矢量DCGAN 教程 - 图26,输出是 3x64x64 RGB 图像。 跨步的转置图层使潜矢量可以转换为与图像具有相同形状的体积。 在本文中,作者还提供了有关如何设置优化器,如何计算损失函数以及如何初始化模型权重的一些技巧,所有这些将在接下来的部分中进行解释。

  1. from __future__ import print_function
  2. #%matplotlib inline
  3. import argparse
  4. import os
  5. import random
  6. import torch
  7. import torch.nn as nn
  8. import torch.nn.parallel
  9. import torch.backends.cudnn as cudnn
  10. import torch.optim as optim
  11. import torch.utils.data
  12. import torchvision.datasets as dset
  13. import torchvision.transforms as transforms
  14. import torchvision.utils as vutils
  15. import numpy as np
  16. import matplotlib.pyplot as plt
  17. import matplotlib.animation as animation
  18. from IPython.display import HTML
  19. # Set random seed for reproducibility
  20. manualSeed = 999
  21. #manualSeed = random.randint(1, 10000) # use if you want new results
  22. print("Random Seed: ", manualSeed)
  23. random.seed(manualSeed)
  24. torch.manual_seed(manualSeed)

出:

  1. Random Seed: 999

输入项

让我们为跑步定义一些输入:

  • dataroot -数据集文件夹根目录的路径。 我们将在下一节中进一步讨论数据集
  • worker -使用 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 上运行
  1. # Root directory for dataset
  2. dataroot = "data/celeba"
  3. # Number of workers for dataloader
  4. workers = 2
  5. # Batch size during training
  6. batch_size = 128
  7. # Spatial size of training images. All images will be resized to this
  8. # size using a transformer.
  9. image_size = 64
  10. # Number of channels in the training images. For color images this is 3
  11. nc = 3
  12. # Size of z latent vector (i.e. size of generator input)
  13. nz = 100
  14. # Size of feature maps in generator
  15. ngf = 64
  16. # Size of feature maps in discriminator
  17. ndf = 64
  18. # Number of training epochs
  19. num_epochs = 5
  20. # Learning rate for optimizers
  21. lr = 0.0002
  22. # Beta1 hyperparam for Adam optimizers
  23. beta1 = 0.5
  24. # Number of GPUs available. Use 0 for CPU mode.
  25. ngpu = 1

数据

在本教程中,我们将使用 Celeb-A Faces 数据集,该数据集可在链接的站点或 Google 云端硬盘中下载。 数据集将下载为名为 img_align_celeba.zip 的文件。 下载完成后,创建一个名为 celeba 的目录,并将 zip 文件解压缩到该目录中。 然后,将此笔记本的数据根输入设置为刚创建的 celeba 目录。 结果目录结构应为:

  1. /path/to/celeba
  2. -> img_align_celeba
  3. -> 188242.jpg
  4. -> 173822.jpg
  5. -> 284702.jpg
  6. -> 537394.jpg
  7. ...

这是重要的一步,因为我们将使用 ImageFolder 数据集类,该类要求数据集的根文件夹中有子目录。 现在,我们可以创建数据集,创建数据加载器,将设备设置为可以运行,最后可视化一些训练数据。

  1. # We can use an image folder dataset the way we have it setup.
  2. # Create the dataset
  3. dataset = dset.ImageFolder(root=dataroot,
  4. transform=transforms.Compose([
  5. transforms.Resize(image_size),
  6. transforms.CenterCrop(image_size),
  7. transforms.ToTensor(),
  8. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
  9. ]))
  10. # Create the dataloader
  11. dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
  12. shuffle=True, num_workers=workers)
  13. # Decide which device we want to run on
  14. device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")
  15. # Plot some training images
  16. real_batch = next(iter(dataloader))
  17. plt.figure(figsize=(8,8))
  18. plt.axis("off")
  19. plt.title("Training Images")
  20. plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=2, normalize=True).cpu(),(1,2,0)))

../_images/sphx_glr_dcgan_faces_tutorial_001.png

实作

设置好输入参数并准备好数据集后,我们现在可以进入实现了。 我们将从 Weigth 初始化策略开始,然后详细讨论生成器,鉴别器,损失函数和训练循环。

重量初始化

从 DCGAN 论文中,作者指定所有模型权重均应从均值= 0,stdev = 0.02 的正态分布中随机初始化。 weights_init函数采用已初始化的模型作为输入,并重新初始化所有卷积,卷积转置和批处理归一化层,以满足该标准。 初始化后立即将此功能应用于模型。

  1. # custom weights initialization called on netG and netD
  2. def weights_init(m):
  3. classname = m.__class__.__name__
  4. if classname.find('Conv') != -1:
  5. nn.init.normal_(m.weight.data, 0.0, 0.02)
  6. elif classname.find('BatchNorm') != -1:
  7. nn.init.normal_(m.weight.data, 1.0, 0.02)
  8. nn.init.constant_(m.bias.data, 0)

生成器

生成器DCGAN 教程 - 图28旨在将潜在空间矢量(DCGAN 教程 - 图29)映射到数据空间。 由于我们的数据是图像,因此将DCGAN 教程 - 图30转换为数据空间意味着最终创建与训练图像大小相同的 RGB 图像(即 3x64x64)。 在实践中,这是通过一系列跨步的二维卷积转置层来完成的,每个层都与 2d 批处理规范层和 relu 激活配对。 生成器的输出通过 tanh 函数进行馈送,以使其返回到DCGAN 教程 - 图31的输入数据范围。 值得注意的是,在卷积转置层之后存在批处理规范函数,因为这是 DCGAN 论文的关键贡献。 这些层有助于训练过程中的梯度流动。 DCGAN 纸生成的图像如下所示。

dcgan_generator

注意,我们在输入部分中设置的输入 (nzngfnc )如何影响代码中的生成器体系结构。 nz 是 z 输入向量的长度, ngf 与通过生成器传播的特征图的大小有关, nc 是 输出图像中的通道(对于 RGB 图像设置为 3)。 下面是生成器的代码。

  1. # Generator Code
  2. class Generator(nn.Module):
  3. def __init__(self, ngpu):
  4. super(Generator, self).__init__()
  5. self.ngpu = ngpu
  6. self.main = nn.Sequential(
  7. # input is Z, going into a convolution
  8. nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
  9. nn.BatchNorm2d(ngf * 8),
  10. nn.ReLU(True),
  11. # state size. (ngf*8) x 4 x 4
  12. nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
  13. nn.BatchNorm2d(ngf * 4),
  14. nn.ReLU(True),
  15. # state size. (ngf*4) x 8 x 8
  16. nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
  17. nn.BatchNorm2d(ngf * 2),
  18. nn.ReLU(True),
  19. # state size. (ngf*2) x 16 x 16
  20. nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
  21. nn.BatchNorm2d(ngf),
  22. nn.ReLU(True),
  23. # state size. (ngf) x 32 x 32
  24. nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
  25. nn.Tanh()
  26. # state size. (nc) x 64 x 64
  27. )
  28. def forward(self, input):
  29. return self.main(input)

现在,我们可以实例化生成器并应用weights_init函数。 签出打印的模型以查看生成器对象的结构。

  1. # Create the generator
  2. netG = Generator(ngpu).to(device)
  3. # Handle multi-gpu if desired
  4. if (device.type == 'cuda') and (ngpu > 1):
  5. netG = nn.DataParallel(netG, list(range(ngpu)))
  6. # Apply the weights_init function to randomly initialize all weights
  7. # to mean=0, stdev=0.2.
  8. netG.apply(weights_init)
  9. # Print the model
  10. print(netG)

Out:

  1. Generator(
  2. (main): Sequential(
  3. (0): ConvTranspose2d(100, 512, kernel_size=(4, 4), stride=(1, 1), bias=False)
  4. (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  5. (2): ReLU(inplace=True)
  6. (3): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  7. (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  8. (5): ReLU(inplace=True)
  9. (6): ConvTranspose2d(256, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  10. (7): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  11. (8): ReLU(inplace=True)
  12. (9): ConvTranspose2d(128, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  13. (10): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  14. (11): ReLU(inplace=True)
  15. (12): ConvTranspose2d(64, 3, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  16. (13): Tanh()
  17. )
  18. )

鉴别器

如前所述,鉴别符DCGAN 教程 - 图33是一个二进制分类网络,该二进制分类网络将图像作为输入并输出输入图像是真实的(与假的相对)的标量概率。 在这里,DCGAN 教程 - 图34拍摄 3x64x64 的输入图像,通过一系列的 Conv2d,BatchNorm2d 和 LeakyReLU 层对其进行处理,然后通过 Sigmoid 激活函数输出最终概率。 如果需要解决此问题,可以用更多层扩展此体系结构,但是使用跨步卷积,BatchNorm 和 LeakyReLUs 具有重要意义。 DCGAN 论文提到,使用跨步卷积而不是合并以进行下采样是一个好习惯,因为它可以让网络学习自己的合并功能。 批处理规范和泄漏的 relu 函数还可以促进健康的梯度流,这对于DCGAN 教程 - 图35DCGAN 教程 - 图36的学习过程都是至关重要的。

鉴别码

  1. class Discriminator(nn.Module):
  2. def __init__(self, ngpu):
  3. super(Discriminator, self).__init__()
  4. self.ngpu = ngpu
  5. self.main = nn.Sequential(
  6. # input is (nc) x 64 x 64
  7. nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
  8. nn.LeakyReLU(0.2, inplace=True),
  9. # state size. (ndf) x 32 x 32
  10. nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
  11. nn.BatchNorm2d(ndf * 2),
  12. nn.LeakyReLU(0.2, inplace=True),
  13. # state size. (ndf*2) x 16 x 16
  14. nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
  15. nn.BatchNorm2d(ndf * 4),
  16. nn.LeakyReLU(0.2, inplace=True),
  17. # state size. (ndf*4) x 8 x 8
  18. nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
  19. nn.BatchNorm2d(ndf * 8),
  20. nn.LeakyReLU(0.2, inplace=True),
  21. # state size. (ndf*8) x 4 x 4
  22. nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
  23. nn.Sigmoid()
  24. )
  25. def forward(self, input):
  26. return self.main(input)

现在,与生成器一样,我们可以创建鉴别器,应用weights_init函数,并打印模型的结构。

  1. # Create the Discriminator
  2. netD = Discriminator(ngpu).to(device)
  3. # Handle multi-gpu if desired
  4. if (device.type == 'cuda') and (ngpu > 1):
  5. netD = nn.DataParallel(netD, list(range(ngpu)))
  6. # Apply the weights_init function to randomly initialize all weights
  7. # to mean=0, stdev=0.2.
  8. netD.apply(weights_init)
  9. # Print the model
  10. print(netD)

Out:

  1. Discriminator(
  2. (main): Sequential(
  3. (0): Conv2d(3, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  4. (1): LeakyReLU(negative_slope=0.2, inplace=True)
  5. (2): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  6. (3): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  7. (4): LeakyReLU(negative_slope=0.2, inplace=True)
  8. (5): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  9. (6): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  10. (7): LeakyReLU(negative_slope=0.2, inplace=True)
  11. (8): Conv2d(256, 512, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
  12. (9): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  13. (10): LeakyReLU(negative_slope=0.2, inplace=True)
  14. (11): Conv2d(512, 1, kernel_size=(4, 4), stride=(1, 1), bias=False)
  15. (12): Sigmoid()
  16. )
  17. )

损失函数和优化器

通过DCGAN 教程 - 图37DCGAN 教程 - 图38设置,我们可以指定它们如何通过损失函数和优化器学习。 我们将使用在 PyTorch 中定义的二进制交叉熵损失 (BCELoss)函数:

DCGAN 教程 - 图39

请注意,此函数如何提供目标函数(即DCGAN 教程 - 图40DCGAN 教程 - 图41)中两个日志分量的计算。 我们可以指定[CEG2]输入要使用 BCE 公式的哪一部分。 这是在即将到来的训练循环中完成的,但重要的是要了解我们如何仅通过更改DCGAN 教程 - 图42(即 GT 标签)就可以选择想要计算的组件。

接下来,我们将实际标签定义为 1,将假标签定义为 0。这些标签将在计算DCGAN 教程 - 图43DCGAN 教程 - 图44的损耗时使用,这也是 GAN 原始文件中使用的惯例。 最后,我们设置了两个单独的优化器,一个用于DCGAN 教程 - 图45,一个用于DCGAN 教程 - 图46。 如 DCGAN 论文中所述,这两个都是 Adam 优化器,学习率均为 0.0002,Beta1 = 0.5。 为了跟踪生成器的学习进度,我们将生成一批固定的潜在矢量,这些矢量是从高斯分布(即 fixed_noise)中提取的。 在训练循环中,我们将定期将此 fixed_noise 输入到DCGAN 教程 - 图47中,并且在迭代过程中,我们将看到图像形成于噪声之外。

  1. # Initialize BCELoss function
  2. criterion = nn.BCELoss()
  3. # Create batch of latent vectors that we will use to visualize
  4. # the progression of the generator
  5. fixed_noise = torch.randn(64, nz, 1, 1, device=device)
  6. # Establish convention for real and fake labels during training
  7. real_label = 1
  8. fake_label = 0
  9. # Setup Adam optimizers for both G and D
  10. optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
  11. optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))

训练

最后,既然我们已经定义了 GAN 框架的所有部分,我们就可以对其进行训练。 请注意,训练 GAN 某种程度上是一种艺术形式,因为不正确的超参数设置会导致模式崩溃,而对失败的原因几乎没有解释。 在这里,我们将严格遵循 Goodfellow 论文中的算法 1,同时遵守 ganhacks 中显示的一些最佳做法。 即,我们将“为真实和伪造构建不同的小批量”图像,并调整 G 的目标函数以最大化DCGAN 教程 - 图48。 训练分为两个主要部分。 第 1 部分更新了鉴别器,第 2 部分更新了生成器。

第 1 部分-训练鉴别器

回想一下,训练鉴别器的目的是最大程度地提高将给定输入正确分类为真实或伪造的可能性。 关于古德费罗,我们希望“通过提高随机梯度来更新鉴别器”。 实际上,我们要最大化DCGAN 教程 - 图49。 由于 ganhacks 提出了单独的小批量建议,因此我们将分两步进行计算。 首先,我们将从训练集中构造一批真实样本,向前通过DCGAN 教程 - 图50,计算损失(DCGAN 教程 - 图51),然后在向后通过中计算梯度。 其次,我们将使用电流发生器构造一批假样本,将这批样本通过DCGAN 教程 - 图52正向传递,计算损失(DCGAN 教程 - 图53),然后向后传递累积梯度。 现在,利用从所有真实批次和所有伪批次累积的渐变,我们将其称为“鉴别器”优化器的一个步骤。

第 2 部分-训练生成器

如原始论文所述,我们希望通过最小化DCGAN 教程 - 图54来训练 Generator,以产生更好的假货。 如前所述,Goodfellow 指出这不能提供足够的梯度,尤其是在学习过程的早期。 作为解决方法,我们改为希望最大化DCGAN 教程 - 图55。 在代码中,我们通过以下步骤来实现此目的:将第 1 部分的 Generator 输出与 Discriminator 进行分类,使用实数标签 GT 计算 G 的损耗,反向计算 G 的梯度,最后使用优化器更新 G 的参数 步。 将真实标签用作损失函数的 GT 标签似乎违反直觉,但这使我们可以使用 BCELoss 的DCGAN 教程 - 图56部分(而不是DCGAN 教程 - 图57部分),这正是我们想要的。

最后,我们将进行一些统计报告,并在每个时期结束时,将我们的 fixed_noise 批次推入生成器,以直观地跟踪 G 的训练进度。 报告的训练统计数据是:

  • Loss_D -鉴别器损失,计算为所有真实批次和所有假批次的损失总和(DCGAN 教程 - 图58)。
  • Loss_G -生成器损耗计算为DCGAN 教程 - 图59
  • D(x)-所有真实批次的鉴别器的平均输出(整个批次)。 这应该从接近 1 开始,然后在 G 变得更好时理论上收敛到 0.5。 想想这是为什么。
  • D(G(z))-所有假批次的平均鉴别器输出。 第一个数字在 D 更新之前,第二个数字在 D 更新之后。 这些数字应从 0 开始,并随着 G 的提高收敛到 0.5。 想想这是为什么。

注意:此步骤可能需要一段时间,具体取决于您运行了多少个时期以及是否从数据集中删除了一些数据。

  1. # Training Loop
  2. # Lists to keep track of progress
  3. img_list = []
  4. G_losses = []
  5. D_losses = []
  6. iters = 0
  7. print("Starting Training Loop...")
  8. # For each epoch
  9. for epoch in range(num_epochs):
  10. # For each batch in the dataloader
  11. for i, data in enumerate(dataloader, 0):
  12. ############################
  13. # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))
  14. ###########################
  15. ## Train with all-real batch
  16. netD.zero_grad()
  17. # Format batch
  18. real_cpu = data[0].to(device)
  19. b_size = real_cpu.size(0)
  20. label = torch.full((b_size,), real_label, device=device)
  21. # Forward pass real batch through D
  22. output = netD(real_cpu).view(-1)
  23. # Calculate loss on all-real batch
  24. errD_real = criterion(output, label)
  25. # Calculate gradients for D in backward pass
  26. errD_real.backward()
  27. D_x = output.mean().item()
  28. ## Train with all-fake batch
  29. # Generate batch of latent vectors
  30. noise = torch.randn(b_size, nz, 1, 1, device=device)
  31. # Generate fake image batch with G
  32. fake = netG(noise)
  33. label.fill_(fake_label)
  34. # Classify all fake batch with D
  35. output = netD(fake.detach()).view(-1)
  36. # Calculate D's loss on the all-fake batch
  37. errD_fake = criterion(output, label)
  38. # Calculate the gradients for this batch
  39. errD_fake.backward()
  40. D_G_z1 = output.mean().item()
  41. # Add the gradients from the all-real and all-fake batches
  42. errD = errD_real + errD_fake
  43. # Update D
  44. optimizerD.step()
  45. ############################
  46. # (2) Update G network: maximize log(D(G(z)))
  47. ###########################
  48. netG.zero_grad()
  49. label.fill_(real_label) # fake labels are real for generator cost
  50. # Since we just updated D, perform another forward pass of all-fake batch through D
  51. output = netD(fake).view(-1)
  52. # Calculate G's loss based on this output
  53. errG = criterion(output, label)
  54. # Calculate gradients for G
  55. errG.backward()
  56. D_G_z2 = output.mean().item()
  57. # Update G
  58. optimizerG.step()
  59. # Output training stats
  60. if i % 50 == 0:
  61. print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
  62. % (epoch, num_epochs, i, len(dataloader),
  63. errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))
  64. # Save Losses for plotting later
  65. G_losses.append(errG.item())
  66. D_losses.append(errD.item())
  67. # Check how the generator is doing by saving G's output on fixed_noise
  68. if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)):
  69. with torch.no_grad():
  70. fake = netG(fixed_noise).detach().cpu()
  71. img_list.append(vutils.make_grid(fake, padding=2, normalize=True))
  72. iters += 1

Out:

  1. Starting Training Loop...
  2. [0/5][0/1583] Loss_D: 2.0937 Loss_G: 5.2060 D(x): 0.5704 D(G(z)): 0.6680 / 0.0090
  3. [0/5][50/1583] Loss_D: 0.2073 Loss_G: 12.9653 D(x): 0.9337 D(G(z)): 0.0000 / 0.0000
  4. [0/5][100/1583] Loss_D: 0.0364 Loss_G: 34.5761 D(x): 0.9917 D(G(z)): 0.0000 / 0.0000
  5. [0/5][150/1583] Loss_D: 0.0078 Loss_G: 39.3111 D(x): 0.9947 D(G(z)): 0.0000 / 0.0000
  6. [0/5][200/1583] Loss_D: 0.0029 Loss_G: 38.7681 D(x): 0.9974 D(G(z)): 0.0000 / 0.0000
  7. [0/5][250/1583] Loss_D: 1.2861 Loss_G: 13.3356 D(x): 0.8851 D(G(z)): 0.2970 / 0.0035
  8. [0/5][300/1583] Loss_D: 1.2933 Loss_G: 6.7655 D(x): 0.8533 D(G(z)): 0.5591 / 0.0020
  9. [0/5][350/1583] Loss_D: 0.7473 Loss_G: 3.2617 D(x): 0.5798 D(G(z)): 0.0514 / 0.0483
  10. [0/5][400/1583] Loss_D: 0.5454 Loss_G: 4.0144 D(x): 0.8082 D(G(z)): 0.2346 / 0.0310
  11. [0/5][450/1583] Loss_D: 1.1872 Loss_G: 3.2918 D(x): 0.4389 D(G(z)): 0.0360 / 0.0858
  12. [0/5][500/1583] Loss_D: 0.7546 Loss_G: 4.7428 D(x): 0.9072 D(G(z)): 0.4049 / 0.0178
  13. [0/5][550/1583] Loss_D: 0.3514 Loss_G: 3.7726 D(x): 0.8937 D(G(z)): 0.1709 / 0.0394
  14. [0/5][600/1583] Loss_D: 0.4400 Loss_G: 4.1662 D(x): 0.7768 D(G(z)): 0.1069 / 0.0284
  15. [0/5][650/1583] Loss_D: 0.3275 Loss_G: 4.3374 D(x): 0.8452 D(G(z)): 0.0852 / 0.0214
  16. [0/5][700/1583] Loss_D: 0.7711 Loss_G: 5.0677 D(x): 0.9103 D(G(z)): 0.3848 / 0.0190
  17. [0/5][750/1583] Loss_D: 0.5346 Loss_G: 5.7441 D(x): 0.8971 D(G(z)): 0.2969 / 0.0064
  18. [0/5][800/1583] Loss_D: 0.5027 Loss_G: 2.5982 D(x): 0.6897 D(G(z)): 0.0431 / 0.1196
  19. [0/5][850/1583] Loss_D: 0.4479 Loss_G: 4.8790 D(x): 0.7407 D(G(z)): 0.0456 / 0.0200
  20. [0/5][900/1583] Loss_D: 0.9812 Loss_G: 5.8792 D(x): 0.8895 D(G(z)): 0.4801 / 0.0070
  21. [0/5][950/1583] Loss_D: 0.5154 Loss_G: 3.4813 D(x): 0.7722 D(G(z)): 0.1549 / 0.0449
  22. [0/5][1000/1583] Loss_D: 0.8468 Loss_G: 6.6179 D(x): 0.8914 D(G(z)): 0.4262 / 0.0030
  23. [0/5][1050/1583] Loss_D: 0.4425 Loss_G: 3.9902 D(x): 0.8307 D(G(z)): 0.1872 / 0.0270
  24. [0/5][1100/1583] Loss_D: 0.6800 Loss_G: 4.3945 D(x): 0.8244 D(G(z)): 0.3022 / 0.0223
  25. [0/5][1150/1583] Loss_D: 0.7227 Loss_G: 2.2669 D(x): 0.6177 D(G(z)): 0.0625 / 0.1613
  26. [0/5][1200/1583] Loss_D: 0.4061 Loss_G: 5.7088 D(x): 0.9269 D(G(z)): 0.2367 / 0.0071
  27. [0/5][1250/1583] Loss_D: 0.8514 Loss_G: 3.8994 D(x): 0.7686 D(G(z)): 0.3573 / 0.0330
  28. [0/5][1300/1583] Loss_D: 0.5323 Loss_G: 3.0046 D(x): 0.7102 D(G(z)): 0.0742 / 0.1138
  29. [0/5][1350/1583] Loss_D: 0.5793 Loss_G: 4.6804 D(x): 0.8722 D(G(z)): 0.2877 / 0.0169
  30. [0/5][1400/1583] Loss_D: 0.6849 Loss_G: 5.4391 D(x): 0.8974 D(G(z)): 0.3630 / 0.0100
  31. [0/5][1450/1583] Loss_D: 1.1515 Loss_G: 6.0096 D(x): 0.8054 D(G(z)): 0.5186 / 0.0049
  32. [0/5][1500/1583] Loss_D: 0.4771 Loss_G: 3.3768 D(x): 0.8590 D(G(z)): 0.2357 / 0.0541
  33. [0/5][1550/1583] Loss_D: 0.6947 Loss_G: 5.9660 D(x): 0.8989 D(G(z)): 0.3671 / 0.0064
  34. [1/5][0/1583] Loss_D: 0.5001 Loss_G: 3.9243 D(x): 0.8238 D(G(z)): 0.2077 / 0.0377
  35. [1/5][50/1583] Loss_D: 0.4494 Loss_G: 4.4726 D(x): 0.8514 D(G(z)): 0.2159 / 0.0187
  36. [1/5][100/1583] Loss_D: 0.4519 Loss_G: 2.6781 D(x): 0.7331 D(G(z)): 0.0688 / 0.0948
  37. [1/5][150/1583] Loss_D: 0.3808 Loss_G: 3.6005 D(x): 0.8827 D(G(z)): 0.1908 / 0.0456
  38. [1/5][200/1583] Loss_D: 0.4373 Loss_G: 4.0625 D(x): 0.8281 D(G(z)): 0.1719 / 0.0306
  39. [1/5][250/1583] Loss_D: 0.5906 Loss_G: 3.1507 D(x): 0.7603 D(G(z)): 0.1952 / 0.0682
  40. [1/5][300/1583] Loss_D: 1.4315 Loss_G: 6.2042 D(x): 0.9535 D(G(z)): 0.6480 / 0.0051
  41. [1/5][350/1583] Loss_D: 0.8529 Loss_G: 1.2236 D(x): 0.5291 D(G(z)): 0.0552 / 0.3978
  42. [1/5][400/1583] Loss_D: 0.8166 Loss_G: 5.3178 D(x): 0.8460 D(G(z)): 0.3872 / 0.0104
  43. [1/5][450/1583] Loss_D: 0.6699 Loss_G: 2.4998 D(x): 0.6921 D(G(z)): 0.1719 / 0.1220
  44. [1/5][500/1583] Loss_D: 0.4986 Loss_G: 4.3763 D(x): 0.8835 D(G(z)): 0.2643 / 0.0212
  45. [1/5][550/1583] Loss_D: 0.9149 Loss_G: 5.6209 D(x): 0.9476 D(G(z)): 0.5069 / 0.0088
  46. [1/5][600/1583] Loss_D: 0.5116 Loss_G: 3.4946 D(x): 0.8368 D(G(z)): 0.2444 / 0.0488
  47. [1/5][650/1583] Loss_D: 0.4408 Loss_G: 2.8180 D(x): 0.7795 D(G(z)): 0.1262 / 0.0926
  48. [1/5][700/1583] Loss_D: 0.3821 Loss_G: 3.5735 D(x): 0.8237 D(G(z)): 0.1387 / 0.0432
  49. [1/5][750/1583] Loss_D: 0.5042 Loss_G: 2.4218 D(x): 0.6897 D(G(z)): 0.0541 / 0.1319
  50. [1/5][800/1583] Loss_D: 1.3208 Loss_G: 4.7094 D(x): 0.9466 D(G(z)): 0.5988 / 0.0158
  51. [1/5][850/1583] Loss_D: 0.3780 Loss_G: 2.9969 D(x): 0.8475 D(G(z)): 0.1662 / 0.0648
  52. [1/5][900/1583] Loss_D: 0.4350 Loss_G: 3.2726 D(x): 0.8306 D(G(z)): 0.1925 / 0.0531
  53. [1/5][950/1583] Loss_D: 0.4228 Loss_G: 2.5205 D(x): 0.7438 D(G(z)): 0.0493 / 0.1090
  54. [1/5][1000/1583] Loss_D: 0.4680 Loss_G: 4.4448 D(x): 0.8652 D(G(z)): 0.2433 / 0.0190
  55. [1/5][1050/1583] Loss_D: 0.4261 Loss_G: 2.7076 D(x): 0.7683 D(G(z)): 0.1049 / 0.0999
  56. [1/5][1100/1583] Loss_D: 0.5115 Loss_G: 1.9458 D(x): 0.6730 D(G(z)): 0.0449 / 0.2070
  57. [1/5][1150/1583] Loss_D: 0.6619 Loss_G: 2.0092 D(x): 0.6320 D(G(z)): 0.1115 / 0.1926
  58. [1/5][1200/1583] Loss_D: 0.4824 Loss_G: 2.0529 D(x): 0.7735 D(G(z)): 0.1647 / 0.1758
  59. [1/5][1250/1583] Loss_D: 0.4529 Loss_G: 4.3564 D(x): 0.9270 D(G(z)): 0.2881 / 0.0223
  60. [1/5][1300/1583] Loss_D: 0.5469 Loss_G: 2.5909 D(x): 0.7217 D(G(z)): 0.1403 / 0.1101
  61. [1/5][1350/1583] Loss_D: 0.4525 Loss_G: 1.4998 D(x): 0.7336 D(G(z)): 0.0904 / 0.2715
  62. [1/5][1400/1583] Loss_D: 0.5267 Loss_G: 2.3458 D(x): 0.7594 D(G(z)): 0.1700 / 0.1311
  63. [1/5][1450/1583] Loss_D: 0.4700 Loss_G: 3.7640 D(x): 0.9059 D(G(z)): 0.2852 / 0.0316
  64. [1/5][1500/1583] Loss_D: 0.7703 Loss_G: 1.4253 D(x): 0.5655 D(G(z)): 0.0683 / 0.3071
  65. [1/5][1550/1583] Loss_D: 0.5535 Loss_G: 2.4315 D(x): 0.6773 D(G(z)): 0.0834 / 0.1280
  66. [2/5][0/1583] Loss_D: 0.7237 Loss_G: 3.4642 D(x): 0.8383 D(G(z)): 0.3687 / 0.0442
  67. [2/5][50/1583] Loss_D: 0.4401 Loss_G: 2.4749 D(x): 0.7939 D(G(z)): 0.1526 / 0.1107
  68. [2/5][100/1583] Loss_D: 0.7470 Loss_G: 1.8611 D(x): 0.5830 D(G(z)): 0.0871 / 0.2102
  69. [2/5][150/1583] Loss_D: 0.7930 Loss_G: 1.3743 D(x): 0.5201 D(G(z)): 0.0343 / 0.3171
  70. [2/5][200/1583] Loss_D: 0.5059 Loss_G: 2.9394 D(x): 0.8044 D(G(z)): 0.2128 / 0.0739
  71. [2/5][250/1583] Loss_D: 0.5873 Loss_G: 1.6961 D(x): 0.6329 D(G(z)): 0.0561 / 0.2297
  72. [2/5][300/1583] Loss_D: 0.5341 Loss_G: 1.9229 D(x): 0.7022 D(G(z)): 0.1145 / 0.1921
  73. [2/5][350/1583] Loss_D: 0.7095 Loss_G: 1.3619 D(x): 0.5855 D(G(z)): 0.0707 / 0.3038
  74. [2/5][400/1583] Loss_D: 0.5163 Loss_G: 3.0209 D(x): 0.8695 D(G(z)): 0.2828 / 0.0657
  75. [2/5][450/1583] Loss_D: 0.5413 Loss_G: 3.5822 D(x): 0.8450 D(G(z)): 0.2748 / 0.0387
  76. [2/5][500/1583] Loss_D: 0.4929 Loss_G: 2.1009 D(x): 0.7645 D(G(z)): 0.1692 / 0.1552
  77. [2/5][550/1583] Loss_D: 0.5042 Loss_G: 2.5833 D(x): 0.7047 D(G(z)): 0.0888 / 0.1107
  78. [2/5][600/1583] Loss_D: 0.4562 Loss_G: 2.5190 D(x): 0.8316 D(G(z)): 0.2151 / 0.0987
  79. [2/5][650/1583] Loss_D: 0.9564 Loss_G: 2.5315 D(x): 0.7157 D(G(z)): 0.3861 / 0.1153
  80. [2/5][700/1583] Loss_D: 0.6706 Loss_G: 3.0991 D(x): 0.7382 D(G(z)): 0.2497 / 0.0603
  81. [2/5][750/1583] Loss_D: 0.5803 Loss_G: 2.9059 D(x): 0.7523 D(G(z)): 0.2092 / 0.0785
  82. [2/5][800/1583] Loss_D: 0.8315 Loss_G: 3.7972 D(x): 0.9184 D(G(z)): 0.4829 / 0.0325
  83. [2/5][850/1583] Loss_D: 0.6177 Loss_G: 2.2548 D(x): 0.7526 D(G(z)): 0.2470 / 0.1306
  84. [2/5][900/1583] Loss_D: 0.7398 Loss_G: 3.2303 D(x): 0.8604 D(G(z)): 0.3999 / 0.0572
  85. [2/5][950/1583] Loss_D: 0.7914 Loss_G: 1.5464 D(x): 0.6001 D(G(z)): 0.1507 / 0.2605
  86. [2/5][1000/1583] Loss_D: 0.9693 Loss_G: 4.0590 D(x): 0.9251 D(G(z)): 0.5270 / 0.0275
  87. [2/5][1050/1583] Loss_D: 0.5805 Loss_G: 2.1703 D(x): 0.6749 D(G(z)): 0.1185 / 0.1465
  88. [2/5][1100/1583] Loss_D: 0.8626 Loss_G: 0.9626 D(x): 0.5259 D(G(z)): 0.0865 / 0.4571
  89. [2/5][1150/1583] Loss_D: 0.7256 Loss_G: 4.0511 D(x): 0.9135 D(G(z)): 0.4172 / 0.0300
  90. [2/5][1200/1583] Loss_D: 0.5937 Loss_G: 3.8598 D(x): 0.8982 D(G(z)): 0.3440 / 0.0320
  91. [2/5][1250/1583] Loss_D: 0.6144 Loss_G: 1.8087 D(x): 0.6660 D(G(z)): 0.1424 / 0.2062
  92. [2/5][1300/1583] Loss_D: 0.8017 Loss_G: 1.2032 D(x): 0.5450 D(G(z)): 0.0746 / 0.3562
  93. [2/5][1350/1583] Loss_D: 0.7563 Loss_G: 1.6629 D(x): 0.6002 D(G(z)): 0.1437 / 0.2351
  94. [2/5][1400/1583] Loss_D: 0.7457 Loss_G: 1.5831 D(x): 0.6069 D(G(z)): 0.1493 / 0.2511
  95. [2/5][1450/1583] Loss_D: 0.6697 Loss_G: 2.8194 D(x): 0.7597 D(G(z)): 0.2677 / 0.0804
  96. [2/5][1500/1583] Loss_D: 0.5681 Loss_G: 2.2054 D(x): 0.7171 D(G(z)): 0.1626 / 0.1358
  97. [2/5][1550/1583] Loss_D: 0.6741 Loss_G: 2.9537 D(x): 0.8373 D(G(z)): 0.3492 / 0.0760
  98. [3/5][0/1583] Loss_D: 1.0265 Loss_G: 1.1510 D(x): 0.4474 D(G(z)): 0.0685 / 0.3681
  99. [3/5][50/1583] Loss_D: 0.6190 Loss_G: 1.9895 D(x): 0.7136 D(G(z)): 0.1900 / 0.1705
  100. [3/5][100/1583] Loss_D: 0.7754 Loss_G: 3.2350 D(x): 0.8117 D(G(z)): 0.3782 / 0.0535
  101. [3/5][150/1583] Loss_D: 1.8367 Loss_G: 5.1895 D(x): 0.9408 D(G(z)): 0.7750 / 0.0095
  102. [3/5][200/1583] Loss_D: 0.6821 Loss_G: 2.4254 D(x): 0.7709 D(G(z)): 0.3020 / 0.1152
  103. [3/5][250/1583] Loss_D: 1.1273 Loss_G: 4.2718 D(x): 0.9373 D(G(z)): 0.5970 / 0.0206
  104. [3/5][300/1583] Loss_D: 0.5944 Loss_G: 2.2868 D(x): 0.7547 D(G(z)): 0.2306 / 0.1256
  105. [3/5][350/1583] Loss_D: 0.7941 Loss_G: 3.4394 D(x): 0.7585 D(G(z)): 0.3472 / 0.0437
  106. [3/5][400/1583] Loss_D: 0.7588 Loss_G: 3.7067 D(x): 0.8416 D(G(z)): 0.3981 / 0.0347
  107. [3/5][450/1583] Loss_D: 0.7671 Loss_G: 2.7477 D(x): 0.7932 D(G(z)): 0.3686 / 0.0823
  108. [3/5][500/1583] Loss_D: 1.0295 Loss_G: 1.6097 D(x): 0.6318 D(G(z)): 0.3568 / 0.2429
  109. [3/5][550/1583] Loss_D: 0.5186 Loss_G: 2.1037 D(x): 0.7998 D(G(z)): 0.2266 / 0.1473
  110. [3/5][600/1583] Loss_D: 0.5855 Loss_G: 1.9740 D(x): 0.6520 D(G(z)): 0.0972 / 0.1770
  111. [3/5][650/1583] Loss_D: 0.5954 Loss_G: 2.2880 D(x): 0.7819 D(G(z)): 0.2611 / 0.1234
  112. [3/5][700/1583] Loss_D: 1.0706 Loss_G: 1.1761 D(x): 0.4335 D(G(z)): 0.0681 / 0.3609
  113. [3/5][750/1583] Loss_D: 0.7128 Loss_G: 1.5402 D(x): 0.5909 D(G(z)): 0.0993 / 0.2702
  114. [3/5][800/1583] Loss_D: 0.8883 Loss_G: 2.4234 D(x): 0.8035 D(G(z)): 0.4176 / 0.1206
  115. [3/5][850/1583] Loss_D: 0.7085 Loss_G: 2.7516 D(x): 0.7502 D(G(z)): 0.2918 / 0.0878
  116. [3/5][900/1583] Loss_D: 0.8472 Loss_G: 3.5935 D(x): 0.8553 D(G(z)): 0.4403 / 0.0397
  117. [3/5][950/1583] Loss_D: 0.4454 Loss_G: 2.3438 D(x): 0.7763 D(G(z)): 0.1519 / 0.1226
  118. [3/5][1000/1583] Loss_D: 1.2425 Loss_G: 1.0600 D(x): 0.3930 D(G(z)): 0.0889 / 0.4122
  119. [3/5][1050/1583] Loss_D: 1.0465 Loss_G: 1.4973 D(x): 0.4618 D(G(z)): 0.1165 / 0.2906
  120. [3/5][1100/1583] Loss_D: 0.5885 Loss_G: 2.7760 D(x): 0.8852 D(G(z)): 0.3356 / 0.0854
  121. [3/5][1150/1583] Loss_D: 0.5940 Loss_G: 2.5669 D(x): 0.7481 D(G(z)): 0.2109 / 0.1001
  122. [3/5][1200/1583] Loss_D: 0.9074 Loss_G: 3.0569 D(x): 0.7762 D(G(z)): 0.4214 / 0.0644
  123. [3/5][1250/1583] Loss_D: 0.7487 Loss_G: 3.0959 D(x): 0.8534 D(G(z)): 0.4052 / 0.0601
  124. [3/5][1300/1583] Loss_D: 0.5956 Loss_G: 2.5807 D(x): 0.7263 D(G(z)): 0.1887 / 0.1039
  125. [3/5][1350/1583] Loss_D: 1.7038 Loss_G: 0.6425 D(x): 0.2487 D(G(z)): 0.0507 / 0.5746
  126. [3/5][1400/1583] Loss_D: 0.5863 Loss_G: 1.7754 D(x): 0.6609 D(G(z)): 0.1044 / 0.2069
  127. [3/5][1450/1583] Loss_D: 0.4925 Loss_G: 2.7946 D(x): 0.7665 D(G(z)): 0.1660 / 0.0864
  128. [3/5][1500/1583] Loss_D: 0.6616 Loss_G: 2.9829 D(x): 0.9091 D(G(z)): 0.3944 / 0.0654
  129. [3/5][1550/1583] Loss_D: 1.2097 Loss_G: 1.0897 D(x): 0.4433 D(G(z)): 0.1887 / 0.3918
  130. [4/5][0/1583] Loss_D: 0.5653 Loss_G: 2.1567 D(x): 0.6781 D(G(z)): 0.1105 / 0.1464
  131. [4/5][50/1583] Loss_D: 0.7300 Loss_G: 1.7770 D(x): 0.7472 D(G(z)): 0.3011 / 0.2104
  132. [4/5][100/1583] Loss_D: 0.5735 Loss_G: 1.7644 D(x): 0.6723 D(G(z)): 0.1219 / 0.2092
  133. [4/5][150/1583] Loss_D: 1.0598 Loss_G: 0.6708 D(x): 0.4336 D(G(z)): 0.0800 / 0.5560
  134. [4/5][200/1583] Loss_D: 0.6098 Loss_G: 2.0432 D(x): 0.6658 D(G(z)): 0.1378 / 0.1655
  135. [4/5][250/1583] Loss_D: 0.7227 Loss_G: 1.6686 D(x): 0.5750 D(G(z)): 0.0759 / 0.2371
  136. [4/5][300/1583] Loss_D: 0.8077 Loss_G: 2.7966 D(x): 0.7647 D(G(z)): 0.3703 / 0.0771
  137. [4/5][350/1583] Loss_D: 0.7086 Loss_G: 1.3171 D(x): 0.5890 D(G(z)): 0.1103 / 0.3079
  138. [4/5][400/1583] Loss_D: 0.6418 Loss_G: 2.3383 D(x): 0.6284 D(G(z)): 0.1060 / 0.1303
  139. [4/5][450/1583] Loss_D: 0.7046 Loss_G: 3.6138 D(x): 0.8926 D(G(z)): 0.4057 / 0.0354
  140. [4/5][500/1583] Loss_D: 1.7355 Loss_G: 2.1156 D(x): 0.5473 D(G(z)): 0.4802 / 0.2431
  141. [4/5][550/1583] Loss_D: 0.6479 Loss_G: 2.5634 D(x): 0.7987 D(G(z)): 0.3139 / 0.0956
  142. [4/5][600/1583] Loss_D: 0.5650 Loss_G: 1.9429 D(x): 0.6772 D(G(z)): 0.1203 / 0.1713
  143. [4/5][650/1583] Loss_D: 0.9440 Loss_G: 3.2048 D(x): 0.7789 D(G(z)): 0.4225 / 0.0533
  144. [4/5][700/1583] Loss_D: 0.5745 Loss_G: 2.5296 D(x): 0.7004 D(G(z)): 0.1496 / 0.1075
  145. [4/5][750/1583] Loss_D: 0.7448 Loss_G: 1.5417 D(x): 0.5864 D(G(z)): 0.1132 / 0.2617
  146. [4/5][800/1583] Loss_D: 0.5315 Loss_G: 2.4287 D(x): 0.7047 D(G(z)): 0.1254 / 0.1159
  147. [4/5][850/1583] Loss_D: 1.1006 Loss_G: 0.9708 D(x): 0.4101 D(G(z)): 0.0549 / 0.4226
  148. [4/5][900/1583] Loss_D: 0.8635 Loss_G: 1.1581 D(x): 0.5057 D(G(z)): 0.0711 / 0.3618
  149. [4/5][950/1583] Loss_D: 0.5915 Loss_G: 2.8714 D(x): 0.8364 D(G(z)): 0.3005 / 0.0727
  150. [4/5][1000/1583] Loss_D: 1.5283 Loss_G: 0.4922 D(x): 0.2847 D(G(z)): 0.0228 / 0.6394
  151. [4/5][1050/1583] Loss_D: 0.7626 Loss_G: 1.7556 D(x): 0.5865 D(G(z)): 0.1282 / 0.2159
  152. [4/5][1100/1583] Loss_D: 0.6571 Loss_G: 1.7024 D(x): 0.6470 D(G(z)): 0.1505 / 0.2243
  153. [4/5][1150/1583] Loss_D: 0.7735 Loss_G: 1.2737 D(x): 0.5851 D(G(z)): 0.1427 / 0.3350
  154. [4/5][1200/1583] Loss_D: 0.4104 Loss_G: 3.2208 D(x): 0.8835 D(G(z)): 0.2290 / 0.0520
  155. [4/5][1250/1583] Loss_D: 0.4898 Loss_G: 2.1841 D(x): 0.7873 D(G(z)): 0.1912 / 0.1451
  156. [4/5][1300/1583] Loss_D: 0.6657 Loss_G: 2.5232 D(x): 0.6504 D(G(z)): 0.1283 / 0.1273
  157. [4/5][1350/1583] Loss_D: 1.0126 Loss_G: 4.9254 D(x): 0.9131 D(G(z)): 0.5439 / 0.0115
  158. [4/5][1400/1583] Loss_D: 1.2293 Loss_G: 5.6073 D(x): 0.9281 D(G(z)): 0.6209 / 0.0062
  159. [4/5][1450/1583] Loss_D: 0.3908 Loss_G: 2.4251 D(x): 0.7873 D(G(z)): 0.1181 / 0.1124
  160. [4/5][1500/1583] Loss_D: 1.1000 Loss_G: 0.9861 D(x): 0.4594 D(G(z)): 0.1542 / 0.4324
  161. [4/5][1550/1583] Loss_D: 0.9504 Loss_G: 3.8109 D(x): 0.9275 D(G(z)): 0.5386 / 0.0277

结果

最后,让我们看看我们是如何做到的。 在这里,我们将看三个不同的结果。 首先,我们将了解 D 和 G 的损失在训练过程中如何变化。 其次,我们将在每个时期将 G 的输出显示为 fixed_noise 批次。 第三,我们将查看一批真实数据和来自 G 的一批伪数据。

损失与训练迭代

下面是 D & G 的损失与训练迭代的关系图。

  1. plt.figure(figsize=(10,5))
  2. plt.title("Generator and Discriminator Loss During Training")
  3. plt.plot(G_losses,label="G")
  4. plt.plot(D_losses,label="D")
  5. plt.xlabel("iterations")
  6. plt.ylabel("Loss")
  7. plt.legend()
  8. plt.show()

../_images/sphx_glr_dcgan_faces_tutorial_002.png

可视化 G 的进度

请记住,在每次训练之后,我们如何将生成器的输出保存为 fixed_noise 批次。 现在,我们可以用动画形象化 G 的训练进度。 按下播放按钮开始动画。

  1. #%%capture
  2. fig = plt.figure(figsize=(8,8))
  3. plt.axis("off")
  4. ims = [[plt.imshow(np.transpose(i,(1,2,0)), animated=True)] for i in img_list]
  5. ani = animation.ArtistAnimation(fig, ims, interval=1000, repeat_delay=1000, blit=True)
  6. HTML(ani.to_jshtml())

../_images/sphx_glr_dcgan_faces_tutorial_003.png

实像与假像

最后,让我们并排查看一些真实图像和伪图像。

  1. # Grab a batch of real images from the dataloader
  2. real_batch = next(iter(dataloader))
  3. # Plot the real images
  4. plt.figure(figsize=(15,15))
  5. plt.subplot(1,2,1)
  6. plt.axis("off")
  7. plt.title("Real Images")
  8. plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=5, normalize=True).cpu(),(1,2,0)))
  9. # Plot the fake images from the last epoch
  10. plt.subplot(1,2,2)
  11. plt.axis("off")
  12. plt.title("Fake Images")
  13. plt.imshow(np.transpose(img_list[-1],(1,2,0)))
  14. plt.show()

../_images/sphx_glr_dcgan_faces_tutorial_004.png

下一步去哪里

我们已经走到了旅程的尽头,但是您可以从这里到达几个地方。 你可以:

  • 训练更长的时间,看看效果如何
  • 修改此模型以采用其他数据集,并可能更改图像的大小和模型架构
  • 在处查看其他一些不错的 GAN 项目
  • 创建可生成音乐的 GAN

脚本的总运行时间:(28 分钟 39.288 秒)

Download Python source code: dcgan_faces_tutorial.py Download Jupyter notebook: dcgan_faces_tutorial.ipynb

由狮身人面像画廊生成的画廊