提交 ca3db17b 编写于 作者: W wizardforcel

2021-01-24 20:31:55

上级 64680687
......@@ -16,7 +16,7 @@ GAN 中的两个模型称为生成器和判别器,其中生成器负责创建
![](img/13480561-764e-43be-b39a-07d7656540db.png)
在此图中,有一个数据源,其中包含训练图像`x`,生成器必须捕获其属性并重新创建。 生成器接收随机向量`z`,该向量充当生成器创建伪图像的种子。 生成器获取种子并生成`x*`图像,判别器从真实和虚假图像中获取图像,并输出给定​​输入为真实的概率(假设真实图像用 1 表示,伪图像用 0 表示)。 然后,我们获得分类误差,并使用它来迭代训练判别器和生成器。 判别器的目的是使分类误差最小,而发生器的目的是使分类误差最大化。
在此图中,有一个数据源,其中包含训练图像`x`,生成器必须捕获其属性并重新创建。 生成器接收随机向量`z`,该向量充当生成器创建伪图像的种子。 生成器获取种子并生成`x*`图像,判别器从真实和虚假图像中获取图像,并输出给定​​输入为真实的概率(假设真实图像用 1 表示,伪图像用 0 表示)。 然后,我们获得分类误差,并使用它来迭代训练判别器和生成器。 判别器的目的是使分类误差最小,而生成器的目的是使分类误差最大化。
从理论上讲,生成器和判别器达到平衡,其中生成器已捕获了生成的伪图像中真实图像的所有特征,而从进一步的训练中没有任何收获。 类似地,鉴别者只能以 50% 的概率猜测图像是伪造的还是真实的,因为这两个图像就其性质而言完全无法区分。 在那个状态下,GAN 已经收敛。 然而,实际上,这种状态很难实现。 在本章中,我们将探讨 GAN 的概念以及 PyTorch 中各种 GAN 的实现。
......@@ -110,7 +110,7 @@ def forward(self, input):
>>generator = Generator_model(z_dim).to(device)
```
完成此过程后,我们已经准备好 DCGAN 发生器。
完成此过程后,我们已经准备好 DCGAN 生成器。
# 工作原理
......@@ -118,7 +118,7 @@ def forward(self, input):
在构造器中,我们传递了`z_dim`参数,这是我们的噪声向量大小。 然后,我们定义了一个全连接单元`self.fc`,我们将噪声向量传递给该单元,并为其提供了`256 * 7 * 7`输出。 然后,我们定义了一个称为`self.gen``nn.Sequential`单元,其中包含用于定义生成器的关键组件。 我们使用 PyTorch 中提供的`nn.ConvTranspose2d``nn.BatchNorm2d``nn.LeakyReLU`使用一组反卷积,批量规范化和激活层。 `ConvTranspose2d`接受输入通道,输出通道,内核大小,步幅和填充等参数。 `BatchNorm2d`接受上一层的要素/通道数作为其参数,而 LeakyReLU 接受负斜率的角度。
与 ReLU 不同,LeakyReLU 允许传递小的梯度信号以获取负值。 它使来自判别器的梯度流入发生器。 我们在输出层中使用了 tanh 激活,但是从 DCGAN 论文中我们观察到,使用有界激活可以使模型学会快速饱和并覆盖训练分布的色彩空间。 tanh 的对称性在这里可能是一个优势,因为网络应该以对称方式处理较深的颜色和较浅的颜色。
与 ReLU 不同,LeakyReLU 允许传递小的梯度信号以获取负值。 它使来自判别器的梯度流入生成器。 我们在输出层中使用了 tanh 激活,但是从 DCGAN 论文中我们观察到,使用有界激活可以使模型学会快速饱和并覆盖训练分布的色彩空间。 tanh 的对称性在这里可能是一个优势,因为网络应该以对称方式处理较深的颜色和较浅的颜色。
让我们看一下`forward`方法的工作方式。 `z_dim`维度的输入噪声向量经过全连接层以提供 12544 输出。 然后,我们将 12544 输出调整为`256 x 7 x 7`,其中 256 是通道数。 `256 x 7 x 7`张量然后通过反卷积层以提供`128 x 14 x 14`输出,然后通过具有 128 个特征和泄漏 ReLU 的`Batchnorm`层。 `128 x 14 x 14`然后在第二次反卷积中转换为`64 x 14 x 14`张量,在第三次反卷积中变为`1 x 28 x 28`张量; 这些只是我们需要的尺寸。 然后,我们创建生成器对象并将其移动到设备。
......@@ -549,7 +549,7 @@ Epoch : | 005 / 050 |
我们遍历了每个小批量并开始训练判别器。 我们仅从 MNIST 数据集中获取图像,然后使用`real_images = data[0].to(device)`将其移动到设备中; 由于图像全部来自 MNIST 数据集,因此我们知道它们是真实的,因此我们创建了与小批量相同大小的标签向量,并用真实图像标签 1 进行填充。然后,将这些真实图像传递到判别器来预测,然后使用此预测从准则中得出误差`derror_real`并计算梯度。 然后,我们创建了相等数量的噪声向量,并将它们传递给生成器以生成图像,然后将这些生成的图像传递给判别器以获取预测,然后从准则`derror_fake`中获取误差。 然后,我们再次进行此操作以计算和累积梯度。 然后,我们从真实图像和伪图像中获得了误差之和,以得出总的判别器误差,并更新判别器的权重。
然后,我们开始训练生成器,并且生成器应该能够欺骗判别器。 生成器必须纠正鉴别者正确预测生成的图像为假的情况。 因此,只要来自判别器的预测将生成的图像标记为伪造,就会增加生成器损失`gerror`。 然后,我们计算梯度并更新发生器权重。
然后,我们开始训练生成器,并且生成器应该能够欺骗判别器。 生成器必须纠正鉴别者正确预测生成的图像为假的情况。 因此,只要来自判别器的预测将生成的图像标记为伪造,就会增加生成器损失`gerror`。 然后,我们计算梯度并更新生成器权重。
然后,我们定期显示模型指标,并保存固定噪声生成器生成的图像,以可视化模型在各个时期的表现。
......@@ -642,7 +642,7 @@ pip install numpy
# 工作原理
在本秘籍中,我们使用 matplotlib 绘制图形和图像。 我们使用`figure()`和`title()`方法设置图形尺寸和标题,然后使用`plot()`方法绘制发生器和判别器损失。 我们还使用`xlabel`和`ylabel`方法添加了`x`和`y`标签。 我们还使用`legend()`方法为图添加了图例,最后使用`show()`方法显示了图。
在本秘籍中,我们使用 matplotlib 绘制图形和图像。 我们使用`figure()`和`title()`方法设置图形尺寸和标题,然后使用`plot()`方法绘制生成器和判别器损失。 我们还使用`xlabel`和`ylabel`方法添加了`x`和`y`标签。 我们还使用`legend()`方法为图添加了图例,最后使用`show()`方法显示了图。
我们遍历训练期间保存在`image_list`中的图像,并使用 NumPy 的`transpose()`方法以所需顺序固定图像的尺寸。 `image_list`中的图像是使用`torchvision.util.make_grid()`方法生成的,我们根据噪声向量创建了生成图像的网格。
......
......@@ -400,7 +400,7 @@ class Linear(torch.nn.Module):
return input.matmul(self.weight.t()) + self.bias
```
该代码段是 PyTorch 源代码中`Linear`层的修改版本。 用`Parameter`包裹张量对于您来说似乎很奇怪,但是不必担心。 `Parameter`类将权重和偏差添加到模块参数列表中,当您调用`model.parameters()`时将可用。 初始化程序将所有参数保存为对象属性。 `forward`函数的功能与我们在上一示例中的自定义线性层中完全一样。
该代码段是 PyTorch 源代码中`Linear`层的修改版本。 用`Parameter`包裹张量对于您来说似乎很奇怪,但是不必担心。 `Parameter`类将权重和偏差添加到模块参数列表中,当您调用`model.parameters()`时将可用。 初始化将所有参数保存为对象属性。 `forward`函数的功能与我们在上一示例中的自定义线性层中完全一样。
```py
a2 = x_.matmul(w1)
......
......@@ -444,7 +444,7 @@ python -m torch.utils.bottleneck /path/to/source/script.py [args]
Ignite 是一种神经网络训练工具,可将某些样板代码抽象出来,以使代码简洁明了。 Ignite 的核心是`Engine`模块。 该模块非常强大,因为:
* 它基于默认/自定义训练器或评估者运行模型。
* 它可以接受处理程序和指标,并对其执行操作。
* 它可以接受处理和指标,并对其执行操作。
* 它可以创建触发器并执行回调。
#### `Engine`
......@@ -535,7 +535,7 @@ def run_evaluator_on_validation_data(engine):
**注意**:在`create_supervised_trainer`的情况下,`training_loop`函数返回损失,在`create_supervised_evaluator`的情况下,`training_loop`函数返回模型的输出。 但是,如果我们定义一个自定义`training_loop`函数,则此函数返回的内容将是`Engine.state.output`保留的内容。
第二和第三事件处理程序正在`EPOCH_COMPLETED`上运行`evaluator`,但具有不同的数据集。 在第一个函数中,`evaluator`使用训练数据集,在第二个函数中,它使用评估数据集。 太好了,因为现在我们可以在每个时期完成时运行`evaluator`,而不是像第一个示例那样在整个执行结束时运行。 但是,除了运行它之外,处理程序实际上并没有做任何事情。 通常,这里是我们检查平均准确率和平均损失的地方,并且我们会进行更复杂的分析,例如混淆度量的创建,我们将在后面看到。 但是,目前的主要收获是:可以为单个事件设置`n`处理程序数量,Ignite 会毫不犹豫地依次调用所有这些处理程序。 接下来是事件的内部`_fire_event`函数,该事件在`training_loop`函数的每个事件中触发。
第二和第三事件处理器正在`EPOCH_COMPLETED`上运行`evaluator`,但具有不同的数据集。 在第一个函数中,`evaluator`使用训练数据集,在第二个函数中,它使用评估数据集。 太好了,因为现在我们可以在每个时期完成时运行`evaluator`,而不是像第一个示例那样在整个执行结束时运行。 但是,除了运行它之外,处理器实际上并没有做任何事情。 通常,这里是我们检查平均准确率和平均损失的地方,并且我们会进行更复杂的分析,例如混淆度量的创建,我们将在后面看到。 但是,目前的主要收获是:可以为单个事件设置`n`处理器数量,Ignite 会毫不犹豫地依次调用所有这些处理器。 接下来是事件的内部`_fire_event`函数,该事件在`training_loop`函数的每个事件中触发。
```py
def _fire_event(self, event_name, *event_args):
......@@ -545,11 +545,11 @@ def _fire_event(self, event_name, *event_args):
func(self, *(event_args + args), **kwargs)
```
在下一节中,我们将使`EPOCH_COMPLETED`事件处理程序使用 Ignite 的指标进行更明智的操作。
在下一节中,我们将使`EPOCH_COMPLETED`事件处理使用 Ignite 的指标进行更明智的操作。
#### 指标
就像`Engine`一样,指标也是 Ignite 源代码的重要组成部分,源代码正在不断发展。 度量将用于分析神经网络的表现和效率的几种常用度量包装为`Engine`可以理解的简单可配置类。 接下来给出当前构建的指标。 我们将使用其中一些来构建前面的事件处理程序
就像`Engine`一样,指标也是 Ignite 源代码的重要组成部分,源代码正在不断发展。 度量将用于分析神经网络的表现和效率的几种常用度量包装为`Engine`可以理解的简单可配置类。 接下来给出当前构建的指标。 我们将使用其中一些来构建前面的事件处理
* `Accuracy`
* `Loss`
......@@ -571,7 +571,7 @@ metrics = {'accuracy': CategoricalAccuracy(), 'null': Loss(F.null_loss)}
evaluator = create_supervised_evaluator(model, metrics=metrics)
```
`Engine`的初始化程序获取指标,并调用`Metrics.attach`函数来设置触发器,以计算`EPOCH_STARTED``ITERATION_COMPLETED``EPOCH_COMPLETED`的指标。 来自`Metrics`源代码的`attach`函数如下:
`Engine`的初始化获取指标,并调用`Metrics.attach`函数来设置触发器,以计算`EPOCH_STARTED``ITERATION_COMPLETED``EPOCH_COMPLETED`的指标。 来自`Metrics`源代码的`attach`函数如下:
```py
def attach(self, engine, name):
......@@ -580,7 +580,7 @@ def attach(self, engine, name):
engine.add_event_handler(Events.EPOCH_COMPLETED, self.completed, name)
```
通过`Engine`设置事件处理程序后,事件发生时将自动调用它们。 `EPOCH_STARTED`事件通过调用`reset()`方法来清理指标,并使存储对于当前周期指标集合保持干净。
通过`Engine`设置事件处理后,事件发生时将自动调用它们。 `EPOCH_STARTED`事件通过调用`reset()`方法来清理指标,并使存储对于当前周期指标集合保持干净。
`ITERATION_COMPLETED`触发器将调用相应指标的`update()`方法并进行指标更新。 例如,如果度量等于损失,则它会在创建`Engine`时调用我们作为参数传递给`Loss`类的损失函数来计算当前损失。 然后将计算出的损失保存到对象变量中,以备将来使用。
......@@ -600,15 +600,15 @@ def run_evaluator_on_validation_data(engine):
#### 保存检查点
使用 Ignite 的另一个好处是检查点保存功能,PyTorch 中不提供此功能。 人们想出了不同的方法来有效地编写和加载检查点。 `EngineCheckpoint`是 Ignite 处理程序的一部分,可以这样导入:
使用 Ignite 的另一个好处是检查点保存功能,PyTorch 中不提供此功能。 人们想出了不同的方法来有效地编写和加载检查点。 `EngineCheckpoint`是 Ignite 处理的一部分,可以这样导入:
```py
from ignite.handlers import EngineCheckpoint
```
Ignite 的检查点保护程序具有非常简单的 API。 用户需要定义检查点的保存位置,检查点的保存频率以及除默认参数(如迭代计数,用于恢复操作的时期数)以外的对象要保存的内容。 在该示例中,我们为每一百次迭代检查点。 然后可以将定义的值作为参数传递给`EngineCheckpoint`模块,以获取检查点事件处理程序对象。
Ignite 的检查点保护程序具有非常简单的 API。 用户需要定义检查点的保存位置,检查点的保存频率以及除默认参数(如迭代计数,用于恢复操作的时期数)以外的对象要保存的内容。 在该示例中,我们为每一百次迭代检查点。 然后可以将定义的值作为参数传递给`EngineCheckpoint`模块,以获取检查点事件处理对象。
返回的处理程序具有常规事件处理程序的所有功能,并且可以为 Ignite 触发的任何事件进行设置。 在以下示例中,我们将其设置为`ITERATION_COMPLETED`事件:
返回的处理器具有常规事件处理器的所有功能,并且可以为 Ignite 触发的任何事件进行设置。 在以下示例中,我们将其设置为`ITERATION_COMPLETED`事件:
```py
dirname = 'path/to/checkpoint/directory'
......@@ -617,7 +617,7 @@ engine_checkpoint = EngineCheckpoint(dirname=dirname,to_save=objects_to_checkpoi
trainer.add_event_handler(Events.ITERATION_COMPLETED, engine_checkpoint)
```
触发器在每个`ITERATION_COMPLETED`事件上调用处理程序,但是我们只需要为每百次迭代保存一次即可,并且 Ignite 没有用于自定义事件的方法。 Ignite 通过为用户提供在处理程序内部进行此检查的灵活性来解决此问题。 对于检查点处理程序,Ignite 在内部检查当前完成的迭代是否为百分之一,并仅在检查通过后才保存该迭代,如以下代码片段所示:
触发器在每个`ITERATION_COMPLETED`事件上调用处理器,但是我们只需要为每百次迭代保存一次即可,并且 Ignite 没有用于自定义事件的方法。 Ignite 通过为用户提供在处理器内部进行此检查的灵活性来解决此问题。 对于检查点处理器,Ignite 在内部检查当前完成的迭代是否为百分之一,并仅在检查通过后才保存该迭代,如以下代码片段所示:
```py
if engine.state.iteration % self.save_interval !=0:
......
......@@ -407,7 +407,7 @@ class Encoder(nn.Module):
##### `LSTMCell`和`GRUCell`
`LSTMCell``GRUCell`的函数式 API 绝对相似,这也正是定制`RNNCell`的方式。 它们接受输入大小和初始化程序的隐藏大小。 `forward`调用接受具有输入大小的微型输入批量,并为该实例创建单元状态和隐藏状态,然后将其传递给下一个执行输入。 在静态图框架中实现这种的实现非常困难,因为该图在整个执行期间都是预先编译的并且是静态的。 循环语句也应作为图节点作为图的一部分。 这需要用户学习那些额外的操作节点或其他在内部处理循环的函数式 API。
`LSTMCell``GRUCell`的函数式 API 绝对相似,这也正是定制`RNNCell`的方式。 它们接受输入大小和初始化的隐藏大小。 `forward`调用接受具有输入大小的微型输入批量,并为该实例创建单元状态和隐藏状态,然后将其传递给下一个执行输入。 在静态图框架中实现这种的实现非常困难,因为该图在整个执行期间都是预先编译的并且是静态的。 循环语句也应作为图节点作为图的一部分。 这需要用户学习那些额外的操作节点或其他在内部处理循环的函数式 API。
#### LSTM 和 GRU
......@@ -553,7 +553,7 @@ class Tracker(nn.Module):
#### `SPINN`
`SPINN`模块是所有小型组件的包装器类。 `SPINN`的初始化程序与一样简单,包括组件模块`Reduce``Tracker`的初始化。 内部节点之间的所有繁重工作和协调都通过 SPINN 的`forward`调用进行管理。
`SPINN`模块是所有小型组件的包装器类。 `SPINN`的初始化与一样简单,包括组件模块`Reduce``Tracker`的初始化。 内部节点之间的所有繁重工作和协调都通过 SPINN 的`forward`调用进行管理。
```py
class SPINN(nn.Module):
......
......@@ -360,7 +360,7 @@ GAN 从随机分布中获取样本,然后由网络将其转换为输出。 GAN
### 简单的 GAN
了解 GAN 的直观方法是从博弈论的角度了解它。 简而言之,GAN 由两个参与者组成,一个生成器和一个判别器,每一个都试图击败对方。 发生器从分布中获取一些随机噪声,并尝试从中生成一些输出分布。 生成器总是尝试创建与真实分布没有区别的分布; 也就是说,伪造的输出应该看起来像是真实的图像。
了解 GAN 的直观方法是从博弈论的角度了解它。 简而言之,GAN 由两个参与者组成,一个生成器和一个判别器,每一个都试图击败对方。 生成器从分布中获取一些随机噪声,并尝试从中生成一些输出分布。 生成器总是尝试创建与真实分布没有区别的分布; 也就是说,伪造的输出应该看起来像是真实的图像。
![Simple GAN](img/B09475_06_14.jpg)
......
......@@ -315,7 +315,7 @@ pip install model-archiver[onnx]
现在,我们可以安装 MXNet 模型服务器。 它基于 **Java 虚拟机****JVM**)构建,因此从 JVM 调用了运行有我们模型实例的多个线程。 利用 JVM 支持的复杂性,可以将 MXNet 服务器扩展为处理数千个请求的多个进程。
MXNet 服务器带有管理 API,该 API 通过 HTTP 提供。 这有助于生产团队根据需要增加/减少资源。 除了处理工规模之外,管理 API 还具有其他选项。 但是我们不会在这里深入探讨。 由于模型服务器在 JVM 上运行,因此我们需要安装 Java8。此外,MXNet 模型服务器在 Windows 上仍处于试验模式,但在 Linux 风味和 Mac 上稳定。
MXNet 服务器带有管理 API,该 API 通过 HTTP 提供。 这有助于生产团队根据需要增加/减少资源。 除了处理工作器规模之外,管理 API 还具有其他选项。 但是我们不会在这里深入探讨。 由于模型服务器在 JVM 上运行,因此我们需要安装 Java8。此外,MXNet 模型服务器在 Windows 上仍处于试验模式,但在 Linux 风味和 Mac 上稳定。
```py
pip install mxnet-model-server
......@@ -492,9 +492,9 @@ mxnet-model-server \
--models fizbuz_package.mar
```
现在,我们的模型服务器已启动并在端口 8080 上运行(如果您尚未更改端口)。 我们可以尝试执行与 Flask 应用相同的`curl`命令(显然,我们必须更改端口号)并检查模型。 我们应该获得与 Flask 应用完全相同的结果,但是现在我们可以根据需要动态地动态扩展或缩减工作人员的数量。 MMS 为此提供了管理 API。 管理 API 带有几个可配置的选项,但是这里我们只关注于增加或减少工作人员的数量。
现在,我们的模型服务器已启动并在端口 8080 上运行(如果您尚未更改端口)。 我们可以尝试执行与 Flask 应用相同的`curl`命令(显然,我们必须更改端口号)并检查模型。 我们应该获得与 Flask 应用完全相同的结果,但是现在我们可以根据需要动态地动态扩展或缩减工作器的数量。 MMS 为此提供了管理 API。 管理 API 带有几个可配置的选项,但是这里我们只关注于增加或减少工作器的数量。
除了在端口 8080 上运行的服务器之外,还将在 8081 上运行管理 API 服务,我们可以对其进行调用和控制配置。 使用简单的`GET`请求命中该端点将为您提供服务器的状态。 但是在探究这一点之前,我们将工人数量设为 1(默认情况下为 4)。 API 端点是适当的 REST 端点; 我们在路径中指定模型名称,并传递参数`max_worker=1`以使工人数为 1。 我们也可以通过`min_worker=<number>`来增加工人数量。 官方文档[2]中详细介绍了管理 API 上可能的配置。
除了在端口 8080 上运行的服务器之外,还将在 8081 上运行管理 API 服务,我们可以对其进行调用和控制配置。 使用简单的`GET`请求命中该端点将为您提供服务器的状态。 但是在探究这一点之前,我们将工作器数量设为 1(默认情况下为 4)。 API 端点是适当的 REST 端点; 我们在路径中指定模型名称,并传递参数`max_worker=1`以使工作器数为 1。 我们也可以通过`min_worker=<number>`来增加工作器数量。 官方文档[2]中详细介绍了管理 API 上可能的配置。
```py
-> curl -v -X PUT "http://localhost:8081/models/fizbuz_package?max_worker=1"
......@@ -506,7 +506,7 @@ mxnet-model-server \
```
一旦减少了工作人员的数量,我们就可以命中端点来确定服务器的状态。 示例输出(在我们减少了工人数量之后)如下:
一旦减少了工作器的数量,我们就可以命中端点来确定服务器的状态。 示例输出(在我们减少了工作器数量之后)如下:
```py
-> curl "http://localhost:8081/models/fizbuz_package"
......
......@@ -117,7 +117,7 @@ imshow(out, title=[class_names[x] for x in classes])
* 安排学习率
* 保存最佳模型
以下,参数`scheduler`是来自`torch.optim.lr_scheduler`的 LR 调度程序对象。
以下,参数`scheduler`是来自`torch.optim.lr_scheduler`的 LR 调度对象。
```py
def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
......
......@@ -14,11 +14,11 @@
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`)生成假样本。
因此,`D(G(z))`发生`G`的输出是真实图像的概率(标量)。 如 [Goodfellow 的论文](https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)中所述,`D``G`玩一个 minimax 游戏,其中`D`试图最大化其正确分类实物和假物`log D(x)`,并且`G`尝试最小化`D`预测其输出为假的概率`log(1 - D(G(g(x))))`。 从本文来看,GAN 损失函数为
因此,`D(G(z))`生成`G`的输出是真实图像的概率(标量)。 如 [Goodfellow 的论文](https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)中所述,`D``G`玩一个 minimax 游戏,其中`D`试图最大化其正确分类实物和假物`log D(x)`,并且`G`尝试最小化`D`预测其输出为假的概率`log(1 - D(G(g(x))))`。 从本文来看,GAN 损失函数为
![](img/tex22-1.gif)
......
......@@ -439,7 +439,7 @@ int main() {
这里有一个微妙的问题值得一提。 默认构造的`std::shared_ptr`为“空”,即包含空指针。 什么是默认构造的`Linear``Net`? 好吧,这是一个棘手的选择。 我们可以说它应该是一个空(`null``std::shared_ptr<LinearImpl>`。 但是,请记住`Linear(3, 4)``std::make_shared<LinearImpl>(3, 4)`相同。 这意味着如果我们已确定`Linear linear;`应该为空指针,则将无法构造不采用任何构造器参数或都不使用所有缺省构造器的模块。 因此,在当前的 API 中,默认构造的模块持有人(如`Linear()`)将调用基础模块的默认构造器(`LinearImpl()`)。 如果基础模块没有默认构造器,则会出现编译器错误。 要构造空持有人,可以将`nullptr`传递给持有人的构造器。
实际上,这意味着您可以使用如先前所示的子模块,在*初始化程序列表*中注册并构造该模块:
实际上,这意味着您可以使用如先前所示的子模块,在*初始化列表*中注册并构造该模块:
```py
struct Net : torch::nn::Module {
......@@ -475,7 +475,7 @@ struct Net : torch::nn::Module {
#### 什么是 GAN aGAN?
GAN 由两个不同的神经网络模型组成:*生成器**判别器*。 生成器从噪声分布中接收样本,其目的是将每个噪声样本转换为类似于目标分布的图像(在我们的情况下为 MNIST 数据集)。 判别器又从 MNIST 数据集接收*实际*图像,或从生成器接收*假*图像。 要求发出一个概率来判断特定图像的真实程度(接近`1`)或伪造(接近`0`)。 来自判别器的关于由发生器产生的图像有多真实的反馈被用来训练发生器。 判别器对真实性有多好的反馈将用于优化判别器。 从理论上讲,生成器和判别器之间的微妙平衡使它们连接起来得到改善,从而导致生成器生成与目标分布无法区分的图像,从而使判别器(那时)的敏锐眼睛冒出了散发`0.5`的真实和真实可能性。 假图片。 对我们来说,最终结果是一台接收噪声作为输入并生成逼真的数字图像作为其输出的机器。
GAN 由两个不同的神经网络模型组成:*生成器**判别器*。 生成器从噪声分布中接收样本,其目的是将每个噪声样本转换为类似于目标分布的图像(在我们的情况下为 MNIST 数据集)。 判别器又从 MNIST 数据集接收*实际*图像,或从生成器接收*假*图像。 要求发出一个概率来判断特定图像的真实程度(接近`1`)或伪造(接近`0`)。 来自判别器的关于由生成器产生的图像有多真实的反馈被用来训练生成器。 判别器对真实性有多好的反馈将用于优化判别器。 从理论上讲,生成器和判别器之间的微妙平衡使它们连接起来得到改善,从而导致生成器生成与目标分布无法区分的图像,从而使判别器(那时)的敏锐眼睛冒出了散发`0.5`的真实和真实可能性。 假图片。 对我们来说,最终结果是一台接收噪声作为输入并生成逼真的数字图像作为其输出的机器。
#### 生成器模块
......@@ -608,7 +608,7 @@ auto data_loader = torch::data::make_data_loader(std::move(dataset));
```
数据加载器确实提供了很多选项。 [您可以在这里检查全套](https://github.com/pytorch/pytorch/blob/master/torch/csrc/api/include/torch/data/dataloader_options.h)。 例如,为了加快数据加载速度,我们可以增加工作人员的数量。 默认数字为零,这意味着将使用主线程。 如果将`workers`设置为`2`,将产生两个线程并发加载数据。 我们还应该将批量大小从其默认值`1`增加到更合理的值,例如`64``kBatchSize`的值)。 因此,让我们创建一个`DataLoaderOptions`对象并设置适当的属性:
数据加载器确实提供了很多选项。 [您可以在这里检查全套](https://github.com/pytorch/pytorch/blob/master/torch/csrc/api/include/torch/data/dataloader_options.h)。 例如,为了加快数据加载速度,我们可以增加工作的数量。 默认数字为零,这意味着将使用主线程。 如果将`workers`设置为`2`,将产生两个线程并发加载数据。 我们还应该将批量大小从其默认值`1`增加到更合理的值,例如`64``kBatchSize`的值)。 因此,让我们创建一个`DataLoaderOptions`对象并设置适当的属性:
```py
auto data_loader = torch::data::make_data_loader(
......
......@@ -2,7 +2,7 @@
> 原文:<https://pytorch.org/tutorials/advanced/dispatcher.html>
调度程序是 PyTorch 的内部组件,负责确定调用`torch::add`之类的函数时应实际运行哪些代码。 这是不平凡的,因为 PyTorch 操作需要处理很多交叉关注点,这些关注点“层叠”在另一个之上。 以下是其处理的一些示例:
调度是 PyTorch 的内部组件,负责确定调用`torch::add`之类的函数时应实际运行哪些代码。 这是不平凡的,因为 PyTorch 操作需要处理很多交叉关注点,这些关注点“层叠”在另一个之上。 以下是其处理的一些示例:
* 根据输入张量的设备,在运算符的 CPU 和 CUDA 实现之间切换。
* 在运算符的自动微分和后端实现之间切换,这取决于是否需要自动微分处理。
......@@ -10,13 +10,13 @@
* 当运算符在`vmap`调用下运行时,应用批量规则。
* 如果要跟踪导出的模型,则跟踪操作的执行。
如果在[自定义运算符代码](torch_script_custom_ops)中发现自己手动编写了`if`语句来处理这些情况,则调度程序 API 可以帮助组织代码。 (相反,如果您的自定义运算符非常简单并且仅用于 CPU 推断,则可能不需要使用调度程序,只需使用基本 API。)
如果在[自定义运算符代码](torch_script_custom_ops)中发现自己手动编写了`if`语句来处理这些情况,则调度器 API 可以帮助组织代码。 (相反,如果您的自定义运算符非常简单并且仅用于 CPU 推断,则可能不需要使用调度器,只需使用基本 API。)
在本教程中,我们将描述如何构造自定义运算符注册以使用调度程序来组织各种组件。 我们假设您熟悉如何[注册运算符](torch_script_custom_ops)以及如何编写[自定义自动微分函数](cpp_autograd)
在本教程中,我们将描述如何构造自定义运算符注册以使用调度来组织各种组件。 我们假设您熟悉如何[注册运算符](torch_script_custom_ops)以及如何编写[自定义自动微分函数](cpp_autograd)
## 定义模式和后端实现
调度程序背后的一般原理是将一个运算符的实现分为多个内核,每个内核都为特定的*调度键*实现功能; 例如,CPU,CUDA 或 Autograd。 调度程序在您调用运算符时确定最高优先级的调度键是什么(这通过查看张量参数和某些线程本地状态来完成),并将控制权传递给内核以使用该调度键。 最终结果是,当您调用运算符时,我们首先执行 Autograd 内核,然后根据传入的张量的设备类型将其重新分配到 CPU 或 CUDA 内核。
调度器背后的一般原理是将一个运算符的实现分为多个内核,每个内核都为特定的*调度键*实现功能; 例如,CPU,CUDA 或 Autograd。 调度器在您调用运算符时确定最高优先级的调度键是什么(这通过查看张量参数和某些线程本地状态来完成),并将控制权传递给内核以使用该调度键。 最终结果是,当您调用运算符时,我们首先执行 Autograd 内核,然后根据传入的张量的设备类型将其重新分配到 CPU 或 CUDA 内核。
让我们看一下实现这一目标所涉及的各个部分。 首先,我们必须为所讨论的运算符定义架构。 与简单的`pybind11`样式的运算符注册不同,我们目前实际上并未提供运算符的实现; 我们只提供一个模式字符串,指定所有其他内核将遵守的运算符的类型签名:
......@@ -79,7 +79,7 @@ TORCH_LIBRARY_IMPL(myops, CUDA, m) {
至此,我们有了一个同时具有 CPU 和 CUDA 实现的运算符。 我们如何为它添加 Autograd 支持? 您可能会猜到,我们将注册一个 Autograd 内核(类似于[自定义 Autograd 函数](cpp_autograd)教程中描述的内容)! 但是,有一个变数:与 CPU 和 CUDA 内核不同,Autograd 内核需要*重新分发*:它需要回调分派器才能到达最终的 CPU 和 CUDA 实现。
因此,在编写 Autograd 内核之前,让我们编写一个*调度函数*,该函数调用调度程序以为您的运算符找到合适的内核。 该函数构成了供您的运算符使用的公共 C++ API,实际上,PyTorch C++ API 中的所有张量函数都在后台完全以相同的方式调用了调度程序。 调度函数如下所示:
因此,在编写 Autograd 内核之前,让我们编写一个*调度函数*,该函数调用调度器以为您的运算符找到合适的内核。 该函数构成了供您的运算符使用的公共 C++ API,实际上,PyTorch C++ API 中的所有张量函数都在后台完全以相同的方式调用了调度器。 调度函数如下所示:
```py
Tensor myadd(const Tensor& self, const Tensor& other) {
......@@ -93,11 +93,11 @@ Tensor myadd(const Tensor& self, const Tensor& other) {
让我们分解一下:
* 在第一行中,我们从调度程序中查找与要调度到的运算符相对应的类型化运算符句柄。 `findSchemaOrThrow`具有两个参数:运算符的(名称空间限定)名称和运算符的重载名称(通常只是空字符串)。 `typed`将动态类型的句柄转换为静态类型的句柄(进行运行时测试以确保您提供了正确的 C++ 类型),以便我们可以对其进行常规的 C++ 调用。 我们将其传递给`decltype(myadd)`,因为调度函数的类型与注册到调度程序的基础内核的类型相同。
* 在第一行中,我们从调度器中查找与要调度到的运算符相对应的类型化运算符句柄。 `findSchemaOrThrow`具有两个参数:运算符的(名称空间限定)名称和运算符的重载名称(通常只是空字符串)。 `typed`将动态类型的句柄转换为静态类型的句柄(进行运行时测试以确保您提供了正确的 C++ 类型),以便我们可以对其进行常规的 C++ 调用。 我们将其传递给`decltype(myadd)`,因为调度函数的类型与注册到调度器的基础内核的类型相同。
为了提高性能,此计算是在静态变量中完成的,因此我们只需要进行一次(慢速)查找。 如果键入了要调用的运算符的名称,则第一次调用此函数时,此查找将出错。
* 在第二行中,我们只需将所有参数传递到调度函数中,就可以简单地`call`运算符句柄。 这实际上将调用调度程序,最终控制权将转移到适合此调用的任何内核。
* 在第二行中,我们只需将所有参数传递到调度函数中,就可以简单地`call`运算符句柄。 这实际上将调用调度,最终控制权将转移到适合此调用的任何内核。
有了分发函数,我们现在可以编写 Autograd 内核:
......@@ -125,9 +125,9 @@ Tensor myadd_autograd(const Tensor& self, const Tensor& other) {
使用`torch::autograd::Function`正常编写 Autograd 函数,除了代替直接在`forward()`中编写实现,我们:
1. 使用`at::AutoNonVariableTypeMode` RAII 保护器关闭 Autograd 处理,然后
2. 调用调度函数`myadd`以回调调度程序
2. 调用调度函数`myadd`以回调调度
如果没有(1),您的调用将无限循环(并且堆栈溢出),因为`myadd`将使您返回此函数(因为最高优先级分配键仍将是自动微分的。)对于(1),自动微分从一组正在考虑的调度键中排除,我们将转到下一个处理程序,即 CPU 和 CUDA。
如果没有(1),您的调用将无限循环(并且堆栈溢出),因为`myadd`将使您返回此函数(因为最高优先级分配键仍将是自动微分的。)对于(1),自动微分从一组正在考虑的调度键中排除,我们将转到下一个处理,即 CPU 和 CUDA。
现在,我们可以按照注册 CPU/CUDA 函数的相同方式注册此函数:
......@@ -161,11 +161,11 @@ public:
```
那么为什么要使用调度程序呢? 有几个原因:
那么为什么要使用调度呢? 有几个原因:
1. 它是分散的。 您可以组装运算符的所有部分(CPU,CUDA,Autograd),而不必编写引用所有元素的集中式`if`语句。 重要的是,第三方可以注册其他方面的额外实现,而不必修补运算符的原始定义。
2. 它比 CPU,CUDA 和 Autograd 支持更多的调度键。 您可以在`c10/core/DispatchKey.h`中查看 PyTorch 中当前实现的调度密钥的完整列表。 这些分派密钥为运算符实现了多种可选功能,如果您决定希望自定义运算符支持该功能,则只需为相应的密钥注册内核即可。
3. 调度程序实现对盒装后备函数的支持,后者是可以一次实现并应用于系统中所有运算符的函数。 盒装后备可用于提供调度键的默认行为。 如果您使用调度程序来实现您的运算符,那么您还可以选择所有这些操作的备用。
3. 调度器实现对盒装后备函数的支持,后者是可以一次实现并应用于系统中所有运算符的函数。 盒装后备可用于提供调度键的默认行为。 如果您使用调度器来实现您的运算符,那么您还可以选择所有这些操作的备用。
这是一些特定的调度键,您可能需要为其定义一个运算符。
......
......@@ -156,7 +156,7 @@ tune.report(loss=(val_loss / val_steps), accuracy=correct / total)
在这里,我们首先保存一个检查点,然后将一些指标报告给 Ray Tune。 具体来说,我们将验证损失和准确率发送回 Ray Tune。 然后,Ray Tune 可以使用这些指标来决定哪种超参数配置可以带来最佳结果。 这些指标还可用于尽早停止效果不佳的试验,以避免浪费资源进行试验。
保存检查点是可选的,但是,如果我们想使用高级调度程序,例如[基于总体的训练](https://docs.ray.io/en/master/tune/tutorials/tune-advanced-tutorial.html),则有必要。 另外,通过保存检查点,我们可以稍后加载经过训练的模型并在测试集上对其进行验证。
保存检查点是可选的,但是,如果我们想使用高级调度,例如[基于总体的训练](https://docs.ray.io/en/master/tune/tutorials/tune-advanced-tutorial.html),则有必要。 另外,通过保存检查点,我们可以稍后加载经过训练的模型并在测试集上对其进行验证。
### 完整的训练函数
......
......@@ -58,7 +58,7 @@ DDP 材料如下:
* [RPC](https://pytorch.org/docs/master/rpc.html#rpc) 支持在远程工作器上运行给定函数
* [RRef](https://pytorch.org/docs/master/rpc.html#rref) 帮助管理远程对象的生存期。 引用计数协议在 [RRef 注解](https://pytorch.org/docs/master/rpc/rref.html#remote-reference-protocol)中提供。
* [分布式自动微分](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework)将自动微分引擎扩展到机器范围之外。 有关更多详细信息,请参考[分布式 Autograd 设计](https://pytorch.org/docs/master/rpc/distributed_autograd.html#distributed-autograd-design)
* [分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim),它使用分布式 Autograd 引擎计算的梯度自动与所有参与的工作人员联系以更新参数。
* [分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim),它使用分布式 Autograd 引擎计算的梯度自动与所有参与的工作联系以更新参数。
RPC 教程如下:
......
......@@ -349,8 +349,8 @@ def allreduce(send, recv):
* `MASTER_PORT`:计算机上的空闲端口,它将托管等级为 0 的进程。
* `MASTER_ADDR`:将以等级 0 托管进程的计算机的 IP 地址。
* `WORLD_SIZE`:进程总数,以便主机知道要等待多少个工
* `RANK`:每个进程的等级,因此他们将知道它是否是工的主人。
* `WORLD_SIZE`:进程总数,以便主机知道要等待多少个工作器
* `RANK`:每个进程的等级,因此他们将知道它是否是工作器的主人。
**共享文件系统**
......@@ -366,7 +366,7 @@ dist.init_process_group(
**TCP**
通过提供等级 0 和可访问的端口号的进程的 IP 地址,可以实现通过 TCP 进行初始化。 在这里,所有工作人员都可以连接到等级为 0 的流程,并交换有关如何相互联系的信息。
通过提供等级 0 和可访问的端口号的进程的 IP 地址,可以实现通过 TCP 进行初始化。 在这里,所有工作都可以连接到等级为 0 的流程,并交换有关如何相互联系的信息。
```py
dist.init_process_group(
......
......@@ -299,13 +299,13 @@ Solved! Running reward is now 475.3163778435275!
```
在此示例中,我们展示了如何使用 RPC 作为通信工具来跨工作人员传递数据,以及如何使用 RRef 引用远程对象。 的确,您可以直接在`ProcessGroup` `send``recv` API 之上构建整个结构,也可以使用其他通信/ RPC 库。 但是,通过使用`torch.distributed.rpc`,您可以在后台获得本机支持并不断优化性能。
在此示例中,我们展示了如何使用 RPC 作为通信工具来跨工作传递数据,以及如何使用 RRef 引用远程对象。 的确,您可以直接在`ProcessGroup` `send``recv` API 之上构建整个结构,也可以使用其他通信/ RPC 库。 但是,通过使用`torch.distributed.rpc`,您可以在后台获得本机支持并不断优化性能。
接下来,我们将展示如何将 RPC 和 RRef 与分布式 Autograd 和分布式优化器结合起来执行分布式模型并行训练。
## 使用分布式 Autograd 和分布式优化器的分布式 RNN
在本节中,我们将使用 RNN 模型来展示如何使用 RPC API 构建分布式模型并行训练。 示例 RNN 模型非常小,可以轻松地放入单个 GPU 中,但是我们仍将其层划分为两个不同的工来演示这一想法。 开发人员可以应用类似的技术在多个设备和机器上分布更大的模型。
在本节中,我们将使用 RNN 模型来展示如何使用 RPC API 构建分布式模型并行训练。 示例 RNN 模型非常小,可以轻松地放入单个 GPU 中,但是我们仍将其层划分为两个不同的工作器来演示这一想法。 开发人员可以应用类似的技术在多个设备和机器上分布更大的模型。
RNN 模型设计是从 PyTorch [示例](https://github.com/pytorch/examples/tree/master/word_language_model)存储库中的词语言模型中借用的,该存储库包含三个主要组件,一个嵌入表,一个`LSTM`层和一个解码器。 下面的代码将嵌入表和解码器包装到子模块中,以便它们的构造器可以传递给 RPC API。 在`EmbeddingTable`子模块中,我们有意将`Embedding`层放在 GPU 上以涵盖用例。 在 v1.4 中,RPC 始终在目标工作线程上创建 CPU 张量参数或返回值。 如果函数使用 GPU 张量,则需要将其显式移动到适当的设备。
......
......@@ -189,7 +189,7 @@ def run_parameter_server(rank, world_size):
```
请注意,以上`rpc.shutdown()`不会立即关闭参数服务器。 相反,它将等待所有工作人员(在这种情况下为训练人员)也呼唤`rpc.shutdown()`。 这样可以保证参数服务器在所有训练人员(尚未定义)完成训练过程之前不会脱机。
请注意,以上`rpc.shutdown()`不会立即关闭参数服务器。 相反,它将等待所有工作(在这种情况下为训练人员)也呼唤`rpc.shutdown()`。 这样可以保证参数服务器在所有训练人员(尚未定义)完成训练过程之前不会脱机。
接下来,我们将定义`TrainerNet`类。 这也将是`nn.Module`的子类,并且我们的`__init__`方法将使用`rpc.remote` API 获取对我们的参数服务器的 RRef 或远程引用。 请注意,此处我们没有将参数服务器复制到本地进程,而是可以将`self.param_server_rref`视为指向驻留在单独进程中的参数服务器的分布式共享指针。
......@@ -316,7 +316,7 @@ def run_worker(rank, world_size, num_gpus, train_loader, test_loader):
```
请注意,类似于`run_parameter_server``rpc.shutdown()`默认情况下将等待该节点退出之前,所有训练器和`ParameterServer`的所有工作人员都调用`rpc.shutdown()`。 这样可确保节点正常终止,并且没有一个节点脱机,而另一个节点则期望其联机。
请注意,类似于`run_parameter_server``rpc.shutdown()`默认情况下将等待该节点退出之前,所有训练器和`ParameterServer`的所有工作都调用`rpc.shutdown()`。 这样可确保节点正常终止,并且没有一个节点脱机,而另一个节点则期望其联机。
现在,我们已经完成了特定于训练器和参数服务器的代码,剩下的就是添加代码以启动训练器和参数服务器。 首先,我们必须接受适用于我们的参数服务器和训练器的各种参数。 `world_size`对应于将参加训练的节点总数,并且是所有训练器和参数服务器的总和。 我们还必须为每个单独的进程传递唯一的`rank`,从 0(将在其中运行单个参数服务器的地方)到`world_size - 1``master_addr``master_port`是可用于标识等级 0 进程在何处运行的参数,并且各个节点将使用它们来相互发现。 要在本地测试此示例,只需将`localhost`和相同的`master_port`传递给所有产生的实例。 请注意,出于演示目的,此示例仅支持 0-2 个 GPU,尽管可以扩展该模式以使用其他 GPU。
......@@ -409,4 +409,4 @@ for p in processes:
要在本地运行示例,请在单独的终端窗口中为服务器和要生成的每个工作程序运行以下命令工作程序:`python rpc_parameter_server.py --world_size=WORLD_SIZE --rank=RANK`。 例如,对于世界大小为 2 的主节点,命令为`python rpc_parameter_server.py --world_size=2 --rank=0`。 然后可以在单独的窗口中使用命令`python rpc_parameter_server.py --world_size=2 --rank=1`启动训练器,这将开始使用一台服务器和一台训练器进行训练。 请注意,本教程假定使用 0 到 2 个 GPU 进行训练,并且可以通过将`--num_gpus=N`传递到训练脚本中来配置此参数。
您可以传入命令行参数`--master_addr=ADDRESS``--master_port=PORT`来指示主工作器正在侦听的地址和端口,例如,以测试在其他机器上运行训练者和主节点的功能。
\ No newline at end of file
您可以传入命令行参数`--master_addr=ADDRESS``--master_port=PORT`来指示主工作器正在监听的地址和端口,例如,以测试在其他机器上运行训练者和主节点的功能。
\ No newline at end of file
......@@ -183,7 +183,7 @@ class DistResNet50(nn.Module):
## 步骤 3:定义训练循环
定义模型后,让我们实现训练循环。 我们使用专门的“主”工作人员来准备随机输入和标签,并控制分布式反向传递和分布式优化器步骤。 它首先创建`DistResNet50`模块的实例。 它指定每个批量的微批数量,并提供两个 RPC 工作程序的名称(即`worker1``worker2`)。 然后,它定义损失函数,并使用`parameter_rrefs()`帮助器创建`DistributedOptimizer`以获取参数`RRefs`的列表。 然后,主训练循环与常规本地训练非常相似,除了它使用`dist_autograd`向后启动并为反向和优化器`step()`提供`context_id`之外。
定义模型后,让我们实现训练循环。 我们使用专门的“主”工作来准备随机输入和标签,并控制分布式反向传递和分布式优化器步骤。 它首先创建`DistResNet50`模块的实例。 它指定每个批量的微批数量,并提供两个 RPC 工作程序的名称(即`worker1``worker2`)。 然后,它定义损失函数,并使用`parameter_rrefs()`帮助器创建`DistributedOptimizer`以获取参数`RRefs`的列表。 然后,主训练循环与常规本地训练非常相似,除了它使用`dist_autograd`向后启动并为反向和优化器`step()`提供`context_id`之外。
```py
import torch.distributed.autograd as dist_autograd
......@@ -225,7 +225,7 @@ def run_master(num_split):
## 第 4 步:启动 RPC 进程
最后,下面的代码显示了所有进程的目标函数。 主要逻辑在`run_master`中定义。 工作人员被动地等待主服务器发出的命令,因此只需运行`init_rpc``shutdown`即可,其中默认情况下`shutdown`会阻塞,直到所有 RPC 参与者都完成。
最后,下面的代码显示了所有进程的目标函数。 主要逻辑在`run_master`中定义。 工作被动地等待主服务器发出的命令,因此只需运行`init_rpc``shutdown`即可,其中默认情况下`shutdown`会阻塞,直到所有 RPC 参与者都完成。
```py
import os
......
......@@ -11,7 +11,7 @@
1. 如果我们的模型具有稀疏部分(较大的嵌入表)和密集部分(FC 层),则可能需要将嵌入表放在参数服务器上,并使用[`DistributedDataParallel`](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)[分布式 RPC 框架](https://pytorch.org/docs/master/rpc.html)可用于在参数服务器上执行嵌入查找。
2.[PipeDream](https://arxiv.org/abs/1806.03377) 论文中所述,启用混合并行性。 我们可以使用[分布式 RPC 框架](https://pytorch.org/docs/master/rpc.html)在多个工作程序之间流水线化模型的各个阶段,并使用[`DistributedDataParallel`](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)复制每个阶段(如果需要)。
在本教程中,我们将介绍上述情况 1。 我们的设置中共有 4 个工,如下所示:
在本教程中,我们将介绍上述情况 1。 我们的设置中共有 4 个工作器,如下所示:
1. 1 个主机,负责在参数服务器上创建嵌入表(`nn.EmbeddingBag`)。 主人还会在两个教练上驱动训练循环。
2. 1 参数服务器,它基本上将嵌入表保存在内存中,并响应来自主服务器和训练器的 RPC。
......@@ -31,7 +31,7 @@
如果您将 DDP 和 RPC 结合使用,则应始终使用[分布式 Autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework) 进行反向传播。
现在,让我们详细介绍每个部分。 首先,我们需要先设置所有工,然后才能进行任何训练。 我们创建 4 个过程,使等级 0 和 1 是我们的训练器,等级 2 是主控制器,等级 3 是参数服务器。
现在,让我们详细介绍每个部分。 首先,我们需要先设置所有工作器,然后才能进行任何训练。 我们创建 4 个过程,使等级 0 和 1 是我们的训练器,等级 2 是主控制器,等级 3 是参数服务器。
我们使用 TCP init_method 在所有 4 个工作器上初始化 RPC 框架。 RPC 初始化完成后,主服务器使用[`rpc.remote`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.remote)在参数服务器上创建[`EmbeddingBag`](https://pytorch.org/docs/master/generated/torch.nn.EmbeddingBag.html)。 然后,主控制器通过使用[`rpc_async`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.rpc_async)在每个教练上调用`_run_trainer`,循环遍历每个教练并开始训练循环。 最后,主人在退出之前等待所有训练结束。
......
......@@ -762,7 +762,7 @@ A3C 算法由 Google DeepMind 和 MILA 的联合团队于 2016 年 6 月发布
我们将在此处重点讨论,但是我们将在第 6 章,“异步方法”中进行更深入的探讨。 让我们从名称开始,即**异步优势演员评论家****A3C**)算法,然后将其解压缩以获得该算法的基本概述:
* **异步**:在 DQN 中,您还记得我们将神经网络与我们的智能体一起使用来预测动作。 这意味着只有一个智能体,并且它正在与一个环境交互。 A3C 所做的是创建智能体环境的多个副本,以使智能体更有效地学习。 A3C 具有一个全球网络和多个工作人员智能体,其中每个智能体都有其自己的一组网络参数,并且每个参数都与其环境副本同时进行交互,而无需与另一个智能体的环境进行交互。 它比单个智能体更好的原因是每个智能体的经验独立于其他智能体的经验。 因此,所有工人智能体的总体经验导致了各种各样的训练。
* **异步**:在 DQN 中,您还记得我们将神经网络与我们的智能体一起使用来预测动作。 这意味着只有一个智能体,并且它正在与一个环境交互。 A3C 所做的是创建智能体环境的多个副本,以使智能体更有效地学习。 A3C 具有一个全球网络和多个工作器智能体,其中每个智能体都有其自己的一组网络参数,并且每个参数都与其环境副本同时进行交互,而无需与另一个智能体的环境进行交互。 它比单个智能体更好的原因是每个智能体的经验独立于其他智能体的经验。 因此,所有工作器智能体的总体经验导致了各种各样的训练。
* **演员评论家**:演员评论家结合了值迭代和策略迭代的优势。 因此,网络将为给定状态 s 估计值函数`V(s)`和策略`π(s)`。 函数逼近器神经网络的顶部将有两个单独的全连接层,分别输出状态的值和状态策略。 智能体使用值,该值充当批评者来更新策略,即,智能角色。
* **优势**:策略梯度使用折扣收益告诉智能体该行动是好是坏。 用优势代替它不仅可以量化操作的好坏状态,而且可以更好地鼓励和劝阻操作(我们将在第 4 章,“策略梯度”中进行讨论)。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册