一、理论基础
1.什么是深度卷积对抗网络(Deep Convolutional Generative Adversarial Network,)
深度卷积对抗网络(Deep Convolutional Generative Adversarial Network,DCGAN)是一种生成对抗网络(GAN)的变体,它结合了深度卷积神经网络(CNN)的特性和生成对抗网络的架构。
生成对抗网络是由生成器(Generator)和判别器(Discriminator)组成的模型。生成器尝试生成与真实数据相似的样本,而判别器则尝试区分生成的样本和真实样本。两者通过博弈的方式不断优化,使生成器生成更逼真的样本。
DCGAN引入了卷积神经网络的结构,以处理图像生成任务。它的主要特点包括:
卷积层替代全连接层: DCGAN中的生成器和判别器都使用卷积层,这有助于模型学习图像中的空间层次特征,从而更好地捕捉图像的结构信息。
批归一化(Batch Normalization): 在生成器和判别器中广泛使用批归一化,有助于加速训练过程,同时提高模型的稳定性和生成效果。
去除全连接层: DCGAN中移除了全连接层,这有助于减少模型参数数量,降低过拟合的风险。
使用Leaky ReLU激活函数: 生成器和判别器中使用Leaky ReLU激活函数,以避免梯度消失的问题,同时引入一定的负斜率,促使模型更容易学习。
DCGAN的目标是通过训练生成器生成逼真的图像,同时训练判别器以有效地区分真实和生成的图像。这种架构的成功应用包括图像生成、图像编辑、图像超分辨率等领域。
2.DCGAN原理
DCGAN(Deep Convolutional Generative Adversarial Network)由GAN进行改进得到,它由两个子网络组成:生成器和判别器。
生成器网络接受一个随机噪声向量作为输入,并尝试生成看起来像真实数据的输出。具体来说,生成器网络通常由多个卷积层和反卷积层组成,这些层将随机噪声转换为具有现实特征的图像。
判别器网络则接受输入并尝试将其分类为“真实”或“生成”。判别器网络通常由多个卷积层组成,这些层将输入转换为具有现实特征的表示形式,并输出一个二进制数字,表示输入是否是真实数据。
在训练过程中,生成器和判别器交替进行预测和生成,以逐渐提高生成器输出的质量。生成器试图生成看起来像真实数据的输出,而判别器则试图将其与真实数据区分开来。通过不断地调整生成器和判别器,最终生成器可以生成非常逼真的数据。
3.DCGAN与GAN相同点与不同点
GAN(Generative Adversarial Network)和DCGAN(Deep Convolutional Generative Adversarial Network)都是生成对抗网络,它们的基本原理是相同的,即通过两个相互对抗的网络(生成器和判别器)来进行无监督的学习,以生成高质量的新数据。但是,DCGAN相对于GAN有一些改进和不同点,主要表现在以下方面:
相同点:
基本原理相同:DCGAN和GAN的基本原理都是生成对抗网络(Generative Adversarial Networks),其中两个子网络(生成器和判别器)在对抗中优化彼此。
判别器结构相同:在DCGAN和GAN中,判别器结构都是相同的,都是使用卷积神经网络(CNN)进行特征提取和分类。
不同点:
网络结构不同:DCGAN将卷积运算的思想引入到生成式模型当中,生成器和判别器模型都使用了卷积层,而GAN使用了全连接层。
训练方法不同:DCGAN使用正交标注(Orthogonal Annotation)来生成伪标签(Pseudo Labels),以用于半监督学习。而GAN通常使用真实标签(True Labels)进行监督学习。
输入输出不同:DCGAN输入的数据通常是3D体积,而GAN输入的数据通常是2D图像。此外,DCGAN输出的数据也是3D体积,而GAN输出的数据是2D图像。
判别器模型不同:在DCGAN中,判别器模型使用卷积步长取代了空间池化,以更好地提取图像特征。而在GAN中,判别器模型通常使用空间池化。
生成器模型不同:在DCGAN中,生成器模型中使用反卷积操作扩大数据维度,以更好地处理高分辨率的图像。而在GAN中,生成器模型通常使用上采样操作。
模型优化不同:在DCGAN中,整个网络去除了全连接层,直接使用卷积层连接生成器和判别器的输入层以及输出层。此外,DCGAN还使用了一些其他的技术,如Batch Normalization、Leaky ReLU激活函数和Tanh输出层,以帮助控制输出范围和提高生成质量。而在GAN中,这些技术并不常见。
4.训练原理
DCGAN的训练原理是基于半监督学习,利用已有的少量标注数据和大量无标注数据来生成新的数据。以下是DCGAN训练原理的主要步骤:
正交标注:首先,对一个标记的3D体积进行两个正交切片的标注,即正交标注。这样可以减少标注的负担,并且利用了不同方向的切片提供的互补信息。
注册模块:通过注册模块,将正交标注传播到整个体积,生成了伪标签。注册模块利用了正交标注的信息,将其传播到整个体积,从而生成了伪标签,用于半监督学习的训练过程中。
生成器训练:使用已经生成的伪标签和未标注的数据来训练生成器模型。生成器模型的目标是最小化判别器模型的输出,即让判别器无法区分生成器和真实数据之间的差异。
判别器训练:使用已经生成的伪标签和真实数据来训练判别器模型。判别器模型的目标是最小化生成器模型的输出,即让生成器无法生成看起来像真实数据的输出。
迭代训练:不断地迭代训练生成器和判别器模型,直到生成器能够生成看起来像真实数据的输出,并且判别器能够准确地区分生成数据和真实数据。
DCGAN的训练原理是基于半监督学习,通过生成伪标签和使用注册模块来减少标注负担,从而利用未标注数据来生成新的数据。在整个训练过程中,生成器和判别器相互对抗,以提高生成器的性能和生成数据的质量。
二、前期准备
import os
import random
import argparse
import numpy as np
import torch
import torch.nn as nn
import torch.nn.parallel
import torch.optim as optim
import torch.utils.data
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import datasets as dset
import torchvision.utils as vutils
from torchvision.utils import save_image
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTMLmanualSeed = 999 # 随机种子
print("Random Seed: ", manualSeed)
random.seed(manualSeed)
torch.manual_seed(manualSeed)
torch.use_deterministic_algorithms(True) # Needed for reproducible results# 超参数配置
dataroot = "D:/GAN-Data" # 数据路径
batch_size = 128 # 训练过程中的批次大小
n_epochs = 5 # 训练的总轮数
img_size = 64 # 图像的尺寸(宽度和高度)
nz = 100 # z潜在向量的大小(生成器输入的尺寸)
ngf = 64 # 生成器中的特征图大小
ndf = 64 # 判别器中的特征图大小
beta1 = 0.5 # Adam优化器的Beta1超参数
beta2 = 0.2 # Adam优化器的Beta1超参数
lr = 0.0002 # 学习率# 创建数据集
dataset = dset.ImageFolder(root=dataroot,transform=transforms.Compose([transforms.Resize(img_size), # 调整图像大小transforms.CenterCrop(img_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) # 是否打乱数据集
# 选择要在哪个设备上运行代码
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)))
三、定义模型
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))
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_fakeoptimizerD.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
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()