Pytorch Note48 DCGAN生成人脸
Posted 风信子的猫Redamancy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pytorch Note48 DCGAN生成人脸相关的知识,希望对你有一定的参考价值。
Pytorch Note48 DCGAN生成人脸
文章目录
全部笔记的汇总贴: Pytorch Note 快乐星球
生成对抗网络
什么是 GAN?
之前已经对GAN有了一个简单的介绍,并且对生成对抗网络的数学原理进行了一个较为简单的推导。详细可以查看Note45 生成对抗网络,里面对GAN进行了一个较为详细的介绍,这里还是粗略的介绍一下。
GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们可以从同一分布中生成新数据。 GAN 由 Ian Goodfellow 于 2014 年发明,并在论文《生成对抗网络》中首次进行了描述。 它们由两个不同的模型组成:生成器和判别器。 生成器的工作是生成看起来像训练图像的“假”图像。 判别器的工作是查看图像并从生成器输出它是真实的训练图像还是伪图像。 在训练过程中,生成器不断尝试通过生成越来越好的伪造品而使判别器的表现超过智者,而判别器正在努力成为更好的侦探并正确地对真实和伪造图像进行分类。 博弈的平衡点是当生成器生成的伪造品看起来像直接来自训练数据时,而判别器则总是猜测生成器输出是真实还是伪造品的 50% 置信度。
现在,让我们从判别器开始定义一些在整个教程中使用的符号。 令x
为代表图像的数据。 D(x)
是判别器网络,其输出x
来自训练数据而不是生成器的(标量)概率。 在这里,由于我们要处理图像,因此D(x)
的输入是 CHW 大小为3x64x64
的图像。 直观地,当x
来自训练数据时,D(x)
应该为高,而当x
来自生成器时,它应该为低。 D(x)
也可以被认为是传统的二分类器。
对于生成器的表示法,令z
是从标准正态分布中采样的潜在空间向量。 G(z)
表示将隐向量z
映射到数据空间的生成器函数。 G
的目标是估计训练数据来自p_data
的分布,以便它可以从该估计分布(p_g
)生成假样本。
因此,D(G(z))
是生成器G
的输出是真实图像的概率(标量)。 如 Goodfellow 的论文中所述,D
和G
玩一个$ minimax $游戏,其中D
试图最大化其正确分类实物和假物log D(x)
,并且G
尝试最小化D
预测其输出为假的概率log(1 - D(G(g(x))))
。 从论文中来看,GAN 损失为
min G max D V ( G , D ) \\min _{G} \\max _{D} V(G, D) GminDmaxV(G,D)
V ( G , D ) = E x ∼ P data [ log D ( X ) ] + E x ∼ P G [ log ( 1 − D ( X ) ) ] V(G, D)=E_{x \\sim P_{\\text {data }}}[\\log D(X)]+E_{x \\sim P_{G}}[\\log (1-D(X))] V(G,D)=Ex∼Pdata [logD(X)]+Ex∼PG[log(1−D(X))]
从理论上讲,此极小极大游戏的解决方案是 p g = p d a t a p_g = p_{data} pg=pdata,判别器会随机猜测输入是真实的还是假的。 但是,GAN 的收敛理论仍在积极研究中,实际上,模型并不总是能达到这一目的。
什么是 DCGAN?
DCGAN 是上述 GAN 的直接扩展,不同之处在于,DCGAN 分别在判别器和生成器中分别使用卷积
和卷积转置层
。 它最早由 Radford 等人,在论文《使用深度卷积生成对抗网络的无监督表示学习》中描述。 判别器由分层的卷积层,批量规范层和 LeakyReLU 激活组成。 输入是3x64x64
的输入图像,输出是输入来自真实数据分布的标量概率。 生成器由转置卷积层,批量规范层和 ReLU 激活组成。 输入是从标准正态分布中提取的潜向量z
,输出是3x64x64
RGB 图像。 跨步的转置层使潜向量可以转换为具有与图像相同形状的体积。 在本文中,作者还提供了一些有关如何设置优化器,如何计算损失函数以及如何初始化模型权重的提示,所有这些都将在接下来的部分中进行解释。
导入所需要的库
首先导入所需要的库,并且设置随机种子为999
from __future__ import print_function
%matplotlib inline
import argparse
import os
import random
import torch
import torch.nn as nn
import torch.nn.parallel
import torch.backends.cudnn as cudnn
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
# Set random seed for reproducibility
manualSeed = 999
#manualSeed = random.randint(1, 10000) # use if you want new results
print("Random Seed: ", manualSeed)
random.seed(manualSeed)
torch.manual_seed(manualSeed)
Random Seed: 999
输入
让我们为跑步定义一些输入:
dataroot
-数据集文件夹根目录的路径。worker
-使用DataLoader
加载数据的工作线程数batch_size
-训练中使用的批量大小。 DCGAN 使用的Batch大小为 128(可以根据自己的电脑性能进行修改)image_size
-用于训练的图像的空间大小。 此实现默认为64x64
。 如果需要其他尺寸,则必须更改D
和G
的结构。nc
-输入图像中的彩色通道数。 对于彩色图像,这是 3nz
-潜向量的长度ngf
-与通过生成器传送的特征映射的深度有关ndf
-设置通过判别器传播的特征映射的深度num_epochs
-要运行的训练周期数。 训练更长的时间可能会导致更好的结果,但也会花费更长的时间lr
-训练的学习率。 如 DCGAN 论文中所述,此数字应为 0.0002beta1
-Adam 优化器的beta1
超参数。 如论文所述,该数字应为 0.5ngpu
-可用的 GPU 数量。 如果为 0,则代码将在 CPU 模式下运行。 如果此数字大于 0,它将在该数量的 GPU 上运行
# Root directory for dataset
dataroot = 'D:/data/img_align_celeba'
# Number of workers for dataloader
workers = 2
# Batch size during training
batch_size = 128
# Spatial size of training images. All images will be resized to this
# size using a transformer.
image_size = 64
# Number of channels in the training images. For color images this is 3
nc = 3
# Size of z latent vector (i.e. size of generator input)
nz = 100
# Size of feature maps in generator
ngf = 64
# Size of feature maps in discriminator
ndf = 64
# Number of training epochs
num_epochs = 5
# Learning rate for optimizers
lr = 0.0002
# Beta1 hyperparam for Adam optimizers
beta1 = 0.5
# Number of GPUs available. Use 0 for CPU mode.
ngpu = 1
数据
在本教程中,我们将使用 Celeb-A Faces 数据集,该数据集可在链接的站点或 Google 云端硬盘中下载。 数据集将下载为名为img_align_celeba.zip
的文件。 下载完成后,创建一个名为celeba
的目录,并将 zip 文件解压缩到该目录中。 然后,将此笔记本的dataroot
输入设置为刚创建的celeba
目录。 结果目录结构应为:
/path/to/img_align_celeba
-> img_align_celeba
-> 188242.jpg
-> 173822.jpg
-> 284702.jpg
-> 537394.jpg
...
大约一共有202,601张图片,所以数据集还是比较大的,如果希望测试数据,可以适当对数据集减小,所以我也存在一个little_celaba,里面有50,000张图片,虽然结果不会很好,但是也可以看出结果。此外,如果数据集下载有问题,无法连接Google硬盘,也可以从我的CSDN链接进行下载,或者自制数据集都是可以的。这里给出链接
这是重要的一步,因为我们将使用ImageFolder
数据集类,该类要求数据集的根文件夹中有子目录。 现在,我们可以创建数据集,创建数据加载器,将设备设置为可以运行,并最终可视化一些训练数据。
# We can use an image folder dataset the way we have it setup.
# Create the dataset
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)),
]))
# Create the dataloader
dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
shuffle=True, num_workers=workers)
# Decide which device we want to run on
device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")
# Plot some training images
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)[:64], padding=2, normalize=True).cpu(),(1,2,0)))
实现
设置好输入参数并准备好数据集后,我们现在可以进入实现了。 我们将从权重初始化策略开始,然后详细讨论生成器,判别器,损失函数和训练循环。
权重初始化
在 DCGAN 论文中,作者指定所有模型权重均应从均值为 0,stdev = 0.02
的正态分布中随机初始化。 weights_init
函数采用已初始化的模型作为输入,并重新初始化所有卷积,卷积转置和批量归一化层以满足此标准。 初始化后立即将此函数应用于模型。
# custom weights initialization called on netG and netD
def weights_init(m):
classname = m.__class__.__name__
# 存在卷积Conv层
if classname.find('Conv') != -1:
nn.init.normal_(m.weight.data, 0.0, 0.02) # 标准初始化权重
# 存在Batch层
elif classname.find('BatchNorm') != -1:
nn.init.normal_(m.weight.data, 1.0, 0.02) # 标准初始化权重
nn.init.constant_(m.bias.data, 0) # 偏置项为0
生成器
生成器G
用于将潜在空间向量(z
)映射到数据空间。 由于我们的数据是图像,因此将z
转换为数据空间意味着最终创建与训练图像大小相同的 RGB 图像(即3x64x64
)。 在实践中,这是通过一系列跨步的二维卷积转置层来完成的,每个层都与 2d 批量规范层和 relu 激活配对。 生成器的输出通过 tanh 函数馈送,以使其返回到输入数据范围[-1,1]
。 值得注意的是,在卷积转置层之后存在批量规范函数,因为这是 DCGAN 论文的关键贡献。 这些层有助于训练过程中的梯度流动。 DCGAN 纸生成的图像如下所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJA5IXaj-1629638857365)(https://pytorch.apachecn.org/docs/1.7/img/85974d98be6202902f21ce274418953f.png)]
请注意,我们在输入部分中设置的输入(nz
,ngf
和nc
)如何影响代码中的生成器架构。 nz
是z
输入向量的长度,ngf
与通过生成器传播的特征映射的大小有关, nc
是输出图像中的通道(对于 RGB 图像设置为 3)。 下面是生成器的代码。
# Generator Code
class Generator(nn.Module):
def __init__(self, ngpu):
super(Generator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# input is Z, going into a convolution
nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
# state size. (ngf*8) x 4 x 4
nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 4),
nn.ReLU(True),
# state size. (ngf*4) x 8 x 8
nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 2),
nn.ReLU(True),
# state size. (ngf*2) x 16 x 16
nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf),
nn.ReLU(True),
# state size. (ngf) x 32 x 32
nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
nn.Tanh()
# state size. (nc) x 64 x 64
)
def forward(self, input):
return self.main(input)
现在,我们可以实例化生成器并应用weights_init
函数。 签出打印的模型以查看生成器对象的结构。
# Create the generator
netG = Generator(ngpu).to(device)
# Handle multi-gpu if desired
if (device.type == 'cuda') and (ngpu > 1):
netG = nn.DataParallel(netG, list(range(ngpu))) # 如果ngpu > 1,可以利用此代码并行运算
# Apply the weights_init function to randomly initialize all weights
# to mean=0, stdev=0.2.
netG.apply(weights_init)
# Print the model
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()
)
)
判别器
如前所述,判别器D
是一个二分类网络,将图像作为输入并输出标量概率,即输入图像是真实的(与假的相对)。 在这里,D
拍摄3x64x64
的输入图像,通过一系列的Conv2d
,BatchNorm2d
和LeakyReLU
层对其进行处理,然后通过 Sigmoid 激活函数输出最终概率。 如果需要解决此问题,可以用更多层扩展此架构,但是使用跨步卷积,BatchNorm
和LeakyReLU
仍然很重要。 DCGAN 论文提到,使用跨步卷积而不是通过池化来进行下采样是一个好习惯,因为它可以让网络学习自己的池化特征。 批量规范和泄漏 ReLU 函数还可以促进健康的梯度流,这对于G
和D
的学习过程都是至关重要的。
class Discriminator(nn.Module):
def __init__(self, ngpu):
super(Discriminator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# input is (nc) x 64 x 64
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
# state size. (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),
# state size. (ndf*2) x 16 x 16
nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d以上是关于Pytorch Note48 DCGAN生成人脸的主要内容,如果未能解决你的问题,请参考以下文章
GAN-生成对抗神经网络(Pytorch)-合集GAN-DCGAN-CGAN