Python Day53

Task:
1.对抗生成网络的思想:关注损失从何而来
2.生成器、判别器
3.nn.sequential容器:适合于按顺序运算的情况,简化前向传播写法
4.leakyReLU介绍:避免relu的神经元失活现象


1. 对抗生成网络的思想:关注损失从何而来

这是理解GANs的关键!传统的神经网络训练中,我们通常会直接定义一个损失函数(如均方误差MSE、交叉熵CE),然后通过反向传播来优化这个损失。这个损失的“来源”是固定的,是我们预设的标签或目标值与模型输出之间的差异。

但在GAN中,情况有所不同:

  • 没有直接的“真实标签”来指导生成器(Generator)。 如果我们要生成逼真的人脸,我们没有一个“完美人脸”的标签让生成器去拟合。
  • 损失的来源是“对抗”。 生成器和判别器是两个相互竞争的网络:
    • 判别器(Discriminator) 的目标是尽可能准确地判别输入是真实数据还是生成数据。它的损失来自它自己的判断错误。
    • 生成器(Generator) 的目标是尽可能生成足以“欺骗”判别器的数据。生成器的损失不是来自它与某个真实标签的直接比较,而是来自判别器对它生成数据的判断。如果判别器认为生成的数据是假的,那么生成器就会得到一个大的损失,从而调整自己以生成更真实的数据。如果判别器被骗了,认为生成的数据是真的,那么生成器就成功了,损失就会小。

所以,对抗生成网络的核心思想是:通过建立一个动态的、对抗性的训练环境,让两个网络相互学习和改进,最终生成器能够生成以假乱真的数据。生成器不再直接拟合一个固定目标,而是拟合判别器的“判断标准”。


2. 生成器(Generator)、判别器(Discriminator)

这是GANs的两个核心组成部分。

  • 生成器(Generator, G)

    • 作用: 接收一个随机噪声向量(通常是服从某种简单分布,如高斯分布或均匀分布)作为输入,并将其转换为一个与真实数据具有相同结构的数据样本(例如,图像、文本、音频)。
    • 结构: 通常是一个深度神经网络,其结构取决于要生成的数据类型。例如,生成图像时可能是包含转置卷积(或称反卷积)的卷积神经网络。
    • 目标: 生成的数据尽可能逼真,以至于判别器无法分辨其真伪。可以理解为“造假者”。
    • 训练方式: 通过判别器的反馈来更新参数。它的损失函数通常与判别器的输出有关(例如,希望判别器将它生成的数据判断为真)。
  • 判别器(Discriminator, D)

    • 作用: 接收一个数据样本作为输入(这个样本可能来自真实数据集,也可能来自生成器),然后输出一个概率值,表示该样本是“真实数据”的概率(通常接近1表示真实,接近0表示虚假)。
    • 结构: 通常是一个二分类器,例如一个卷积神经网络(用于图像)或多层感知机,其最后一层通常是Sigmoid激活函数。
    • 目标: 尽可能准确地分辨出输入数据是真实的还是生成器伪造的。可以理解为“鉴别者”或“警察”。
    • 训练方式: 接收真实数据和生成数据,并根据它们的标签(真实为1,生成为0)来更新参数。它的损失函数通常是标准的二分类损失(如二元交叉熵)。

训练过程的交替性:
在训练GAN时,通常会采取交替训练的方式:

  1. 训练判别器: 冻结生成器的参数,用真实数据和生成数据来训练判别器,使其能更好地区分两者。
  2. 训练生成器: 冻结判别器的参数,生成器生成数据,然后通过判别器的输出(判断结果)来更新生成器的参数,使其生成的数据更能欺骗判别器。
    这个过程不断重复,直到达到一个纳什均衡点:生成器能够生成非常逼真的数据,判别器也只能以50%的概率随机猜测。

3. nn.Sequential 容器:适合于按顺序运算的情况,简化前向传播写法

nn.Sequential 是 PyTorch 中一个非常方便的模块容器。

  • 作用: 允许你将多个神经网络模块(例如 nn.Linear, nn.Conv2d, nn.ReLU, nn.BatchNorm2d 等)按照它们被添加的顺序堆叠起来。

  • 特性:

    • 有序执行: 数据会依次通过容器中的每一个模块。第一个模块的输出是第二个模块的输入,以此类推。
    • 简化 forward 方法: 如果你的模型的前向传播逻辑只是简单地将数据从一个层传递到下一个层,那么使用 nn.Sequential 可以极大地简化 nn.Module 子类中的 forward 方法。你不需要手动连接每一层。
    • 易于构建: 可以通过传递一个模块列表或在构造函数中直接添加模块来创建。
  • 示例:

    import torch.nn as nn
    
    # 不使用 nn.Sequential
    class MyModelManual(nn.Module):
        def __init__(self):
            super().__init__()
            self.linear1 = nn.Linear(10, 20)
            self.relu = nn.ReLU()
            self.linear2 = nn.Linear(20, 1)
    
        def forward(self, x):
            x = self.linear1(x)
            x = self.relu(x)
            x = self.linear2(x)
            return x
    
    # 使用 nn.Sequential
    class MyModelSequential(nn.Module):
        def __init__(self):
            super().__init__()
            self.layers = nn.Sequential(
                nn.Linear(10, 20),
                nn.ReLU(),
                nn.Linear(20, 1)
            )
    
        def forward(self, x):
            return self.layers(x)
    
    # 你甚至可以直接这样定义一个简单的Sequential模型
    model = nn.Sequential(
        nn.Conv2d(3, 64, kernel_size=3, padding=1),
        nn.ReLU(),
        nn.MaxPool2d(kernel_size=2, stride=2),
        nn.Flatten(),
        nn.Linear(64 * 16 * 16, 10) # 假设输入是 3x32x32
    )
    

    在GANs中,生成器和判别器内部的很多层级结构都可以通过 nn.Sequential 来简洁地定义。


4. LeakyReLU 介绍:避免 ReLU 的神经元失活现象

ReLU (Rectified Linear Unit) 是目前神经网络中最常用的激活函数之一,其定义是:
f ( x ) = max ⁡ ( 0 , x ) f(x) = \max(0, x) f(x)=max(0,x)

优点: 解决了传统Sigmoid和Tanh激活函数在反向传播时梯度消失的问题,计算简单,收敛速度快。

缺点: 神经元失活(Dying ReLU)问题。
当输入 x x x 为负数时,ReLU 的输出为0,梯度也为0。这意味着如果一个神经元的权重更新导致其输入总是负数,那么这个神经元将永远输出0,并且梯度永远为0,它将无法再更新其权重,变得“失活”或“死亡”。一旦神经元失活,它对网络的贡献就会停止。

LeakyReLU (Leaky Rectified Linear Unit) 旨在解决这个问题。其定义是:
f ( x ) = { x if  x > 0 α x if  x ≤ 0 f(x) = \begin{cases} x & \text{if } x > 0 \\ \alpha x & \text{if } x \le 0 \end{cases} f(x)={xαxif x>0if x0

其中 α \alpha α 是一个很小的正数(例如,0.01 或 0.001),通常被称为“泄漏”系数。

LeakyReLU 的优点:

  • 当输入为负数时,LeakyReLU 不会完全输出0,而是输出一个很小的负值(alpha * x)。
  • 这意味着当输入为负数时,梯度也不再是0,而是 alpha。这允许失活的神经元仍然能够进行一些微小的权重更新,从而避免了“死亡”问题。
  • 它保留了ReLU计算效率高、不会饱和的优点。

在GAN中的应用:
GANs 对激活函数的选择非常敏感。生成器和判别器通常都倾向于使用 LeakyReLU 而不是 ReLU。

  • 原因: 在对抗训练中,梯度流动至关重要。如果判别器或生成器中的大量神经元因 ReLU 的“失活”问题而停止更新,那么整个训练过程可能会变得不稳定或陷入局部最优。LeakyReLU 能够提供更稳定的梯度,帮助网络更好地学习和收敛。

PReLU(Parametric ReLU)是 LeakyReLU 的一个变体,其中 α \alpha α 不是一个固定的常数,而是作为一个可学习的参数。


你可能感兴趣的:(python机器学习,python,开发语言)