Pytorch 实现 GAN 对抗网络

发布于:2024-05-06 ⋅ 阅读:(26) ⋅ 点赞:(0)

GAN 对抗网络

GAN(Generative Adversarial Network)对抗网络指的是神经网络中包括两个子网络,一个用于生成信息,一个用于验证信息。下面的例子是生成图片的对抗网络,一个网络用于生成图片,另一个网络用于验证。G 网络用于生成图片,不断的学习并生成更接近于训练数据的图像,D 网络用于鉴别图片,通过学习更准确的识别出图片的真假,最终通过学习让 G 网络能够生成高质量的目标图片。下面通过代码实现两种不同的 GAN,图片为自动生成手写图片,采用 MNIST数据集。

  • DCGAN (Deep Convolution)深度卷积生成对抗网络
  • SAGAN(Self Attention)自注意力生成对抗网络

安装依赖

本文将使用 sklearn,首先安装 sklearn。

pip install -U scikit-learn

DCGAN 深度卷积对抗网络

数据准备

采用 MNIST 数据,并只选用 7、8 两个数字,MINST 中 7、8 数字各有 200 张。

def make_datapath_list():
    """创建用于学习和验证的图像数据及标注数据的文件路径列表。 """

    train_img_list = list() #保存图像文件的路径

    for img_idx in range(200):
        img_path = "./data/img_78/img_7_" + str(img_idx)+'.jpg'
        train_img_list.append(img_path)

        img_path = "./data/img_78/img_8_" + str(img_idx)+'.jpg'
        train_img_list.append(img_path)

    return train_img_list

class ImageTransform():
    """图像的预处理类"""

    def __init__(self, mean, std):
        self.data_transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize(mean, std)
        ])

    def __call__(self, img):
        return self.data_transform(img)

class GAN_Img_Dataset(data.Dataset):
    """图像的 Dataset 类,继承自 PyTorchd 的 Dataset 类"""

    def __init__(self, file_list, transform):
        self.file_list = file_list
        self.transform = transform

    def __len__(self):
        '''返回图像的张数'''
        return len(self.file_list)

    def __getitem__(self, index):
        '''获取经过预处理后的图像的张量格式的数据'''

        img_path = self.file_list[index]
        img = Image.open(img_path)  #[ 高度 ][ 宽度 ] 黑白

        #图像的预处理
        img_transformed = self.transform(img)

        return img_transformed

#创建DataLoader并确认执行结果

#创建文件列表
train_img_list=make_datapath_list()

#创建Dataset
mean = (0.5,)
std = (0.5,)
train_dataset = GAN_Img_Dataset(
    file_list=train_img_list, transform=ImageTransform(mean, std))

#创建DataLoader
batch_size = 64

train_dataloader = torch.utils.data.DataLoader(
    train_dataset, batch_size=batch_size, shuffle=True)

#确认执行结果
batch_iterator = iter(train_dataloader)  #转换为迭代器
imges = next(batch_iterator)  #取出位于第一位的元素
print(imges.size())  # torch.Size([64, 1, 64, 64])

生成网络实现

需要根据输入的随机数生成图像,对数据的维度进行放大,并增加维度中的元素数量,通过 nn.ConvTranspose2d 转置卷积进行实现。转置卷积是卷积的反向操作,卷积输出特征通常比输入数据小,反向卷积输出比输入大,可以看做数据放大操作。

# 导入软件包
import random
import math
import time
import pandas as pd
import numpy as np
from PIL import Image

import torch
import torch.utils.data as data
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

from torchvision import transforms

# Setup seeds
torch.manual_seed(1234)
np.random.seed(1234)
random.seed(1234)
class Generator(nn.Module):

    def __init__(self, z_dim=20, image_size=64):
        super(Generator, self).__init__()

        self.layer1 = nn.Sequential(
            nn.ConvTranspose2d(z_dim, image_size * 8,
                               kernel_size=4, stride=1),
            nn.BatchNorm2d(image_size * 8),
            nn.ReLU(inplace=True))

        self.layer2 = nn.Sequential(
            nn.ConvTranspose2d(image_size * 8, image_size * 4,
                               kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(image_size * 4),
            nn.ReLU(inplace=True))

        self.layer3 = nn.Sequential(
            nn.ConvTranspose2d(image_size * 4, image_size * 2,
                               kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(image_size * 2),
            nn.ReLU(inplace=True))

        self.layer4 = nn.Sequential(
            nn.ConvTranspose2d(image_size * 2, image_size,
                               kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(image_size),
            nn.ReLU(inplace=True))

        self.last = nn.Sequential(
            nn.ConvTranspose2d(image_size, 1, kernel_size=4,
                               stride=2, padding=1),
            nn.Tanh())
        # 注意:因为是黑白图像,所以只有一个输出通道

    def forward(self, z):
        out = self.layer1(z)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = self.last(out)

        return out

根据输入,生成图片

#动作确认
import matplotlib.pyplot as plt
%matplotlib inline

G = Generator(z_dim=20, image_size=64)

# 输入的随机数
input_z = torch.randn(1, 20)

# 将张量尺寸变形为(1,20,1,1)
input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)

#输出假图像
fake_images = G(input_z)

img_transformed = fake_images[0][0].detach().numpy()
plt.imshow(img_transformed, 'gray')
plt.show()

没有经过学习生成的图片,目标是通过学习生成手写数字的效果。
在这里插入图片描述

鉴别网络实现

鉴别网络是一个进行图片分类的神经网络模型,由 5 层网络组成。

class Discriminator(nn.Module):

    def __init__(self, z_dim=20, image_size=64):
        super(Discriminator, self).__init__()

        self.layer1 = nn.Sequential(
            nn.Conv2d(1, image_size, kernel_size=4,
                      stride=2, padding=1),
            nn.LeakyReLU(0.1, inplace=True))
       #注意:因为是黑白图像,所以输入通道只有一个

        self.layer2 = nn.Sequential(
            nn.Conv2d(image_size, image_size*2, kernel_size=4,
                      stride=2, padding=1),
            nn.LeakyReLU(0.1, inplace=True))

        self.layer3 = nn.Sequential(
            nn.Conv2d(image_size*2, image_size*4, kernel_size=4,
                      stride=2, padding=1),
            nn.LeakyReLU(0.1, inplace=True))

        self.layer4 = nn.Sequential(
            nn.Conv2d(image_size*4, image_size*8, kernel_size=4,
                      stride=2, padding=1),
            nn.LeakyReLU(0.1, inplace=True))

        self.last = nn.Conv2d(image_size*8, 1, kernel_size=4, stride=1)

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = self.last(out)

        return out

DCGAN 训练

生成网络和识别网络使用 BCEWithLogitsLoss 作为损失函数。

#网络的初始化处理
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        #Conv2d和ConvTranspose2d的初始化
        nn.init.normal_(m.weight.data, 0.0, 0.02)
        nn.init.constant_(m.bias.data, 0)
    elif classname.find('BatchNorm') != -1:
        #BatchNorm2d的初始化
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)


#开始初始化
G.apply(weights_init)
D.apply(weights_init)

print("网络已经成功地完成了初始化")

#网络已经成功地完成了初始化


def train_model(G, D, dataloader, num_epochs):

    #确认是否能够使用GPU加速
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("使用设备:", device)

    #设置最优化算法
    g_lr, d_lr = 0.0001, 0.0004
    beta1, beta2 = 0.0, 0.9
    g_optimizer = torch.optim.Adam(G.parameters(), g_lr, [beta1, beta2])
    d_optimizer = torch.optim.Adam(D.parameters(), d_lr, [beta1, beta2])

    #定义误差函数
    criterion = nn.BCEWithLogitsLoss(reduction='mean')

    #使用硬编码的参数
    z_dim = 20
    mini_batch_size = 64

    #将网络载入GPU中
    G.to(device)
    D.to(device)

    G.train()  #将模式设置为训练模式
    D.train()  #将模式设置为训练模式

    #如果网络相对固定,则开启加速
    torch.backends.cudnn.benchmark = True

    #图像张数
    num_train_imgs = len(dataloader.dataset)
    batch_size = dataloader.batch_size

    #设置迭代计数器
    iteration = 1
    logs = []

    #epoch循环
    for epoch in range(num_epochs):

        #保存开始时间
        t_epoch_start = time.time()
        epoch_g_loss = 0.0  #epoch的损失总和
        epoch_d_loss = 0.0  #epoch的损失总和

        print('-------------')
        print('Epoch {}/{}'.format(epoch, num_epochs))
        print('-------------')
        print('(train)')

        #以minibatch为单位从数据加载器中读取数据的循环
        for imges in dataloader:

            # --------------------
            #1.判别器D的学习
            # --------------------
            #如果小批次的尺寸设置为1,会导致批次归一化处理产生错误,因此需要避免
            if imges.size()[0] == 1:
                continue

            #如果能使用GPU,则将数据送入GPU中
            imges = imges.to(device)

            #创建正确答案标签和伪造数据标签
            #在epoch最后的迭代中,小批次的数量会减少
            mini_batch_size = imges.size()[0]
            label_real = torch.full((mini_batch_size,), 1).to(device)
            label_fake = torch.full((mini_batch_size,), 0).to(device)

           #对真正的图像进行判定
            d_out_real = D(imges)

            #生成伪造图像并进行判定
            input_z = torch.randn(mini_batch_size, z_dim).to(device)
            input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
            fake_images = G(input_z)
            d_out_fake = D(fake_images)

            #计算误差
            d_loss_real = criterion(d_out_real.view(-1), label_real.float())
            d_loss_fake = criterion(d_out_fake.view(-1), label_fake.float())
            d_loss = d_loss_real + d_loss_fake

            #反向传播处理
            g_optimizer.zero_grad()
            d_optimizer.zero_grad()

            d_loss.backward()
            d_optimizer.step()

            # --------------------
            #2.生成器G的学习
            # --------------------
            #生成伪造图像并进行判定
            input_z = torch.randn(mini_batch_size, z_dim).to(device)
            input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
            fake_images = G(input_z)
            d_out_fake = D(fake_images)

            #计算误差
            g_loss = criterion(d_out_fake.view(-1), label_real.float())

            #反向传播处理
            g_optimizer.zero_grad()
            d_optimizer.zero_grad()
            g_loss.backward()
            g_optimizer.step()

            # --------------------
            #3.记录结果
            # --------------------
            epoch_d_loss += d_loss.item()
            epoch_g_loss += g_loss.item()
            iteration += 1

       #epoch的每个phase的loss和准确率
        t_epoch_finish = time.time()
        print('-------------')
        print('epoch {} || Epoch_D_Loss:{:.4f} ||Epoch_G_Loss:{:.4f}'.format(
            epoch, epoch_d_loss/batch_size, epoch_g_loss/batch_size))
        print('timer:  {:.4f} sec.'.format(t_epoch_finish - t_epoch_start))
        t_epoch_start = time.time()

    return G, D

#执行训练和验证操作
num_epochs = 200
G_update, D_update = train_model(
    G, D, dataloader=train_dataloader, num_epochs=num_epochs)

#将生成的图像和训练数据可视化
#反复执行本单元中的代码,直到生成感觉良好的图像为止

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

#生成用于输入的随机数
batch_size = 8
z_dim = 20
fixed_z = torch.randn(batch_size, z_dim)
fixed_z = fixed_z.view(fixed_z.size(0), fixed_z.size(1), 1, 1)

#生成图像
fake_images = G_update(fixed_z.to(device))

#训练数据
batch_iterator = iter(train_dataloader) #转换成迭代器
imges = next(batch_iterator)  #取出位于第一位的元素


#输出结果
fig = plt.figure(figsize=(15, 6))
for i in range(0, 5):
    #将训练数据放入上层
    plt.subplot(2, 5, i+1)
    plt.imshow(imges[i][0].cpu().detach().numpy(), 'gray')

    #将生成数据放入下层
    plt.subplot(2, 5, 5+i+1)
    plt.imshow(fake_images[i][0].cpu().detach().numpy(), 'gray')

SAGAN自注意力生成对抗网络

自注意力对抗网络是为了解决卷积操作只关注局部的问题,在深度对抗网络中,卷积操作只是对周围的信息进行了放大而忽略了全局信息。自注意力机制中用于计算特征向量是关注与其相似的那些像素点,而不只是周围的像素点。首先,通过 1x1 卷积进行逐点卷积,当数据进行压缩,之后再通过频谱归一化,对网络层的权重进行归一化操作吗,更有助于模型的收敛。

准备数据

准备 Dataset、Dataloader和图像预处理

def make_datapath_list():
    """制作用于学习、验证的图像数据和标注数据的文件路径表。 """

    train_img_list = list()  # 保存图像文件路径

    for img_idx in range(200):
        img_path = "./data/img_78/img_7_" + str(img_idx)+'.jpg'
        train_img_list.append(img_path)

        img_path = "./data/img_78/img_8_" + str(img_idx)+'.jpg'
        train_img_list.append(img_path)

    return train_img_list
class ImageTransform():
    """图像的预处理类"""

    def __init__(self, mean, std):
        self.data_transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize(mean, std)
        ])

    def __call__(self, img):
        return self.data_transform(img)

class GAN_Img_Dataset(data.Dataset):
    """图像的Dataset类。继承PyTorch的Dataset类"""

    def __init__(self, file_list, transform):
        self.file_list = file_list
        self.transform = transform

    def __len__(self):
        '''返回图像的张数'''
        return len(self.file_list)

    def __getitem__(self, index):
        '''获取预处理图像的Tensor格式的数据'''

        img_path = self.file_list[index]
        img = Image.open(img_path)  #[高][宽]黑白

        # 图像的预处理
        img_transformed = self.transform(img)

        return img_transformed

# 创建DataLoader并确认操作

# 制作文件列表
train_img_list=make_datapath_list()

#制作Dataset
mean = (0.5,)
std = (0.5,)
train_dataset = GAN_Img_Dataset(
    file_list=train_img_list, transform=ImageTransform(mean, std))

# 制作DataLoader
batch_size = 64

train_dataloader = torch.utils.data.DataLoader(
    train_dataset, batch_size=batch_size, shuffle=True)

# 动作的确认
batch_iterator = iter(train_dataloader)  # 转换成迭代器
imges = next(batch_iterator)  # 找出第一个要素
print(imges.size())  # torch.Size([64, 1, 64, 64])


创建网络

以下代码用于实现 SelfAttention、生成网络和验证网络。

#导入软件包
import random
import math
import time
import pandas as pd
import numpy as np
from PIL import Image

import torch
import torch.utils.data as data
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

from torchvision import transforms

# Setup seeds
torch.manual_seed(1234)
np.random.seed(1234)
random.seed(1234)


class Self_Attention(nn.Module):
    """ Self-AttentionのLayer"""

    def __init__(self, in_dim):
        super(Self_Attention, self).__init__()

        #准备1×1的卷积层的逐点卷积
        self.query_conv = nn.Conv2d(
            in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
        self.key_conv = nn.Conv2d(
            in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
        self.value_conv = nn.Conv2d(
            in_channels=in_dim, out_channels=in_dim, kernel_size=1)

        #创建Attention Map时归一化用的SoftMax函数
        self.softmax = nn.Softmax(dim=-2)

        #原有输入数据x与作为Self−Attention Map的o进行加法运算时使用的系数
        # output = x +gamma*o
        #刚开始gamma=0,之后让其进行学习
        self.gamma = nn.Parameter(torch.zeros(1))

    def forward(self, x):

        #输入变量
        X = x

       #先计算卷积,再对尺寸进行变形,将形状由B、C'、W、H变为B、C'、N
        proj_query = self.query_conv(X).view(
            X.shape[0], -1, X.shape[2]*X.shape[3]) #尺寸 :B、C'、N
        proj_query = proj_query.permute(0, 2, 1)  #转置操作
        proj_key = self.key_conv(X).view(
            X.shape[0], -1, X.shape[2]*X.shape[3])  #尺寸 :B、C'、N

        #乘法运算
        S = torch.bmm(proj_query, proj_key)  #bmm是以批次为单位进行的矩阵乘法运算

        #归一化
        attention_map_T = self.softmax(S)  #将行i方向上的和转换为1的SoftMax函数
        attention_map = attention_map_T.permute(0, 2, 1)  #进行转置

        #计算Self-Attention Map
        proj_value = self.value_conv(X).view(
            X.shape[0], -1, X.shape[2]*X.shape[3])  #尺寸 :B、C、N
        o = torch.bmm(proj_value, attention_map.permute(
            0, 2, 1))  #对Attention Map进行转置并计算乘积

        #将作为Self−Attention Map的o的张量尺寸与x对齐,并输出结果
        o = o.view(X.shape[0], X.shape[1], X.shape[2], X.shape[3])
        out = x+self.gamma*o

        return out, attention_map

class Generator(nn.Module):

    def __init__(self, z_dim=20, image_size=64):
        super(Generator, self).__init__()

        self.layer1 = nn.Sequential(
            #添加频谱归一化处理
            nn.utils.spectral_norm(nn.ConvTranspose2d(z_dim, image_size * 8,
                                                      kernel_size=4, stride=1)),
            nn.BatchNorm2d(image_size * 8),
            nn.ReLU(inplace=True))

        self.layer2 = nn.Sequential(
           #添加频谱归一化处理
            nn.utils.spectral_norm(nn.ConvTranspose2d(image_size * 8, image_size * 4,
                                                      kernel_size=4, stride=2, padding=1)),
            nn.BatchNorm2d(image_size * 4),
            nn.ReLU(inplace=True))

        self.layer3 = nn.Sequential(
            #添加频谱归一化处理
            nn.utils.spectral_norm(nn.ConvTranspose2d(image_size * 4, image_size * 2,
                                                      kernel_size=4, stride=2, padding=1)),
            nn.BatchNorm2d(image_size * 2),
            nn.ReLU(inplace=True))

        #添加Self−Attentin网络层
        self.self_attntion1 = Self_Attention(in_dim=image_size * 2)

        self.layer4 = nn.Sequential(
            #添加频谱归一化处理
            nn.utils.spectral_norm(nn.ConvTranspose2d(image_size * 2, image_size,
                                                      kernel_size=4, stride=2, padding=1)),
            nn.BatchNorm2d(image_size),
            nn.ReLU(inplace=True))

        #添加Self−Attentin网络层
        self.self_attntion2 = Self_Attention(in_dim=image_size)

        self.last = nn.Sequential(
            nn.ConvTranspose2d(image_size, 1, kernel_size=4,
                               stride=2, padding=1),
            nn.Tanh())
        #注意 :由于是黑白图像,因此输出的通道数为1

        self.self_attntion2 = Self_Attention(in_dim=64)

    def forward(self, z):
        out = self.layer1(z)
        out = self.layer2(out)
        out = self.layer3(out)
        out, attention_map1 = self.self_attntion1(out)
        out = self.layer4(out)
        out, attention_map2 = self.self_attntion2(out)
        out = self.last(out)

        return out, attention_map1, attention_map2

class Discriminator(nn.Module):

    def __init__(self, z_dim=20, image_size=64):
        super(Discriminator, self).__init__()

        self.layer1 = nn.Sequential(
            #追加Spectral Normalization
            nn.utils.spectral_norm(nn.Conv2d(1, image_size, kernel_size=4,
                                             stride=2, padding=1)),
            nn.LeakyReLU(0.1, inplace=True))
       #注意 :由于是黑白图像,因此输入的通道数为1

        self.layer2 = nn.Sequential(
           #追加Spectral Normalization
            nn.utils.spectral_norm(nn.Conv2d(image_size, image_size*2, kernel_size=4,
                                             stride=2, padding=1)),
            nn.LeakyReLU(0.1, inplace=True))

        self.layer3 = nn.Sequential(
            #追加频谱归一化
            nn.utils.spectral_norm(nn.Conv2d(image_size*2, image_size*4, kernel_size=4,
                                             stride=2, padding=1)),
            nn.LeakyReLU(0.1, inplace=True))

        #追加Self-Attentin层
        self.self_attntion1 = Self_Attention(in_dim=image_size*4)

        self.layer4 = nn.Sequential(
            #追加频谱归一化
            nn.utils.spectral_norm(nn.Conv2d(image_size*4, image_size*8, kernel_size=4,
                                             stride=2, padding=1)),
            nn.LeakyReLU(0.1, inplace=True))

        #追加Self-Attentin层
        self.self_attntion2 = Self_Attention(in_dim=image_size*8)

        self.last = nn.Conv2d(image_size*8, 1, kernel_size=4, stride=1)

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = self.layer3(out)
        out, attention_map1 = self.self_attntion1(out)
        out = self.layer4(out)
        out, attention_map2 = self.self_attntion2(out)
        out = self.last(out)

        return out, attention_map1, attention_map2

模型训练和验证

# 创建一个函数来学习模型


def train_model(G, D, dataloader, num_epochs):

    # 确认是否可以使用GPU
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("使用设备:", device)

    # 优化方法的设定
    g_lr, d_lr = 0.0001, 0.0004
    beta1, beta2 = 0.0, 0.9
    g_optimizer = torch.optim.Adam(G.parameters(), g_lr, [beta1, beta2])
    d_optimizer = torch.optim.Adam(D.parameters(), d_lr, [beta1, beta2])

    #将误差函数从定义变更为hinge version of the adversarial loss
    # criterion = nn.BCEWithLogitsLoss(reduction='mean')

    #参数硬编码
    z_dim = 20
    mini_batch_size = 64

    # 将网络变成GPU
    G.to(device)
    D.to(device)

    G.train()  # 将模型转换为训练模式
    D.train()  #将模型转换为训练模式

    # 如果网络固定到一定程度,就可以提高速度
    torch.backends.cudnn.benchmark = True

    #图像的张数
    num_train_imgs = len(dataloader.dataset)
    batch_size = dataloader.batch_size

    # 设置了迭代计数器
    iteration = 1
    logs = []

    # epoch循环
    for epoch in range(num_epochs):

        # 保存开始时间
        t_epoch_start = time.time()
        epoch_g_loss = 0.0  #epoch损失总和
        epoch_d_loss = 0.0  # epoch损失总和

        print('-------------')
        print('Epoch {}/{}'.format(epoch, num_epochs))
        print('-------------')
        print('(train)')

        # 从数据加载器中每次提取minibatch的循环
        for imges in dataloader:

            # --------------------
            # 1. Discriminator的学习
            # --------------------
            # 如果迷你batch的大小为1,则在batch normatization中会出错,所以避免
            if imges.size()[0] == 1:
                continue

            # 如果你能用GPU,就把数据传送给GPU。
            imges = imges.to(device)

            # 制作正确标签和假标签
            # epoch的最后迭代会导致小批量的数量变少
            mini_batch_size = imges.size()[0]
            #label_real = torch.full((mini_batch_size,), 1).to(device)
            #label_fake = torch.full((mini_batch_size,), 0).to(device)

            # 判断真正的图像
            d_out_real, _, _ = D(imges)

            # 生成假图像并进行判定
            input_z = torch.randn(mini_batch_size, z_dim).to(device)
            input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
            fake_images, _, _ = G(input_z)
            d_out_fake, _, _ = D(fake_images)

            # 计算误差→hinge version of the adversarial loss变更
            # d_loss_real = criterion(d_out_real.view(-1), label_real)
            # d_loss_fake = criterion(d_out_fake.view(-1), label_fake)

            d_loss_real = torch.nn.ReLU()(1.0 - d_out_real).mean()
            #误差d_out_real大于1,误差为0。d out译文:real & gt;在1中,
            #1.0 - d_out_real为负时用ReLU设为0

            d_loss_fake = torch.nn.ReLU()(1.0 + d_out_fake).mean()
            # 如果误差d_out_fake小于-1,则误差为0。
            # 1.0 + d_out_real为负时用ReLU设为0

            d_loss = d_loss_real + d_loss_fake

            # 反向传播
            g_optimizer.zero_grad()
            d_optimizer.zero_grad()

            d_loss.backward()
            d_optimizer.step()

            # --------------------
            # 2. Generator的学习
            # --------------------
            # 生成假图像并进行判定
            input_z = torch.randn(mini_batch_size, z_dim).to(device)
            input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
            fake_images, _, _ = G(input_z)
            d_out_fake, _, _ = D(fake_images)

            # 计算误差→hinge version of the adversarial loss变更
            #g_loss = criterion(d_out_fake.view(-1), label_real)
            g_loss = - d_out_fake.mean()

            # 反向传播
            g_optimizer.zero_grad()
            d_optimizer.zero_grad()
            g_loss.backward()
            g_optimizer.step()

            # --------------------
            # 3. 记录
            # --------------------
            epoch_d_loss += d_loss.item()
            epoch_g_loss += g_loss.item()
            iteration += 1

        # epoch的每个phase的loss和正确答案率
        t_epoch_finish = time.time()
        print('-------------')
        print('epoch {} || Epoch_D_Loss:{:.4f} ||Epoch_G_Loss:{:.4f}'.format(
            epoch, epoch_d_loss/batch_size, epoch_g_loss/batch_size))
        print('timer:  {:.4f} sec.'.format(t_epoch_finish - t_epoch_start))
        t_epoch_start = time.time()

    # print("总迭代次数:", iteration)

    return G, D
# 网络的初始化
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        # Conv2d和ConvTranspose2d的初始化
        nn.init.normal_(m.weight.data, 0.0, 0.02)
        nn.init.constant_(m.bias.data, 0)
    elif classname.find('BatchNorm') != -1:
        #  BatchNorm2dの初期化
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)


#初始化的实施
G.apply(weights_init)
D.apply(weights_init)

print("网络的初始化完成")

# 进行训练和验证
num_epochs = 300
G_update, D_update = train_model(
    G, D, dataloader=train_dataloader, num_epochs=num_epochs)

# 将生成图像和训练数据可视化
# 本直到生成感觉良好的图像为止,这个单元格会重新运行几次。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 输入的随机数。
batch_size = 8
z_dim = 20
fixed_z = torch.randn(batch_size, z_dim)
fixed_z = fixed_z.view(fixed_z.size(0), fixed_z.size(1), 1, 1)

# 画像生成
fake_images, am1, am2 = G_update(fixed_z.to(device))

#训练数据
batch_iterator = iter(train_dataloader)  #转换成迭代器
imges = next(batch_iterator)  # 找出第一个要素


fig = plt.figure(figsize=(15, 6))
for i in range(0, 5):
    # 把训练数据放在上层
    plt.subplot(2, 5, i+1)
    plt.imshow(imges[i][0].cpu().detach().numpy(), 'gray')

    # 在下层显示生成数据
    plt.subplot(2, 5, 5+i+1)
    plt.imshow(fake_images[i][0].cpu().detach().numpy(), 'gray')

GAN 对抗网络可以通过两个网络互相对抗,最终让生成网络更加有效的生成目标图片,由于两个网络都是在不断学习中互相促进,可以完善学习的效果。如果通过人工识别的方式,生成网络很难进行学习,在网络开始阶段生成效果很差,人眼直接可以识别图片是假的。而鉴别网络模型刚开始可以给出一些错误信息,反而促进模型的最终效果。