torchrun(弹性启动)¶
torchrun 是 torch.distributed.launch 功能的超集。
torchrun 提供了 torch.distributed.launch 的所有功能,并增加了以下功能
- 工作进程故障会通过重新启动所有工作进程来优雅地处理。 
- 工作进程的 - RANK和- WORLD_SIZE会自动分配。
- 节点数量可以在最小值和最大值之间变化(弹性)。 
注意
torchrun 是一个 Python 控制台脚本,指向主模块 torch.distributed.run,该模块在 setup.py 中的 entry_points 配置中声明。它等效于调用 python -m torch.distributed.run。
从 torch.distributed.launch 迁移到 torchrun¶
torchrun 支持与 torch.distributed.launch 相同的参数,**除了** --use-env,该参数现已弃用。要从 torch.distributed.launch 迁移到 torchrun,请按照以下步骤操作
- 如果您的训练脚本已经从 - LOCAL_RANK环境变量中读取- local_rank。那么您只需省略- --use-env标志,例如:- torch.distributed.launch- torchrun- $ python -m torch.distributed.launch --use-env train_script.py - $ torchrun train_script.py 
- 如果您的训练脚本从 - --local-rank命令行参数中读取本地排名。请将您的训练脚本更改为从- LOCAL_RANK环境变量中读取,如下面的代码片段所示- torch.distributed.launch- torchrun- import argparse parser = argparse.ArgumentParser() parser.add_argument("--local-rank", type=int) args = parser.parse_args() local_rank = args.local_rank - import os local_rank = int(os.environ["LOCAL_RANK"]) 
上述更改足以从 torch.distributed.launch 迁移到 torchrun。要利用 torchrun 的新功能,例如弹性、容错和错误报告,请参考
- 训练脚本,以获取有关编写与 - torchrun兼容的训练脚本的更多信息。
- 本页面的其余部分,以获取有关 - torchrun功能的更多信息。
用法¶
单节点多工作器¶
torchrun
    --standalone
    --nnodes=1
    --nproc-per-node=$NUM_TRAINERS
    YOUR_TRAINING_SCRIPT.py (--arg1 ... train script args...)
堆叠单节点多工作器¶
要在同一主机上运行多个单节点多工作进程实例(独立作业),我们需要确保每个实例(作业)都设置在不同的端口上,以避免端口冲突(或更糟的是,两个作业合并为一个作业)。为此,您必须使用 --rdzv-backend=c10d 运行,并通过设置 --rdzv-endpoint=localhost:$PORT_k 指定不同的端口。对于 --nodes=1,通常让 torchrun 自动选择一个空闲的随机端口,而不是为每次运行手动分配不同的端口。
torchrun
    --rdzv-backend=c10d
    --rdzv-endpoint=localhost:0
    --nnodes=1
    --nproc-per-node=$NUM_TRAINERS
    YOUR_TRAINING_SCRIPT.py (--arg1 ... train script args...)
容错(固定数量的工作进程,无弹性,容忍 3 次故障)¶
torchrun
    --nnodes=$NUM_NODES
    --nproc-per-node=$NUM_TRAINERS
    --max-restarts=3
    --rdzv-id=$JOB_ID
    --rdzv-backend=c10d
    --rdzv-endpoint=$HOST_NODE_ADDR
    YOUR_TRAINING_SCRIPT.py (--arg1 ... train script args...)
HOST_NODE_ADDR,格式为 <主机>[:<端口>](例如 node1.example.com:29400),指定 C10d 会合后端应实例化和托管的节点和端口。它可以是您的训练集群中的任何节点,但理想情况下,您应该选择带宽较高的节点。
注意
如果没有指定端口号,HOST_NODE_ADDR 默认值为 29400。
弹性(min=1,max=4,容忍最多 3 次成员更改或故障)¶
torchrun
    --nnodes=1:4
    --nproc-per-node=$NUM_TRAINERS
    --max-restarts=3
    --rdzv-id=$JOB_ID
    --rdzv-backend=c10d
    --rdzv-endpoint=$HOST_NODE_ADDR
    YOUR_TRAINING_SCRIPT.py (--arg1 ... train script args...)
HOST_NODE_ADDR,格式为 <主机>[:<端口>](例如 node1.example.com:29400),指定 C10d 会合后端应实例化和托管的节点和端口。它可以是您的训练集群中的任何节点,但理想情况下,您应该选择带宽较高的节点。
注意
如果没有指定端口号,HOST_NODE_ADDR 默认值为 29400。
关于会合后端的说明¶
对于多节点训练,您需要指定
- --rdzv-id:一个唯一的作业 ID(由参与作业的所有节点共享)
- --rdzv-backend:- torch.distributed.elastic.rendezvous.RendezvousHandler的实现
- --rdzv-endpoint:会合后端运行的端点;通常为- host:port格式。
目前,c10d(推荐)、etcd-v2 和 etcd(旧版)会合后端开箱即用。要使用 etcd-v2 或 etcd,请设置一个启用了 v2 API 的 etcd 服务器(例如 --enable-v2)。
警告
etcd-v2 和 etcd 协调使用 etcd API v2。您必须在 etcd 服务器上启用 v2 API。我们的测试使用 etcd v3.4.3。
警告
对于基于 etcd 的协调,我们建议使用 etcd-v2 而不是 etcd,后者在功能上等效,但使用了修改后的实现。 etcd 处于维护模式,将在未来版本中移除。
定义¶
- Node- 物理实例或容器;映射到作业管理器使用的单元。
- Worker- 分布式训练中的工作器。
- WorkerGroup- 执行相同功能的工作器集合(例如训练器)。
- LocalWorkerGroup- 在同一节点上运行的工作器组中的工作器子集。
- RANK- 工作器在工作器组中的排名。
- WORLD_SIZE- 工作器组中工作器的总数。
- LOCAL_RANK- 工作器在本地工作器组中的排名。
- LOCAL_WORLD_SIZE- 本地工作器组的大小。
- rdzv_id- 用户定义的 ID,用于唯一标识作业的工作器组。每个节点使用此 ID 加入特定工作器组的成员。
- rdzv_backend- 协调的后端(例如- c10d)。这通常是一个强一致性键值存储。
- rdzv_endpoint- 协调后端端点;通常以- <host>:<port>的形式。
一个 Node 运行 LOCAL_WORLD_SIZE 个工作器,这些工作器构成一个 LocalWorkerGroup。作业中所有节点的 LocalWorkerGroups 的并集构成 WorkerGroup。
环境变量¶
以下环境变量在您的脚本中可用
- LOCAL_RANK- 本地排名。
- RANK- 全局排名。
- GROUP_RANK- 工作组的排名。介于 0 和- max_nnodes之间的数字。在每个节点运行单个工作组时,这是节点的排名。
- ROLE_RANK- 跨所有具有相同角色的工作者的工作者排名。工作者的角色在- WorkerSpec中指定。
- LOCAL_WORLD_SIZE- 本地世界大小(例如,本地运行的工作者数量);等于- torchrun上指定的- --nproc-per-node。
- WORLD_SIZE- 世界大小(作业中的工作者总数)。
- ROLE_WORLD_SIZE- 使用- WorkerSpec中指定的相同角色启动的工作者总数。
- MASTER_ADDR- 运行排名为 0 的工作者的主机的 FQDN;用于初始化 Torch Distributed 后端。
- MASTER_PORT-- MASTER_ADDR上的端口,可用于托管 C10d TCP 存储。
- TORCHELASTIC_RESTART_COUNT- 到目前为止的工作组重启次数。
- TORCHELASTIC_MAX_RESTARTS- 配置的最大重启次数。
- TORCHELASTIC_RUN_ID- 等于 rendezvous- run_id(例如,唯一的作业 ID)。
- PYTHON_EXEC- 系统可执行文件覆盖。如果提供,python 用户脚本将使用- PYTHON_EXEC的值作为可执行文件。默认情况下使用 sys.executable。
部署¶
- (C10d 后端不需要) 启动 rendezvous 后端服务器并获取端点(作为 - --rdzv-endpoint传递给启动器脚本)
- 单节点多工作器:在主机上启动启动器,以启动创建和监控本地工作器组的代理进程。 
- 多节点多工作器:在参与训练的所有节点上使用相同的参数启动启动器。 
当使用作业/集群管理器时,多节点作业的入口点命令应该是此启动器。
故障模式¶
- 工作器故障:对于具有 - n个工作器的训练作业,如果- k<=n个工作器发生故障,所有工作器将停止并重新启动,最多- max_restarts次。
- 代理故障:代理故障会导致本地工作器组故障。由作业管理器决定是使整个作业失败(组语义)还是尝试替换节点。代理支持这两种行为。 
- 节点故障:与代理故障相同。 
成员资格变更¶
- 节点离开(缩容):代理会收到离开通知,所有现有工作器都会停止,会形成一个新的 - WorkerGroup,所有工作器都会使用新的- RANK和- WORLD_SIZE启动。
- 节点加入(扩容):新节点加入作业,所有现有工作器都会停止,会形成一个新的 - WorkerGroup,所有工作器都会使用新的- RANK和- WORLD_SIZE启动。
重要通知¶
- 此实用程序和多进程分布式(单节点或多节点)GPU 训练目前仅在使用 NCCL 分布式后端时才能实现最佳性能。因此,NCCL 后端是 GPU 训练的推荐后端。 
- 此模块为您提供初始化 Torch 进程组所需的環境变量,无需手动传递 - RANK。要在您的训练脚本中初始化进程组,只需运行
>>> import torch.distributed as dist
>>> dist.init_process_group(backend="gloo|nccl")
- 在您的训练程序中,您可以使用常规的分布式函数,也可以使用 - torch.nn.parallel.DistributedDataParallel()模块。如果您的训练程序使用 GPU 进行训练,并且您想使用- torch.nn.parallel.DistributedDataParallel()模块,以下是配置方法。
local_rank = int(os.environ["LOCAL_RANK"])
model = torch.nn.parallel.DistributedDataParallel(model,
                                                  device_ids=[local_rank],
                                                  output_device=local_rank)
请确保 device_ids 参数设置为您的代码将要操作的唯一 GPU 设备 ID。这通常是进程的本地排名。换句话说,device_ids 需要是 [int(os.environ("LOCAL_RANK"))],并且 output_device 需要是 int(os.environ("LOCAL_RANK")) 才能使用此工具。
- 在发生故障或成员资格更改时,所有幸存的 worker 会立即被终止。请确保对您的进度进行检查点。检查点的频率应取决于您的作业对丢失工作的容忍度。 
- 此模块仅支持同构 - LOCAL_WORLD_SIZE。也就是说,假设所有节点运行相同数量的本地 worker(每个角色)。
- RANK不稳定。在重启之间,节点上的本地 worker 可能被分配到与之前不同的排名范围。切勿对排名的稳定性或- RANK和- LOCAL_RANK之间的一些相关性进行硬编码假设。
- 在使用弹性 ( - min_size!=max_size) 时,请勿对- WORLD_SIZE进行硬编码假设,因为世界大小可能会随着节点的加入和离开而改变。
- 建议您的脚本具有以下结构 
def main():
  load_checkpoint(checkpoint_path)
  initialize()
  train()
def train():
  for batch in iter(dataset):
    train_step(batch)
    if should_checkpoint:
      save_checkpoint(checkpoint_path)
- (推荐) 在 worker 错误时,此工具将总结错误的详细信息(例如时间、排名、主机、pid、回溯等)。在每个节点上,第一个错误(按时间戳)被启发式地报告为“根本原因”错误。要获取此错误摘要打印输出中的回溯,您必须像下面示例中所示的那样装饰训练脚本中的主入口点函数。如果未装饰,则摘要将不包含异常的回溯,并且只包含退出代码。有关 torchelastic 错误处理的详细信息,请参阅:https://pytorch.ac.cn/docs/stable/elastic/errors.html 
from torch.distributed.elastic.multiprocessing.errors import record
@record
def main():
    # do train
    pass
if __name__ == "__main__":
    main()