您的当前位置:首页正文

第G2周:人脸图像生成(DCGAN)

来源:华佗健康网

一、课题背景和开发环境

📌第G1周:生成对抗网络(GAN)入门📌
Python 3.8.12
numpy==1.21.5 -> 1.24.3
pytorch==1.8.1+cu111
本周任务: 
📌 基础任务:
深度卷积对抗网络(DCGAN)的基本原理
学习本文DCGAN代码,并跑通代码
了解DCGAN与GAN的区别
🎈进阶任务:
调用训练好的模型生成新图像

二、理论基础

1、DCGAN原理

深度卷积对抗网络(Deep Convolutional Generative Adversarial Networks,简称DCGAN)是一种深度学习模型,由生成器(Generator)和判别器(Discriminator)两个神经网络组成。DCGAN结合了卷积神经网络(Convolutional Neural Networks,简称CNN)和生成对抗网络(Generative Adversarial Networks,简称GAN)的思想,用于生成逼真的图像。 它是GAN的一种模型改进,其将卷积运算的思想引入到生成式模型当中来做无监督的训练,利用卷积网络强大的特征提取能力来提高生成网络的学习效果。DCGAN模型有以下特点:

  • 判别器模型使用卷积步长取代了空间池化,生成器模型中使用反卷积操作扩大数据维度。

  • 除了生成器模型的输出层和判别器模型的输入层,在整个对抗网络的其它层上都使用了Batch Normalization,原因是Batch

  • Normalization可以稳定学习,有助于优化初始化参数值不良而导致的训练问题。

  • 整个网络去除了全连接层,直接使用卷积层连接生成器和判别器的输入层以及输出层。

  • 在生成器的输出层使用Tanh激活函数以控制输出范围,而在其它层中均使用了ReLU激活函数;在判别器上使用Leaky ReLU激活函数。

  • 左边生成网络(Generator):四个转置卷积层(DeConv)和四个卷积层(Conv)

  • 右边判别网络(Discriminator):四个转置卷积层(DeConv)和四个卷积层(Conv)
    44512代表这一层共有512个大小为4*4的特征图 在卷积层之前和之后分别使用了批归一化和修正线性单元激活两种处理方法。Tanh 和 LeakyReLU 分别表示双切正切激活和弱修正线性激活。

2、训练原理

三、前期准备

1、导入第三方库

import torch, random, random, os
import torch.nn as nn
import torch.nn.parallel
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

manualSeed = 42  # 随机种子
print("Random Seed: ", manualSeed)
random.seed(manualSeed)
torch.manual_seed(manualSeed)
torch.use_deterministic_algorithms(True) # Needed for reproducible results

2、设置超参数

dataroot = "./data/GAN-Data/"  # 数据路径
batch_size = 128  # 训练过程中的批次大小
image_size = 64   # 图像的尺寸(宽度和高度)
nz  = 100         # z潜在向量的大小(生成器输入的尺寸)
ngf = 64          # 生成器中的特征图大小
ndf = 64          # 判别器中的特征图大小
num_epochs = 5    # 训练的总轮数
lr    = 0.0002    # 学习率
beta1 = 0.5       # Adam优化器的Beta1超参数

3、导入数据

# 创建数据集
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=5 # 使用多个线程加载数据的工作进程数
                                        )

# 选择要在哪个设备上运行代码
device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu")
print("使用的设备是:",device)

# 绘制一些训练图像
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)[:24], 
                                         padding=2, 
                                         normalize=True).cpu(),(1,2,0)))

四、定义模型

1、定义训练参数

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))

2、训练模型

img_list = []  # 用于存储生成的图像列表
G_losses = []  # 用于存储生成器的损失列表
D_losses = []  # 用于存储判别器的损失列表
iters = 0  # 迭代次数
print("Starting Training Loop...")
for epoch in range(num_epochs ):
    for i, data in enumerate(dataloader, 0):
        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)
        output = netD(real_cpu).view(-1)
        errD_real = criterion(output, label)
        errD_real.backward()
        D_x = output.mean().item()
        
        noise = torch.randn(b_size, nz, 1, 1, device=device)
        fake = netG(noise)
        label.fill_(fake_label)
        output = netD(fake.detach( )).view(-1)
        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_(real_label)
        output = netD(fake).view(-1)
        errG = criterion(output, label)
        errG.backward()
        D_G_z2 = output.mean().item()
        optimizerG.step()
        
        if i % 400 == 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())
        
        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

3、可视化

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()

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())

因篇幅问题不能全部显示,请点此查看更多更全内容