【CS231n assignment 2022】Assignment 3 - Part 4,GAN

前言

  • 博客主页:睡晚不猿序程
  • ⌚首发时间:2022.8.19
  • ⏰最近更新时间:2022.8.19
  • 本文由 睡晚不猿序程 原创,首发于 CSDN
  • 作者是蒻蒟本蒟,如果文章里有任何错误或者表述不清,请 tt 我,万分感谢!orz

相关文章目录

  • 【CS231n assignment 2022】Assignment 2 - Part 1,全连接网络的初始化以及正反向传播
  • 【CS231n assignment 2022】Assignment 2 - Part 2,优化器,批归一化以及层归一化
  • 【CS231n assignment 2022】Assignment 3 - Part 1,RNN
  • 【CS231n assignment 2022】Assignment 3 - Part 2,LSTM
  • 【CS231n assignment 2022】Assignment 3 - Part 3,Transformer

文章目录

  • 前言
  • 1. 内容简介
  • 2. Generative Adversarial Networks
    • 2.1 What is GAN
    • 2.2 Dataset
    • 2.3 Random Noise
    • 2.4 Flatten
    • 2.5 Discriminator
    • 2.6 Generator
    • 2.7 GAN loss
    • 2.8 Optimizing our Loss
    • Training a GAN
    • 2.9 Least Squares GAN
    • 2.10 Deeply Convolutional GANs
      • 2.10.1 Discriminator
      • 2.10.2 Generator
  • 3. 总结、预告


1. 内容简介

上一次作业我们完成了 Transformer,在这次作业,我们将会完成 GAN


2. Generative Adversarial Networks

也就是生成对抗网络

在之前,我们实现的都是判别模型,接受一个输入,然后训练模型来生成一个输出的标签

在这里,我们将会构建一个生成模型,具体而言,我们将会学习如果构建可以生成奇特图片的模型

2.1 What is GAN

这是一种训练生成模型的方法,首先我们要构建两个不同的神经网络

第一个网络是传统的分类网络,称为判别器

这个网络被训练来分类图片是假的(不在训练集的)或者是真实的(来自训练集)

第二个网络是生成器,将会输入随机噪声,并使用这个噪声来生成图片,其目标是迷惑判别器
minimize ⁡ G maximize ⁡ D E x ∼ p data  [ log ⁡ D ( x ) ] + E z ∼ p ( z ) [ log ⁡ ( 1 − D ( G ( z ) ) ) ] \underset{G}{\operatorname{minimize}} \underset{D}{\operatorname{maximize}} \mathbb{E}_{x \sim p_{\text {data }}}[\log D(x)]+\mathbb{E}_{z \sim p(z)}[\log (1-D(G(z)))] GminimizeDmaximizeExpdata [logD(x)]+Ezp(z)[log(1D(G(z)))]
解释一下上面的公式,D代表着判别器对图像的判断,1代表是绝对真实,0代表着绝对虚假

所以:

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

判别器的目标:判别出所有生成器生成的虚假图像

我们再来看一下上面的公式,如果判别器非常优秀,D(x)=1,1-D(G(z))=1,所以整个损失函数为0

反过来,生成器非常优秀,D(G(z))=1,那么整个损失会倾向于无负无穷大

所以生成器要让损失接近负无穷,而判别器要让损失接近0,所以这就是这个 maxmin game

优化

我们要对这场博弈进行优化,我们使用梯度下降来交替更新G和D

  • 更新G,最小化判别器做出正确判断的几率
  • 更新D,最大化判别器能做出的正确判断的几率

上面所说的方法虽然理论可行,但是实践上表现并不好。所以我们用另一种方法——更新G的时候,最大化让判别器做出错误判断的几率

上面这种方法可以避免在判别器过于强大的时候,生成器的梯度消失

所以在作业中,我们采取以下的更新方式:

  1. 更新G,最大化判别器做错误判断的可能性:

maximize ⁡ G E z ∼ p ( z ) [ log ⁡ D ( G ( z ) ) ] \underset{G}{\operatorname{maximize}} \mathbb{E}_{z \sim p(z)}[\log D(G(z))] GmaximizeEzp(z)[logD(G(z))]

  1. 更新D,最大化判别器准确判断的可能性

maximize ⁡ D E x ∼ p data  [ log ⁡ D ( x ) ] + E z ∼ p ( z ) [ log ⁡ ( 1 − D ( G ( z ) ) ) ] \underset{D}{\operatorname{maximize}} \mathbb{E}_{x \sim p_{\text {data }}}[\log D(x)]+\mathbb{E}_{z \sim p(z)}[\log (1-D(G(z)))] DmaximizeExpdata [logD(x)]+Ezp(z)[log(1D(G(z)))]

接下来给我们展示以下,我们将要完成的模型的三个输出

【CS231n assignment 2022】Assignment 3 - Part 4,GAN_第1张图片

可以看出,我们要完成的是手写数字的模拟

2.2 Dataset

GANs 对超参数非常的挑剔,而且要求更多的训练 epoch,所以我们选择了 MNIST 数据集,简单实用。

我们直接使用 pytorch 来导入数据集,记得查看文档

2.3 Random Noise

我们要生成一个均匀分布的噪声,数值范围[-1,1],大小为(batch_size,dim)

现在打开文件cs231n/gan_pytorch.py完成sample_noise部分

def sample_noise(batch_size, dim, seed=None):
    """
    Generate a PyTorch Tensor of uniform random noise.

    Input:
    - batch_size: Integer giving the batch size of noise to generate.
    - dim: Integer giving the dimension of noise to generate.

    Output:
    - A PyTorch Tensor of shape (batch_size, dim) containing uniform
      random noise in the range (-1, 1).
    """
    if seed is not None:
        torch.manual_seed(seed)

    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    return 2*torch.rand((batch_size, dim))-1

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

代码详解

  • 由于torch.rand只能生成[0,1]范围的均匀分布,我们把他乘2减1就可以得到均匀分布啦(约等于是平移了一下(

2.4 Flatten

调用我们之前实现过的函数,我们也提供了Unflatten,以供我们在完成基于卷积的生成器的时候使用

同时也提供了初始化权重的方法initialize_weights,使用的是Xavier的初始化方法

2.5 Discriminator

我们的第一步将会是构建判别器,使用nn.Sequential来进行构建,结构如作业中所示,全连接层包含偏置

结构:

  • FC(全连接层),(784,256)
  • LeakyReLU
  • FC,(256,256)
  • LeakyReLU
  • FC,(256,1)
def discriminator(seed=None):
    """
    Build and return a PyTorch model implementing the architecture above.
    """

    if seed is not None:
        torch.manual_seed(seed)

    model = None

    ##############################################################################
    # TODO: Implement architecture                                               #
    #                                                                            #
    # HINT: nn.Sequential might be helpful. You'll start by calling Flatten().   #
    ##############################################################################
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    model = nn.Sequential(
        Flatten(),
        nn.Linear(784, 256),
        nn.LeakyReLU(),
        nn.Linear(256, 256),
        nn.LeakyReLU(),
        nn.Linear(256, 1)
    )

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    ##############################################################################
    #                               END OF YOUR CODE                             #
    ##############################################################################
    return model

代码详解

  • 按部就班的简单实现~

2.6 Generator

在这里我们要完成生成器的实现了

生成器结构:

  • FC (noise_dim,1024)
  • ReLU
  • FC (1024,1024)
  • ReLU
  • FC (1024,784)
  • Tanh

完成 generator 部分

def generator(noise_dim=NOISE_DIM, seed=None):
    """
    Build and return a PyTorch model implementing the architecture above.
    """

    if seed is not None:
        torch.manual_seed(seed)

    model = None

    ##############################################################################
    # TODO: Implement architecture                                               #
    #                                                                            #
    # HINT: nn.Sequential might be helpful.                                      #
    ##############################################################################
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    model = nn.Sequential(
        nn.Linear(noise_dim, 1024),
        nn.ReLU(),
        nn.Linear(1024, 1024),
        nn.ReLU(),
        nn.Linear(1024, 784),
        nn.Tanh()
    )

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    ##############################################################################
    #                               END OF YOUR CODE                             #
    ##############################################################################
    return model

2.7 GAN loss

计算生成器和判别器的损失

首先是生成器损失:
ℓ G = − E z ∼ p ( z ) [ log ⁡ D ( G ( z ) ) ] \ell_{G}=-\mathbb{E}_{z \sim p(z)}[\log D(G(z))] G=Ezp(z)[logD(G(z))]
然后是判别器损失:
ℓ D = − E x ∼ p data  [ log ⁡ D ( x ) ] − E z ∼ p ( z ) [ log ⁡ ( 1 − D ( G ( z ) ) ) ] \ell_{D}=-\mathbb{E}_{x \sim p_{\text {data }}}[\log D(x)]-\mathbb{E}_{z \sim p(z)}[\log (1-D(G(z)))] D=Expdata [logD(x)]Ezp(z)[log(1D(G(z)))]
因为我们要最小化损失,所以我们在损失函数前面增加了负号

提示

使用bce_loss损失函数来计算交叉熵损失,这是计算判别器给出的真实标签 log 概率所需要的

给出分数 s 和标签 y,交叉熵损失被定义为:
b c e ( s , y ) = − y ∗ l o g ( s ) − ( 1 − y ) ∗ l o g ( 1 − s ) bce(s,y)=-y*log(s)-(1-y)*log(1-s) bce(s,y)=ylog(s)(1y)log(1s)
这里的s如果越接近1,表示真图片,越接近0则表示假图片

这种简单的实现会导致数值不稳定,我们基于nn.BCEWithlogitsLoss实现了一个稳定的版本

您还需要计算与真假相对应的标签,并使用 logit 参数来确定它们的大小。 确保使用全局 dtype 变量将这些标签转换为正确的数据类型,例如:true_labels = torch.ones(size).type(dtype)

讲了这么多,就是损失函数用上面这个函数来替代啦!

请记得对比最开始的损失函数公式,只是D(X)和D(G(x))被s替代了而已

discriminator loss

def discriminator_loss(logits_real, logits_fake):
    """
    Computes the discriminator loss described above.

    Inputs:
    - logits_real: PyTorch Tensor of shape (N,) giving scores for the real data.
    - logits_fake: PyTorch Tensor of shape (N,) giving scores for the fake data.

    Returns:
    - loss: PyTorch Tensor containing (scalar) the loss for the discriminator.
    """
    loss = None
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    N = logits_real.shape[0]
    real_labels = torch.ones(N).type(dtype)
    fake_labels = 1 - real_labels
    loss = bce_loss(logits_real, real_labels) + \
        bce_loss(logits_fake, fake_labels)
    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    return loss

generator loss

def generator_loss(logits_fake):
    """
    Computes the generator loss described above.

    Inputs:
    - logits_fake: PyTorch Tensor of shape (N,) giving scores for the fake data.

    Returns:
    - loss: PyTorch Tensor containing the (scalar) loss for the generator.
    """
    loss = None
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    N = logits_fake.shape[0]
    fake_label = torch.ones(N).type(dtype)
    loss = bce_loss(logits_fake, fake_label)

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    return loss

2.8 Optimizing our Loss

使用 Adam 优化器来进行优化

def get_optimizer(model):
    """
    Construct and return an Adam optimizer for the model with learning rate 1e-3,
    beta1=0.5, and beta2=0.999.

    Input:
    - model: A PyTorch model that we want to optimize.

    Returns:
    - An Adam optimizer for the model with the desired hyperparameters.
    """
    optimizer = None
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    optimizer = optim.Adam(model.parameters(), lr=1e-3, betas=(0.5, 0.999))

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    return optimizer

简单的加上一行代码就可以了

Training a GAN

接下来我们要及逆行训练,我们不需要修改,但是鼓励我们去看一下下训练代码

看代码就大家自己去看一下啦~我们直接运行底下的 cell,看一下最终的结果

【CS231n assignment 2022】Assignment 3 - Part 4,GAN_第2张图片

可以看出效果还是可以的,有点数字的样子了

2.9 Least Squares GAN

这是一个对原本的损失函数进行的改动,使其更稳定

公式如下:
ℓ G = 1 2 E z ∼ p ( z ) [ ( D ( G ( z ) ) − 1 ) 2 ] ℓ D = 1 2 E x ∼ p data  [ ( D ( x ) − 1 ) 2 ] + 1 2 E z ∼ p ( z ) [ ( D ( G ( z ) ) ) 2 ] \ell_{G}=\frac{1}{2} \mathbb{E}_{z \sim p(z)}\left[(D(G(z))-1)^{2}\right]\\ \ell_{D}=\frac{1}{2} \mathbb{E}_{x \sim p_{\text {data }}}\left[(D(x)-1)^{2}\right]+\frac{1}{2} \mathbb{E}_{z \sim p(z)}\left[(D(G(z)))^{2}\right] G=21Ezp(z)[(D(G(z))1)2]D=21Expdata [(D(x)1)2]+21Ezp(z)[(D(G(z)))2]
其中, D ( x ) 和 D ( G ( z ) ) D(x)和D(G(z)) D(x)D(G(z))scores_realscores_fake来代替

不会很困难,一定要记得加上绝对值,我们直接来看

def ls_discriminator_loss(scores_real, scores_fake):
    """
    Compute the Least-Squares GAN loss for the discriminator.

    Inputs:
    - scores_real: PyTorch Tensor of shape (N,) giving scores for the real data.
    - scores_fake: PyTorch Tensor of shape (N,) giving scores for the fake data.

    Outputs:
    - loss: A PyTorch Tensor containing the loss.
    """
    loss = None
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    loss = (0.5*(scores_real-1)**2).mean()+(0.5*scores_fake**2).mean()

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    return loss


def ls_generator_loss(scores_fake):
    """
    Computes the Least-Squares GAN loss for the generator.

    Inputs:
    - scores_fake: PyTorch Tensor of shape (N,) giving scores for the fake data.

    Outputs:
    - loss: A PyTorch Tensor containing the loss.
    """
    loss = None
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    loss = 0.5*((scores_fake-1)**2).mean()

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    return loss

接下来我们运行训练,来看看我们的此次修改效果如何

可以明显感觉到,收敛的速度似乎变快了,最后生成的效果如下

【CS231n assignment 2022】Assignment 3 - Part 4,GAN_第3张图片

2.10 Deeply Convolutional GANs

上面我们完全拷贝的最原始的 GAN,我们可以看出边缘并不锐利,在这里我们引入卷积,我们将实现 DCGAN 中的一些想法,也就是使用 CNN

2.10.1 Discriminator

使用如下的结构,修改build_dc_classifier函数

【CS231n assignment 2022】Assignment 3 - Part 4,GAN_第4张图片

按部就班实现即可:

def build_dc_classifier(batch_size):
    """
    Build and return a PyTorch model for the DCGAN discriminator implementing
    the architecture above.
    """

    ##############################################################################
    # TODO: Implement architecture                                               #
    #                                                                            #
    # HINT: nn.Sequential might be helpful.                                      #
    ##############################################################################
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    model = nn.Sequential(
        Unflatten(batch_size, 1, 28, 28),
        nn.Conv2d(1, 32, 5, 1),
        nn.LeakyReLU(),
        nn.MaxPool2d(2, 2),
        nn.Conv2d(32, 64, 5, 1),
        nn.LeakyReLU(),
        nn.MaxPool2d(2, 2),
        Flatten(),
        nn.Linear(64*4*4, 4*4*64),
        nn.LeakyReLU(),
        nn.Linear(4*4*64, 1)
    )
    return model

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    ##############################################################################
    #                               END OF YOUR CODE                             #
    ##############################################################################

代码详解

  • 注意使用公式计算激活图的大小

2.10.2 Generator

我们直接使用 InfoGAN 中的生成器,建议看一下nn.ConvTranspose2d的文档

生成器结构如下:

【CS231n assignment 2022】Assignment 3 - Part 4,GAN_第5张图片

接下来比较需要注意的是转置卷积的公式,首先要先进行填充,假设核大小k=3,步长stride=1,填充padding=0的情况:

  • 首先在元素之间填充s-1行/列(s-1按照此假设为0)
  • 接着在特征图四周填充 k-p-1行/列(此处为2)
  • 对卷积核参数进行反转
  • 按照原本的卷积操作进行运算

有了上面的假设,我们就可以把转置卷积转化成卷积操作,也就可以使用卷积操作的公式进行计算了

所以一起来看代码:

def build_dc_generator(noise_dim=NOISE_DIM):
    """
    Build and return a PyTorch model implementing the DCGAN generator using
    the architecture described above.
    """

    ##############################################################################
    # TODO: Implement architecture                                               #
    #                                                                            #
    # HINT: nn.Sequential might be helpful.                                      #
    ##############################################################################
    # *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****

    model = nn.Sequential(
        nn.Linear(noise_dim, 1024),
        nn.ReLU(),
        nn.BatchNorm1d(1024),
        nn.Linear(1024, 7*7*128),
        nn.ReLU(),
        nn.BatchNorm1d(7*7*128),
        Unflatten(-1, 128, 7, 7),
        nn.ConvTranspose2d(128, 64, 4, 2, 1),
        nn.ReLU(),
        nn.BatchNorm2d(64),
        nn.ConvTranspose2d(64, 1, 4, 2, 1),
        nn.Tanh(),
        Flatten(),
    )
    return model

    # *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
    ##############################################################################
    #                               END OF YOUR CODE                             #
    ##############################################################################

代码详解

  • 注意nn.BatchNorm需要我们手动算出特征的维度

好了接下来看看结果,明显生成的图像边缘更为锐利了,背景也更纯粹

【CS231n assignment 2022】Assignment 3 - Part 4,GAN_第6张图片


3. 总结、预告

在这次作业中,我们成功实现了几个简单的 GAN 模型,相信大家对 GAN 也有了简单的了解,接下来就剩下最后一项作业啦,cs231n作业也即将更新完毕(除了漏了的几个作业二的),我们下期见!

你可能感兴趣的:(cs231n学习,生成对抗网络,深度学习)