第G3周:CGAN入门 | 生成手势图像(DCGAN)
第G3周:CGAN入门 | 生成手势图像(DCGAN)
🍨 本文為🔗365天深度學習訓練營 中的學習紀錄博客
🍖 原作者:K同学啊 | 接輔導、項目定制
基础任务
-
- 条件生成对抗网络(CGAN)的基本原理
-
- CGAN 是如何实现条件控制的
-
- 学习本文 CGAN 代码,并跑通代码
进阶任务
- 生成指定手势的图像(下周公布)
一、理论基础
CGAN 原理
条件生成对抗网络(CGAN)是在生成对抗网络(GAN)基础上的改进。原始 GAN 生成的图像数据随机不可控,实际操作可控性弱。针对此问题,Mehdi Mirza 等人于 2014 年提出 CGAN,给生成器 G 和判别器 D 增加额外条件,如生成无阴影图像,判别器判断生成图像是否符合条件。
CGAN 本质是将额外信息融入生成器和判别器,这些信息可以是图像类别、人脸表情等,旨在将无监督学习的 GAN 转化为有监督学习的 CGAN,以更好地掌控网络训练,其网络结构如图 1 所示。
二、项目介绍
🏡 我的环境:
- 语言环境:Python3.10.11
- 编译器:Jupyter Notebook
- 深度学习框架:Pytorch 2.0.1+cu118
- 显卡(GPU):NVIDIA GeForce RTX 4060
1.基本流程
该实验现了一个生成对抗网络(CGAN)用于生成手势图像的基本流程。CGAN模型是一种深度学习模型,它通过两个神经网络(生成器和判别器)的对抗训练,生成逼真的手势图像。
首先,导入了相关的Python库,如torch用于深度学习模型的构建,numpy用于数组操作,torchvision提供数据集和图像处理工具。然后,通过设定设备(GPU或CPU)和批量大小为128,确保模型在适当的硬件上运行,并能高效处理数据。
接下来,定义了一个用于数据增强的变换序列,包括图像大小调整、归一化等操作。然后加载数据集,并使用PyTorch的DataLoader模块将其加载为可迭代的数据批次,为训练模型做好准备。
在数据可视化部分,通过定义show_images和show_batch函数,使用matplotlib库展示数据批次中的图像。这些函数使用torchvision.utils中的make_grid将多张图像组合成网格形式,并通过imshow函数显示。这一步对于检查输入数据是否符合预期非常重要,确保数据在进入模型之前是正确的。
接下来,代码定义了生成器和判别器模型。生成器网络接收随机噪声作为输入,并生成与手势相关的图像;而判别器网络则判断输入的图像是来自生成器的伪造图像还是来自真实数据集的真实图像。两个网络通过对抗训练,使生成器能够生成越来越逼真的图像,而判别器能够准确区分真实图像和伪造图像。
训练过程通过定义损失函数和优化器进行。使用交叉熵损失函数来衡量生成器和判别器的性能,分别为它们设定优化器以更新模型参数。训练循环中,生成器首先生成图像,然后判别器对这些图像进行判断。通过反向传播优化两个模型,使生成器生成的图像越来越接近真实图像,而判别器能够更准确地判断图像的真实性。
这个CGAN模型的代码通过加载和处理数据、定义生成器和判别器、以及执行对抗训练,最终实现了手势图像的生成。该代码不仅展示了CGAN的基本工作原理,还通过可视化工具和训练监控手段确保模型训练过程的透明性和可控性。这种生成对抗网络的应用为手势图像的生成提供了一种有效的深度学习方法,展示了CGAN在计算机视觉领域的强大潜力。
2.代码介绍
CGAN模型代码
CGAN模型代码
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
from torchvision.utils import save_image
from torchvision.utils import make_grid
from torch.utils.tensorboard import SummaryWriter
from torchsummary import summary
import matplotlib.pyplot as plt
import datetime
# 设置随机种子
torch.manual_seed(1)
# 设备设置与批量大小
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
batch_size = 128
# 导入数据
train_transform = transforms.Compose([
transforms.Resize(128),
transforms.ToTensor(),
transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
])
train_dataset = datasets.ImageFolder(root='./data/rps/', transform=train_transform)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=6)
# 数据可视化
def show_images(images):
fig, ax = plt.subplots(figsize=(20, 20))
ax.set_xticks([]); ax.set_yticks([])
ax.imshow(make_grid(images.detach(), nrow=22).permute(1, 2, 0))
def show_batch(dl):
for images, _ in dl:
show_images(images)
break
# 生成器模型定义
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
self.main = nn.Sequential(
nn.ConvTranspose2d(100, 512, 4, 1, 0, bias=False),
nn.BatchNorm2d(512),
nn.ReLU(True),
nn.ConvTranspose2d(512, 256, 4, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.ReLU(True),
nn.ConvTranspose2d(256, 128, 4, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU(True),
nn.ConvTranspose2d(128, 64, 4, 2, 1, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(True),
nn.ConvTranspose2d(64, 3, 4, 2, 1, bias=False),
nn.Tanh()
)
def forward(self, input):
return self.main(input)
# 判别器模型定义
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.main = nn.Sequential(
nn.Conv2d(3, 64, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(64, 128, 4, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(128, 256, 4, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(256, 512, 4, 2, 1, bias=False),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(512, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
def forward(self, input):
return self.main(input)
# 模型初始化
netG = Generator().to(device)
netD = Discriminator().to(device)
# 损失函数和优化器
criterion = nn.BCELoss()
optimizerD = optim.Adam(netD.parameters(), lr=0.0002, betas=(0.5, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=0.0002, betas=(0.5, 0.999))
# 训练过程
num_epochs = 25
fixed_noise = torch.randn(64, 100, 1, 1, device=device)
for epoch in range(num_epochs):
for i, data in enumerate(train_loader, 0):
# 更新判别器网络
netD.zero_grad()
real_cpu = data[0].to(device)
batch_size = real_cpu.size(0)
label = torch.full((batch_size,), 1., device=device)
output = netD(real_cpu)
errD_real = criterion(output, label)
errD_real.backward()
D_x = output.mean().item()
noise = torch.randn(batch_size, 100, 1, 1, device=device)
fake = netG(noise)
label.fill_(0.)
output = netD(fake.detach())
errD_fake = criterion(output, label)
errD_fake.backward()
D_G_z1 = output.mean().item()
errD = errD_real + errD_fake
optimizerD.step()
# 更新生成器网络
netG.zero_grad()
label.fill_(1.)
output = netD(fake)
errG = criterion(output, label)
errG.backward()
D_G_z2 = output.mean().item()
optimizerG.step()
if i % 100 == 0:
print(f'[{epoch}/{num_epochs}][{i}/{len(train_loader)}] '
f'Loss_D: {errD.item():.4f} Loss_G: {errG.item():.4f} '
f'D(x): {D_x:.4f} D(G(z)): {D_G_z1:.4f} / {D_G_z2:.4f}')
save_image(fake.detach(), f'output_{epoch}.png', normalize=True)
# 模型保存与加载
## 保存模型
```python
torch.save(netG.state_dict(), 'generator.pth')
torch.save(netD.state_dict(), 'discriminator.pth')
生成新图像
netG.load_state_dict(torch.load('generator.pth'))
netD.load_state_dict(torch.load('discriminator.pth'))
netG.eval()
netD.eval()
noise = torch.randn(64, 100, 1, 1, device=device)
fake_images = netG(noise)
save_image(fake_images.detach(), 'generated_images.png', normalize=True)
noise = torch.randn(64, 100, 1, 1, device=device)
fake_images = netG(noise)
save_image(fake_images.detach(), 'generated_images.png', normalize=True)
这段代码实现了一个CGAN(条件生成对抗网络)用于生成特定类别的手势图像。通过定义生成器和判别器网络、加载和预处理数据、设置损失函数和优化器,然后在训练过程中交替更新生成器和判别器的参数,最终使生成器能够生成逼真的图像。训练完成后,模型可以被保存,并用于生成新的图像。