GAN 如何打造人造名人身份?
文章目录
- 一、介绍
- 二、生成对抗网络(GAN)
- 三、什么是发电机?
- 四、什么是鉴别器?
- 五、对抗性训练
- 六、实现
- 七、数据
- 7.1 初始配置和设置
- 7.2 数据加载器
- 7.3 噪声产生
- 7.4 发电机
- 7.5 鉴别器
- 八、训练
- 九、可视 化
- 十、结论
一、介绍
在人工智能时代,一个非凡的现象正在显现——生成对抗网络(GAN)正在巧妙地打造人工名人身份。这种技术与创造力的有趣融合催生了全新的数字名人。加入我们,踏上一段引人入胜的旅程,深入 GAN 世界,揭开创造迷人的虚拟领域的人造名人角色背后的魔力。GANs如何使这成为可能?让我们来探索这种数字艺术背后的秘密。
我们下文将学到:
生成对抗网络 (GAN) 的概念
如何训练生成器和鉴别器?
实现GAN模型的分步过程
通过对抗性训练深入了解 GAN 如何随着时间的推移而改进
本文是作为数据科学博客马拉松的一部分发表的。
二、生成对抗网络(GAN)
生成对抗网络 (GAN) 是一种深度学习模型,由 Goodfellow 提供。通过名称本身,我们可以理解这些 GAN 的用途。是的!我们将其用于生成目的。它是一个产生一些东西的网络。使用 GAN 生成合成数据。这些数据包括图像、文本、音频等,这与真实世界的数据类似。GAN包含两个神经网络。这些网络被称为生成器和鉴别器。在训练期间,这两个网络的训练方式使它们相互竞争并变得更好。
三、什么是发电机?
生成器是负责生成的神经网络。因此,要给出输出,它需要输入。发生器采用的输入是一些随机噪声。发生器接受这种随机噪声,并试图生成类似于真实数据的输出。每当它从鉴别器那里获得反馈时,它都会自我改进,并在下次生成更好的数据。例如,以图像生成为例,生成器将生成图像。随着它通过训练的改进,它从随机噪声开始,最终改进其输出,变得越来越逼真。第一次,它可能不会产生与原始数据最相似的数据。有时它甚至会生成根本不是图像的图像。随着训练的进行,会生成更准确、更准确的更好数据。
四、什么是鉴别器?
鉴别器是负责评估的神经网络。为了便于理解,我们可以称它为侦探。该生成器接收生成器生成的真实数据和虚假数据。它必须区分虚假数据和真实数据。简单来说,它涉及将实际数据与虚假数据进行分类。与生成器类似,随着训练的继续,它将能够更好地区分。它可能无法在第一次尝试时发挥出最佳效果。但是在训练过程中,它会变得越来越好,最后,它将能够区分大部分虚假数据。正如我所说,它必须像侦探一样工作。
五、对抗性训练
生成器和鉴别器都经过训练,称为对抗训练。正如我已经提到的,他们都从事竞技训练。我们知道生成器生成看起来像真实数据的假数据,而鉴别器试图区分假数据。在训练过程的下一步中,生成器旨在改进和生成虚假数据以欺骗鉴别器。同样,鉴别器检测假数据。这样,在训练过程中,他们俩在各自的任务中都变得更好。这个过程一直持续到生成器生成真实的数据,并且鉴别器无法将其与真实数据区分开来。至此,GAN已经达到了一种平衡,生成的数据与真实数据非常相似。
六、实现
让我们从导入所有必要的库开始。这主要包括一些火炬模块。我们将使用 matplotlib 进行可视化。
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
七、数据
对于此项目实施,我们将使用 CelebFaces 属性 (CelebA) 数据集。此数据集在 Kaggle 中可用。你可以从这里下载它。
数据集链接:https://www.kaggle.com/datasets/jessicali9530/celeba-dataset
该数据集包含 202,599 张不同名人的脸部图像。使用它来训练和测试人脸检测模型,尤其是那些能够识别特定面部特征的模型。
7.1 初始配置和设置
现在让我们在开始实际实现之前设置一些参数和配置。首先,我们必须提供数据集所在的路径。定义工作人员的数量。它表示数据加载程序的数据加载工作线程数。我们使用数据加载工作线程并行加载批量数据,从而加快训练期间的数据加载过程。定义批量大小和图像大小、训练图像中的通道数、潜在向量的大小、生成器中特征图的大小、鉴别器中特征图的大小、纪元数、学习率、Adam 优化器的 beta1 超参数。可用于训练的 GPU 数量。如果没有可用的 GPU,它将使用 CPU。该代码设置这些配置并打印所选设备(CPU 或 GPU)以及用于训练的 GPU 数量。在训练 GAN 模型时,必须严格控制这些设置。
# dataset
dataroot = r"C:\Users\Admin\Downloads\faces\img_align_celeba"
workers = 1
batch_size = 16
image_size = 64
nc = 3
# Size of z latent vector
nz = 64
# Size of feature maps in generator
ngf = 64
# Size of feature maps in discriminator
ndf = 64
num_epochs = 5
lr = 0.0001
beta1 = 0.2
ngpu = 1
device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")
torch.backends.cudnn.benchmark = True
print(device)
print(ngpu)
7.2 数据加载器
让我们为训练数据创建一个 PyTorch 数据集和数据加载器。使用 dset 创建数据集变量。ImageFolder 类。我们创建了这个 PyTorch 数据集类来加载组织到文件夹中的数据,这需要两个基本参数。我们对数据集中的每个图像应用了一系列图像更改,称为转换。
使用 torch.utils.data.DataLoader 创建 dataloader 变量。这负责批量加载数据。它采用定义的数据集和批量大小。除了它们,我们还必须提到用于数据加载的工作进程的数量以及是否对数据进行洗牌。我们之前定义了这个工人数量。若要确保模型不会在每个时期查看相同顺序的图像,必须在训练前对数据进行随机排序。
然后我们绘制一些训练图像,以了解我们的训练数据是什么样子的。
# 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)
# 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)))
7.3 噪声产生
现在让我们制造一些噪音。该噪声是GAN中发生器的输入。noise() 函数生成随机噪声。因此,让我们来定义它。它不接受任何参数,并创建一个形状的随机张量 (nz, ngf),其中包含从 0 到 1 之间的均匀分布中采样的值。此外,缩放随机张量,使值介于 -1 和 1 之间。它通过将随机张量乘以 2 然后减去 1 来实现这一点。该噪声张量可以与其他数据(例如标签或潜在向量)连接,并输入生成器以生成图像。
def noise():
return 2*torch.rand(nz, ngf, device = device) - 1
7.4 发电机
是时候定义生成器了。为此,我们必须定义生成器类。接下来,指定构造函数方法。它接受 ngpu 输入,该输入指示有多少个 GPU 可用于训练。ngpu 参数用于处理多 GPU 训练,但在此代码中,未使用它。在构造函数中,定义了生成器体系结构。
顺序模块将一系列卷积运算、批量归一化和激活函数应用于输入 x。然后,我们定义生成输出图像的最终卷积层。那就是 self.toImage。它使用过去图层的特征图来创建所需的 3 通道图像。
接下来,我们必须定义正向方法。发电机的发电机前传由此方法定义。它接受输入 x。此 x 是噪声向量或潜在向量,并传递它通过构造函数中定义的层。结果是生成的图像。最初,输入 x 在通过潜在层后进行转换。然后通过定义的所有卷积层对其进行处理。在每个卷积层之后,特征图与相应层的特征图连接起来,然后再进行上采样。这有助于生成器捕获高级要素和空间细节。生成的图像是从最终卷积层的输出中获得的,并作为前向传递的结果返回。
# GeneratorCode
class Generator(nn.Module):
def __init__(self, ngpu):
super(Generator, self).__init__()
self.ngpu = ngpu
self.fromLatent = nn.Linear(ngf, (image_size*image_size//16)*ndf)
self.dlayer1 = nn.Sequential(
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True)
)
self.dlayer2 = nn.Sequential(
nn.Upsample(scale_factor=2),
nn.Conv2d(2 * ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True)
)
self.dlayer3 = nn.Sequential(
nn.Upsample(scale_factor=2),
nn.Conv2d(2 * ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True)
)
self.toImage = nn.Conv2d(ndf, 3, 3, padding = 1)
def forward(self, x):
x = self.fromLatent(x)
x = x.view(x.size(0), ndf, image_size//4, image_size//4)
h0 = x
x = self.dlayer1(x)
x = torch.cat([x, h0], dim=1)
x = self.dlayer2(x)
h0 = nn.functional.interpolate(h0, scale_factor=2, mode='nearest')
x = torch.cat([x, h0], dim=1)
x = self.dlayer3(x)
x = self.toImage(x)
return x
7.5 鉴别器
为了识别真实和虚假的图像,我们必须构建一个类似于我们之前定义的生成器的鉴别器网络。在这种情况下,将存在一个鉴别器类。定义鉴别器类的构造函数方法。它接受 ngpu 输入,该输入指定可用于训练的 GPU 数量,就像生成器一样。
elayer1 包含三个卷积层,每层后面都跟着批量归一化和 ELU 激活函数。self.toLatent 是一个 FC 层,它获取卷积层的输出并将其映射到张量。self.fromLatent 是另一个 FC 层。它从生成器获取输出并将其映射到张量。
这些块类似于生成器中的块,但它们用于解码。这意味着图像生成。self.toImage 是生成输出图像的最后一个卷积层。它使用早期图层的特征图创建 3 通道图像。然后,forward 方法指定鉴别器的前向传递。它接受真实或生成的图像输入,并将其传递到构造函数中定义的层。结果是类似图像的输出。
7.5 代码实现
class Discriminator(nn.Module):
def __init__(self, ngpu):
super(Discriminator, self).__init__()
self.ngpu = ngpu
self.elayer1 = nn.Sequential(
nn.Conv2d(nc, ndf, 3, padding = 1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, ndf, 3, padding = 1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, 2 * ndf, 3, padding = 1),
nn.BatchNorm2d(2 * ndf),
nn.ELU(inplace=True),
)
self.elayer2 = nn.Sequential(
nn.MaxPool2d(2),
nn.Conv2d(2 * ndf, 2 * ndf, 3, padding = 1),
nn.BatchNorm2d(2 * ndf),
nn.ELU(inplace=True),
nn.Conv2d(2 * ndf, 3 * ndf, 3, padding = 1),
nn.BatchNorm2d(3 * ndf),
nn.ELU(inplace=True)
)
self.elayer3 = nn.Sequential(
nn.MaxPool2d(2),
nn.Conv2d(3 * ndf, 3 * ndf, 3, padding = 1),
nn.BatchNorm2d(3 * ndf),
nn.ELU(inplace=True),
nn.Conv2d(3 * ndf, 3 * ndf, 3, padding = 1),
nn.BatchNorm2d(3 * ndf),
nn.ELU(inplace=True)
)
self.toLatent = nn.Linear((image_size*image_size//16)*3*ndf, ngf)
self.fromLatent = nn.Linear(ngf, (image_size*image_size//16)*ndf)
self.dlayer1 = nn.Sequential(
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True)
)
self.dlayer2 = nn.Sequential(
nn.Upsample(scale_factor=2),
nn.Conv2d(2*ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True)
)
self.dlayer3 = nn.Sequential(
nn.Upsample(scale_factor=2),
nn.Conv2d(2*ndf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True),
nn.Conv2d(nsdf, ndf, 3, padding=1),
nn.BatchNorm2d(ndf),
nn.ELU(inplace=True)
)
self.toImage = nn.Conv2d(ndf, 3, 3, padding = 1)
def forward(self, x):
x = self.elayer1(x)
x = self.elayer2(x)
x = self.elayer3(x)
x = x.view(x.size(0), -1)
x = self.toLatent(x)
x = self.fromLatent(x)
x = x.view(x.size(0), ndf, image_size//4, image_size//4)
h0 = x
x = self.dlayer1(x)
x = torch.cat([x, h0], dim=1)
x = self.dlayer2(x)
h0 = torch.nn.functional.interpolate(h0, scale_factor=2, mode='nearest')
x = torch.cat([x, h0], dim=1)
x = self.dlayer3(x)
x = self.toImage(x)
return x
现在,创建一个名为 Generator 的类,并传递 ngpu 来构建生成器神经网络。如果有,您可以使用更多的 GPU 来提高训练效率。Discriminator 神经网络也是通过实例化一个名为 Discriminator 的类来创建的,类似于生成器。在下一步中启动损失函数。
接下来,我们必须创建一批称为 fixed_noise 的潜在向量。这些潜在向量通常是随机噪声向量,通常从正态分布中抽取。它们用于在训练期间从生成器生成假图像。然后,我们必须为生成器和鉴别器设置优化器。我们将对两者使用 Adam 优化器。Adam 是一种流行的优化算法。
# Create the generator
netG = Generator(ngpu).to(device)
# To handle multi-gpu
if (device.type == 'cuda') and (ngpu > 1):
netG = nn.DataParallel(netG, list(range(ngpu)))
# Create the Discriminator
netD = Discriminator(ngpu).to(device)
# To handle multi-gpu
if (device.type == 'cuda') and (ngpu > 1):
netD = nn.DataParallel(netD, list(range(ngpu)))
# Initiate Loss function
criterion = nn.L1Loss()
fixed_noise = noise()
# Setup Adam optimizers for both Generator and Discriminator
optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
八、训练
是时候训练我们的模型了。在此之前,我们必须创建一个名为 Timer 的类,该类将帮助您计算每个步骤的训练时间。创建一些必要的空列表,并定义一些必要的参数来存储训练期间的数据。在已设置的训练循环中,从数据加载器中循环访问预定数量的 epoch 和批次数据。鉴别器使用生成器生成的虚假数据进行训练。在整个训练过程中,生成器和鉴别器都会得到更新。同时,在训练过程中的每一步,都会打印出所有统计数据。它收集损失和生成的图像,以便在训练期间和之后进行分析和可视化。
import time
class Timer():
def __init__(self):
self.startTime = time.time()
self.lastTime = time.time()
def timeElapsed(self):
auxTime = self.lastTime
self.lastTime = time.time()
return self.lastTime - auxTime
def timeSinceStart(self):
self.lastTime = time.time()
return self.lastTime - self.startTime
# Training Loop
k = 0
gamma = 0.4
lambda_k = 0.005
img_list = []
G_losses = []
D_losses = []
iters = 0
print("Training Loop Started...")
timer = Timer()
for epoch in range(num_epochs):
# For each batch in the dataloader
for i, data in enumerate(dataloader, 0):
netD.zero_grad()
# Format batch
real_cpu = data[0].to(device)
b_size = real_cpu.size(0)
# Forward pass real batch through D
output = netD(real_cpu).view(-1)
# Calculate loss on all-real batch
errD_real = criterion(output, real_cpu.view(-1))
# Calculate gradients for D in backward pass
D_x = output.mean().item()
fake = netG(noise())
# Classify all fake batch with D
output = netD(fake.detach()).view(-1)
# Calculate D's loss on the all-fake batch
errD_fake = criterion(output, fake.view(-1))
# Calculate the gradients for this batch
D_loss = errD_real - k * errD_fake
D_loss.backward()
D_G_z1 = output.mean().item()
# Add the gradients from the all-real and all-fake batches
optimizerD.step()
netG.zero_grad()
fake = netG(noise())
output = netD(fake).view(-1)
# Calculate G's loss based on this output
errG = criterion(output, fake.view(-1))
# Calculate gradients for G
errG.backward()
D_G_z2 = output.mean().item()
# Update G
optimizerG.step()
delta = (gamma*errD_real - errG).data
k = max(min(k + lambda_k*delta, 1.0), 0.0)
# Output training stats
if i % 50 == 0:
print(
'[%.4f] [%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
% (timer.timeElapsed(), epoch, num_epochs, i, len(dataloader),
D_loss.item(), errG.item(), D_x, D_G_z1, D_G_z2))
# Save Losses for plotting later
G_losses.append(errG.item())
D_losses.append(D_loss.item())
if (iters % 1000 == 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
九、可视 化
现在,让我们生成一个图来可视化 GAN 训练期间的生成器和鉴别器损失。
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()
同样,让我们生成一个图,用于比较 GAN 生成的真实图像和虚假图像。为此,我们必须从数据加载器中获取一批真实图像。这些真实图像用于与GAN生成的图像进行比较。然后我们将绘制 GAN 生成的真实图像和虚假图像。这使您可以直观地将生成的图像的质量与真实数据进行比较。
# Grab a batch of real images from the dataloader
real_batch = next(iter(dataloader))
# Plot the real images
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)))
# Plot the fake images from the last epoch
plt.subplot(1,2,2)
plt.axis("off")
plt.title("Fake Images")
plt.imshow(np.transpose(img_list[-1],(1,2,0)))
plt.savefig('fake_image.png')
plt.show()
十、结论
在本文中,我们在 CelebFaces 属性 (CelebA) 数据集上使用了生成对抗网络 (GAN),并生成了一些虚假的名人面孔。生成对抗网络 (GAN) 是一项了不起的技术突破。他们可以创建看起来非常真实的虚假数据。这有很多用途,并且非常有用,尤其是当项目需要大量数据时。它确实发展迅速,近年来被更多地使用。这项技术是人工智能领域的一个有趣的发展,因为它在许多不同的应用中都有广阔的前景。
关键要点
生成对抗网络 (GAN) 是一种革命性的 AI 技术,能够生成与真实数据非常相似的数据。
它由两个神经网络组成。一个是生成器,另一个是鉴别器。这两个网络正在进行对抗性训练。
GAN 在多个领域都有应用,包括图像生成、超分辨率、风格传输和数据增强。
GAN有很多种,它们各有优缺点,也有自己的应用。
GAN 可能会引发与深度伪造、虚假内容生成和侵犯隐私相关的道德问题。
本文中显示的媒体不归 Analytics Vidhya 所有,由作者自行决定使用。