提交 6ad796ce 编写于 作者: W wizardforcel

2021-01-20 21:50:59

上级 234a38a6
......@@ -4,7 +4,7 @@
**作者**[Shen Li](https://mrshenli.github.io/)
模型并行在分布式训练技术中被广泛使用。 先前的帖子已经解释了如何使用 [DataParallel](https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html) 在多个 GPU 上训练神经网络; 此功能将相同的模型复制到所有 GPU,其中每个 GPU 消耗输入数据的不同分区。 尽管它可以极大地加快训练过程,但不适用于模型太大而无法容纳单个 GPU 的某些用例。 这篇文章展示了如何通过使用**模型并行**解决该问题,与`DataParallel`相比,该模型将单个模型拆分到不同的 GPU 上,而不是在每个 GPU 上复制整个模型(具体来说, 假设模型`m`包含 10 层:使用`DataParallel`时,每个 GPU 都具有这 10 层中的每一个的副本,而当在两个 GPU 上并行使用模型时,每个 GPU 可以承载 5 层。
模型并行在分布式训练技术中被广泛使用。 先前的帖子已经解释了如何使用[`DataParallel`](https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html)在多个 GPU 上训练神经网络; 此功能将相同的模型复制到所有 GPU,其中每个 GPU 消耗输入数据的不同分区。 尽管它可以极大地加快训练过程,但不适用于模型太大而无法容纳单个 GPU 的某些用例。 这篇文章展示了如何通过使用**模型并行**解决该问题,与`DataParallel`相比,该模型将单个模型拆分到不同的 GPU 上,而不是在每个 GPU 上复制整个模型(具体来说, 假设模型`m`包含 10 层:使用`DataParallel`时,每个 GPU 都具有这 10 层中的每一个的副本,而当在两个 GPU 上并行使用模型时,每个 GPU 可以承载 5 层。
模型并行化的高级思想是将模型的不同子网放置在不同的设备上,并相应地实现`forward`方法以在设备之间移动中间输出。 由于模型的一部分仅在任何单个设备上运行,因此一组设备可以共同为更大的模型服务。 在本文中,我们将不会尝试构建庞大的模型并将其压缩到有限数量的 GPU 中。 取而代之的是,本文着重展示并行模型的思想。 读者可以将这些想法应用到实际应用中。
......
......@@ -12,7 +12,7 @@
* [`DistributedDataParallel` API 文档](https://pytorch.org/docs/master/generated/torch.nn.parallel.DistributedDataParallel.html)
* [`DistributedDataParallel`注意事项](https://pytorch.org/docs/master/notes/ddp.html)
[`DistributedDataParallel`](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)(DDP)在模块级别实现可在多台计算机上运行的数据并行性。 使用 DDP 的应用程序应产生多个进程,并为每个进程创建一个 DDP 实例。 DDP 在[火炬。分布式](https://pytorch.org/tutorials/intermediate/dist_tuto.html)程序包中使用集体通信来同步梯度和缓冲区。 更具体地说,DDP 为`model.parameters()`给定的每个参数注册一个 Autograd 挂钩,当在后向传递中计算相应的梯度时,挂钩将触发。 然后,DDP 使用该信号触发跨进程的梯度同步。 有关更多详细信息,请参考 [DDP 设计说明](https://pytorch.org/docs/master/notes/ddp.html)
[`DistributedDataParallel`](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)(DDP)在模块级别实现可在多台计算机上运行的数据并行性。 使用 DDP 的应用程序应产生多个进程,并为每个进程创建一个 DDP 实例。 DDP 在[`torch.distributed`](https://pytorch.org/tutorials/intermediate/dist_tuto.html)程序包中使用集体通信来同步梯度和缓冲区。 更具体地说,DDP 为`model.parameters()`给定的每个参数注册一个 Autograd 挂钩,当在后向传递中计算相应的梯度时,挂钩将触发。 然后,DDP 使用该信号触发跨进程的梯度同步。 有关更多详细信息,请参考 [DDP 设计说明](https://pytorch.org/docs/master/notes/ddp.html)
推荐的使用 DDP 的方法是为每个模型副本生成一个进程,其中一个模型副本可以跨越多个设备。 DDP 进程可以放在同一台计算机上,也可以在多台计算机上,但是 GPU 设备不能在多个进程之间共享。 本教程从一个基本的 DDP 用例开始,然后演示了更高级的用例,包括检查点模型以及将 DDP 与模型并行结合。
......@@ -115,7 +115,7 @@ def run_demo(demo_fn, world_size):
## 歪斜的处理速度
在 DDP 中,构造函数,正向传递和反向传递都是分布式同步点。 预期不同的进程将启动相同数量的同步,并以相同的顺序到达这些同步点,并在大致相同的时间进入每个同步点。 否则,快速流程可能会提早到达,并在等待流浪者时超时。 因此,用户负责平衡流程之间的工作负载分配。 有时,由于例如网络延迟,资源争夺,不可预测的工作量峰值,不可避免地会出现处理速度偏差。 为了避免在这种情况下超时,请在调用 [init_process_group](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group) 时传递足够大的`timeout`值。
在 DDP 中,构造函数,正向传递和反向传递都是分布式同步点。 预期不同的进程将启动相同数量的同步,并以相同的顺序到达这些同步点,并在大致相同的时间进入每个同步点。 否则,快速流程可能会提早到达,并在等待流浪者时超时。 因此,用户负责平衡流程之间的工作负载分配。 有时,由于例如网络延迟,资源争夺,不可预测的工作量峰值,不可避免地会出现处理速度偏差。 为了避免在这种情况下超时,请在调用[`init_process_group`](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group)时传递足够大的`timeout`值。
## 保存和加载检查点
......
......@@ -14,7 +14,7 @@
PyTorch 中包含的分布式软件包(即`torch.distributed`)使研究人员和从业人员可以轻松地并行化他们在跨进程和机器集群的计算。 为此,它利用了传递消息的语义,从而允许每个进程将数据传递给任何其他进程。 与多处理包相反,进程可以使用不同的通信后端,而不仅限于在同一台计算机上执行。
为了开始,我们需要同时运行多个进程的能力。 如果您有权访问计算群集,则应咨询本地系统管理员或使用您喜欢的协调工具。 (例如 [pdsh](https://linux.die.net/man/1/pdsh)[clustershell](https://cea-hpc.github.io/clustershell/)[其他](https://slurm.schedmd.com/))。出于本教程的目的,我们将使用以下模板使用一台计算机并分叉多个进程。
为了开始,我们需要同时运行多个进程的能力。 如果您有权访问计算群集,则应咨询本地系统管理员或使用您喜欢的协调工具。 (例如 [pdsh](https://linux.die.net/man/1/pdsh)[clustershell](https://cea-hpc.github.io/clustershell/)[其他](https://slurm.schedmd.com/))。出于本教程的目的,我们将使用以下模板使用一台计算机并分叉多个进程。
```py
"""run.py:"""
......@@ -54,7 +54,7 @@ if __name__ == "__main__":
## 点对点通信
[![Send and Recv](img/f29264b289639882a61fb5c3447b1ecc.png)](../_img/send_recv.png)
![Send and Recv](img/f29264b289639882a61fb5c3447b1ecc.png)
发送和接收
......@@ -152,11 +152,11 @@ def run(rank, size):
## 分布式训练
**注意**您可以在此 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)的功能。 当然,这将是一个教学示例,在现实世界中,您应该使用上面链接的经过官方测试,优化的最佳版本。
很简单,我们想要实现随机梯度下降的分布式版本。 我们的脚本将允许所有进程在其数据批量上计算其模型的梯度,然后平均其梯度。 为了在更改进程数时确保相似的收敛结果,我们首先必须对数据集进行分区。 (您也可以使用 [tnt.dataset.SplitDataset](https://github.com/pytorch/tnt/blob/master/torchnet/dataset/splitdataset.py#L4) 代替下面的代码段。)
很简单,我们想要实现随机梯度下降的分布式版本。 我们的脚本将允许所有进程在其数据批量上计算其模型的梯度,然后平均其梯度。 为了在更改进程数时确保相似的收敛结果,我们首先必须对数据集进行分区。 (您也可以使用[`tnt.dataset.SplitDataset`](https://github.com/pytorch/tnt/blob/master/torchnet/dataset/splitdataset.py#L4)代替下面的代码段。)
```py
""" Dataset partitioning helper """
......@@ -259,7 +259,7 @@ def average_gradients(model):
等等! 我们成功实现了分布式同步 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
......@@ -293,7 +293,7 @@ def allreduce(send, recv):
```
在上面的脚本中,`allreduce(send, recv)`函数的签名与 PyTorch 中的签名略有不同。 它需要一个`recv`张量,并将所有`send`张量的总和存储在其中。 作为练习留给读者,我们的版本与 DeepSpeech 中的版本之间仍然有一个区别:它们的实现将梯度张量划分为*块*,以便最佳地利用通信带宽。 (提示: [torch.chunk](https://pytorch.org/docs/stable/torch.html#torch.chunk)
在上面的脚本中,`allreduce(send, recv)`函数的签名与 PyTorch 中的签名略有不同。 它需要一个`recv`张量,并将所有`send`张量的总和存储在其中。 作为练习留给读者,我们的版本与 DeepSpeech 中的版本之间仍然有一个区别:它们的实现将梯度张量划分为*块*,以便最佳地利用通信带宽。 (提示:[`torch.chunk`](https://pytorch.org/docs/stable/torch.html#torch.chunk)
## 高级主题
......@@ -320,9 +320,9 @@ def allreduce(send, recv):
**MPI 后端**
消息传递接口(MPI)是来自高性能计算领域的标准化工具。 它允许进行点对点和集体通信,并且是`torch.distributed` API 的主要灵感。 存在几种针对不同目的而优化的 MPI 实现(例如 [Open-MPI](https://www.open-mpi.org/)[MVAPICH2](http://mvapich.cse.ohio-state.edu/)[Intel MPI](https://software.intel.com/en-us/intel-mpi-library) )。 使用 MPI 后端的优势在于 MPI 在大型计算机群集上的广泛可用性-和高水平的优化。 [一些](https://developer.nvidia.com/mvapich) [最近的](https://developer.nvidia.com/ibm-spectrum-mpi) [实现](https://www.open-mpi.org/)也能够利用 CUDA IPC 和 GPU Direct 技术,以避免通过 CPU 进行内存复制。
消息传递接口(MPI)是来自高性能计算领域的标准化工具。 它允许进行点对点和集体通信,并且是`torch.distributed` API 的主要灵感。 存在几种针对不同目的而优化的 MPI 实现(例如 [Open-MPI](https://www.open-mpi.org/)[MVAPICH2](http://mvapich.cse.ohio-state.edu/)[Intel MPI](https://software.intel.com/en-us/intel-mpi-library))。 使用 MPI 后端的优势在于 MPI 在大型计算机群集上的广泛可用性-和高水平的优化。 [一些](https://developer.nvidia.com/mvapich)[最近的](https://developer.nvidia.com/ibm-spectrum-mpi)[实现](https://www.open-mpi.org/)也能够利用 CUDA IPC 和 GPU Direct 技术,以避免通过 CPU 进行内存复制。
不幸的是,PyTorch 的二进制文件不能包含 MPI 实现,我们将不得不手动对其进行重新编译。 幸运的是,鉴于编译后,PyTorch 会单独查看以查找可用的 MPI 实现,因此此过程相当简单。 以下步骤通过从源安装 PyTorch [来安装 MPI 后端。](https://github.com/pytorch/pytorch#from-source)
不幸的是,PyTorch 的二进制文件不能包含 MPI 实现,我们将不得不手动对其进行重新编译。 幸运的是,鉴于编译后,PyTorch 会单独查看以查找可用的 MPI 实现,因此此过程相当简单。 [以下步骤通过从源安装 PyTorch 来安装 MPI 后端](https://github.com/pytorch/pytorch#from-source)
1. 创建并激活 Anaconda 环境,按照[指南](https://github.com/pytorch/pytorch#from-source)的要求安装所有先决条件,但是**尚未**运行。
2. 选择并安装您喜欢的 MPI 实现。 请注意,启用支持 CUDA 的 MPI 可能需要一些其他步骤。 在我们的情况下,我们将坚持不支持 GPU 的 Open-MPI:`conda install -c conda-forge openmpi`
......@@ -333,7 +333,7 @@ def allreduce(send, recv):
1.`if __name__ == '__main__':`下的内容替换为`init_process(0, 0, run, backend='mpi')`
2. 运行`mpirun -n 4 python myscript.py`
这些更改的原因是,MPI 需要在生成进程之前创建自己的环境。 MPI 还将生成自己的进程,并执行[初始化方法](#initialization-methods)中描述的握手,使`init_process_group``rank``size`参数多余。 实际上,这非常强大,因为您可以将其他参数传递给`mpirun`,以便为每个进程定制计算资源。 (诸如每个进程的内核数,将计算机手动分配给特定级别,以及[](https://www.open-mpi.org/faq/?category=running#mpirun-hostfile)之类的东西。)这样做,您应该获得与其他通信后端相同的熟悉输出。
这些更改的原因是,MPI 需要在生成进程之前创建自己的环境。 MPI 还将生成自己的进程,并执行[初始化方法](#initialization-methods)中描述的握手,使`init_process_group``rank``size`参数多余。 实际上,这非常强大,因为您可以将其他参数传递给`mpirun`,以便为每个进程定制计算资源。 (诸如每个进程的内核数,将计算机手动分配给特定级别之类的东西,以及[其它](https://www.open-mpi.org/faq/?category=running#mpirun-hostfile)。)这样做,您应该获得与其他通信后端相同的熟悉输出。
**NCCL 后端**
......@@ -354,7 +354,7 @@ def allreduce(send, recv):
**共享文件系统**
共享文件系统要求所有进程都有权访问共享文件系统,并将通过共享文件进行协调。 这意味着每个进程都将打开文件,写入文件信息,然后等到每个人都打开文件。 之后,所有必需的信息将可用于所有过程。 为了避免争用情况,文件系统必须通过 [fcntl](http://man7.org/linux/man-pages/man2/fcntl.2.html) 支持锁定。
共享文件系统要求所有进程都有权访问共享文件系统,并将通过共享文件进行协调。 这意味着每个进程都将打开文件,写入文件信息,然后等到每个人都打开文件。 之后,所有必需的信息将可用于所有过程。 为了避免争用情况,文件系统必须通过[`fcntl`](http://man7.org/linux/man-pages/man2/fcntl.2.html)支持锁定。
```py
dist.init_process_group(
......
......@@ -11,12 +11,12 @@
本教程使用两个简单的示例来演示如何使用[`torch.distributed.rpc`](https://pytorch.org/docs/master/rpc.html)包构建分布式训练,该包首先在 PyTorch v1.4 中作为原型功能引入。 这两个示例的源代码可以在 [PyTorch 示例](https://github.com/pytorch/examples)中找到。
先前的教程[分布式数据并行入门](ddp_tutorial.html)[使用 PyTorch](dist_tuto.html) 编写分布式应用程序,描述了 [DistributedDataParallel](https://pytorch.org/docs/stable/_modules/torch/nn/parallel/distributed.html) ,该模型支持特定的训练范例,该模型可在多个过程之间复制模型 每个进程都处理输入数据的拆分。 有时,您可能会遇到需要不同训练范例的场景。 例如:
先前的教程[分布式数据并行入门](ddp_tutorial.html)[使用 PyTorch](dist_tuto.html) 编写分布式应用程序,描述了[`DistributedDataParallel`](https://pytorch.org/docs/stable/_modules/torch/nn/parallel/distributed.html),该模型支持特定的训练范例,该模型可在多个过程之间复制模型 每个进程都处理输入数据的拆分。 有时,您可能会遇到需要不同训练范例的场景。 例如:
1. 在强化学习中,从环境中获取训练数据可能相对昂贵,而模型本身可能很小。 在这种情况下,产生多个并行运行的观察者并共享一个代理可能会很有用。 在这种情况下,代理将在本地负责训练,但是应用程序仍将需要库在观察者和训练者之间发送和接收数据。
2. 您的模型可能太大,无法容纳在一台计算机上的 GPU 中,因此需要一个库来帮助将模型拆分到多台计算机上。 或者,您可能正在实现[参数服务器](https://www.cs.cmu.edu/~muli/file/parameter_server_osdi14.pdf)训练框架,其中模型参数和训练器位于不同的机器上。
[torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 程序包可以帮助解决上述情况。 在情况 1 中, [RPC](https://pytorch.org/docs/master/rpc.html#rpc)[RRef](https://pytorch.org/docs/master/rpc.html#rref) 允许将数据从一个工作程序发送到另一个工作程序,同时轻松引用远程数据对象。 在情况 2 中,[分布式 autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework)[分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim)使执行反向传递和优化器步骤就像本地训练一样。 在接下来的两节中,我们将使用强化学习示例和语言模型示例来演示 [torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 的 API。 请注意,本教程并非旨在构建最准确或最有效的模型来解决给定的问题,相反,此处的主要目标是演示如何使用 [torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 包来构建分布式训练 应用程序。
[`torch.distributed.rpc`](https://pytorch.org/docs/master/rpc.html)程序包可以帮助解决上述情况。 在情况 1 中, [RPC](https://pytorch.org/docs/master/rpc.html#rpc)[RRef](https://pytorch.org/docs/master/rpc.html#rref) 允许将数据从一个工作程序发送到另一个工作程序,同时轻松引用远程数据对象。 在情况 2 中,[分布式 Autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework)[分布式优化器](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim)使执行反向传递和优化器步骤就像本地训练一样。 在接下来的两节中,我们将使用强化学习示例和语言模型示例来演示[`torch.distributed.rpc`](https://pytorch.org/docs/master/rpc.html)的 API。 请注意,本教程并非旨在构建最准确或最有效的模型来解决给定的问题,相反,此处的主要目标是演示如何使用[`torch.distributed.rpc`](https://pytorch.org/docs/master/rpc.html)包来构建分布式训练 应用程序。
## 使用 RPC 和 RRef 进行分布式强化学习
......@@ -46,7 +46,7 @@ class Policy(nn.Module):
```
首先,让我们准备一个帮助程序,以在`RRef`的所有者工作程序上远程运行功能。 您将在本教程的示例中的多个地方发现该功能。 理想情况下,`torch.distributed.rpc`软件包应立即提供这些帮助程序功能。 例如,如果应用程序可以直接调用`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
from torch.distributed.rpc import rpc_sync
......@@ -214,7 +214,7 @@ class Agent:
```
使用`Policy``Observer``Agent`类,我们准备启动多个过程来执行分布式训练。 在此示例中,所有进程都运行相同的`run_worker`函数,并且它们使用等级来区分其角色。 等级 0 始终是代理,其他所有等级都是观察者。 代理通过重复调用`run_episode``finish_episode`作为主设备,直到运行的奖励超过环境指定的奖励阈值为止。 所有观察者都被动地等待来自代理的命令。 该代码由 [rpc.init_rpc](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.init_rpc)[rpc.shutdown](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.shutdown) 包装,它们分别初始化和终止 RPC 实例。 [API 页面](https://pytorch.org/docs/master/rpc.html)中提供了更多详细信息。
使用`Policy``Observer``Agent`类,我们准备启动多个过程来执行分布式训练。 在此示例中,所有进程都运行相同的`run_worker`函数,并且它们使用等级来区分其角色。 等级 0 始终是代理,其他所有等级都是观察者。 代理通过重复调用`run_episode``finish_episode`作为主设备,直到运行的奖励超过环境指定的奖励阈值为止。 所有观察者都被动地等待来自代理的命令。 该代码由[`rpc.init_rpc`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.init_rpc)[`rpc.shutdown`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.shutdown)包装,它们分别初始化和终止 RPC 实例。 [API 页面](https://pytorch.org/docs/master/rpc.html)中提供了更多详细信息。
```py
import os
......
......@@ -77,7 +77,7 @@ class Net(nn.Module):
```
接下来,让我们定义一些辅助功能,这些功能将对其余脚本有用。 下面使用 [rpc_sync](https://pytorch.org/docs/stable/rpc.html#torch.distributed.rpc.rpc_sync) [RRef](https://pytorch.org/docs/stable/rpc.html#torch.distributed.rpc.RRef) 来定义一个函数,该函数在远程节点上的对象上调用给定方法。 下面,通过`rref`参数指定了对远程对象的句柄,并在其拥有的节点`rref.owner()`上运行它。 在调用者节点上,我们通过使用`rpc_sync`同步运行此命令,这意味着我们将阻塞直到收到响应。
接下来,让我们定义一些辅助功能,这些功能将对其余脚本有用。 下面使用[`rpc_sync`](https://pytorch.org/docs/stable/rpc.html#torch.distributed.rpc.rpc_sync)[RRef](https://pytorch.org/docs/stable/rpc.html#torch.distributed.rpc.RRef) 来定义一个函数,该函数在远程节点上的对象上调用给定方法。 下面,通过`rref`参数指定了对远程对象的句柄,并在其拥有的节点`rref.owner()`上运行它。 在调用者节点上,我们通过使用`rpc_sync`同步运行此命令,这意味着我们将阻塞直到收到响应。
```py
# --------- Helper Methods --------------------
......@@ -132,7 +132,7 @@ class ParameterServer(nn.Module):
```
接下来,我们将定义一些其他功能,可用于训练和验证。 第一个`get_dist_gradients`将采用分布式 Autograd 上下文 ID,并调用`dist_autograd.get_gradients` API,以检索由分布式 Autograd 计算的梯度。 可以在[分布式 Autograd 文档](https://pytorch.org/docs/stable/rpc.html#distributed-autograd-framework)中找到更多信息。 请注意,由于该框架当前仅支持通过 RPC 发送张量,因此我们还会迭代生成的字典并将每个张量转换为 CPU 张量。 接下来,`get_param_rrefs`将迭代我们的模型参数,并将它们包装为(本地) [RRef](https://pytorch.org/docs/stable/rpc.html#torch.distributed.rpc.RRef) 。 训练者节点将通过 RPC 调用此方法,并将返回要优化的参数列表。 这是[分布式优化器](https://pytorch.org/docs/stable/rpc.html#module-torch.distributed.optim)的输入,它需要所有必须优化的参数作为`RRef`的列表。
接下来,我们将定义一些其他功能,可用于训练和验证。 第一个`get_dist_gradients`将采用分布式 Autograd 上下文 ID,并调用`dist_autograd.get_gradients` API,以检索由分布式 Autograd 计算的梯度。 可以在[分布式 Autograd 文档](https://pytorch.org/docs/stable/rpc.html#distributed-autograd-framework)中找到更多信息。 请注意,由于该框架当前仅支持通过 RPC 发送张量,因此我们还会迭代生成的字典并将每个张量转换为 CPU 张量。 接下来,`get_param_rrefs`将迭代我们的模型参数,并将它们包装为(本地)[RRef](https://pytorch.org/docs/stable/rpc.html#torch.distributed.rpc.RRef)。 训练者节点将通过 RPC 调用此方法,并将返回要优化的参数列表。 这是[分布式优化器](https://pytorch.org/docs/stable/rpc.html#module-torch.distributed.optim)的输入,它需要所有必须优化的参数作为`RRef`的列表。
```py
# Use dist autograd to retrieve gradients accumulated for this model.
......@@ -208,7 +208,7 @@ class TrainerNet(nn.Module):
```
接下来,我们将定义一个名为`get_global_param_rrefs`的方法。 为了激发对这种方法的需求,值得阅读 [DistributedOptimizer](https://pytorch.org/docs/stable/rpc.html#module-torch.distributed.optim) 上的文档,尤其是 API 签名。 必须向优化器传递与要优化的远程参数相对应的`RRef`列表,因此在这里我们获得了必要的`RRef`。 由于给定`TrainerNet`与之交互的唯一远程工作者是`ParameterServer`,因此我们只需在`ParameterServer`上调用`remote_method`。 我们使用在`ParameterServer`类中定义的`get_param_rrefs`方法。 此方法将`RRef`的列表返回到需要优化的参数。 请注意,在这种情况下,我们的`TrainerNet`没有定义自己的参数; 如果确实如此,我们还需要将每个参数都包装在`RRef`中,并将其包含在`DistributedOptimizer`的输入中。
接下来,我们将定义一个名为`get_global_param_rrefs`的方法。 为了激发对这种方法的需求,值得阅读[`DistributedOptimizer`](https://pytorch.org/docs/stable/rpc.html#module-torch.distributed.optim)上的文档,尤其是 API 签名。 必须向优化器传递与要优化的远程参数相对应的`RRef`列表,因此在这里我们获得了必要的`RRef`。 由于给定`TrainerNet`与之交互的唯一远程工作者是`ParameterServer`,因此我们只需在`ParameterServer`上调用`remote_method`。 我们使用在`ParameterServer`类中定义的`get_param_rrefs`方法。 此方法将`RRef`的列表返回到需要优化的参数。 请注意,在这种情况下,我们的`TrainerNet`没有定义自己的参数; 如果确实如此,我们还需要将每个参数都包装在`RRef`中,并将其包含在`DistributedOptimizer`的输入中。
```py
class TrainerNet(nn.Module):
......@@ -248,7 +248,7 @@ def run_training_loop(rank, num_gpus, train_loader, test_loader):
```
接下来,我们定义我们的主要训练循环。 我们遍历了 PyTorch 的 [DataLoader](https://pytorch.org/docs/stable/data.html) 提供的可迭代项。 在编写典型的前向/后向/优化器循环之前,我们首先将逻辑包装在[分布式 Autograd 上下文](https://pytorch.org/docs/stable/rpc.html#torch.distributed.autograd.context)中。 请注意,这需要记录在模型的前向传递中调用的 RPC,以便可以构造一个适当的图,其中包括在后向传递中所有参与的分布式工作者。 分布式 Autograd 上下文返回`context_id`,它用作用于累积和优化与特定迭代对应的梯度的标识符。
接下来,我们定义我们的主要训练循环。 我们遍历了 PyTorch 的[`DataLoader`](https://pytorch.org/docs/stable/data.html)提供的可迭代项。 在编写典型的前向/后向/优化器循环之前,我们首先将逻辑包装在[分布式 Autograd 上下文](https://pytorch.org/docs/stable/rpc.html#torch.distributed.autograd.context)中。 请注意,这需要记录在模型的前向传递中调用的 RPC,以便可以构造一个适当的图,其中包括在后向传递中所有参与的分布式工作者。 分布式 Autograd 上下文返回`context_id`,它用作用于累积和优化与特定迭代对应的梯度的标识符。
与调用典型的`loss.backward()`会启动此本地工作程序的向后传递相反,我们调用`dist_autograd.backward()`并传递我们的`context_id``loss`,这是我们希望向后传递的根 开始。 另外,我们将此`context_id`传递到优化程序调用中,该调用程序必须能够在所有节点上查找由该特定向后传递计算出的相应梯度。
......
......@@ -19,17 +19,17 @@
注意
本教程的完整源代码可以在 [pytorch / examples](https://github.com/pytorch/examples/tree/master/distributed/rpc/pipeline) 中找到。
本教程的完整源代码可以在[`pytorch/examples`](https://github.com/pytorch/examples/tree/master/distributed/rpc/pipeline)中找到。
## 基础知识
上一教程[分布式 RPC 框架入门](rpc_tutorial.html)显示了如何使用 [torch.distributed.rpc](https://pytorch.org/docs/master/rpc.html) 为 RNN 模型实现分布式模型并行性。 该教程使用一个 GPU 来托管`EmbeddingTable`,并且提供的代码可以正常工作。 但是,如果模型驻留在多个 GPU 上,则将需要一些额外的步骤来增加所有 GPU 的摊销利用率。 管道并行性是在这种情况下可以提供帮助的一种范例。
上一教程[分布式 RPC 框架入门](rpc_tutorial.html)显示了如何使用[`torch.distributed.rpc`](https://pytorch.org/docs/master/rpc.html)为 RNN 模型实现分布式模型并行性。 该教程使用一个 GPU 来托管`EmbeddingTable`,并且提供的代码可以正常工作。 但是,如果模型驻留在多个 GPU 上,则将需要一些额外的步骤来增加所有 GPU 的摊销利用率。 管道并行性是在这种情况下可以提供帮助的一种范例。
在本教程中,我们使用`ResNet50`作为示例模型,[单机模型并行最佳实践](model_parallel_tutorial.html)教程也使用了该模型。 类似地,`ResNet50`模型被分为两个碎片,输入批量被划分为多个拆分,并以流水线方式馈入两个模型碎片。 区别在于,本教程将调用异步 RPC,而不是使用 CUDA 流来并行执行。 因此,本教程中介绍的解决方案也可以跨计算机边界使用。 本教程的其余部分分四个步骤介绍了实现。
## 第 1 步:对 ResNet50 模型进行分区
这是在两个模型分片中实现`ResNet50`的准备步骤。 以下代码是从`torchvision`中的 [ResNet 实现中借用的。 `ResNetBase`模块包含两个 ResNet 碎片的通用构件和属性。](https://github.com/pytorch/vision/blob/7c077f6a986f05383bcb86b535aedb5a63dd5c4b/torchvision/models/resnet.py#L124)
这是在两个模型分片中实现`ResNet50`的准备步骤。 以下代码是从`torchvision`中的 [ResNet 实现](https://github.com/pytorch/vision/blob/7c077f6a986f05383bcb86b535aedb5a63dd5c4b/torchvision/models/resnet.py#L124)中借用的。 `ResNetBase`模块包含两个 ResNet 碎片的通用构件和属性。
```py
import threading
......
......@@ -11,7 +11,7 @@
* [使用分布式 RPC 框架](rpc_param_server_tutorial.html)实施参数服务器
* [RPC 异步执行装饰器](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)
本教程演示了如何使用[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰器来构建批量 RPC 应用程序,该装饰器通过减少阻止的 RPC 线程数和合并被调用方上的 CUDA 操作来帮助加快训练速度。 这与与 TorchServer 进行[批量推断具有相同的想法。](https://pytorch.org/serve/batch_inference_with_ts.html)
本教程演示了如何使用[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰器来构建批量 RPC 应用程序,该装饰器通过减少阻止的 RPC 线程数和合并被调用方上的 CUDA 操作来帮助加快训练速度。 这使用 TorchServer 的相同想法进行[批量推断](https://pytorch.org/serve/batch_inference_with_ts.html)
注意
......@@ -19,16 +19,16 @@
## 基础知识
先前的教程显示了使用 [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 通过引入两个新概念来解决此问题:
* [torch.futures.Future](https://pytorch.org/docs/master/futures.html) 类型封装了异步执行,还支持安装回调函数。
* 一个 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰器,允许应用程序告诉被调用方目标函数将返回将来的函数,并且在执行期间可以暂停并产生多次。
* [`torch.futures.Future`](https://pytorch.org/docs/master/futures.html) 类型封装了异步执行,还支持安装回调函数。
* 一个[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰器,允许应用程序告诉被调用方目标函数将返回将来的函数,并且在执行期间可以暂停并产生多次。
使用这两个工具,应用程序代码可以将用户功能分解为多个较小的功能,将它们作为`Future`对象上的回调链接在一起,然后返回包含最终结果的`Future`。 在被调用方,当获取`Future`对象时,它还将安装后续的 RPC 响应准备和通讯作为回调,这将在最终结果准备好时触发。 这样,被调用者不再需要阻塞一个线程并等待直到最终返回值准备就绪。 有关简单示例,请参考[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 的 API 文档。
除了减少被调用方上的空闲线程数之外,这些工具还有助于使批量 RPC 处理更容易,更快捷。 本教程的以下两节演示了如何使用 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰器来构建分布式批更新参数服务器和批量强化学习应用程序。
除了减少被调用方上的空闲线程数之外,这些工具还有助于使批量 RPC 处理更容易,更快捷。 本教程的以下两节演示了如何使用[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰器来构建分布式批更新参数服务器和批量强化学习应用程序。
## 批量更新参数服务器
......@@ -124,13 +124,13 @@ class Trainer(object):
```
在本教程中,我们将跳过启动多个进程的代码,有关完整实现,请参考[示例](https://github.com/pytorch/examples/tree/master/distributed/rpc)回购。 请注意,可以在没有 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰器的情况下实现批量。 但是,这将需要在 PS 上阻塞更多的 RPC 线程,或者使用另一轮 RPC 来获取更新的模型,后者将增加代码的复杂性和通信开销。
在本教程中,我们将跳过启动多个进程的代码,有关完整实现,请参考[示例](https://github.com/pytorch/examples/tree/master/distributed/rpc)回购。 请注意,可以在没有[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰器的情况下实现批量。 但是,这将需要在 PS 上阻塞更多的 RPC 线程,或者使用另一轮 RPC 来获取更新的模型,后者将增加代码的复杂性和通信开销。
本节使用一个简单的参数服务器训练示例来说明如何使用 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰器实现批量 RPC 应用程序。 在下一节中,我们将使用批量重新实现上一[分布式 RPC 框架](https://pytorch.org/tutorials/intermediate/rpc_tutorial.html)入门指南中的强化学习示例,并演示其对训练速度的影响。
本节使用一个简单的参数服务器训练示例来说明如何使用[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰器实现批量 RPC 应用程序。 在下一节中,我们将使用批量重新实现上一[分布式 RPC 框架](https://pytorch.org/tutorials/intermediate/rpc_tutorial.html)入门指南中的强化学习示例,并演示其对训练速度的影响。
## 批量 CartPole 解算器
本节以 [OpenAI Gym](https://gym.openai.com/) 中的 CartPole-v1 为例,说明批量 RPC 的性能影响。 请注意,我们的目标是演示 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 的用法,而不是构建最佳的 CartPole 求解器或解决大多数不同的 RL 问题,我们使用非常简单的策略和奖励计算策略,并将重点放在 多观察者单代理批量 RPC 实现。 我们使用与前面的教程类似的`Policy`模型,如下所示。 与上一教程相比,不同之处在于其构造函数使用了一个附加的`batch`参数来控制`F.softmax``dim`参数,因为进行批量时,`forward`函数中的`x`参数包含来自多个观察者的状态 因此尺寸需要适当更改。 其他所有内容保持不变。
本节以 [OpenAI Gym](https://gym.openai.com/) 中的 CartPole-v1 为例,说明批量 RPC 的性能影响。 请注意,我们的目标是演示[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)的用法,而不是构建最佳的 CartPole 求解器或解决大多数不同的 RL 问题,我们使用非常简单的策略和奖励计算策略,并将重点放在 多观察者单代理批量 RPC 实现。 我们使用与前面的教程类似的`Policy`模型,如下所示。 与上一教程相比,不同之处在于其构造函数使用了一个附加的`batch`参数来控制`F.softmax``dim`参数,因为进行批量时,`forward`函数中的`x`参数包含来自多个观察者的状态 因此尺寸需要适当更改。 其他所有内容保持不变。
```py
import argparse
......@@ -165,7 +165,7 @@ class Policy(nn.Module):
```
`Observer`的构造函数也会相应地进行调整。 它还带有`batch`参数,该参数控制用于选择动作的`Agent`函数。 在批量模式下,它将调用`Agent`上的`select_action_batch`函数,该函数将很快出现,并且该函数将以 [@ rpc.functions.async_execution](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution) 装饰。
`Observer`的构造函数也会相应地进行调整。 它还带有`batch`参数,该参数控制用于选择动作的`Agent`函数。 在批量模式下,它将调用`Agent`上的`select_action_batch`函数,该函数将很快出现,并且该函数将以[`@rpc.functions.async_execution`](https://pytorch.org/docs/master/rpc.html#torch.distributed.rpc.functions.async_execution)装饰。
```py
import gym
......@@ -387,7 +387,7 @@ if __name__ == '__main__':
## 了解更多
* [批量更新参数服务器源代码](https://github.com/pytorch/examples/blob/master/distributed/rpc/batch/parameter_server.py)
* [批量更新参数服务器源代码](https://github.com/pytorch/examples/blob/master/distributed/rpc/batch/parameter_server.py)
* [批量 CartPole 解算器](https://github.com/pytorch/examples/blob/master/distributed/rpc/batch/reinforce.py)
* [分布式 Autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework)
* [分布式管道并行性](dist_pipeline_parallel_tutorial.html)
\ No newline at end of file
......@@ -15,11 +15,11 @@
1. 1 个主机,负责在参数服务器上创建嵌入表(`nn.EmbeddingBag`)。 主人还会在两个教练上驱动训练循环。
2. 1 参数服务器,它基本上将嵌入表保存在内存中,并响应来自主服务器和训练器的 RPC。
3. 2 个训练器,用于存储 FC 层(线性线性),并使用 [DistributedDataParallel](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel) 在它们之间进行复制。 训练人员还负责执行前进,后退和优化器步骤。
3. 2 个训练器,用于存储 FC 层(线性线性),并使用[`DistributedDataParallel`](https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel)在它们之间进行复制。 训练人员还负责执行前进,后退和优化器步骤。
整个训练过程执行如下:
1. 主服务器在参数服务器上创建一个嵌入表,并为其保留一个 [RRef](https://pytorch.org/docs/master/rpc.html#rref)
1. 主服务器在参数服务器上创建一个嵌入表,并为其保留一个 [RRef](https://pytorch.org/docs/master/rpc.html#rref)
2. 然后,主持人开始在训练器上进行训练循环,并将嵌入表 RRef 传递给训练器。
3. 训练器创建一个`HybridModel`,该`HybridModel`首先使用主机提供的嵌入表 RRef 执行嵌入查找,然后执行包装在 DDP 中的 FC 层。
4. 训练者执行模型的正向传递,并使用[分布式 Autograd](https://pytorch.org/docs/master/rpc.html#distributed-autograd-framework) 使用损失执行反向传递。
......@@ -33,9 +33,9 @@
现在,让我们详细介绍每个部分。 首先,我们需要先设置所有工人,然后才能进行任何训练。 我们创建 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`,循环遍历每个教练并开始训练循环。 最后,主人在退出之前等待所有训练结束。
我们使用 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`,循环遍历每个教练并开始训练循环。 最后,主人在退出之前等待所有训练结束。
训练器首先使用 [init_process_group](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group) `world_size = 2`的 DDP 初始化`ProcessGroup`(对于两个训练器)。 接下来,他们使用 TCP `init_method`初始化 RPC 框架。 请注意,RPC 初始化和`ProcessGroup`初始化中的端口不同。 这是为了避免两个框架的初始化之间的端口冲突。 初始化完成后,训练器只需等待主服务器的`_run_trainer` RPC。
训练器首先使用[`init_process_group`](https://pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group)`world_size = 2`的 DDP 初始化`ProcessGroup`(对于两个训练器)。 接下来,他们使用 TCP `init_method`初始化 RPC 框架。 请注意,RPC 初始化和`ProcessGroup`初始化中的端口不同。 这是为了避免两个框架的初始化之间的端口冲突。 初始化完成后,训练器只需等待主服务器的`_run_trainer` RPC。
参数服务器只是初始化 RPC 框架,并等待来自训练者和主服务器的 RPC。
......@@ -140,7 +140,7 @@ class HybridModel(torch.nn.Module):
接下来,让我们看看训练器上的设置。 训练者首先使用对参数服务器上嵌入表的 RRef 及其自身等级创建上述`HybridModel`
现在,我们需要检索要使用 [DistributedOptimizer](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim) 优化的所有参数的 RRef 列表。 为了从参数服务器中检索嵌入表的参数,我们定义了一个简单的辅助函数`_retrieve_embedding_parameters`,该函数基本上遍历了嵌入表的所有参数并返回 RRef 的列表。 训练器通过 RPC 在参数服务器上调用此方法,以接收所需参数的 RRef 列表。 由于`DistributedOptimizer`始终将需要优化的参数的 RRef 列表,因此我们甚至需要为 FC 层的本地参数创建 RRef。 这是通过遍历`model.parameters()`,为每个参数创建 RRef 并将其附加到列表来完成的。 请注意,`model.parameters()`仅返回本地参数,不包含`emb_rref`
现在,我们需要检索要使用[`DistributedOptimizer`](https://pytorch.org/docs/master/rpc.html#module-torch.distributed.optim)优化的所有参数的 RRef 列表。 为了从参数服务器中检索嵌入表的参数,我们定义了一个简单的辅助函数`_retrieve_embedding_parameters`,该函数基本上遍历了嵌入表的所有参数并返回 RRef 的列表。 训练器通过 RPC 在参数服务器上调用此方法,以接收所需参数的 RRef 列表。 由于`DistributedOptimizer`始终将需要优化的参数的 RRef 列表,因此我们甚至需要为 FC 层的本地参数创建 RRef。 这是通过遍历`model.parameters()`,为每个参数创建 RRef 并将其附加到列表来完成的。 请注意,`model.parameters()`仅返回本地参数,不包含`emb_rref`
最后,我们使用所有 RRef 创建我们的`DistributedOptimizer`,并定义`CrossEntropyLoss`函数。
......@@ -231,4 +231,4 @@ def _run_trainer(emb_rref, rank):
```
整个示例的源代码可以在中找到[](https://github.com/pytorch/examples/tree/master/distributed/rpc/ddp_rpc)
\ No newline at end of file
[整个示例的源代码可以在这里找到](https://github.com/pytorch/examples/tree/master/distributed/rpc/ddp_rpc)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册