提交 5e99ffe9 编写于 作者: W wizardforcel

2021-01-16 18:38:43

上级 8a766406
此差异已折叠。
...@@ -210,7 +210,7 @@ for epoch in range(epochs): ...@@ -210,7 +210,7 @@ for epoch in range(epochs):
图 1.7:显示客户购买产品的数据集 图 1.7:显示客户购买产品的数据集
即使不是全部,我们也可以预测大多数结果。 但是,如果我们可以从中进行预测的数据点数量太多而又无法用凡人的大脑来处理它们该怎么办? 计算机可以浏览数据,并可能根据以前的数据吐出答案。 这种数据驱动的方法可以为我们提供很多帮助,因为我们唯一要做的就是假设相关的功能,然后将其交给包含不同算法的黑匣子,以从功能集中学习规则或模式。 即使不是全部,我们也可以预测大多数结果。 但是,如果我们可以从中进行预测的数据点数量太多而又无法用凡人的大脑来处理它们该怎么办? 计算机可以浏览数据,并可能根据以前的数据吐出答案。 这种数据驱动的方法可以为我们提供很多帮助,因为我们唯一要做的就是假设相关的功能,然后将其交给包含不同算法的黑,以从功能集中学习规则或模式。
有问题。 即使我们知道要查找的内容,清理数据并提取功能也不是一件有趣的事情。 然而,最主要的麻烦不是这个。 我们无法有效预测高维数据和其他媒体类型的数据的功能。 例如,在人脸识别中,我们最初使用基于规则的程序找到人脸的细节长度,并将其作为输入输入神经网络,因为我们认为这是人类用来识别人脸的特征集。 有问题。 即使我们知道要查找的内容,清理数据并提取功能也不是一件有趣的事情。 然而,最主要的麻烦不是这个。 我们无法有效预测高维数据和其他媒体类型的数据的功能。 例如,在人脸识别中,我们最初使用基于规则的程序找到人脸的细节长度,并将其作为输入输入神经网络,因为我们认为这是人类用来识别人脸的特征集。
...@@ -294,7 +294,7 @@ Facebook 研究团队发现了一种具有卷积网络的先进自然语言处 ...@@ -294,7 +294,7 @@ Facebook 研究团队发现了一种具有卷积网络的先进自然语言处
#### 生成对抗网络 #### 生成对抗网络
**生成对抗网络****GAN**)由 Ian Goodfellow 于 2014 年发明,从那时起,它们使整个 AI 社区颠倒了。 它们是最简单,最明显的实现方式之一,但具有以其功能吸引世界的力量。 在 GAN 中,两个网络相互竞争并达到平衡,生成器网络可以生成数据,而别器网络很难与实际图像区分开来。 一个真实的例子就是警察与假冒者之间的斗争。 **生成对抗网络****GAN**)由 Ian Goodfellow 于 2014 年发明,从那时起,它们使整个 AI 社区颠倒了。 它们是最简单,最明显的实现方式之一,但具有以其功能吸引世界的力量。 在 GAN 中,两个网络相互竞争并达到平衡,生成器网络可以生成数据,而别器网络很难与实际图像区分开来。 一个真实的例子就是警察与假冒者之间的斗争。
造假者试图制造假币,而警察试图对其进行侦查。 最初,造假者知识不足,无法制作出看起来很原始的假币。 随着时间的流逝,造假者越来越擅长制作看起来更像原始货币的货币。 然后,警察开始无法识别假币,但最终他们会再次变得更好。 这一世代歧视过程最终导致了平衡。 GAN 的优势是巨大的,我们将在后面详细讨论。 造假者试图制造假币,而警察试图对其进行侦查。 最初,造假者知识不足,无法制作出看起来很原始的假币。 随着时间的流逝,造假者越来越擅长制作看起来更像原始货币的货币。 然后,警察开始无法识别假币,但最终他们会再次变得更好。 这一世代歧视过程最终导致了平衡。 GAN 的优势是巨大的,我们将在后面详细讨论。
......
...@@ -328,7 +328,7 @@ print(output.grad_fn.next_functions[0][0].next_functions[0][0]) ...@@ -328,7 +328,7 @@ print(output.grad_fn.next_functions[0][0].next_functions[0][0])
在途中,我们学习了如何在 PyTorch 中包装矩阵(或张量),这有助于我们进行反向传播。 使用 PyTorch 进行相同操作的方式更加方便,这就是我们将在本节中讨论的内容。 PyTorch 可以访问内置的深度学习项目所需的几乎所有功能。 由于 PyTorch 支持 Python 中所有可用的数学函数,因此,如果在内核中不可用,则构建一个函数并不是一件艰巨的任务。 您不仅可以构建所需的任何功能,而且 PyTorch 隐式定义了所构建功能的派生功能。 在途中,我们学习了如何在 PyTorch 中包装矩阵(或张量),这有助于我们进行反向传播。 使用 PyTorch 进行相同操作的方式更加方便,这就是我们将在本节中讨论的内容。 PyTorch 可以访问内置的深度学习项目所需的几乎所有功能。 由于 PyTorch 支持 Python 中所有可用的数学函数,因此,如果在内核中不可用,则构建一个函数并不是一件艰巨的任务。 您不仅可以构建所需的任何功能,而且 PyTorch 隐式定义了所构建功能的派生功能。
PyTorch 对需要了解底层操作的人很有帮助,但同时,PyTorch 通过`torch.nn`模块提供了高层 API。 因此,如果用户不想知道黑匣子内部发生了什么,而只需要构建模型,则 PyTorch 允许他们这样做。 同样,如果用户不喜欢引擎盖下的提升操作,并且需要知道到底发生了什么,PyTorch 也可以提供这种灵活性。 将这种组合构建到单个框架上可以改变游戏规则,并使 PyTorch 成为整个深度学习社区最喜欢的框架之一。 PyTorch 对需要了解底层操作的人很有帮助,但同时,PyTorch 通过`torch.nn`模块提供了高层 API。 因此,如果用户不想知道黑内部发生了什么,而只需要构建模型,则 PyTorch 允许他们这样做。 同样,如果用户不喜欢引擎盖下的提升操作,并且需要知道到底发生了什么,PyTorch 也可以提供这种灵活性。 将这种组合构建到单个框架上可以改变游戏规则,并使 PyTorch 成为整个深度学习社区最喜欢的框架之一。
### 高级 API ### 高级 API
......
...@@ -360,13 +360,13 @@ GAN 从随机分布中获取样本,然后由网络将其转换为输出。 GAN ...@@ -360,13 +360,13 @@ GAN 从随机分布中获取样本,然后由网络将其转换为输出。 GAN
### Simple GAN ### Simple GAN
了解 GAN 的直观方法是从博弈论的角度了解它。 简而言之,GAN 由两个参与者组成,一个生成器和一个别器,每一个都试图击败对方。 发生器从分布中获取一些随机噪声,并尝试从中生成一些输出分布。 生成器总是尝试创建与真实分布没有区别的分布; 也就是说,伪造的输出应该看起来像是真实的图像。 了解 GAN 的直观方法是从博弈论的角度了解它。 简而言之,GAN 由两个参与者组成,一个生成器和一个别器,每一个都试图击败对方。 发生器从分布中获取一些随机噪声,并尝试从中生成一些输出分布。 生成器总是尝试创建与真实分布没有区别的分布; 也就是说,伪造的输出应该看起来像是真实的图像。
![Simple GAN](img/B09475_06_14.jpg) ![Simple GAN](img/B09475_06_14.jpg)
Figure 6.13: GAN architecture Figure 6.13: GAN architecture
但是,如果没有明确的训练或标签,生成器将无法确定真实图像的外观,并且其唯一的来源就是随机浮点数的张量。 然后,GAN 将第二个玩家介绍给游戏,这是一个判别器。 鉴别器仅负责通知生成器生成的输出看起来不像真实图像,以便生成器更改其生成图像的方式以使鉴别器确信它是真实图像。 但是鉴别器总是可以告诉生成器图像不是真实的,因为鉴别器知道图像是从生成器生成的。 这就是事情变得有趣的地方。 GAN 将真实,真实的图像引入游戏中,并将鉴别器与生成器隔离。 现在,鉴别器从一组真实图像中获取一个图像,并从生成器中获取一个伪图像,并且鉴别器必须找出每个图像的来源。 最初,判别器什么都不知道,只能预测随机结果。 但是,如果没有明确的训练或标签,生成器将无法确定真实图像的外观,并且其唯一的来源就是随机浮点数的张量。 然后,GAN 将第二个玩家介绍给游戏,这是一个判别器。 判别器仅负责通知生成器生成的输出看起来不像真实图像,以便生成器更改其生成图像的方式以使判别器确信它是真实图像。 但是判别器总是可以告诉生成器图像不是真实的,因为判别器知道图像是从生成器生成的。 这就是事情变得有趣的地方。 GAN 将真实,真实的图像引入游戏中,并将判别器与生成器隔离。 现在,判别器从一组真实图像中获取一个图像,并从生成器中获取一个伪图像,并且判别器必须找出每个图像的来源。 最初,判别器什么都不知道,只能预测随机结果。
```py ```py
class DiscriminatorNet(torch.nn.Module): class DiscriminatorNet(torch.nn.Module):
...@@ -406,7 +406,7 @@ class DiscriminatorNet(torch.nn.Module): ...@@ -406,7 +406,7 @@ class DiscriminatorNet(torch.nn.Module):
return x return x
``` ```
但是,可以将辨别器的任务修改为分类任务。 鉴别器可以将输入图像分类为**原始****生成的**,这是二进制分类。 同样,我们训练鉴别器网络正确地对图像进行分类,最终,通过反向传播,鉴别器学会了区分真实图像和生成的图像。 但是,可以将辨别器的任务修改为分类任务。 判别器可以将输入图像分类为**原始****生成的**,这是二进制分类。 同样,我们训练判别器网络正确地对图像进行分类,最终,通过反向传播,判别器学会了区分真实图像和生成的图像。
该会话中使用的示例将生成类似 MNIST 的输出。 前面的代码显示了 MNIST 上的鉴别播放器,该播放器总是从真实源数据集或生成器中获取图像。 GAN 众所周知非常不稳定,因此使用`LeakyReLU`是研究人员发现比常规`ReLU`更好工作的黑客之一。 现在,`LeakyReLU`通过它泄漏了负极,而不是将所有内容限制为零到零。 与正常的`ReLU`相比,这有助于使梯度更好地流过网络,对于小于零的值,梯度为零。 该会话中使用的示例将生成类似 MNIST 的输出。 前面的代码显示了 MNIST 上的鉴别播放器,该播放器总是从真实源数据集或生成器中获取图像。 GAN 众所周知非常不稳定,因此使用`LeakyReLU`是研究人员发现比常规`ReLU`更好工作的黑客之一。 现在,`LeakyReLU`通过它泄漏了负极,而不是将所有内容限制为零到零。 与正常的`ReLU`相比,这有助于使梯度更好地流过网络,对于小于零的值,梯度为零。
...@@ -439,9 +439,9 @@ def train_discriminator(optimizer, real_data, fake_data): ...@@ -439,9 +439,9 @@ def train_discriminator(optimizer, real_data, fake_data):
return error_real + error_fake, prediction_real, prediction_fake return error_real + error_fake, prediction_real, prediction_fake
``` ```
在前面的代码块中定义的函数`train_generator`接受`optimizer`对象,伪数据和实数据,然后将它们传递给鉴别器。 函数`fake_data_target`(在下面的代码块中提供)创建一个零张量,该张量的大小与预测大小相同,其中预测是从鉴别器返回的值。 鉴别器的训练策略是使任何真实数据被归类为真实分布的概率最大化,并使任何数据点被归类为真实分布的概率最小化。 在实践中,使用了来自鉴别器或生成器的结果的日志,因为这会严重损害网络的分类错误。 然后在应用`optimizer.step`函数之前将错误反向传播,该函数将通过学习速率以梯度更新权重。 在前面的代码块中定义的函数`train_generator`接受`optimizer`对象,伪数据和实数据,然后将它们传递给判别器。 函数`fake_data_target`(在下面的代码块中提供)创建一个零张量,该张量的大小与预测大小相同,其中预测是从判别器返回的值。 判别器的训练策略是使任何真实数据被归类为真实分布的概率最大化,并使任何数据点被归类为真实分布的概率最小化。 在实践中,使用了来自判别器或生成器的结果的日志,因为这会严重损害网络的分类错误。 然后在应用`optimizer.step`函数之前将错误反向传播,该函数将通过学习速率以梯度更新权重。
接下来给出用于获得真实数据目标和伪数据目标的功能,这与前面讨论的最小化或最大化概率的概念基本一致。 实际数据生成器返回一个张量为 1s 的张量,该张量是我们作为输入传递的形状。 在训练生成器时,我们正在尝试通过生成图像来最大程度地提高其概率,该图像看起来应该是从真实数据分布中获取的。 这意味着别器应将 1 预测为图像来自真实分布的置信度分数。 接下来给出用于获得真实数据目标和伪数据目标的功能,这与前面讨论的最小化或最大化概率的概念基本一致。 实际数据生成器返回一个张量为 1s 的张量,该张量是我们作为输入传递的形状。 在训练生成器时,我们正在尝试通过生成图像来最大程度地提高其概率,该图像看起来应该是从真实数据分布中获取的。 这意味着别器应将 1 预测为图像来自真实分布的置信度分数。
```py ```py
def real_data_target(size): def real_data_target(size):
...@@ -457,7 +457,7 @@ def fake_data_target(size): ...@@ -457,7 +457,7 @@ def fake_data_target(size):
return torch.zeros(size, 1).to(device) return torch.zeros(size, 1).to(device)
``` ```
因此,别器的实现很容易实现,因为它本质上只是分类任务。 生成器网络将涉及所有卷积上采样/下采样,因此有点复杂。 但是对于当前示例,由于我们希望它尽可能简单,因此我们将在全连接网络而不是卷积网络上进行工作。 因此,别器的实现很容易实现,因为它本质上只是分类任务。 生成器网络将涉及所有卷积上采样/下采样,因此有点复杂。 但是对于当前示例,由于我们希望它尽可能简单,因此我们将在全连接网络而不是卷积网络上进行工作。
```py ```py
def noise(size): def noise(size):
...@@ -467,7 +467,7 @@ def noise(size): ...@@ -467,7 +467,7 @@ def noise(size):
可以定义一个噪声生成函数,该函数可以生成随机样本(事实证明,这种采样在高斯分布而非随机分布下是有效的,但为简单起见,此处使用随机分布)。 如果 CUDA 可用,我们会将随机产生的噪声从 CPU 内存传输到 GPU 内存,并返回张量,其输出大小为`100`。 因此,生成网络期望输入噪声的特征数量为 100,而我们知道 MNIST 数据集中有 784 个数据点(28x28)。 可以定义一个噪声生成函数,该函数可以生成随机样本(事实证明,这种采样在高斯分布而非随机分布下是有效的,但为简单起见,此处使用随机分布)。 如果 CUDA 可用,我们会将随机产生的噪声从 CPU 内存传输到 GPU 内存,并返回张量,其输出大小为`100`。 因此,生成网络期望输入噪声的特征数量为 100,而我们知道 MNIST 数据集中有 784 个数据点(28x28)。
对于生成器,我们具有与鉴别器类似的结构,但是在最后一层具有 tanh 层,而不是 S 型。 进行此更改是为了与我们对 MNIST 数据进行的归一化同步,以将其转换为-1 到 1 的范围,以便判别器始终获得具有相同范围内数据点的数据集。 生成器中的三层中的每一层都将输入噪声上采样到 784 的输出大小,就像我们在鉴别器中下采样以进行分类一样。 对于生成器,我们具有与判别器类似的结构,但是在最后一层具有 tanh 层,而不是 S 型。 进行此更改是为了与我们对 MNIST 数据进行的归一化同步,以将其转换为-1 到 1 的范围,以便判别器始终获得具有相同范围内数据点的数据集。 生成器中的三层中的每一层都将输入噪声上采样到 784 的输出大小,就像我们在判别器中下采样以进行分类一样。
```py ```py
class GeneratorNet(torch.nn.Module): class GeneratorNet(torch.nn.Module):
...@@ -505,11 +505,11 @@ class GeneratorNet(torch.nn.Module): ...@@ -505,11 +505,11 @@ class GeneratorNet(torch.nn.Module):
return x return x
``` ```
生成器训练器功能比鉴别器训练器功能简单得多,因为它不需要从两个来源获取输入,也不必针对不同的目的进行训练,而鉴别器则必须最大化将真实图像分类为真实图像的可能性。 图像,并最小化将噪声图像分类为真实图像的可能性。 此功能仅接受伪图像数据和优化器,其中伪图像是生成器生成的图像。 生成器训练器功能代码可以在 GitHub 存储库中找到。 生成器训练器功能比判别器训练器功能简单得多,因为它不需要从两个来源获取输入,也不必针对不同的目的进行训练,而判别器则必须最大化将真实图像分类为真实图像的可能性。 图像,并最小化将噪声图像分类为真实图像的可能性。 此功能仅接受伪图像数据和优化器,其中伪图像是生成器生成的图像。 生成器训练器功能代码可以在 GitHub 存储库中找到。
我们分别创建别器和生成器网络的实例。 到目前为止,我们所有的网络实现都具有单个模型或单个神经网络,但第一次,我们有两个单独的网络在同一个数据集上工作,并具有不同的优化目标。 对于两个单独的网络,我们还需要创建两个单独的优化器。 从历史上看,`Adam`优化器最适合学习速度非常慢的 GAN。 我们分别创建别器和生成器网络的实例。 到目前为止,我们所有的网络实现都具有单个模型或单个神经网络,但第一次,我们有两个单独的网络在同一个数据集上工作,并具有不同的优化目标。 对于两个单独的网络,我们还需要创建两个单独的优化器。 从历史上看,`Adam`优化器最适合学习速度非常慢的 GAN。
两个网络都使用鉴别器的输出进行训练。 唯一的区别是,在训练鉴别器时,我们尝试使伪造图像被分类为真实图像的可能性最小,而在训练生成器时,我们试图使伪造图像被分类为真实图像的可能性最大。 由于它始终是试图预测 0 和 1 的二进制分类器,因此我们使用`torch.nn`中的`BCELoss`来尝试预测 0 或 1: 两个网络都使用判别器的输出进行训练。 唯一的区别是,在训练判别器时,我们尝试使伪造图像被分类为真实图像的可能性最小,而在训练生成器时,我们试图使伪造图像被分类为真实图像的可能性最大。 由于它始终是试图预测 0 和 1 的二进制分类器,因此我们使用`torch.nn`中的`BCELoss`来尝试预测 0 或 1:
```py ```py
discriminator = DiscriminatorNet().to(device) discriminator = DiscriminatorNet().to(device)
...@@ -545,7 +545,7 @@ CycleGAN 是 GAN 类型的智能变体之一。 在同一架构中,两个 GAN ...@@ -545,7 +545,7 @@ CycleGAN 是 GAN 类型的智能变体之一。 在同一架构中,两个 GAN
CycleGAN 学习每种分布的模式,并尝试将图像从一种分布映射到另一种分布。 “图 6.19”中给出了 CycleGAN 的简单架构图。 上面的图显示了如何训练一个 GAN,下面的图显示了如何使用正在工作的 CycleGAN 典型示例:马和斑马来训练另一个。 CycleGAN 学习每种分布的模式,并尝试将图像从一种分布映射到另一种分布。 “图 6.19”中给出了 CycleGAN 的简单架构图。 上面的图显示了如何训练一个 GAN,下面的图显示了如何使用正在工作的 CycleGAN 典型示例:马和斑马来训练另一个。
在 CycleGAN 中,我们不是从分布中随机采样的数据开始,而是使用来自集合 A(在本例中为一组马)的真实图像。 委托生成器 A 到 B(我们称为 A2B)将同一匹马转换为斑马,但没有将成对的马匹转换为斑马的配对图像。 训练开始时,A2B 会生成无意义的图像。 鉴别器 B 从 A2B 生成的图像或从集合 B(斑马的集合)中获取真实图像。 与其他任何鉴别器一样,它负责预测图像是生成的还是真实的。 这个过程是正常的 GAN,它永远不能保证同一匹马转换为斑马。 而是将马的图像转换为斑马的任何图像,因为损失只是为了确保图像看起来像集合 B 的分布; 它不需要与集合 A 相关。为了强加这种相关性,CycleGAN 引入了循环。 在 CycleGAN 中,我们不是从分布中随机采样的数据开始,而是使用来自集合 A(在本例中为一组马)的真实图像。 委托生成器 A 到 B(我们称为 A2B)将同一匹马转换为斑马,但没有将成对的马匹转换为斑马的配对图像。 训练开始时,A2B 会生成无意义的图像。 判别器 B 从 A2B 生成的图像或从集合 B(斑马的集合)中获取真实图像。 与其他任何判别器一样,它负责预测图像是生成的还是真实的。 这个过程是正常的 GAN,它永远不能保证同一匹马转换为斑马。 而是将马的图像转换为斑马的任何图像,因为损失只是为了确保图像看起来像集合 B 的分布; 它不需要与集合 A 相关。为了强加这种相关性,CycleGAN 引入了循环。
然后,从 A2B 生成的图像会通过另一个生成器 B2A,以获得 Cyclic_A。 施加到 Cyclic_A 的损耗是 CycleGAN 的关键部分。 在这里,我们尝试减小 Cyclic_A 和 Input_A 之间的距离。 第二个损失背后的想法是,第二个生成器必须能够生成马,因为我们开始时的分布是马。 如果 A2B 知道如何将马匹映射到斑马而不改变图片中的任何其他内容,并且如果 B2A 知道如何将斑马线映射到匹马而不改变图片中的其他任何东西,那么我们对损失所做的假设应该是正确的。 然后,从 A2B 生成的图像会通过另一个生成器 B2A,以获得 Cyclic_A。 施加到 Cyclic_A 的损耗是 CycleGAN 的关键部分。 在这里,我们尝试减小 Cyclic_A 和 Input_A 之间的距离。 第二个损失背后的想法是,第二个生成器必须能够生成马,因为我们开始时的分布是马。 如果 A2B 知道如何将马匹映射到斑马而不改变图片中的任何其他内容,并且如果 B2A 知道如何将斑马线映射到匹马而不改变图片中的其他任何东西,那么我们对损失所做的假设应该是正确的。
...@@ -553,9 +553,9 @@ CycleGAN 学习每种分布的模式,并尝试将图像从一种分布映射 ...@@ -553,9 +553,9 @@ CycleGAN 学习每种分布的模式,并尝试将图像从一种分布映射
图 6.19:CycleGAN 架构 图 6.19:CycleGAN 架构
当判别器 A 获得马的真实图像时,判别器 B 从 A2B 获得斑马的生成图像,当判别器 B 获得斑马的真实图像时,判别器 A 从 B2A 获得马的生成图像。 要注意的一点是,鉴别器 A 总是能够预测图像是否来自马具,而鉴别器 B 总是能够预测图像是否来自斑马具。 同样,A2B 始终负责将马集合映射到斑马分布,而 B2A 始终负责将斑马集合映射到马分布。 当判别器 A 获得马的真实图像时,判别器 B 从 A2B 获得斑马的生成图像,当判别器 B 获得斑马的真实图像时,判别器 A 从 B2A 获得马的生成图像。 要注意的一点是,判别器 A 总是能够预测图像是否来自马具,而判别器 B 总是能够预测图像是否来自斑马具。 同样,A2B 始终负责将马集合映射到斑马分布,而 B2A 始终负责将斑马集合映射到马分布。
生成器和别器的这种周期性训练可确保网络学会使用模式变化来映射图像,但图像的所有其他特征均保持不变。 生成器和别器的这种周期性训练可确保网络学会使用模式变化来映射图像,但图像的所有其他特征均保持不变。
```py ```py
Generator( Generator(
...@@ -594,7 +594,7 @@ Generator( ...@@ -594,7 +594,7 @@ Generator(
PyTorch 为用户提供了进入网络并进行操作的完全灵活性。 其中一部分是将模型打印到终端上,以显示其中包含所有模块的地形排序图。 PyTorch 为用户提供了进入网络并进行操作的完全灵活性。 其中一部分是将模型打印到终端上,以显示其中包含所有模块的地形排序图。
之前我们在 CycleGAN 中看到了发电机的图形。 与我们探讨的第一个简单 GAN 不同,A2B 和 B2A 都具有相同的内部结构,内部具有卷积。 整个生成器都包装在以`ReflectionPad2D`开头的单个序列模块中。 之前我们在 CycleGAN 中看到了生成器的图形。 与我们探讨的第一个简单 GAN 不同,A2B 和 B2A 都具有相同的内部结构,内部具有卷积。 整个生成器都包装在以`ReflectionPad2D`开头的单个序列模块中。
反射填充涉及填充输入的边界,跳过批量尺寸和通道尺寸。 填充之后是典型的卷积模块布置,即二维卷积。 反射填充涉及填充输入的边界,跳过批量尺寸和通道尺寸。 填充之后是典型的卷积模块布置,即二维卷积。
......
...@@ -25,7 +25,7 @@ PyTorch 的核心是提供两个主要功能: ...@@ -25,7 +25,7 @@ PyTorch 的核心是提供两个主要功能:
* [自动微分](#autograd) * [自动微分](#autograd)
* [PyTorch:张量和自定等级](#pytorch-tensors-and-autograd) * [PyTorch:张量和自定等级](#pytorch-tensors-and-autograd)
* [PyTorch:定义新的 autograd 功能](#pytorch-defining-new-autograd-functions) * [PyTorch:定义新的 autograd 功能](#pytorch-defining-new-autograd-functions)
* [<cite>nn</cite> 模块](#nn-module) * [nn 模块](#nn-module)
* [PyTorch:nn](#pytorch-nn) * [PyTorch:nn](#pytorch-nn)
* [PyTorch:最佳](#pytorch-optim) * [PyTorch:最佳](#pytorch-optim)
* [PyTorch:自定义 nn 模块](#pytorch-custom-nn-modules) * [PyTorch:自定义 nn 模块](#pytorch-custom-nn-modules)
...@@ -33,7 +33,7 @@ PyTorch 的核心是提供两个主要功能: ...@@ -33,7 +33,7 @@ PyTorch 的核心是提供两个主要功能:
* [范例](#examples) * [范例](#examples)
* [张量](#id1) * [张量](#id1)
* [自动微分](#id2) * [自动微分](#id2)
* [<cite>nn</cite> 模块](#id3) * [nn 模块](#id3)
## [张量](#id12) ## [张量](#id12)
...@@ -311,7 +311,7 @@ print(f'Result: y = {a.item()} + {b.item()} * P3({c.item()} + {d.item()} x)') ...@@ -311,7 +311,7 @@ print(f'Result: y = {a.item()} + {b.item()} * P3({c.item()} + {d.item()} x)')
``` ```
## [<cite>nn</cite> 模块](#id18) ## [nn 模块](#id18)
### [PyTorch:nn](#id19) ### [PyTorch:nn](#id19)
...@@ -630,7 +630,7 @@ print(f'Result: {model.string()}') ...@@ -630,7 +630,7 @@ print(f'Result: {model.string()}')
[PyTorch:定义新的 autograd 函数](examples_autograd/polynomial_custom_function.html#sphx-glr-beginner-examples-autograd-polynomial-custom-function-py) [PyTorch:定义新的 autograd 函数](examples_autograd/polynomial_custom_function.html#sphx-glr-beginner-examples-autograd-polynomial-custom-function-py)
### [<cite>nn</cite> 模块](#id26) ### [nn 模块](#id26)
![../_img/sphx_glr_polynomial_nn_thumb.png](img/335fb81e535f98bfda7cbdb3e50d8832.png) ![../_img/sphx_glr_polynomial_nn_thumb.png](img/335fb81e535f98bfda7cbdb3e50d8832.png)
......
...@@ -117,7 +117,7 @@ bias = torch.zeros(10, requires_grad=True) ...@@ -117,7 +117,7 @@ bias = torch.zeros(10, requires_grad=True)
``` ```
由于 PyTorch 具有自动计算梯度的功能,我们可以将任何标准的 Python 函数(或可调用对象)用作模型! 因此,我们只需编写一个普通矩阵乘法和广播加法即可创建一个简单的线性模型。 我们还需要激活函数,因此我们将编写并使用 <cite>log_softmax</cite> 。 请记住:尽管 PyTorch 提供了许多预写的损失函数,激活函数等,但是您可以使用纯 Python 轻松编写自己的函数。 PyTorch 甚至会自动为您的功能创建快速 GPU 或向量化的 CPU 代码。 由于 PyTorch 具有自动计算梯度的功能,我们可以将任何标准的 Python 函数(或可调用对象)用作模型! 因此,我们只需编写一个普通矩阵乘法和广播加法即可创建一个简单的线性模型。 我们还需要激活函数,因此我们将编写并使用 log_softmax 。 请记住:尽管 PyTorch 提供了许多预写的损失函数,激活函数等,但是您可以使用纯 Python 轻松编写自己的函数。 PyTorch 甚至会自动为您的功能创建快速 GPU 或向量化的 CPU 代码。
```py ```py
def log_softmax(x): def log_softmax(x):
...@@ -207,7 +207,7 @@ tensor(0.0938) ...@@ -207,7 +207,7 @@ tensor(0.0938)
现在,我们使用这些梯度来更新权重和偏差。 我们在`torch.no_grad()`上下文管理器中执行此操作,因为我们不希望在下一步的梯度计算中记录这些操作。 您可以在上阅读有关 PyTorch 的 Autograd 如何记录操作[的更多信息。](https://pytorch.org/docs/stable/notes/autograd.html) 现在,我们使用这些梯度来更新权重和偏差。 我们在`torch.no_grad()`上下文管理器中执行此操作,因为我们不希望在下一步的梯度计算中记录这些操作。 您可以在上阅读有关 PyTorch 的 Autograd 如何记录操作[的更多信息。](https://pytorch.org/docs/stable/notes/autograd.html)
然后,将梯度设置为零,以便为下一个循环做好准备。 否则,我们的梯度会记录所有已发生操作的运行记录(即`loss.backward()` *将*梯度添加到已存储的内容中,而不是替换它们)。 然后,将梯度设置为零,以便为下一个循环做好准备。 否则,我们的梯度会记录所有已发生操作的运行记录(即`loss.backward()`梯度添加到已存储的内容中,而不是替换它们)。
小费 小费
...@@ -608,7 +608,7 @@ tensor(0.0821, grad_fn=<NllLossBackward>) ...@@ -608,7 +608,7 @@ tensor(0.0821, grad_fn=<NllLossBackward>)
## 添加验证 ## 添加验证
在第 1 节中,我们只是试图建立一个合理的训练循环以用于我们的训练数据。 实际上,您**始终**也应该具有[验证集](https://www.fast.ai/2017/11/13/validation-sets/),以便识别您是否过拟合。 在第 1 节中,我们只是试图建立一个合理的训练循环以用于我们的训练数据。 实际上,您也应该**始终**具有[验证集](https://www.fast.ai/2017/11/13/validation-sets/),以便识别您是否过拟合。
对训练数据进行改组对于[很重要,](https://www.quora.com/Does-the-order-of-training-data-matter-when-training-neural-networks)对于防止批量与过拟合之间的相关性很重要。 另一方面,无论我们是否改组验证集,验证损失都是相同的。 由于改组需要花费更多时间,因此改组验证数据没有任何意义。 对训练数据进行改组对于[很重要,](https://www.quora.com/Does-the-order-of-training-data-matter-when-training-neural-networks)对于防止批量与过拟合之间的相关性很重要。 另一方面,无论我们是否改组验证集,验证损失都是相同的。 由于改组需要花费更多时间,因此改组验证数据没有任何意义。
...@@ -775,7 +775,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl) ...@@ -775,7 +775,7 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
`torch.nn`还有另一个方便的类,可以用来简化我们的代码: [Sequential](https://pytorch.org/docs/stable/nn.html#torch.nn.Sequential)`Sequential`对象以顺序方式运行其中包含的每个模块。 这是编写神经网络的一种简单方法。 `torch.nn`还有另一个方便的类,可以用来简化我们的代码: [Sequential](https://pytorch.org/docs/stable/nn.html#torch.nn.Sequential)`Sequential`对象以顺序方式运行其中包含的每个模块。 这是编写神经网络的一种简单方法。
为了利用这一点,我们需要能够从给定的函数轻松定义**自定义层**。 例如,PyTorch 没有<cite>视图</cite>层,我们需要为我们的网络创建一个层。 `Lambda`将创建一个层,然后在使用`Sequential`定义网络时可以使用该层。 为了利用这一点,我们需要能够从给定的函数轻松定义**自定义层**。 例如,PyTorch 没有视图层,我们需要为我们的网络创建一个层。 `Lambda`将创建一个层,然后在使用`Sequential`定义网络时可以使用该层。
```py ```py
class Lambda(nn.Module): class Lambda(nn.Module):
...@@ -948,19 +948,19 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl) ...@@ -948,19 +948,19 @@ fit(epochs, model, loss_func, opt, train_dl, valid_dl)
## 结束语 ## 结束语
现在,我们有了一个通用的数据管道和训练循环,您可以将其用于使用 Pytorch 训练许多类型的模型。 要了解现在可以轻松进行模型训练,请查看 <cite>mnist_sample</cite> 示例笔记本。 现在,我们有了一个通用的数据管道和训练循环,您可以将其用于使用 Pytorch 训练许多类型的模型。 要了解现在可以轻松进行模型训练,请查看 mnist_sample 示例笔记本。
当然,您需要添加很多内容,例如数据增强,超参数调整,监控训练,迁移学习等。 这些功能可在 fastai 库中使用,该库是使用本教程中所示的相同设计方法开发的,为希望进一步推广其模型的从业人员提供了自然的下一步。 当然,您需要添加很多内容,例如数据增强,超参数调整,监控训练,迁移学习等。 这些功能可在 fastai 库中使用,该库是使用本教程中所示的相同设计方法开发的,为希望进一步推广其模型的从业人员提供了自然的下一步。
我们承诺在本教程开始时将通过示例分别说明`torch.nn``torch.optim``Dataset``DataLoader`。 因此,让我们总结一下我们所看到的: 我们承诺在本教程开始时将通过示例分别说明`torch.nn``torch.optim``Dataset``DataLoader`。 因此,让我们总结一下我们所看到的:
> * **torch.nn** > * `torch.nn`
> * `Module` :创建一个行为类似于函数的可调用对象,但也可以包含状态(例如神经网络层权重)。 它知道其中包含的 `Parameter` ,并且可以将其所有坡度归零,遍历它们以进行权重更新等。 > * `Module`:创建一个行为类似于函数的可调用对象,但也可以包含状态(例如神经网络层权重)。 它知道其中包含的 `Parameter` ,并且可以将其所有坡度归零,遍历它们以进行权重更新等。
> * `Parameter` :张量的包装器,用于告知 `Module` 具有在反向传播期间需要更新的权重。 仅更新具有 <cite>require_grad</cite> 属性集的张量 > * `Parameter`:张量的包装器,用于告知 `Module` 具有在反向传播期间需要更新的权重。 仅更新具有 require_grad 属性集的张量
> * `functional` :一个模块(通常按照惯例导入到 `F` 名称空间中),其中包含激活函数,损失函数等。 以及卷积和线性层等层的无状态版本。 > * `functional`:一个模块(通常按照惯例导入到 `F` 名称空间中),其中包含激活函数,损失函数等。 以及卷积和线性层等层的无状态版本。
> * `torch.optim` :包含诸如 `SGD` 的优化程序,这些优化程序在后退步骤 > * `torch.optim`:包含诸如 `SGD` 的优化程序,这些优化程序在后退步骤
> * `Dataset` 中更新 `Parameter` 的权重。 具有 `__len__` 和 `__getitem__` 的对象,包括 Pytorch 提供的类,例如 `TensorDataset` > * `Dataset` 中更新 `Parameter` 的权重。 具有 `__len__` 和 `__getitem__` 的对象,包括 Pytorch 提供的类,例如 `TensorDataset`
> * `DataLoader` :获取任何 `Dataset` 并创建一个迭代器,该迭代器返回批量数据。 > * `DataLoader`:获取任何 `Dataset` 并创建一个迭代器,该迭代器返回批量数据。
**脚本的总运行时间**:(0 分钟 57.062 秒) **脚本的总运行时间**:(0 分钟 57.062 秒)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
> 原文:<https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html> > 原文:<https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html>
[60 分钟闪电战](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)中,我们向您展示了如何加载数据,如何通过定义为`nn.Module`子类的模型提供数据,如何在训练数据上训练该模型以及在测试数据上对其进行测试。 为了了解发生的情况,我们在模型训练期间打印一些统计数据,以了解训练是否在进行中。 但是,我们可以做得更好:PyTorch 与 TensorBoard 集成在一起,TensorBoard 是一种工具,用于可视化神经网络训练运行的结果。 本教程使用 [Fashion-MNIST 数据集](https://github.com/zalandoresearch/fashion-mnist)说明了其某些功能,可以使用 <cite>torchvision.datasets</cite> 将其读入 PyTorch。 [60 分钟闪电战](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)中,我们向您展示了如何加载数据,如何通过定义为`nn.Module`子类的模型提供数据,如何在训练数据上训练该模型以及在测试数据上对其进行测试。 为了了解发生的情况,我们在模型训练期间打印一些统计数据,以了解训练是否在进行中。 但是,我们可以做得更好:PyTorch 与 TensorBoard 集成在一起,TensorBoard 是一种工具,用于可视化神经网络训练运行的结果。 本教程使用 [Fashion-MNIST 数据集](https://github.com/zalandoresearch/fashion-mnist)说明了其某些功能,可以使用 torchvision.datasets 将其读入 PyTorch。
在本教程中,我们将学习如何: 在本教程中,我们将学习如何:
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
`pip install git+https://github.com/gautamchitnis/cocoapi.git@cocodataset-master#subdirectory=PythonAPI` `pip install git+https://github.com/gautamchitnis/cocoapi.git@cocodataset-master#subdirectory=PythonAPI`
关于`labels`的注释。 该模型将`0`类作为背景。 如果您的数据集不包含背景类,则`labels`中不应包含`0`。 例如,假设您只有*猫**狗*两类,则可以定义`1`(不是`0`)来表示*猫*和[HTG6 代表*狗*。 因此,例如,如果其中一个图像同时具有两个类,则您的`labels`张量应类似于`[1,2]` 关于`labels`的注释。 该模型将`0`类作为背景。 如果您的数据集不包含背景类,则`labels`中不应包含`0`。 例如,假设您只有*猫**狗*两类,则可以定义`1`来表示*猫*`0`代表*狗*。 因此,例如,如果其中一个图像同时具有两个类,则您的`labels`张量应类似于`[1,2]`
此外,如果要在训练过程中使用宽高比分组(以便每个批量仅包含具有相似长宽比的图像),则建议您还实施`get_height_and_width`方法,该方法返回图像的高度和宽度。 如果未提供此方法,我们将通过`__getitem__`查询数据集的所有元素,这会将图像加载到内存中,并且比提供自定义方法慢。 此外,如果要在训练过程中使用宽高比分组(以便每个批量仅包含具有相似长宽比的图像),则建议您还实施`get_height_and_width`方法,该方法返回图像的高度和宽度。 如果未提供此方法,我们将通过`__getitem__`查询数据集的所有元素,这会将图像加载到内存中,并且比提供自定义方法慢。
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
这两个主要的迁移学习方案如下所示: 这两个主要的迁移学习方案如下所示:
* **对卷积网络进行微调**:代替随机初始化,我们使用经过预训练的网络初始化网络,例如在 imagenet 1000 数据集上进行训练的网络。 其余的训练照常进行。 * **卷积网络的微调**:代替随机初始化,我们使用经过预训练的网络初始化网络,例如在 imagenet 1000 数据集上进行训练的网络。 其余的训练照常进行。
* **作为固定特征提取器的 ConvNet**:在这里,我们将冻结除最终全连接层之外的所有网络的权重。 最后一个全连接层将替换为具有随机权重的新层,并且仅训练该层。 * **作为固定特征提取器的 ConvNet**:在这里,我们将冻结除最终全连接层之外的所有网络的权重。 最后一个全连接层将替换为具有随机权重的新层,并且仅训练该层。
```py ```py
......
...@@ -6,23 +6,23 @@ ...@@ -6,23 +6,23 @@
如果您正在阅读本文,希望您能体会到某些机器学习模型的有效性。 研究不断推动 ML 模型更快,更准确和更高效。 但是,设计和训练模型的一个经常被忽略的方面是安全性和鲁棒性,尤其是在面对想要欺骗模型的对手的情况下。 如果您正在阅读本文,希望您能体会到某些机器学习模型的有效性。 研究不断推动 ML 模型更快,更准确和更高效。 但是,设计和训练模型的一个经常被忽略的方面是安全性和鲁棒性,尤其是在面对想要欺骗模型的对手的情况下。
本教程将提高您对 ML 模型的安全漏洞的认识,并深入了解对抗性机器学习的热门话题。 您可能会惊讶地发现,在图像*上添加无法察觉的扰动会导致*导致完全不同的模型性能。 鉴于这是一个教程,我们将通过图像分类器上的示例来探讨该主题。 具体而言,我们将使用最流行的一种攻击方法,即快速梯度符号攻击(FGSM)来欺骗 MNIST 分类器。 本教程将提高您对 ML 模型的安全漏洞的认识,并深入了解对抗性机器学习的热门话题。 您可能会惊讶地发现,*在图像上添加无法察觉的扰动会导致完全不同的模型表现*。 鉴于这是一个教程,我们将通过图像分类器上的示例来探讨该主题。 具体而言,我们将使用最流行的一种攻击方法,即快速梯度符号攻击(FGSM)来欺骗 MNIST 分类器。
## 威胁模型 ## 威胁模型
就上下文而言,有多种类型的对抗性攻击,每种攻击者的目标和假设都不同。 但是,总的来说,总体目标是向输入数据添加最少的扰动,以引起所需的错误分类。 攻击者的知识有几种假设,其中两种是:**白盒****黑盒***白盒*攻击假定攻击者具有完全的知识并可以访问模型,包括架构,输入,输出和权重。 *黑匣子*攻击假定攻击者只能访问模型的输入和输出,并且对底层架构或权重一无所知。 目标也有几种类型,包括**错误分类****源/目标错误分类***错误分类的目标是*,这意味着对手只希望输出分类错误,而不在乎新分类是什么。 *源/目标错误分类*意味着对手想要更改最初属于特定源类别的图像,以便将其分类为特定目标类别。 就上下文而言,有多种类型的对抗性攻击,每种攻击者的目标和假设都不同。 但是,总的来说,总体目标是向输入数据添加最少的扰动,以引起所需的错误分类。 攻击者的知识有几种假设,其中两种是:**白盒****黑盒***白盒*攻击假定攻击者具有完全的知识并可以访问模型,包括架构,输入,输出和权重。 *黑盒*攻击假定攻击者只能访问模型的输入和输出,并且对底层架构或权重一无所知。 目标也有几种类型,包括**错误分类****源/目标错误分类***错误分类*意味着对手只希望输出分类错误,而不在乎新分类是什么。 *源/目标错误分类*意味着对手想要更改最初属于特定源类别的图像,以便将其分类为特定目标类别。
在这种情况下,FGSM 攻击是*白盒*攻击,目标是*错误分类*。 有了这些背景信息,我们现在可以详细讨论攻击了。 在这种情况下,FGSM 攻击是*白盒*攻击,目标是*错误分类*。 有了这些背景信息,我们现在可以详细讨论攻击了。
## 快速梯度符号攻击 ## 快速梯度符号攻击
迄今为止,最早的也是最流行的对抗性攻击之一被称为*快速梯度符号攻击(FGSM)*,由 Goodfellow 等描述。 等 [中的解释和利用对抗性示例](https://arxiv.org/abs/1412.6572)。 攻击非常强大,而且直观。 它旨在利用神经网络的学习方式*梯度*来攻击神经网络。 这个想法很简单,不是通过基于反向传播的梯度来调整权重来使损失最小化,攻击*会基于相同的反向传播的梯度来调整输入数据以使损失*最大化。 换句话说,攻击使用损失损失的斜率而不输入数据,然后调整输入数据以使损失最大化。 迄今为止,最早的也是最流行的对抗性攻击之一被称为*快速梯度符号攻击(FGSM)*,由 Goodfellow 等描述。 等 [中的解释和利用对抗性示例](https://arxiv.org/abs/1412.6572)。 攻击非常强大,而且直观。 它旨在利用神经网络学习*梯度*的方式来攻击神经网络。 这个想法很简单,不是通过基于反向传播的梯度来调整权重来使损失最小化,攻击会基于相同的反向传播的梯度来调整输入数据,以使损失最大化。 换句话说,攻击使用损失相对于输入数据的梯度,然后调整输入数据以使损失最大化。
在进入代码之前,让我们看一下著名的 [FGSM](https://arxiv.org/abs/1412.6572) 熊猫示例,并提取一些符号。 在进入代码之前,让我们看一下著名的 [FGSM](https://arxiv.org/abs/1412.6572) 熊猫示例,并提取一些符号。
![fgsm_panda_image](img/d74012096c3134b776b5e9f70e8178f3.png) ![fgsm_panda_image](img/d74012096c3134b776b5e9f70e8178f3.png)
从图中,\(\ mathbf {x} \)是正确分类为“熊猫”的原始输入图像,\(y \)\(\ mathbf {x} \)\(\ mathbf {\ theta} \)表示模型参数,而\(J(\ mathbf {\ theta},\ mathbf {x},y)\)是用于训练网络的损耗。 攻击会将梯度反向传播回输入数据,以计算\(\ nabla_ {x} J(\ mathbf {\ theta},\ mathbf {x},y)\)。 然后,它会沿方向(即\(sign(\ nabla_ {x} J(\ mathbf {\ theta}})沿一小步(图片中的\(\ epsilon \)\(0.007 \))调整输入数据 ,\ mathbf {x},y))\))),这将使损失最大化。 然后,当目标图像仍明显是“熊猫”时,目标网络将它们*误分类为“长臂猿”。* 从图中,\(\ mathbf {x} \)是正确分类为“熊猫”的原始输入图像,\(y \)\(\ mathbf {x} \)\(\ mathbf {\ theta} \)表示模型参数,而\(J(\ mathbf {\ theta},\ mathbf {x},y)\)是用于训练网络的损耗。 攻击会将梯度反向传播回输入数据,以计算\(\ nabla_ {x} J(\ mathbf {\ theta},\ mathbf {x},y)\)。 然后,它会沿方向(即\(sign(\ nabla_ {x} J(\ mathbf {\ theta}})沿一小步(图片中的\(\ epsilon \)\(0.007 \))调整输入数据 ,\ mathbf {x},y))\))),这将使损失最大化。 然后,当目标图像仍明显是“熊猫”时,目标网络将它们误分类为“长臂猿”。
希望本教程的动机已经明确,所以让我们跳入实施过程。 希望本教程的动机已经明确,所以让我们跳入实施过程。
...@@ -46,9 +46,9 @@ import matplotlib.pyplot as plt ...@@ -46,9 +46,9 @@ import matplotlib.pyplot as plt
本教程只有三个输入,定义如下: 本教程只有三个输入,定义如下:
* **epsilons** -用于运行的 epsilon 值列表。 在列表中保留 0 很重要,因为它表示原始测试集上的模型性能。 同样,从直觉上讲,我们期望ε越大,扰动越明显,但是从降低模型准确率的角度来看,攻击越有效。 由于此处的数据范围为\([0,1] \),因此 epsilon 值不得超过 1。 * `epsilons`-用于运行的 epsilon 值列表。 在列表中保留 0 很重要,因为它表示原始测试集上的模型性能。 同样,从直觉上讲,我们期望ε越大,扰动越明显,但是从降低模型准确率的角度来看,攻击越有效。 由于此处的数据范围为\([0,1] \),因此 epsilon 值不得超过 1。
* **pretrained_model** -到使用 [pytorch / examples / mnist](https://github.com/pytorch/examples/tree/master/mnist) 训练的 MNIST 模型的路径。 为简单起见,请在此处下载预训练模型[](https://drive.google.com/drive/folders/1fn83DF14tWmit0RTKWRhPq5uVXt73e0h?usp=sharing) * `pretrained_model`-到使用 [pytorch / examples / mnist](https://github.com/pytorch/examples/tree/master/mnist) 训练的 MNIST 模型的路径。 为简单起见,请在此处下载预训练模型[](https://drive.google.com/drive/folders/1fn83DF14tWmit0RTKWRhPq5uVXt73e0h?usp=sharing)
* **use_cuda** -布尔标志,如果需要和可用,则使用 CUDA。 请注意,具有 CUDA 的 GPU 在本教程中并不重要,因为 CPU 不会花费很多时间。 * `use_cuda`-布尔标志,如果需要和可用,则使用 CUDA。 请注意,具有 CUDA 的 GPU 在本教程中并不重要,因为 CPU 不会花费很多时间。
```py ```py
epsilons = [0, .05, .1, .15, .2, .25, .3] epsilons = [0, .05, .1, .15, .2, .25, .3]
...@@ -122,7 +122,7 @@ CUDA Available: True ...@@ -122,7 +122,7 @@ CUDA Available: True
### FGSM 攻击 ### FGSM 攻击
现在,我们可以通过干扰原始输入来定义创建对抗示例的函数。 `fgsm_attack`函数接受三个输入,*图像*是原始的干净图像(\(x \)), *epsilon* 是像素级扰动量(\(\ epsilon \) ), *data_grad* 是输入图像损失的梯度(\(\ nabla_ {x} J(\ mathbf {\ theta},\ mathbf {x},y)\))。 该函数然后创建扰动图像为 现在,我们可以通过干扰原始输入来定义创建对抗示例的函数。 `fgsm_attack`函数接受三个输入,`image`是原始的干净图像(\(x \)),`epsilon`是像素级扰动量(\(\ epsilon \) ),`data_grad`是输入图像损失的梯度(\(\ nabla_ {x} J(\ mathbf {\ theta},\ mathbf {x},y)\))。 该函数然后创建扰动图像为
\[perturbed\_image = image + epsilon*sign(data\_grad) = x + \epsilon * sign(\nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y))\] \[perturbed\_image = image + epsilon*sign(data\_grad) = x + \epsilon * sign(\nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y))\]
...@@ -144,7 +144,7 @@ def fgsm_attack(image, epsilon, data_grad): ...@@ -144,7 +144,7 @@ def fgsm_attack(image, epsilon, data_grad):
### 测试功能 ### 测试功能
最后,本教程的主要结果来自`test`函数。 每次调用此测试功能都会在 MNIST 测试集上执行完整的测试步骤,并报告最终精度。 但是,请注意,此功能还需要 *epsilon* 输入。 这是因为`test`函数报告实力为\(\ epsilon \)的来自对手的攻击模型的准确率。 更具体地说,对于测试集中的每个样本,函数都会计算输入数据(\(data \ _grad \))的损耗梯度,并使用`fgsm_attack`创建一个扰动图像(\(perturbed \ _data \)) ,然后检查受干扰的示例是否具有对抗性。 除了测试模型的准确率外,该功能还保存并返回了一些成功的对抗示例,以供以后可视化。 最后,本教程的主要结果来自`test`函数。 每次调用此测试功能都会在 MNIST 测试集上执行完整的测试步骤,并报告最终精度。 但是,请注意,此功能还需要`epsilon`输入。 这是因为`test`函数报告实力为\(\ epsilon \)的来自对手的攻击模型的准确率。 更具体地说,对于测试集中的每个样本,函数都会计算输入数据(\(data \ _grad \))的损耗梯度,并使用`fgsm_attack`创建一个扰动图像(\(perturbed \ _data \)) ,然后检查受干扰的示例是否具有对抗性。 除了测试模型的准确率外,该功能还保存并返回了一些成功的对抗示例,以供以后可视化。
```py ```py
def test( model, device, test_loader, epsilon ): def test( model, device, test_loader, epsilon ):
...@@ -213,7 +213,7 @@ def test( model, device, test_loader, epsilon ): ...@@ -213,7 +213,7 @@ def test( model, device, test_loader, epsilon ):
### 运行攻击 ### 运行攻击
实现的最后一部分是实际运行攻击。 在这里,我们为 *epsilons* 输入中的每个 epsilon 值运行完整的测试步骤。 对于每个 epsilon,我们还保存最终精度,并在接下来的部分中绘制一些成功的对抗示例。 请注意,随着ε值的增加,打印的精度如何降低。 另外,请注意\(\ epsilon = 0 \)表示原始测试准确率,没有受到攻击。 实现的最后一部分是实际运行攻击。 在这里,我们为`epsilon`输入中的每个 epsilon 值运行完整的测试步骤。 对于每个 epsilon,我们还保存最终精度,并在接下来的部分中绘制一些成功的对抗示例。 请注意,随着ε值的增加,打印的精度如何降低。 另外,请注意\(\ epsilon = 0 \)表示原始测试准确率,没有受到攻击。
```py ```py
accuracies = [] accuracies = []
...@@ -262,7 +262,7 @@ plt.show() ...@@ -262,7 +262,7 @@ plt.show()
### 对抗示例样本 ### 对抗示例样本
还记得没有免费午餐的想法吗? 在这种情况下,随着ε的增加,测试精度降低**,但**的扰动变得更容易察觉。 实际上,在攻击者必须考虑的准确率下降和可感知性之间要进行权衡。 在这里,我们展示了每个 epsilon 值下成功对抗示例的一些示例。 绘图的每一行显示不同的ε值。 第一行是\(\ epsilon = 0 \)示例,这些示例表示没有干扰的原始“干净”图像。 每张图片的标题均显示“原始分类->对抗分类”。 注意,扰动在\(\ epsilon = 0.15 \)处开始变得明显,而在\(\ epsilon = 0.3 \)处则非常明显。 但是,在所有情况下,尽管增加了噪音,人类仍然能够识别正确的类别。 还记得没有免费午餐的想法吗? 在这种情况下,随着ε的增加,测试精度降低,但扰动变得更容易察觉。 实际上,在攻击者必须考虑的准确率下降和可感知性之间要进行权衡。 在这里,我们展示了每个 epsilon 值下成功对抗示例的一些示例。 绘图的每一行显示不同的ε值。 第一行是\(\ epsilon = 0 \)示例,这些示例表示没有干扰的原始“干净”图像。 每张图片的标题均显示“原始分类->对抗分类”。 注意,扰动在\(\ epsilon = 0.15 \)处开始变得明显,而在\(\ epsilon = 0.3 \)处则非常明显。 但是,在所有情况下,尽管增加了噪音,人类仍然能够识别正确的类别。
```py ```py
# Plot several examples of adversarial samples at each epsilon # Plot several examples of adversarial samples at each epsilon
...@@ -288,7 +288,7 @@ plt.show() ...@@ -288,7 +288,7 @@ plt.show()
## 接下来要去哪里? ## 接下来要去哪里?
希望本教程对对抗性机器学习主题有所了解。 从这里可以找到许多潜在的方向。 这种攻击代表了对抗性攻击研究的最开始,并且由于随后有许多关于如何攻击和防御来自对手的 ML 模型的想法。 实际上,在 NIPS 2017 上有一个对抗性的攻击和防御竞赛,并且本文描述了该竞赛中使用的许多方法:[对抗性的攻击与防御竞赛](https://arxiv.org/pdf/1804.00097.pdf)。 国防方面的工作还引发了使机器学习模型总体上更健壮*健壮*的想法,以适应自然扰动和对抗性输入。 希望本教程对对抗性机器学习主题有所了解。 从这里可以找到许多潜在的方向。 这种攻击代表了对抗性攻击研究的最开始,并且由于随后有许多关于如何攻击和防御来自对手的 ML 模型的想法。 实际上,在 NIPS 2017 上有一个对抗性的攻击和防御竞赛,并且本文描述了该竞赛中使用的许多方法:[对抗性的攻击与防御竞赛](https://arxiv.org/pdf/1804.00097.pdf)。 防御方面的工作还引发了使机器学习模型总体上更*健壮*的想法,以适应自然扰动和对抗性输入。
另一个方向是不同领域的对抗性攻击和防御。 对抗性研究不仅限于图像领域,请查看[对语音到文本模型的这种](https://arxiv.org/pdf/1801.01944.pdf)攻击。 但是,也许更多地了解对抗性机器学习的最好方法是动手。 尝试实施与 NIPS 2017 竞赛不同的攻击,并查看它与 FGSM 有何不同。 然后,尝试保护模型免受自己的攻击。 另一个方向是不同领域的对抗性攻击和防御。 对抗性研究不仅限于图像领域,请查看[对语音到文本模型的这种](https://arxiv.org/pdf/1801.01944.pdf)攻击。 但是,也许更多地了解对抗性机器学习的最好方法是动手。 尝试实施与 NIPS 2017 竞赛不同的攻击,并查看它与 FGSM 有何不同。 然后,尝试保护模型免受自己的攻击。
......
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
### 什么是 GAN? ### 什么是 GAN?
GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们可以从同一分布中生成新数据。 GAN 由 Ian Goodfellow 于 2014 年发明,并在论文[生成对抗网络](https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)中首次进行了描述。 它们由两个不同的模型组成:*生成器**鉴别器*。 生成器的工作是生成看起来像训练图像的“假”图像。 鉴别器的工作是查看图像并从生成器输出它是真实的训练图像还是伪图像。 在训练过程中,生成器不断尝试通过生成越来越好的伪造品而使鉴别器的性能超过智者,而鉴别器正在努力成为更好的侦探并正确地对真实和伪造图像进行分类。 博弈的平衡点是当生成器生成的伪造品看起来像直接来自训练数据时,而鉴别器则总是猜测生成器输出是真实还是伪造品的 50% 置信度。 GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们可以从同一分布中生成新数据。 GAN 由 Ian Goodfellow 于 2014 年发明,并在论文[生成对抗网络](https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)中首次进行了描述。 它们由两个不同的模型组成:*生成器**判别器*。 生成器的工作是生成看起来像训练图像的“假”图像。 判别器的工作是查看图像并从生成器输出它是真实的训练图像还是伪图像。 在训练过程中,生成器不断尝试通过生成越来越好的伪造品而使判别器的性能超过智者,而判别器正在努力成为更好的侦探并正确地对真实和伪造图像进行分类。 博弈的平衡点是当生成器生成的伪造品看起来像直接来自训练数据时,而判别器则总是猜测生成器输出是真实还是伪造品的 50% 置信度。
现在,让我们从鉴别符开始定义一些在整个教程中使用的符号。 令\(x \)为代表图像的数据。 \(D(x)\)别器网络,其输出\(x \)来自训练数据而不是生成器的(标量)概率。 在这里,由于我们要处理图像,因此\(D(x)\)的输入是 CHW 大小为 3x64x64 的图像。 直观地,当\(x \)来自训练数据时,\(D(x)\)应该为高,而当\(x \)来自发生器时,它应该为低。 \(D(x)\)也可以被认为是传统的二进制分类器。 现在,让我们从鉴别符开始定义一些在整个教程中使用的符号。 令\(x \)为代表图像的数据。 \(D(x)\)别器网络,其输出\(x \)来自训练数据而不是生成器的(标量)概率。 在这里,由于我们要处理图像,因此\(D(x)\)的输入是 CHW 大小为 3x64x64 的图像。 直观地,当\(x \)来自训练数据时,\(D(x)\)应该为高,而当\(x \)来自发生器时,它应该为低。 \(D(x)\)也可以被认为是传统的二进制分类器。
对于生成器的表示法,令\(z \)是从标准正态分布中采样的潜在空间向量。 \(G(z)\)表示将隐向量\(z \)映射到数据空间的生成器函数。 \(G \)的目标是估计训练数据来自(\(p_ {data} \))的分布,以便它可以从该估计分布(\(p_g \))生成假样本。 对于生成器的表示法,令\(z \)是从标准正态分布中采样的潜在空间向量。 \(G(z)\)表示将隐向量\(z \)映射到数据空间的生成器函数。 \(G \)的目标是估计训练数据来自(\(p_ {data} \))的分布,以便它可以从该估计分布(\(p_g \))生成假样本。
...@@ -26,7 +26,7 @@ GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们 ...@@ -26,7 +26,7 @@ GAN 是用于教授 DL 模型以捕获训练数据分布的框架,因此我们
### 什么是 DCGAN? ### 什么是 DCGAN?
DCGAN 是上述 GAN 的直接扩展,不同之处在于,DCGAN 分别在鉴别器和生成器中分别使用卷积和卷积转置层。 它最早由 Radford 等人描述。 等 在论文[中使用深度卷积生成对抗网络](https://arxiv.org/pdf/1511.06434.pdf)进行无监督表示学习。 鉴别器由分层的[卷积](https://pytorch.org/docs/stable/nn.html#torch.nn.Conv2d)层,[批量规范](https://pytorch.org/docs/stable/nn.html#torch.nn.BatchNorm2d)层和 [LeakyReLU](https://pytorch.org/docs/stable/nn.html#torch.nn.LeakyReLU) 激活组成。 输入是 3x64x64 的输入图像,输出是输入来自真实数据分布的标量概率。 生成器由[卷积转置](https://pytorch.org/docs/stable/nn.html#torch.nn.ConvTranspose2d)层,批量规范层和 [ReLU](https://pytorch.org/docs/stable/nn.html#relu) 激活组成。 输入是从标准正态分布中提取的潜向量\(z \),输出是 3x64x64 RGB 图像。 跨步的转置层使潜向量可以转换为具有与图像相同形状的体积。 在本文中,作者还提供了一些有关如何设置优化器,如何计算损失函数以及如何初始化模型权重的提示,所有这些都将在接下来的部分中进行解释。 DCGAN 是上述 GAN 的直接扩展,不同之处在于,DCGAN 分别在判别器和生成器中分别使用卷积和卷积转置层。 它最早由 Radford 等人描述。 等 在论文[中使用深度卷积生成对抗网络](https://arxiv.org/pdf/1511.06434.pdf)进行无监督表示学习。 判别器由分层的[卷积](https://pytorch.org/docs/stable/nn.html#torch.nn.Conv2d)层,[批量规范](https://pytorch.org/docs/stable/nn.html#torch.nn.BatchNorm2d)层和 [LeakyReLU](https://pytorch.org/docs/stable/nn.html#torch.nn.LeakyReLU) 激活组成。 输入是 3x64x64 的输入图像,输出是输入来自真实数据分布的标量概率。 生成器由[卷积转置](https://pytorch.org/docs/stable/nn.html#torch.nn.ConvTranspose2d)层,批量规范层和 [ReLU](https://pytorch.org/docs/stable/nn.html#relu) 激活组成。 输入是从标准正态分布中提取的潜向量\(z \),输出是 3x64x64 RGB 图像。 跨步的转置层使潜向量可以转换为具有与图像相同形状的体积。 在本文中,作者还提供了一些有关如何设置优化器,如何计算损失函数以及如何初始化模型权重的提示,所有这些都将在接下来的部分中进行解释。
```py ```py
from __future__ import print_function from __future__ import print_function
...@@ -68,18 +68,18 @@ Random Seed: 999 ...@@ -68,18 +68,18 @@ Random Seed: 999
让我们为跑步定义一些输入: 让我们为跑步定义一些输入:
* **dataroot** -数据集文件夹根目录的路径。 我们将在下一节中进一步讨论数据集 * `dataroot`-数据集文件夹根目录的路径。 我们将在下一节中进一步讨论数据集
* **worker** -使用 DataLoader 加载数据的工作线程数 * `worker`-使用 DataLoader 加载数据的工作线程数
* **batch_size** -训练中使用的批量大小。 DCGAN 纸使用的批量大小为 128 * `batch_size`-训练中使用的批量大小。 DCGAN 纸使用的批量大小为 128
* **image_size** -用于训练的图像的空间大小。 此实现默认为 64x64。 如果需要其他尺寸,则必须更改 D 和 G 的结构。 有关更多详细信息,请参见此处的[](https://github.com/pytorch/examples/issues/70) * `image_size`-用于训练的图像的空间大小。 此实现默认为 64x64。 如果需要其他尺寸,则必须更改 D 和 G 的结构。 有关更多详细信息,请参见此处的[](https://github.com/pytorch/examples/issues/70)
* **nc** -输入图像中的彩色通道数。 对于彩色图像,这是 3 * `nc`-输入图像中的彩色通道数。 对于彩色图像,这是 3
* **nz** -潜向量的长度 * `nz`-潜向量的长度
* **ngf** -与通过生成器传送的特征图的深度有关 * `ngf`-与通过生成器传送的特征图的深度有关
* **ndf** -设置通过鉴别器传播的特征图的深度 * `ndf`-设置通过判别器传播的特征图的深度
* **num_epochs** -要运行的训练时期数。 训练更长的时间可能会导致更好的结果,但也会花费更长的时间 * `num_epochs`-要运行的训练时期数。 训练更长的时间可能会导致更好的结果,但也会花费更长的时间
* **lr** -训练的学习率。 如 DCGAN 文件中所述,此数字应为 0.0002 * `lr`-训练的学习率。 如 DCGAN 文件中所述,此数字应为 0.0002
* **beta1** -Adam 优化器的 beta1 超参数。 如论文所述,该数字应为 0.5 * `beta1`-Adam 优化器的 beta1 超参数。 如论文所述,该数字应为 0.5
* **ngpu** -可用的 GPU 数量。 如果为 0,则代码将在 CPU 模式下运行。 如果此数字大于 0,它将在该数量的 GPU 上运行 * `ngpu`-可用的 GPU 数量。 如果为 0,则代码将在 CPU 模式下运行。 如果此数字大于 0,它将在该数量的 GPU 上运行
```py ```py
# Root directory for dataset # Root directory for dataset
...@@ -123,7 +123,7 @@ ngpu = 1 ...@@ -123,7 +123,7 @@ ngpu = 1
## 数据 ## 数据
在本教程中,我们将使用 [Celeb-A Faces 数据集](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html),该数据集可在链接的站点或 [Google 云端硬盘](https://drive.google.com/drive/folders/0B7EVK8r0v71pTUZsaXdaSnZBZzg)中下载。 数据集将下载为名为 *img_align_celeba.zip* 的文件。 下载完成后,创建一个名为 *celeba* 的目录,并将 zip 文件解压缩到该目录中。 然后,将此笔记本的*数据根*输入设置为刚创建的 *celeba* 目录。 结果目录结构应为: 在本教程中,我们将使用 [Celeb-A Faces 数据集](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html),该数据集可在链接的站点或 [Google 云端硬盘](https://drive.google.com/drive/folders/0B7EVK8r0v71pTUZsaXdaSnZBZzg)中下载。 数据集将下载为名为`img_align_celeba.zip`的文件。 下载完成后,创建一个名为`celeba`的目录,并将 zip 文件解压缩到该目录中。 然后,将此笔记本的`dataroot `输入设置为刚创建的`celeba`目录。 结果目录结构应为:
```py ```py
/path/to/celeba /path/to/celeba
...@@ -168,7 +168,7 @@ plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding= ...@@ -168,7 +168,7 @@ plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=
## 实现 ## 实现
设置好输入参数并准备好数据集后,我们现在可以进入实现了。 我们将从 weigth 初始化策略开始,然后详细讨论生成器,别器,损失函数和训练循环。 设置好输入参数并准备好数据集后,我们现在可以进入实现了。 我们将从 weigth 初始化策略开始,然后详细讨论生成器,别器,损失函数和训练循环。
### 权重初始化 ### 权重初始化
...@@ -192,7 +192,7 @@ def weights_init(m): ...@@ -192,7 +192,7 @@ def weights_init(m):
![dcgan_generator](img/85974d98be6202902f21ce274418953f.png) ![dcgan_generator](img/85974d98be6202902f21ce274418953f.png)
请注意,我们在输入部分中设置的输入( *nz**ngf**nc* )如何影响代码中的生成器架构。 *nz* 是 z 输入向量的长度, *ngf* 与通过生成器传播的特征图的大小有关, *nc* 是 输出图像中的通道(对于 RGB 图像设置为 3)。 下面是生成器的代码。 请注意,我们在输入部分中设置的输入(`nz``ngf``nc`)如何影响代码中的生成器架构。 `nz`是 z 输入向量的长度,`ngf`与通过生成器传播的特征图的大小有关, `nc`是 输出图像中的通道(对于 RGB 图像设置为 3)。 下面是生成器的代码。
```py ```py
# Generator Code # Generator Code
...@@ -309,7 +309,7 @@ class Discriminator(nn.Module): ...@@ -309,7 +309,7 @@ class Discriminator(nn.Module):
``` ```
现在,与生成器一样,我们可以创建别器,应用`weights_init`函数,并打印模型的结构。 现在,与生成器一样,我们可以创建别器,应用`weights_init`函数,并打印模型的结构。
```py ```py
# Create the Discriminator # Create the Discriminator
...@@ -381,24 +381,24 @@ optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999)) ...@@ -381,24 +381,24 @@ optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
### 训练 ### 训练
最后,既然我们已经定义了 GAN 框架的所有部分,我们就可以对其进行训练。 请注意,训练 GAN 某种程度上是一种艺术形式,因为不正确的超参数设置会导致模式崩溃,而对失败的原因几乎没有解释。 在这里,我们将严格遵循 Goodfellow 论文中的算法 1,同时遵守 [ganhacks](https://github.com/soumith/ganhacks) 中显示的一些最佳做法。 即,我们将“为真实和伪造构建不同的小批量”图像,并调整 G 的目标函数以最大化\(logD(G(z))\)。 训练分为两个主要部分。 第 1 部分更新了别器,第 2 部分更新了生成器。 最后,既然我们已经定义了 GAN 框架的所有部分,我们就可以对其进行训练。 请注意,训练 GAN 某种程度上是一种艺术形式,因为不正确的超参数设置会导致模式崩溃,而对失败的原因几乎没有解释。 在这里,我们将严格遵循 Goodfellow 论文中的算法 1,同时遵守 [ganhacks](https://github.com/soumith/ganhacks) 中显示的一些最佳做法。 即,我们将“为真实和伪造构建不同的小批量”图像,并调整 G 的目标函数以最大化\(logD(G(z))\)。 训练分为两个主要部分。 第 1 部分更新了别器,第 2 部分更新了生成器。
**第 1 部分-训练别器** **第 1 部分-训练别器**
回想一下,训练鉴别器的目的是最大程度地提高将给定输入正确分类为真实或伪造的可能性。 就古德费罗而言,我们希望“通过提高其随机梯度来更新鉴别器”。 实际上,我们要最大化\(log(D(x))+ log(1-D(G(z)))\)。 由于 ganhacks 提出了单独的小批量建议,因此我们将分两步进行计算。 首先,我们将从训练集中构造一批真实样本,向前通过\(D \),计算损失(\(log(D(x(x))\))),然后在向后通过中计算梯度。 其次,我们将使用当前生成器构造一批假样本,将这批伪造通过\(D \),计算损失(\(log(1-D(G(z())))\)),然后*向后累积*梯度。 现在,利用全批量和全批量的累积梯度,我们称之为鉴别器优化程序的一个步骤。 回想一下,训练判别器的目的是最大程度地提高将给定输入正确分类为真实或伪造的可能性。 就古德费罗而言,我们希望“通过提高其随机梯度来更新判别器”。 实际上,我们要最大化\(log(D(x))+ log(1-D(G(z)))\)。 由于 ganhacks 提出了单独的小批量建议,因此我们将分两步进行计算。 首先,我们将从训练集中构造一批真实样本,向前通过\(D \),计算损失(\(log(D(x(x))\))),然后在向后通过中计算梯度。 其次,我们将使用当前生成器构造一批假样本,将这批伪造通过\(D \),计算损失(\(log(1-D(G(z())))\)),然后*反向累积*梯度。 现在,利用全批量和全批量的累积梯度,我们称之为判别器优化程序的一个步骤。
**第 2 部分-训练发电机** **第 2 部分-训练生成器**
如原始论文所述,我们希望通过最小化\(log(1-D(G(z)))\)来训练 Generator,以产生更好的假货。 如前所述,Goodfellow 证明这不能提供足够的梯度,尤其是在学习过程的早期。 作为解决方法,我们希望最大化\(log(D(G(z())))\)。 在代码中,我们通过以下步骤来实现此目的:将第 1 部分的 Generator 输出与 Discriminator 进行分类,使用实数标签 GT 计算 G 的损*,反向计算 G 的梯度,最后使用优化器更新 G 的参数 步。 将真实标签用作损失函数的 GT 标签似乎是违反直觉的,但这使我们可以使用 BCELoss 的\(log(x)\)部分(而不是\(log(1-x) \)部分),这正是我们想要的。* 如原始论文所述,我们希望通过最小化\(log(1-D(G(z)))\)来训练 Generator,以产生更好的假货。 如前所述,Goodfellow 证明这不能提供足够的梯度,尤其是在学习过程的早期。 作为解决方法,我们希望最大化\(log(D(G(z())))\)。 在代码中,我们通过以下步骤来实现此目的:将第 1 部分的 Generator 输出与 Discriminator 进行分类,使用实数标签 GT 计算 G 的损失,反向计算 G 的梯度,最后使用优化器更新 G 的参数 步。 将真实标签用作损失函数的 GT 标签似乎是违反直觉的,但这使我们可以使用 BCELoss 的\(log(x)\)部分(而不是\(log(1-x) \)部分),这正是我们想要的。
最后,我们将进行一些统计报告,并在每个时期结束时,将我们的 fixed_noise 批量推送到生成器中,以直观地跟踪 G 的训练进度。 报告的训练统计数据是: 最后,我们将进行一些统计报告,并在每个时期结束时,将我们的 fixed_noise 批量推送到生成器中,以直观地跟踪 G 的训练进度。 报告的训练统计数据是:
* **Loss_D** -鉴别器损失,计算为所有真实批量和所有假批量的损失总和(\(log(D(x))+ log(D(G(z)))\))。 * `Loss_D`-判别器损失,计算为所有真实批量和所有假批量的损失总和(\(log(D(x))+ log(D(G(z)))\))。
* **Loss_G** -发电机损耗计算为\(log(D(G(z())))\) * `Loss_G`-生成器损耗计算为\(log(D(G(z())))\)
* **D(x)**-所有真实批量的鉴别器的平均输出(整个批量)。 这应该从接近 1 开始,然后在 G 变得更好时理论上收敛到 0.5。 想想这是为什么。 * `D(x)`-所有真实批量的判别器的平均输出(整个批量)。 这应该从接近 1 开始,然后在 G 变得更好时理论上收敛到 0.5。 想想这是为什么。
* **D(G(z))**-所有假批量的平均鉴别器输出。 第一个数字在 D 更新之前,第二个数字在 D 更新之后。 这些数字应从 0 开始,并随着 G 的提高收敛到 0.5。 想想这是为什么。 * `D(G(z))`-所有假批量的平均判别器输出。 第一个数字在 D 更新之前,第二个数字在 D 更新之后。 这些数字应从 0 开始,并随着 G 的提高收敛到 0.5。 想想这是为什么。
**注意:**此步骤可能需要一段时间,具体取决于您运行了多少个纪元以及是否从数据集中删除了一些数据。 **注意**此步骤可能需要一段时间,具体取决于您运行了多少个纪元以及是否从数据集中删除了一些数据。
```py ```py
# Training Loop # Training Loop
...@@ -674,7 +674,7 @@ plt.show() ...@@ -674,7 +674,7 @@ plt.show()
![../_img/sphx_glr_dcgan_faces_tutorial_002.png](img/097cd68a7de6371c697afbe4230ef328.png) ![../_img/sphx_glr_dcgan_faces_tutorial_002.png](img/097cd68a7de6371c697afbe4230ef328.png)
**可视化 G 的进度** **可视化`G`的进度**
请记住,在每次训练之后,我们如何将生成器的输出保存为 fixed_noise 批量。 现在,我们可以用动画形象化 G 的训练进度。 按下播放按钮开始动画。 请记住,在每次训练之后,我们如何将生成器的输出保存为 fixed_noise 批量。 现在,我们可以用动画形象化 G 的训练进度。 按下播放按钮开始动画。
...@@ -691,7 +691,7 @@ HTML(ani.to_jshtml()) ...@@ -691,7 +691,7 @@ HTML(ani.to_jshtml())
![../_img/sphx_glr_dcgan_faces_tutorial_003.png](img/2a31b55ef7bfff0c24c35bc635656078.png) ![../_img/sphx_glr_dcgan_faces_tutorial_003.png](img/2a31b55ef7bfff0c24c35bc635656078.png)
**实像与假像** **真实图像和伪图像**
最后,让我们并排查看一些真实图像和伪图像。 最后,让我们并排查看一些真实图像和伪图像。
......
...@@ -245,7 +245,7 @@ Median relative difference between original and MuLaw reconstucted signals: 1.28 ...@@ -245,7 +245,7 @@ Median relative difference between original and MuLaw reconstucted signals: 1.28
* **lowpass_biquad**:设计双二阶低通滤波器并执行滤波。 * **lowpass_biquad**:设计双二阶低通滤波器并执行滤波。
* **highpass_biquad**:设计双二阶高通滤波器并执行滤波。 * **highpass_biquad**:设计双二阶高通滤波器并执行滤波。
例如,让我们尝试 <cite>mu_law_encoding</cite> 功能: 例如,让我们尝试 mu_law_encoding 功能:
```py ```py
mu_law_encoding_waveform = torchaudio.functional.mu_law_encoding(waveform, quantization_channels=256) mu_law_encoding_waveform = torchaudio.functional.mu_law_encoding(waveform, quantization_channels=256)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
**作者**[Sean Robertson](https://github.com/spro/practical-pytorch) **作者**[Sean Robertson](https://github.com/spro/practical-pytorch)
我们将建立和训练基本的字符级 RNN 对单词进行分类。 本教程与以下两个教程一起,展示了如何“从头开始”进行 NLP 建模的预处理数据,特别是不使用 <cite>torchtext</cite> 的许多便利功能,因此您可以了解如何进行 NLP 建模的预处理 在低水平上工作。 我们将建立和训练基本的字符级 RNN 对单词进行分类。 本教程与以下两个教程一起,展示了如何“从头开始”进行 NLP 建模的预处理数据,特别是不使用 torchtext 的许多便利功能,因此您可以了解如何进行 NLP 建模的预处理 在低水平上工作。
字符级 RNN 将单词作为一系列字符读取-在每个步骤输出预测和“隐藏状态”,将其先前的隐藏状态输入到每个下一步。 我们将最终的预测作为输出,即单词属于哪个类别。 字符级 RNN 将单词作为一系列字符读取-在每个步骤输出预测和“隐藏状态”,将其先前的隐藏状态输入到每个下一步。 我们将最终的预测作为输出,即单词属于哪个类别。
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
**作者**[Sean Robertson](https://github.com/spro/practical-pytorch) **作者**[Sean Robertson](https://github.com/spro/practical-pytorch)
这是我们关于“从零开始的 NLP”的三个教程中的第二个。 在<cite>第一个教程< / intermediate / char_rnn_classification_tutorial ></cite> 中,我们使用了 RNN 将名称分类为源语言。 这次,我们将转过来并使用语言生成名称。 这是我们关于“从零开始的 NLP”的三个教程中的第二个。 在第一个教程< / intermediate / char_rnn_classification_tutorial > 中,我们使用了 RNN 将名称分类为源语言。 这次,我们将转过来并使用语言生成名称。
```py ```py
> python sample.py Russian RUS > python sample.py Russian RUS
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
**作者**[Sean Robertson](https://github.com/spro/practical-pytorch) **作者**[Sean Robertson](https://github.com/spro/practical-pytorch)
这是关于“从头开始进行 NLP”的第三篇也是最后一篇教程,我们在其中编写自己的类和函数来预处理数据以完成 NLP 建模任务。 我们希望在完成本教程后,您将继续学习紧接着本教程的三本教程, <cite>torchtext</cite> 如何为您处理许多此类预处理。 这是关于“从头开始进行 NLP”的第三篇也是最后一篇教程,我们在其中编写自己的类和函数来预处理数据以完成 NLP 建模任务。 我们希望在完成本教程后,您将继续学习紧接着本教程的三本教程, torchtext 如何为您处理许多此类预处理。
在这个项目中,我们将教授将法语翻译成英语的神经网络。 在这个项目中,我们将教授将法语翻译成英语的神经网络。
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
**软件包** **软件包**
首先,让我们导入所需的软件包。 首先,我们需要针对环境的[体育馆](https://gym.openai.com/docs)(使用 <cite>pip install Gym</cite> 进行安装)。 我们还将使用 PyTorch 中的以下内容: 首先,让我们导入所需的软件包。 首先,我们需要针对环境的[体育馆](https://gym.openai.com/docs)(使用 pip install Gym 进行安装)。 我们还将使用 PyTorch 中的以下内容:
* 神经网络(`torch.nn` * 神经网络(`torch.nn`
* 优化(`torch.optim` * 优化(`torch.optim`
...@@ -333,7 +333,7 @@ def optimize_model(): ...@@ -333,7 +333,7 @@ def optimize_model():
在下面,您可以找到主要的训练循环。 首先,我们重置环境并初始化`state` Tensor。 然后,我们采样一个动作,执行它,观察下一个屏幕和奖励(总是 1),并一次优化我们的模型。 当情节结束(我们的模型失败)时,我们重新开始循环。 在下面,您可以找到主要的训练循环。 首先,我们重置环境并初始化`state` Tensor。 然后,我们采样一个动作,执行它,观察下一个屏幕和奖励(总是 1),并一次优化我们的模型。 当情节结束(我们的模型失败)时,我们重新开始循环。
下面,将 <cite>num_episodes</cite> 设置得较小。 您应该下载笔记本并运行更多的片段,例如 300 多个片段,才能显着改善持续时间。 下面,将 num_episodes 设置得较小。 您应该下载笔记本并运行更多的片段,例如 300 多个片段,才能显着改善持续时间。
```py ```py
num_episodes = 50 num_episodes = 50
......
...@@ -105,7 +105,7 @@ def transform_image(image_bytes): ...@@ -105,7 +105,7 @@ def transform_image(image_bytes):
``` ```
上面的方法以字节为单位获取图像数据,应用一系列变换并返回张量。 要测试上述方法,请以字节模式读取图像文件(首先将 <cite>../_static/img/sample_file.jpeg</cite> 替换为计算机上文件的实际路径),然后查看是否获得张量 背部: 上面的方法以字节为单位获取图像数据,应用一系列变换并返回张量。 要测试上述方法,请以字节模式读取图像文件(首先将 ../_static/img/sample_file.jpeg 替换为计算机上文件的实际路径),然后查看是否获得张量 背部:
```py ```py
with open("../_static/img/sample_file.jpeg", 'rb') as f: with open("../_static/img/sample_file.jpeg", 'rb') as f:
...@@ -164,7 +164,7 @@ def get_prediction(image_bytes): ...@@ -164,7 +164,7 @@ def get_prediction(image_bytes):
``` ```
张量`y_hat`将包含预测的类 ID 的索引。 但是,我们需要一个人类可读的类名。 为此,我们需要一个类 ID 来进行名称映射。 将[这个文件](https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json)下载为`imagenet_class_index.json`,并记住它的保存位置(或者,如果您按照本教程中的确切步骤操作,请将其保存在 <cite>tutorials / _static</cite> 中)。 此文件包含 ImageNet 类 ID 到 ImageNet 类名称的映射。 我们将加载此 JSON 文件并获取预测索引的类名称。 张量`y_hat`将包含预测的类 ID 的索引。 但是,我们需要一个人类可读的类名。 为此,我们需要一个类 ID 来进行名称映射。 将[这个文件](https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json)下载为`imagenet_class_index.json`,并记住它的保存位置(或者,如果您按照本教程中的确切步骤操作,请将其保存在 tutorials / _static 中)。 此文件包含 ImageNet 类 ID 到 ImageNet 类名称的映射。 我们将加载此 JSON 文件并获取预测索引的类名称。
```py ```py
import json import json
...@@ -291,7 +291,7 @@ resp = requests.post("http://localhost:5000/predict", ...@@ -291,7 +291,7 @@ resp = requests.post("http://localhost:5000/predict",
``` ```
现在打印 <cite>resp.json()</cite>将显示以下内容: 现在打印 resp.json()将显示以下内容:
```py ```py
{"class_id": "n02124075", "class_name": "Egyptian_cat"} {"class_id": "n02124075", "class_name": "Egyptian_cat"}
......
...@@ -21,14 +21,14 @@ PyTorch 中的命名张量受 [Sasha Rush](https://tech.cornell.edu/people/alexa ...@@ -21,14 +21,14 @@ PyTorch 中的命名张量受 [Sasha Rush](https://tech.cornell.edu/people/alexa
## 基础知识:命名维度 ## 基础知识:命名维度
PyTorch 现在允许张量具有命名尺寸; 工厂函数采用新的<cite>名称</cite>参数,该参数将名称与每个维度相关联。 这适用于大多数工厂功能,例如 PyTorch 现在允许张量具有命名尺寸; 工厂函数采用新的名称参数,该参数将名称与每个维度相关联。 这适用于大多数工厂功能,例如
* <cite>张量</cite> * 张量
* <cite>为空</cite> * 为空
* <cite></cite> *
* <cite></cite> *
* <cite>兰恩</cite> * 兰恩
* <cite>兰特</cite> * 兰特
这里我们用名字构造一个张量: 这里我们用名字构造一个张量:
...@@ -540,7 +540,7 @@ def ignore(): ...@@ -540,7 +540,7 @@ def ignore():
``` ```
`mask`通常具有暗淡`[N, T]`(在自我关注的情况下)或`[N, T, T_key]`(对于编码器注意的情况),而`dot_prod`具有暗淡的`[N, H, T, T_key]`。 为了使`mask``dot_prod`正确广播,我们通常会在自注意的情况下将<cite></cite>调暗`1``-1`压下,在编码器的情况下,我们将`unsqueeze`调暗`unsqueeze` 。 使用命名张量,我们只需使用`align_as``attn_mask``dot_prod`对齐,而不必担心`unsqueeze`变暗的位置。 `mask`通常具有暗淡`[N, T]`(在自我关注的情况下)或`[N, T, T_key]`(对于编码器注意的情况),而`dot_prod`具有暗淡的`[N, H, T, T_key]`。 为了使`mask``dot_prod`正确广播,我们通常会在自注意的情况下将调暗`1``-1`压下,在编码器的情况下,我们将`unsqueeze`调暗`unsqueeze` 。 使用命名张量,我们只需使用`align_as``attn_mask``dot_prod`对齐,而不必担心`unsqueeze`变暗的位置。
**(IV)使用 align_to 和展平**进行更多尺寸操作 **(IV)使用 align_to 和展平**进行更多尺寸操作
......
...@@ -475,7 +475,7 @@ struct Net : torch::nn::Module { ...@@ -475,7 +475,7 @@ struct Net : torch::nn::Module {
#### 什么是 GAN aGAN? #### 什么是 GAN aGAN?
GAN 由两个不同的神经网络模型组成:*生成器**鉴别器*。 生成器从噪声分布中接收样本,其目的是将每个噪声样本转换为类似于目标分布的图像(在我们的情况下为 MNIST 数据集)。 鉴别器又从 MNIST 数据集接收*实际*图像,或从生成器接收*假*图像。 要求发出一个概率来判断特定图像的真实程度(接近`1`)或伪造(接近`0`)。 来自鉴别器的关于由发生器产生的图像有多真实的反馈被用来训练发生器。 鉴别器对真实性有多好的反馈将用于优化鉴别器。 从理论上讲,生成器和鉴别器之间的微妙平衡使它们串联起来得到改善,从而导致生成器生成与目标分布无法区分的图像,从而使鉴别器(那时)的敏锐眼睛冒出了散发`0.5`的真实和真实可能性。 假图片。 对我们来说,最终结果是一台接收噪声作为输入并生成逼真的数字图像作为其输出的机器。 GAN 由两个不同的神经网络模型组成:*生成器**判别器*。 生成器从噪声分布中接收样本,其目的是将每个噪声样本转换为类似于目标分布的图像(在我们的情况下为 MNIST 数据集)。 判别器又从 MNIST 数据集接收*实际*图像,或从生成器接收*假*图像。 要求发出一个概率来判断特定图像的真实程度(接近`1`)或伪造(接近`0`)。 来自判别器的关于由发生器产生的图像有多真实的反馈被用来训练发生器。 判别器对真实性有多好的反馈将用于优化判别器。 从理论上讲,生成器和判别器之间的微妙平衡使它们串联起来得到改善,从而导致生成器生成与目标分布无法区分的图像,从而使判别器(那时)的敏锐眼睛冒出了散发`0.5`的真实和真实可能性。 假图片。 对我们来说,最终结果是一台接收噪声作为输入并生成逼真的数字图像作为其输出的机器。
#### 生成器模块 #### 生成器模块
...@@ -545,7 +545,7 @@ A brief word on the way options are passed to built-in modules like `Conv2d` in ...@@ -545,7 +545,7 @@ A brief word on the way options are passed to built-in modules like `Conv2d` in
The discriminator is similarly a sequence of convolutions, batch normalizations and activations. However, the convolutions are now regular ones instead of transposed, and we use a leaky ReLU with an alpha value of 0.2 instead of a vanilla ReLU. Also, the final activation becomes a Sigmoid, which squashes values into a range between 0 and 1\. We can then interpret these squashed values as the probabilities the discriminator assigns to images being real. The discriminator is similarly a sequence of convolutions, batch normalizations and activations. However, the convolutions are now regular ones instead of transposed, and we use a leaky ReLU with an alpha value of 0.2 instead of a vanilla ReLU. Also, the final activation becomes a Sigmoid, which squashes values into a range between 0 and 1\. We can then interpret these squashed values as the probabilities the discriminator assigns to images being real.
To build the discriminator, we will try something different: a <cite>Sequential</cite> module. Like in Python, PyTorch here provides two APIs for model definition: a functional one where inputs are passed through successive functions (e.g. the generator module example), and a more object-oriented one where we build a <cite>Sequential</cite> module containing the entire model as submodules. Using <cite>Sequential</cite>, the discriminator would look like: To build the discriminator, we will try something different: a Sequential module. Like in Python, PyTorch here provides two APIs for model definition: a functional one where inputs are passed through successive functions (e.g. the generator module example), and a more object-oriented one where we build a Sequential module containing the entire model as submodules. Using Sequential, the discriminator would look like:
```py ```py
nn::Sequential discriminator( nn::Sequential discriminator(
...@@ -662,7 +662,7 @@ Batch size: 64 | Labels: 7 6 5 7 7 5 2 2 4 9 9 4 8 7 4 8 9 4 5 7 1 2 6 9 8 5 1 2 ...@@ -662,7 +662,7 @@ Batch size: 64 | Labels: 7 6 5 7 7 5 2 2 4 9 9 4 8 7 4 8 9 4 5 7 1 2 6 9 8 5 1 2
## 编写训练循环 ## 编写训练循环
现在,让我们完成示例的算法部分,并实现生成器和别器之间的精妙舞蹈。 首先,我们将创建两个优化器,一个用于生成器,一个用于区分器。 我们使用的优化程序实现了 [Adam](https://arxiv.org/pdf/1412.6980.pdf) 算法: 现在,让我们完成示例的算法部分,并实现生成器和别器之间的精妙舞蹈。 首先,我们将创建两个优化器,一个用于生成器,一个用于区分器。 我们使用的优化程序实现了 [Adam](https://arxiv.org/pdf/1412.6980.pdf) 算法:
```py ```py
torch::optim::Adam generator_optimizer( torch::optim::Adam generator_optimizer(
...@@ -722,15 +722,15 @@ for (int64_t epoch = 1; epoch <= kNumberOfEpochs; ++epoch) { ...@@ -722,15 +722,15 @@ for (int64_t epoch = 1; epoch <= kNumberOfEpochs; ++epoch) {
``` ```
上面,我们首先在真实图像上评估别器,为此应为其分配较高的概率。 为此,我们使用`torch::empty(batch.data.size(0)).uniform_(0.8, 1.0)`作为目标概率。 上面,我们首先在真实图像上评估别器,为此应为其分配较高的概率。 为此,我们使用`torch::empty(batch.data.size(0)).uniform_(0.8, 1.0)`作为目标概率。
注意 注意
我们选择均匀分布在 0.8 到 1.0 之间的随机值,而不是各处的 1.0,以使别器训练更可靠。 此技巧称为*标签平滑* 我们选择均匀分布在 0.8 到 1.0 之间的随机值,而不是各处的 1.0,以使别器训练更可靠。 此技巧称为*标签平滑*
在评估鉴别器之前,我们将其参数的梯度归零。 计算完损耗后,我们通过调用`d_loss.backward()`来计算新的梯度,从而在网络中反向传播。 我们对虚假图像重复此步骤。 我们不使用数据集中的图像,而是让生成器通过为它提供一批随机噪声来为此创建伪造图像。 然后,我们将这些伪造图像转发给鉴别器。 这次,我们希望鉴别器发出低概率,最好是全零。 一旦计算了一批真实图像和一批伪造图像的鉴别器损耗,我们就可以一步一步地进行鉴别器的优化程序,以更新其参数。 在评估判别器之前,我们将其参数的梯度归零。 计算完损耗后,我们通过调用`d_loss.backward()`来计算新的梯度,从而在网络中反向传播。 我们对虚假图像重复此步骤。 我们不使用数据集中的图像,而是让生成器通过为它提供一批随机噪声来为此创建伪造图像。 然后,我们将这些伪造图像转发给判别器。 这次,我们希望判别器发出低概率,最好是全零。 一旦计算了一批真实图像和一批伪造图像的判别器损耗,我们就可以一步一步地进行判别器的优化程序,以更新其参数。
为了训练生成器,我们再次首先将其梯度归零,然后在伪图像上重新评估鉴别器。 但是,这一次,我们希望鉴别器将概率分配为非常接近的概率,这将表明生成器可以生成使鉴别器认为它们实际上是真实的图像(来自数据集)。 为此,我们用全部填充`fake_labels`张量。 最后,我们逐步使用生成器的优化器来更新其参数。 为了训练生成器,我们再次首先将其梯度归零,然后在伪图像上重新评估判别器。 但是,这一次,我们希望判别器将概率分配为非常接近的概率,这将表明生成器可以生成使判别器认为它们实际上是真实的图像(来自数据集)。 为此,我们用全部填充`fake_labels`张量。 最后,我们逐步使用生成器的优化器来更新其参数。
现在,我们应该准备在 CPU 上训练我们的模型。 我们还没有任何代码可以捕获状态或示例输出,但是我们稍后会添加。 现在,让我们观察一下我们的模型正在*做某事* –我们稍后将根据生成的图像来验证这是否有意义。 重建和运行应打印如下内容: 现在,我们应该准备在 CPU 上训练我们的模型。 我们还没有任何代码可以捕获状态或示例输出,但是我们稍后会添加。 现在,让我们观察一下我们的模型正在*做某事* –我们稍后将根据生成的图像来验证这是否有意义。 重建和运行应打印如下内容:
......
...@@ -639,7 +639,7 @@ PyTorch C ++扩展工具包提供的 JIT 编译功能可将自定义运算符的 ...@@ -639,7 +639,7 @@ PyTorch C ++扩展工具包提供的 JIT 编译功能可将自定义运算符的
注意 注意
这里的“ JIT 编译”与 TorchScript 编译器中用于优化程序的 JIT 编译无关。 这只是意味着您的自定义运算符 C ++代码将在您首次导入时在系统 <cite>/ tmp</cite> 目录下的文件夹中编译,就像您自己事先对其进行编译一样。 这里的“ JIT 编译”与 TorchScript 编译器中用于优化程序的 JIT 编译无关。 这只是意味着您的自定义运算符 C ++代码将在您首次导入时在系统 / tmp 目录下的文件夹中编译,就像您自己事先对其进行编译一样。
此 JIT 编译功能有两种形式。 首先,您仍然将操作员实现保留在单独的文件(`op.cpp`)中,然后使用`torch.utils.cpp_extension.load()`编译扩展名。 通常,此函数将返回暴露您的 C ++扩展的 Python 模块。 但是,由于我们没有将自定义运算符编译到其自己的 Python 模块中,因此我们只想编译一个普通的共享库。 幸运的是,`torch.utils.cpp_extension.load()`有一个参数`is_python_module`,可以将其设置为`False`,以表明我们仅对构建共享库感兴趣,而对 Python 模块不感兴趣。 然后`torch.utils.cpp_extension.load()`将会编译并将共享库也加载到当前进程中,就像`torch.ops.load_library`之前所做的那样: 此 JIT 编译功能有两种形式。 首先,您仍然将操作员实现保留在单独的文件(`op.cpp`)中,然后使用`torch.utils.cpp_extension.load()`编译扩展名。 通常,此函数将返回暴露您的 C ++扩展的 Python 模块。 但是,由于我们没有将自定义运算符编译到其自己的 Python 模块中,因此我们只想编译一个普通的共享库。 幸运的是,`torch.utils.cpp_extension.load()`有一个参数`is_python_module`,可以将其设置为`False`,以表明我们仅对构建共享库感兴趣,而对 Python 模块不感兴趣。 然后`torch.utils.cpp_extension.load()`将会编译并将共享库也加载到当前进程中,就像`torch.ops.load_library`之前所做的那样:
......
...@@ -181,7 +181,7 @@ custom_class_project/ ...@@ -181,7 +181,7 @@ custom_class_project/
## 从 Python 和 TorchScript 使用 C ++类 ## 从 Python 和 TorchScript 使用 C ++类
现在我们已经将我们的类及其注册编译为`.so`文件,我们可以将 <cite>.so</cite> 加载到 Python 中并进行尝试。 这是一个演示脚本的脚本: 现在我们已经将我们的类及其注册编译为`.so`文件,我们可以将 .so 加载到 Python 中并进行尝试。 这是一个演示脚本的脚本:
```py ```py
import torch import torch
......
...@@ -66,7 +66,7 @@ class LSTMModel(nn.Module): ...@@ -66,7 +66,7 @@ class LSTMModel(nn.Module):
## 2.加载文本数据 ## 2.加载文本数据
接下来,我们再次根据单词模型示例对[预处理](https://github.com/pytorch/examples/blob/master/word_language_model/data.py),将 [Wikitext-2 数据集](https://www.google.com/search?q=wikitext+2+data)加载到<cite>语料库</cite>中。 接下来,我们再次根据单词模型示例对[预处理](https://github.com/pytorch/examples/blob/master/word_language_model/data.py),将 [Wikitext-2 数据集](https://www.google.com/search?q=wikitext+2+data)加载到语料库中。
```py ```py
class Dictionary(object): class Dictionary(object):
......
...@@ -408,7 +408,7 @@ time_model_evaluation(quantized_model, configs, tokenizer) ...@@ -408,7 +408,7 @@ time_model_evaluation(quantized_model, configs, tokenizer)
### 3.3 序列化量化模型 ### 3.3 序列化量化模型
跟踪模型后,我们可以使用 <cite>torch.jit.save</cite> 序列化并保存量化模型,以备将来使用。 跟踪模型后,我们可以使用 torch.jit.save 序列化并保存量化模型,以备将来使用。
```py ```py
input_ids = ids_tensor([8, 128], 2) input_ids = ids_tensor([8, 128], 2)
...@@ -420,7 +420,7 @@ torch.jit.save(traced_model, "bert_traced_eager_quant.pt") ...@@ -420,7 +420,7 @@ torch.jit.save(traced_model, "bert_traced_eager_quant.pt")
``` ```
要加载量化模型,我们可以使用 <cite>torch.jit.load</cite> 要加载量化模型,我们可以使用 torch.jit.load
```py ```py
loaded_quantized_model = torch.jit.load("bert_traced_eager_quant.pt") loaded_quantized_model = torch.jit.load("bert_traced_eager_quant.pt")
......
...@@ -472,7 +472,7 @@ Size (MB): 13.999657 ...@@ -472,7 +472,7 @@ Size (MB): 13.999657
## 4.训练后静态量化 ## 4.训练后静态量化
训练后的静态量化不仅涉及像动态量化中那样将权重从 float 转换为 int,而且还执行额外的步骤,即首先通过网络馈送一批数据并计算不同激活的结果分布(具体来说,这是 通过在记录此数据的不同点插入<cite>观察者</cite>模块来完成)。 然后使用这些分布来确定在推理时如何具体量化不同的激活(一种简单的技术是将整个激活范围简单地划分为 256 个级别,但我们也支持更复杂的方法)。 重要的是,此附加步骤使我们能够在操作之间传递量化值,而不是在每次操作之间将这些值转换为浮点数,然后再转换为整数,从而显着提高了速度。 训练后的静态量化不仅涉及像动态量化中那样将权重从 float 转换为 int,而且还执行额外的步骤,即首先通过网络馈送一批数据并计算不同激活的结果分布(具体来说,这是 通过在记录此数据的不同点插入观察者模块来完成)。 然后使用这些分布来确定在推理时如何具体量化不同的激活(一种简单的技术是将整个激活范围简单地划分为 256 个级别,但我们也支持更复杂的方法)。 重要的是,此附加步骤使我们能够在操作之间传递量化值,而不是在每次操作之间将这些值转换为浮点数,然后再转换为整数,从而显着提高了速度。
```py ```py
num_calibration_batches = 10 num_calibration_batches = 10
......
...@@ -377,7 +377,7 @@ plt.tight_layout() ...@@ -377,7 +377,7 @@ plt.tight_layout()
由于随机初始化,您的结果可能与本教程中显示的结果不同。 由于随机初始化,您的结果可能与本教程中显示的结果不同。
#注意 <cite>quantize = False</cite> model = models.resnet18(pretrained = True,progress = True,quantize = False)num_ftrs = model.fc.in_features #注意 quantize = False model = models.resnet18(pretrained = True,progress = True,quantize = False)num_ftrs = model.fc.in_features
#步骤 1 model.train()model.fuse_model()#步骤 2 model_ft = create_combined_model(model)model_ft [0] .qconfig = torch.quantization.default_qat_qconfig#使用默认 QAT 配置#步骤 3 model_ft = torch.quantization.prepare_qat( model_ft,inplace = True) #步骤 1 model.train()model.fuse_model()#步骤 2 model_ft = create_combined_model(model)model_ft [0] .qconfig = torch.quantization.default_qat_qconfig#使用默认 QAT 配置#步骤 3 model_ft = torch.quantization.prepare_qat( model_ft,inplace = True)
......
...@@ -144,11 +144,11 @@ def run(rank, size): ...@@ -144,11 +144,11 @@ def run(rank, size):
* `dist.scatter(tensor, src, scatter_list, group)`:将\(i ^ {\ text {th}} \)张量`scatter_list[i]`复制到\(i ^ {\ text {th}} \)过程。 * `dist.scatter(tensor, src, scatter_list, group)`:将\(i ^ {\ text {th}} \)张量`scatter_list[i]`复制到\(i ^ {\ text {th}} \)过程。
* `dist.gather(tensor, dst, gather_list, group)`:从`dst`中的所有进程复制`tensor` * `dist.gather(tensor, dst, gather_list, group)`:从`dst`中的所有进程复制`tensor`
* `dist.all_gather(tensor_list, tensor, group)`:将所有进程中的`tensor`从所有进程复制到`tensor_list` * `dist.all_gather(tensor_list, tensor, group)`:将所有进程中的`tensor`从所有进程复制到`tensor_list`
* `dist.barrier(group)`:阻止<cite></cite>中的所有进程,直到每个进程都进入此功能。 * `dist.barrier(group)`:阻止中的所有进程,直到每个进程都进入此功能。
## 分布式训练 ## 分布式训练
**注意:**您可以在此 GitHub 存储库的[中找到本节的示例脚本。](https://github.com/seba-1511/dist_tuto.pth/) **注意**您可以在此 GitHub 存储库的[中找到本节的示例脚本。](https://github.com/seba-1511/dist_tuto.pth/)
现在我们了解了分布式模块的工作原理,让我们用它编写一些有用的东西。 我们的目标是复制 [DistributedDataParallel](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel) 的功能。 当然,这将是一个教学示例,在现实世界中,您应该使用上面链接的经过官方测试,优化的最佳版本。 现在我们了解了分布式模块的工作原理,让我们用它编写一些有用的东西。 我们的目标是复制 [DistributedDataParallel](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel) 的功能。 当然,这将是一个教学示例,在现实世界中,您应该使用上面链接的经过官方测试,优化的最佳版本。
...@@ -255,7 +255,7 @@ def average_gradients(model): ...@@ -255,7 +255,7 @@ def average_gradients(model):
*等*! 我们成功实现了分布式同步 SGD,并且可以在大型计算机集群上训练任何模型。 *等*! 我们成功实现了分布式同步 SGD,并且可以在大型计算机集群上训练任何模型。
**注意:**虽然从技术上来说最后一句话是是正确的,但要实现同步 SGD 的生产级实现,还需要[更多技巧。 同样,请使用经过测试和优化的](https://seba-1511.github.io/dist_blog)[](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel) **注意**虽然从技术上来说最后一句话是是正确的,但要实现同步 SGD 的生产级实现,还需要[更多技巧。 同样,请使用经过测试和优化的](https://seba-1511.github.io/dist_blog)[](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)
### 我们自己的 Ring-Allreduce ### 我们自己的 Ring-Allreduce
......
...@@ -46,7 +46,7 @@ class Policy(nn.Module): ...@@ -46,7 +46,7 @@ class Policy(nn.Module):
``` ```
首先,让我们准备一个帮助程序,以在`RRef`的所有者工作程序上远程运行功能。 您将在本教程的示例中的多个地方发现该功能。 理想情况下, <cite>torch.distributed.rpc</cite> 软件包应立即提供这些帮助程序功能。 例如,如果应用程序可以直接调用`RRef.some_func(*arg)`,然后将其转换为`RRef`所有者的 RPC,将会更容易。 在 [pytorch / pytorch#31743](https://github.com/pytorch/pytorch/issues/31743) 中跟踪了此 API 的进度。 首先,让我们准备一个帮助程序,以在`RRef`的所有者工作程序上远程运行功能。 您将在本教程的示例中的多个地方发现该功能。 理想情况下, torch.distributed.rpc 软件包应立即提供这些帮助程序功能。 例如,如果应用程序可以直接调用`RRef.some_func(*arg)`,然后将其转换为`RRef`所有者的 RPC,将会更容易。 在 [pytorch / pytorch#31743](https://github.com/pytorch/pytorch/issues/31743) 中跟踪了此 API 的进度。
```py ```py
from torch.distributed.rpc import rpc_sync from torch.distributed.rpc import rpc_sync
...@@ -263,7 +263,7 @@ mp.spawn( ...@@ -263,7 +263,7 @@ mp.spawn(
``` ```
以下是使用 <cite>world_size = 2</cite> 进行训练时的一些示例输出。 以下是使用 world_size = 2 进行训练时的一些示例输出。
```py ```py
Episode 10 Last reward: 26.00 Average reward: 10.01 Episode 10 Last reward: 26.00 Average reward: 10.01
...@@ -299,7 +299,7 @@ Solved! Running reward is now 475.3163778435275! ...@@ -299,7 +299,7 @@ Solved! Running reward is now 475.3163778435275!
``` ```
在此示例中,我们展示了如何使用 RPC 作为通信工具来跨工作人员传递数据,以及如何使用 RRef 引用远程对象。 的确,您可以直接在`ProcessGroup` `send``recv` API 之上构建整个结构,也可以使用其他通信/ RPC 库。 但是,通过使用 <cite>torch.distributed.rpc</cite> ,您可以在后台获得本机支持并不断优化性能。 在此示例中,我们展示了如何使用 RPC 作为通信工具来跨工作人员传递数据,以及如何使用 RRef 引用远程对象。 的确,您可以直接在`ProcessGroup` `send``recv` API 之上构建整个结构,也可以使用其他通信/ RPC 库。 但是,通过使用 torch.distributed.rpc ,您可以在后台获得本机支持并不断优化性能。
接下来,我们将展示如何将 RPC 和 RRef 与分布式 autograd 和分布式优化器结合起来执行分布式模型并行训练。 接下来,我们将展示如何将 RPC 和 RRef 与分布式 autograd 和分布式优化器结合起来执行分布式模型并行训练。
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
## 基础知识 ## 基础知识
先前的教程显示了使用 [torch.distributed.rpc](https://pytorch.org/docs/stable/rpc.html) 构建分布式训练应用程序的步骤,但并未详细说明在处理 RPC 请求时被调用方发生的情况。 从 PyTorch v1.5 开始,每个 RPC 请求都会在被调用方上阻塞一个线程,以在该请求中执行该函数,直到该函数返回为止。 这适用于许多用例,但有一个警告。 如果用户功能例如通过嵌套 RPC 调用在 IO 上阻塞,或者例如在等待其他 RPC 请求解除阻塞的信号时阻塞,则被调用方上的 RPC 线程将必须空闲,直到 IO 完成或发生信令事件为止。 结果,RPC 被调用者可能使用了不必要的更多线程。 造成此问题的原因是 RPC 将用户功能视为黑匣子,并且几乎不了解该功能会发生什么。 为了允许用户函数产生和释放 RPC 线程,需要向 RPC 系统提供更多提示。 先前的教程显示了使用 [torch.distributed.rpc](https://pytorch.org/docs/stable/rpc.html) 构建分布式训练应用程序的步骤,但并未详细说明在处理 RPC 请求时被调用方发生的情况。 从 PyTorch v1.5 开始,每个 RPC 请求都会在被调用方上阻塞一个线程,以在该请求中执行该函数,直到该函数返回为止。 这适用于许多用例,但有一个警告。 如果用户功能例如通过嵌套 RPC 调用在 IO 上阻塞,或者例如在等待其他 RPC 请求解除阻塞的信号时阻塞,则被调用方上的 RPC 线程将必须空闲,直到 IO 完成或发生信令事件为止。 结果,RPC 被调用者可能使用了不必要的更多线程。 造成此问题的原因是 RPC 将用户功能视为黑,并且几乎不了解该功能会发生什么。 为了允许用户函数产生和释放 RPC 线程,需要向 RPC 系统提供更多提示。
从 v1.6.0 开始,PyTorch 通过引入两个新概念来解决此问题: 从 v1.6.0 开始,PyTorch 通过引入两个新概念来解决此问题:
......
...@@ -606,7 +606,7 @@ Szegedy 等人从卷积开始深入研究(https://arxiv.org/pdf/1409.4842.pdf ...@@ -606,7 +606,7 @@ Szegedy 等人从卷积开始深入研究(https://arxiv.org/pdf/1409.4842.pdf
# 深度学习的局限性 # 深度学习的局限性
深度神经网络是权重和偏见的黑匣子,它们经过大量数据训练,可以通过内部表示找到隐藏的模式。 对于人类来说这将是不可能的,即使有可能,那么可伸缩性也是一个问题。 每个神经可能都有不同的权重。 因此,它们将具有不同的梯度。 深度神经网络是权重和偏见的黑,它们经过大量数据训练,可以通过内部表示找到隐藏的模式。 对于人类来说这将是不可能的,即使有可能,那么可伸缩性也是一个问题。 每个神经可能都有不同的权重。 因此,它们将具有不同的梯度。
训练在反向传播期间进行。 因此,训练的方向始终是从较后的层(输出/右侧)到较早的层(输入/左侧)。 与早期的层相比,这导致后面的层学习得很好。 网络越深入,情况恶化的程度就越大。 这引起了与深度学习相关的两个可能的问题,它们是: 训练在反向传播期间进行。 因此,训练的方向始终是从较后的层(输出/右侧)到较早的层(输入/左侧)。 与早期的层相比,这导致后面的层学习得很好。 网络越深入,情况恶化的程度就越大。 这引起了与深度学习相关的两个可能的问题,它们是:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册