使用 PyTorch 建立您自己的生成对抗性网络

简介: 使用 PyTorch 建立您自己的生成对抗性网络

Generative Adversarial Networks is the most interesting idea in machine learning in last ten years.

                                       — Yann Lecun (Facebook AI Director)



你听说过GANs吗?还是你才刚刚开始学?GANs是2014年由蒙特利尔大学的学生 Ian Goodfellow 博士首次提出的。GANs最常见的例子是生成图像。有一个网站包含了不存在的人的面孔,便是一个常见的GANs应用示例。也是我们将要在本文中进行分享的。


生成对抗网络由两个神经网络组成,生成器和判别器相互竞争。我将在后面详细解释每个步骤。希望在本文结束时,你将能够从零开始训练和建立自己的生财之道对抗性网络。所以闲话少说,让我们开始吧。


写在前言


步骤0: 导入数据集

步骤1: 加载及预处理图像

步骤2: 定义判别器算法

步骤3: 定义生成器算法

步骤4: 编写训练算法

步骤5: 训练模型

步骤6: 测试模型


步骤0: 导入数据集


第一步是下载并将数据加载到内存中。我们将使用 CelebFaces Attributes Dataset (CelebA)来训练你的对抗性网络。主要分以下三个步骤:


1. 下载数据集:

https://s3.amazonaws.com/video.udacity-data.com/topher/2018/November/5be7eb6f_processed-celeba-small/processed-celeba-small.zip


2. 解压缩数据集;


3. Clone 如下 GitHub地址:

https://github.com/Ahmad-shaikh575/Face-Generation-using-GANS


这样做之后,你可以在 colab 环境中打开它,或者你可以使用你自己的 pc 来训练模型。


导入必要的库


导入在笔记本的第一个块中要使用的所有库一直被认为是一个好的实践。


#import the neccessary libraries
import pickle as pkl
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch
from torchvision import datasets
from torchvision import transforms
import torch
import torch.optim as optim

步骤1: 加载及预处理图像


在这一步中,我们将预处理在前一节中下载的图像数据。


将采取以下步骤:

  1. 调整图片大小
  2. 转换成张量
  3. 加载到 PyTorch 数据集中
  4. 加载到 PyTorch DataLoader 中
    # Define hyperparameters
    batch_size = 32
    img_size = 32
    data_dir='processed_celeba_small/'
    # Apply the transformations
    transform = transforms.Compose([transforms.Resize(image_size)
                                        ,transforms.ToTensor()])
    # Load the dataset
    imagenet_data = datasets.ImageFolder(data_dir,transform= transform)
    # Load the image data into dataloader
    celeba_train_loader = torch.utils.data.DataLoader(imagenet_data,
                                              batch_size,
                                              shuffle=True)

    图像的大小应该足够小,这将有助于更快地训练模型。Tensors 基本上是 NumPy 数组,我们只是将图像转换为在 PyTorch 中所必需的 NumPy 数组。


    然后我们加载这个转换成的 PyTorch 数据集。在那之后,我们将把我们的数据分成小批量。这个数据加载器将在每次迭代时向我们的模型训练过程提供图像数据。

    随着数据的加载完成。现在,我们可以预处理图像。


    图像的预处理


    我们将在训练过程中使用 tanh 激活函数。该生成器的输出范围在 -1到1之间。我们还需要对这个范围内的图像进行缩放。代码如下所示:


    def scale(img, feature_range=(-1, 1)):
      '''
      Scales the input image into given feature_range
      '''
        min,max = feature_range
        img = img * (max-min) + min
        return img

    这个函数将对所有输入图像缩放,我们将在后面的训练中使用这个函数


    现在我们已经完成了无聊的预处理步骤。


    接下来是令人兴奋的部分,现在我们需要为我们的生成器和判别器神经网络编写代码。


    步骤2: 定义判别器算法

    判别器是一个可以区分真假图像的神经网络。真实的图像和由生成器生成的图像都将提供给它。


    我们将首先定义一个辅助函数,这个辅助函数在创建卷积网络层时非常方便。


    # helper conv function
    def conv(in_channels, out_channels, kernel_size, stride=2, padding=1, batch_norm=True):
        layers = []
        conv_layer = nn.Conv2d(in_channels, out_channels, 
                               kernel_size, stride, padding, bias=False)
        #Appending the layer
        layers.append(conv_layer)
        #Applying the batch normalization if it's given true
        if batch_norm:
            layers.append(nn.BatchNorm2d(out_channels))
         # returning the sequential container
        return nn.Sequential(*layers)

    这个辅助函数接收创建任何卷积层所需的参数,并返回一个序列化的容器。现在我们将使用这个辅助函数来创建我们自己的判别器网络。

      class Discriminator(nn.Module):
          def __init__(self, conv_dim):
              super(Discriminator, self).__init__()
              self.conv_dim = conv_dim
              #32 x 32
              self.cv1 = conv(3, self.conv_dim, 4, batch_norm=False)
              #16 x 16
              self.cv2 = conv(self.conv_dim, self.conv_dim*2, 4, batch_norm=True)
              #4 x 4
              self.cv3 = conv(self.conv_dim*2, self.conv_dim*4, 4, batch_norm=True)
              #2 x 2
              self.cv4 = conv(self.conv_dim*4, self.conv_dim*8, 4, batch_norm=True)
              #Fully connected Layer
              self.fc1 = nn.Linear(self.conv_dim*8*2*2,1)
          def forward(self, x):
              # After passing through each layer
              # Applying leaky relu activation function
              x = F.leaky_relu(self.cv1(x),0.2)
              x = F.leaky_relu(self.cv2(x),0.2)
              x = F.leaky_relu(self.cv3(x),0.2)
              x = F.leaky_relu(self.cv4(x),0.2)
              # To pass throught he fully connected layer
              # We need to flatten the image first
              x = x.view(-1,self.conv_dim*8*2*2)
              # Now passing through fully-connected layer
              x = self.fc1(x)
              return x

      步骤3: 定义生成器算法

      正如你们从图中看到的,我们给网络一个高斯矢量或者噪声矢量,它输出 s 中的值。图上的“ z”表示噪声,右边的 G (z)表示生成的样本。


      与判别器一样,我们首先创建一个辅助函数来构建生成器网络,如下所示:

        def deconv(in_channels, out_channels, kernel_size, stride=2, padding=1, batch_norm=True):
            layers = []
            convt_layer = nn.ConvTranspose2d(in_channels, out_channels, 
                                   kernel_size, stride, padding, bias=False)
            # Appending the above conv layer
            layers.append(convt_layer)
            if batch_norm:
                # Applying the batch normalization if True
                layers.append(nn.BatchNorm2d(out_channels))
            # Returning the sequential container
            return nn.Sequential(*layers)

        现在,是时候构建生成器网络了! !

          class Generator(nn.Module):
              def __init__(self, z_size, conv_dim):
                  super(Generator, self).__init__()
                  self.z_size = z_size
                  self.conv_dim = conv_dim
                  #fully-connected-layer
                  self.fc = nn.Linear(z_size, self.conv_dim*8*2*2)
                  #2x2
                  self.dcv1 = deconv(self.conv_dim*8, self.conv_dim*4, 4, batch_norm=True)
                  #4x4
                  self.dcv2 = deconv(self.conv_dim*4, self.conv_dim*2, 4, batch_norm=True)
                  #8x8
                  self.dcv3 = deconv(self.conv_dim*2, self.conv_dim, 4, batch_norm=True)
                  #16x16
                  self.dcv4 = deconv(self.conv_dim, 3, 4, batch_norm=False)
                  #32 x 32
              def forward(self, x):
                  # Passing through fully connected layer
                  x = self.fc(x)
                  # Changing the dimension
                  x = x.view(-1,self.conv_dim*8,2,2)
                  # Passing through deconv layers
                  # Applying the ReLu activation function
                  x = F.relu(self.dcv1(x))
                  x= F.relu(self.dcv2(x))
                  x= F.relu(self.dcv3(x))
                  x= F.tanh(self.dcv4(x))
                  #returning the modified image
                  return x
          

          为了使模型更快地收敛,我们将初始化线性和卷积层的权重。根据相关研究论文中的描述:所有的权重都是从0中心的正态分布初始化的,标准差为0.02


          我们将为此目的定义一个功能如下:

            def weights_init_normal(m):
                classname = m.__class__.__name__
                # For the linear layers
                if 'Linear' in classname:
                    torch.nn.init.normal_(m.weight,0.0,0.02)
                    m.bias.data.fill_(0.01)
                # For the convolutional layers
                if 'Conv' in classname or 'BatchNorm2d' in classname:
                    torch.nn.init.normal_(m.weight,0.0,0.02)

            现在我们将超参数和两个网络初始化如下:


            # Defining the model hyperparamameters
            d_conv_dim = 32
            g_conv_dim = 32
            z_size = 100   #Size of noise vector
            D = Discriminator(d_conv_dim)
            G = Generator(z_size=z_size, conv_dim=g_conv_dim)
            # Applying the weight initialization
            D.apply(weights_init_normal)
            G.apply(weights_init_normal)
            print(D)
            print()
            print(G)

            输出结果大致如下:

            判别器损失:

            根据 DCGAN Research Paper 论文中描述:

                  判别器总损失 = 真图像损失 + 假图像损失,即:d_loss = d_real_loss + d_fake_loss。

                       不过,我们希望鉴别器输出1表示真正的图像和0表示假图像,所以我们需要设置的损失来反映这一点。

            我们将定义双损失函数。一个是真正的损失,另一个是假的损失,如下:

              def real_loss(D_out,smooth=False):
                  batch_size = D_out.size(0)
                  if smooth:
                      labels = torch.ones(batch_size)*0.9
                  else:
                      labels = torch.ones(batch_size)
                  labels = labels.to(device)
                  criterion = nn.BCEWithLogitsLoss()
                  loss = criterion(D_out.squeeze(), labels)
                  return loss
              def fake_loss(D_out):
                  batch_size = D_out.size(0)
                  labels = torch.zeros(batch_size)
                  labels = labels.to(device)
                  criterion = nn.BCEWithLogitsLoss()
                  loss = criterion(D_out.squeeze(), labels)
                  return loss

              生成器损失:

              根据 DCGAN Research Paper 论文中描述:

                生成器的目标是让判别器认为它生成的图像是真实的。


              现在,是时候为我们的网络设置优化器了:

                lr = 0.0005
                beta1 = 0.3
                beta2 = 0.999 # default value
                # Optimizers
                d_optimizer = optim.Adam(D.parameters(), lr, betas=(beta1, beta2))
                g_optimizer = optim.Adam(G.parameters(), lr, betas=(beta1, beta2))

                我将为我们的训练使用 Adam 优化器。因为它目前被认为是对GANs最有效的。根据上述介绍论文中的研究成果,确定了超参数的取值范围。他们已经尝试了它,这些被证明是最好的!超参数设置如下:


                步骤4: 编写训练算法


                我们必须为我们的两个神经网络编写训练算法。首先,我们需要初始化噪声向量,并在整个训练过程中保持一致。

                  # Initializing arrays to store losses and samples
                  samples = []
                  losses = []
                  # We need to initilialize fixed data for sampling
                  # This would help us to evaluate model's performance
                  sample_size=16
                  fixed_z = np.random.uniform(-1, 1, size=(sample_size, z_size))
                  fixed_z = torch.from_numpy(fixed_z).float()

                  对于判别器:


                  我们首先将真实的图像输入判别器网络,然后计算它的实际损失。然后生成伪造图像并输入判别器网络以计算虚假损失。


                  在计算了真实和虚假损失之后,我们对其进行求和,并采取优化步骤进行训练。


                  # setting optimizer parameters to zero
                  # to remove previous training data residue
                  d_optimizer.zero_grad()
                  # move real images to gpu memory
                  real_images = real_images.to(device)
                  # Pass through discriminator network
                  dreal = D(real_images)
                  # Calculate the real loss
                  dreal_loss = real_loss(dreal)
                  # For fake images
                  # Generating the fake images
                  z = np.random.uniform(-1, 1, size=(batch_size, z_size))
                  z = torch.from_numpy(z).float()
                  # move z to the GPU memory
                  z = z.to(device)
                  # Generating fake images by passing it to generator
                  fake_images = G(z)
                  # Passing fake images from the disc network        
                  dfake = D(fake_images)
                  # Calculating the fake loss
                  dfake_loss = fake_loss(dfake)
                  #Adding both lossess
                  d_loss = dreal_loss + dfake_loss
                  # Taking the backpropogation step
                  d_loss.backward()
                  d_optimizer.step()
                  

                  对于生成器:


                  对于生成器网络的训练,我们也会这样做。刚才在通过判别器网络输入假图像之后,我们将计算它的真实损失。然后优化我们的生成器网络。


                  ## Training the generator for adversarial loss
                  #setting gradients to zero
                  g_optimizer.zero_grad()
                  # Generate fake images
                  z = np.random.uniform(-1, 1, size=(batch_size, z_size))
                  z = torch.from_numpy(z).float()
                  # moving to GPU's memory
                  z = z.to(device)
                  # Generating Fake images
                  fake_images = G(z)
                  # Calculating the generator loss on fake images
                  # Just flipping the labels for our real loss function
                  D_fake = D(fake_images)
                  g_loss = real_loss(D_fake, True)
                  # Taking the backpropogation step
                  g_loss.backward()
                  g_optimizer.step()

                  步骤5: 训练模型


                  现在我们将开始100个epoch的训练: D


                  经过训练,损失的图表看起来大概是这样的:

                  我们可以看到,判别器 Loss 是相当平滑的,甚至在100个epoch之后收敛到某个特定值。而生成器的Loss则飙升。


                  我们可以从下面步骤6中的结果看出,60个时代之后生成的图像是扭曲的。由此可以得出结论,60个epoch是一个最佳的训练节点。


                  步骤6: 测试模型


                  10个epoch之后:

                  20个epoch之后:

                  30个epoch之后:

                  40个epoch之后:

                  50个epoch之后:

                  60个epoch之后:

                  70个epoch之后:

                  80个epoch之后:

                  90个epoch之后:

                  100个epoch之后:

                  总结

                  我们可以看到,训练一个生成对抗性网络并不意味着它一定会产生好的图像。


                  从结果中我们可以看出,训练40-60个 epoch 的生成器生成的图像相对比其他更好。


                  您可以尝试更改优化器、学习速率和其他超参数,以使其生成更好的图像!

                  相关文章
                  |
                  10月前
                  |
                  机器学习/深度学习 算法 PyTorch
                  【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
                  【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
                  276 1
                  |
                  10月前
                  |
                  机器学习/深度学习 算法 PyTorch
                  【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
                  【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
                  424 0
                  |
                  机器学习/深度学习 PyTorch 算法框架/工具
                  基于Pytorch 在昇腾上实现GCN图神经网络
                  本文详细讲解了如何在昇腾平台上使用PyTorch实现图神经网络(GCN)对Cora数据集进行分类训练。内容涵盖GCN背景、模型特点、网络架构剖析及实战分析。GCN通过聚合邻居节点信息实现“卷积”操作,适用于非欧氏结构数据。文章以两层GCN模型为例,结合Cora数据集(2708篇科学出版物,1433个特征,7种类别),展示了从数据加载到模型训练的完整流程。实验在NPU上运行,设置200个epoch,最终测试准确率达0.8040,内存占用约167M。
                  基于Pytorch 在昇腾上实现GCN图神经网络
                  |
                  机器学习/深度学习 搜索推荐 PyTorch
                  基于昇腾用PyTorch实现CTR模型DIN(Deep interest Netwok)网络
                  本文详细讲解了如何在昇腾平台上使用PyTorch训练推荐系统中的经典模型DIN(Deep Interest Network)。主要内容包括:DIN网络的创新点与架构剖析、Activation Unit和Attention模块的实现、Amazon-book数据集的介绍与预处理、模型训练过程定义及性能评估。通过实战演示,利用Amazon-book数据集训练DIN模型,最终评估其点击率预测性能。文中还提供了代码示例,帮助读者更好地理解每个步骤的实现细节。
                  |
                  机器学习/深度学习 自然语言处理 PyTorch
                  基于Pytorch Gemotric在昇腾上实现GAT图神经网络
                  本实验基于昇腾平台,使用PyTorch实现图神经网络GAT(Graph Attention Networks)在Pubmed数据集上的分类任务。内容涵盖GAT网络的创新点分析、图注意力机制原理、多头注意力机制详解以及模型代码实战。实验通过两层GAT网络对Pubmed数据集进行训练,验证模型性能,并展示NPU上的内存使用情况。最终,模型在测试集上达到约36.60%的准确率。
                  |
                  算法 PyTorch 算法框架/工具
                  PyTorch 实现FCN网络用于图像语义分割
                  本文详细讲解了在昇腾平台上使用PyTorch实现FCN(Fully Convolutional Networks)网络在VOC2012数据集上的训练过程。内容涵盖FCN的创新点分析、网络架构解析、代码实现以及端到端训练流程。重点包括全卷积结构替换全连接层、多尺度特征融合、跳跃连接和反卷积操作等技术细节。通过定义VOCSegDataset类处理数据集,构建FCN8s模型并完成训练与测试。实验结果展示了模型在图像分割任务中的应用效果,同时提供了内存使用优化的参考。
                  |
                  机器学习/深度学习 算法 PyTorch
                  基于Pytorch Gemotric在昇腾上实现GraphSage图神经网络
                  本实验基于PyTorch Geometric,在昇腾平台上实现GraphSAGE图神经网络,使用CiteSeer数据集进行分类训练。内容涵盖GraphSAGE的创新点、算法原理、网络架构及实战分析。GraphSAGE通过采样和聚合节点邻居特征,支持归纳式学习,适用于未见节点的表征生成。实验包括模型搭建、训练与验证,并在NPU上运行,最终测试准确率达0.665。
                  |
                  SQL 安全 网络安全
                  网络安全与信息安全:知识分享####
                  【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
                  521 17
                  |
                  SQL 安全 网络安全
                  网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
                  随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
                  388 10
                  |
                  存储 SQL 安全
                  网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
                  随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。