Hexo

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

PyTorch——分布式程序启动方式汇总


整体介绍

  • torch.distributed.dist.init_process_group 是 PyTorch 分布式 包中用于初始化进程组的核心函数,它在分布式训练中负责协调多个进程之间的通信
  • 本文重点讲解 torch.distributed.dist.init_process_group 函数的使用

dist.init_process_group 函数使用注意事项

  • 必须在所有进程中调用该函数,且参数需保持一致(除 rank 外)
  • 初始化后需调用 dist.destroy_process_group() 进行清理,否则在复杂的程序中,容易出现资源泄露问题
  • 实际使用中推荐通过 torch.distributed.launch 或 torchrun 工具启动,他们会自动设置环境变量
    • 使用 torch.multiprocessing.spawn 启动则需要自己管理参数或环境变量
  • 不同后端有不同的适用场景:GPU 集群优先用 nccl,CPU 集群用 gloo(Mac 上实验使用 gloo)
  • 确保所有进程能够访问到 init_method 指定的地址或文件
  • 初始化时的 IP 问题,有两个特点:
    • MASTER_ADDR 必须是 rank=0 的机器所在的 IP(torchrun --node_rank=0的机器所在的 IP),该进程负责作为 master 完成交流和初始化操作
    • 在 dist.init_process_group 执行后所有进程的地位是等价的,都可以作为 master (虚拟的含义:处理主要事务)
      • 比如,dist.broadcast(tensor, src) 中的 src 可以被指定为任意值
      • 亲测,在单机启动后,想用哪个进程作为 master(处理主要事务)都可以
    • 一般建议使用 rank=0 的进程作为 master 处理主要事务

函数原型及其参数讲解

  • dist.init_process_group 函数原型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    torch.distributed.init_process_group(
    backend: Optional[str] = None,
    init_method: Optional[str] = None,
    timeout: Optional[timedelta] = None,
    world_size: int = -1,
    rank: int = -1,
    store: Optional[Store] = None,
    group_name: str = "",
    pg_options: Optional[Any] = None,
    device_id: Optional[torch.device] = None,
    )

dist.init_process_group 主要参数说明

  • 1)backend(必填)

    • 指定通信后端,决定了进程间通信使用的底层协议
    • 可选值:'nccl'(推荐 GPU 通信)、'gloo'(CPU 和 GPU 均可)、'mpi'(需 MPI 库支持)
    • 注意:'nccl' 仅支持 GPU 且性能最优,'gloo' 对 CPU 支持更好
  • 2)init_method(可选)

    • 指定进程组的初始化方式,常用方式如下面
    • 'env://'(推荐):从环境变量读取配置(需设置 MASTER_ADDR、MASTER_PORT 等)
    • 'tcp://ip:port':指定主节点的 IP 和端口
    • 'file:///path':通过共享文件系统初始化(需所有进程可访问该路径)
    • init_method 参数的默认值为 None,当使用默认值时,其行为取决于是否设置了环境变量 TORCH_DISTRIBUTED_INIT_METHOD:
      • 如果设置了 TORCH_DISTRIBUTED_INIT_METHOD 环境变量
        • 函数会自动使用该环境变量的值作为初始化方法(等价于显式传入 init_method=环境变量值)
        • 例如,若环境变量设置为 tcp://127.0.0.1:23456,则会以该 TCP 地址进行初始化
      • 如果未设置 TORCH_DISTRIBUTED_INIT_METHOD 环境变量
        • 此时会触发 默认初始化逻辑 ,函数会尝试从环境变量中读取分布式配置(等价于 init_method='env://')
        • 此时要求必须设置以下环境变量才能正常初始化:
          • MASTER_ADDR:主节点的 IP 地址
          • MASTER_PORT:主节点的端口号(需所有进程可访问)
          • WORLD_SIZE:总进程数(可选,部分启动工具会自动设置)
          • RANK:当前进程的全局编号(0 为主进程,可选,部分启动工具会自动设置)
        • 如果这些环境变量未正确设置,会抛出类似 RuntimeError: Expected env:// init method but no MASTER_ADDR or MASTER_PORT found 的错误
          • 但无需担心:使用 torchrun(或 python -m torch.distributed.launch)启动时,若不指定 --master_addr 和 --master_port,则 torchrun 会默认 环境变量设置为 MASTER_ADDR:MASTER_PORT=127.0.0.1:29500
  • 3)world_size(可选)

    • 总进程数,即参与分布式训练的进程总数
    • 使用 'env://' 时可由环境变量 WORLD_SIZE 指定
  • 4)rank(可选)

    • 当前进程的编号(0 到 world_size-1),0 通常为主进程
    • 使用 'env://' 时可由环境变量 RANK 指定
  • 5)timeout

    • 通信超时时间,默认为 30 分钟(1800 秒)
    • 对于耗时较长的操作可适当增大
  • 6)store

    • 与 init_method 参数互斥,不指明 store 时,由 init_method 参数传入的方式决定初始化什么类型的 Store
    • init_method
    • 详情见附录
  • 7)pg_options

    • pg_options 参数用于配置进程组的特定选项,它是一个可选参数,允许用户为不同的后端指定特定的配置选项
    • 该参数的类型通常是Optional[Any],具体的选项内容取决于所使用的通信后端,主要用于:
      • 配置后端特定的优化参数
      • 设置通信超时时间
      • 调整内存使用策略
      • 配置网络相关参数等
    • 举例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import torch.distributed as dist

      # 示例:为NCCL后端配置特定选项
      pg_options = {
      'timeout': 1800, # 30分钟超时
      'init_method_timeout': 300, # 初始化超时
      }

      dist.init_process_group(
      backend="nccl",
      world_size=4,
      rank=0,
      pg_options=pg_options
      )
  • 8)device_id

    • device_id 参数用于将进程”绑定”到单个特定设备,从而实现后端特定优化,是一个 torch.device 类型的可选参数,主要用于 GPU 训练场景
    • NCCL 后端的特殊效果:在 NCCL 后端下,device_id 参数有两个重要影响
      • 1)立即形成通信器 :通信器会立即形成(直接调用ncclCommInit*而非延迟初始化)
      • 2)内存占用优化 :每个进程会在指定的GPU上占用显存,而不是在第一个可访问的GPU上[citation:7]
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import torch
      import torch.distributed as dist

      # 指定当前进程使用的GPU设备
      device_id = torch.device(f"cuda:{local_rank}")

      dist.init_process_group(
      backend="nccl",
      world_size=world_size,
      rank=rank,
      device_id=device_id
      )

示例:单节点多进程(使用 torch.multiprocessing)

  • 下面的代码启动单节点时,无需配置环境变量,也不需要特殊的启动命令,使用 python 命令即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import torch
    import torch.distributed as dist
    import torch.multiprocessing as mp

    def init_process(rank, world_size):
    # 设置环境变量
    os.environ['MASTER_ADDR'] = 'localhost'
    os.environ['MASTER_PORT'] = '12355'

    # 初始化进程组
    dist.init_process_group(
    backend='nccl', # 使用NCCL后端(GPU)
    init_method='env://',
    world_size=world_size,
    rank=rank
    )

    # 后续分布式操作...
    print(f"Process {rank} initialized")

    # 销毁进程组
    dist.destroy_process_group()

    if __name__ == "__main__":
    world_size = 4 # 4个进程
    mp.spawn(init_process, args=(world_size,), nprocs=world_size, join=True)

示例:多节点分布式(通过环境变量配置)

  • 在每个节点上运行的脚本中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import torch.distributed as dist
    import os

    # 环境变量通常由分布式启动工具设置
    # 如 torch.distributed.launch 或 torchrun
    dist.init_process_group(backend='nccl')

    # 可通过以下方式获取当前进程信息
    rank = dist.get_rank()
    world_size = dist.get_world_size()
    local_rank = int(os.environ.get('LOCAL_RANK', 0)) # 节点内的进程编号

    print(f"Rank {rank}/{world_size}, Local rank {local_rank}")
  • 上面的代码运行多节点时需要提前指定环境变量(不同机器不同),或通过 torchrun 等命令启动


使用时的一些常规规范写法

  • 分布式启动后,常用到下面三个参数:

    1
    2
    3
    rank: 当前进程在的全局进程编号
    word_size: 分布式系统总进程数
    local_rank: 当前进程在当前节点上的本地进程编号
  • (标准用法)当使用 torchrun 命令(或 torch.distributed.launch 命令时):

    • 可通过环境变量获取这三个参数(此时是默认设置的)

      1
      2
      3
      rank = int(os.environ["RANK"])
      world_size = int(os.environ["WORLD_SIZE"])
      local_rank = int(os.environ["LOCAL_RANK"])
    • 此时启动环境,可以仅指定后端即可

      1
      dist.init_process_group(backend='nccl')
    • 启动命令需要在不同的机器上执行以下,且在命令中传入当前机器对应的参数(自动转化成环境变量),比如:

      1
      2
      3
      4
      5
      # 机器1
      python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_demo.py

      # 机器2
      python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_demo.py
      • 易错点,要非常小心:
        • python -m torch.distributed.launch(或 torchrun) 的启动参数必须在脚本名 DDP_demo.py 之前
        • DDP_demo.py 之后的参数都是传递给 DDP_demo.py 的,不再是 python -m torch.distributed.launch(或 torchrun)的参数
        • 不建议使用环境变量的方式定义 --master_port 等参数(包括 MASTER_PORT=12368 torchrun xx.py 等方式也不建议)
          • 因为 torchrun 会自动覆盖环境变量 MASTER_PORT(即使没有显示传入 --master_port 参数也会用默认值 27500 覆盖)
  • (使用较少)当使用 torch.multiprocessing.spawn 启动多进程时,需要自己主动管理环境变量,或通过参数传入

  • 无论使用哪种方式使用,均可使用下面的代码获取参数

    1
    2
    3
    rank = dist.get_rank() 
    world_size = dist.get_world_size()
    local_rank = int(os.environ["LOCAL_RANK"]) # 若未配置则需要使用参数显示传入

通过 torch.multiprocessing.spawn 启动完整示例

  • 在代码里面使用 torch.multiprocessing.spawn 函数启动多进程(不使用任何环境变量,整体管理较为复杂)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import torch.distributed as dist
    import torch.multiprocessing as mp

    def train_fn(local_rank, args, world_size):
    rank = args.start_rank + local_rank # 全局进程编号 = 起始rank + 本地进程编号
    print(rank) # 输出全局进程编号
    # 初始化分布式进程组
    dist.init_process_group(
    backend="nccl",
    init_method=f"tcp://{args.master_addr}:{args.master_port}", # 多机多卡需要指定服务器地址,不能写死成 local
    world_size=world_size,
    rank=rank, # 全局进程编号 = 起始rank + 本地进程编号
    )
    # 绑定本地GPU
    torch.cuda.set_device(local_rank)
    device = torch.device("cuda", local_rank)
    # ...

    # 对于多机多卡,为了复用同一套代码(方便管理),使用传入参数的方式启动代码,方便传参实现不同 node 机器的启动
    parser = argparse.ArgumentParser()
    parser.add_argument("--master_addr", type=str, default="localhost", help="主节点IP")
    parser.add_argument("--master_port", type=str, default="12355", help="主节点端口")
    parser.add_argument("--world_size", type=int, required=True, help="总进程数")
    parser.add_argument("--start_rank", type=int, required=True, help="当前机器进程的起始全局rank")
    parser.add_argument("--num_gpus", type=int, default=torch.cuda.device_count(), help="当前机器的GPU数量")
    args = parser.parse_args()
    print(args)
    # 启动当前机器的所有进程(num_gpus个),每台机器启动自己的进程数即可
    mp.spawn(
    train_fn,
    args=(args, args.world_size), # 传递给train_fn的参数
    nprocs=args.num_gpus, # 进程数=当前机器的GPU数
    join=True # 等待所有子进程完成
    )
  • 此时启动命令可以仅使用普通的 python 命令(后面的参数可选)

    1
    python demo.py --xxx xx
    • 每个节点通过传入不同参数即可指定分布式进程数量,允许不同节点不同数量(start_rank 数和启动时的 num_gpus 参数控制)

通过 torch.distributed.launch 命令启动

  • 假设脚本名为ddp_demo.py

  • 单机4卡,使用4个GPU启动:

    1
    python -m torch.distributed.launch --nproc_per_node=4 ddp_demo.py
    • --nproc_per_node:指定单机的GPU数量
  • 多机多卡,2个机器,各有4个GPU启动:

    1
    2
    3
    4
    5
    # 机器1
    python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_demo.py

    # 机器2
    python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_demo.py
    • --nnodes:总节点数
    • --node_rank:当前节点编号
    • --master_addr:指定master节点的IP地址,默认值是”localhost”,但是显示给出来更明确
    • --master_port:指定master节点的端口号,默认值是 29500,但是显示给出来更明确

通过 torchrun 命令启动

  • 以下结论是在 Mac 系统下得到的,暂时没有在 Linux 上尝试

  • torchrun 和 torch.distributed.launch 都是 PyTorch 中用于启动分布式训练的工具

  • torchrun 是 PyTorch 1.10 后推出的新一代工具,旨在替代 torch.distributed.launch

  • 两者的核心区别:

    特性 torch.distributed.launch torchrun
    推出时间 较早版本(已逐步废弃) PyTorch 1.10+ 推出(推荐使用)
    进程管理 依赖用户手动管理进程(如指定 --node_rank 等) 自动管理进程,支持弹性训练(节点故障后自动恢复)
    配置方式 大部分参数需通过命令行传入 支持从环境变量、命令行、配置文件读取参数
    容错能力 无弹性训练支持,进程崩溃后需手动重启 支持弹性训练(--max_restarts 等参数)
    日志管理 日志输出较为基础 提供更规范的日志管理,区分不同进程的输出
  • torchrun 启动单机多卡(与 torch.distributed.launch 相同):

    1
    2
    3
    4
    5
    # 回顾 `torch.distributed.launch` 的启动方式
    python -m torch.distributed.launch --nproc_per_node=4 ddp_demo.py

    # torchrun 的启动方式:替换 `python -m torch.distributed.launch` 为 `torchrun` 即可
    torchrun --nproc_per_node=4 ddp_demo.py
  • torchrun 启动多机多卡(相对 torch.distributed.launch而言,torchrun 会自动推断部分参数(如节点数、进程数))

    • 回顾 torch.distributed.launch 需要手动指定总节点数(--nnodes)和当前节点序号(--node_rank)

      1
      2
      3
      4
      5
      # 机器1
      python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_demo.py

      # 机器2
      python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_demo.py
    • (待确认:Mac 系统下失败)**torchrun** 无需手动指定 node_rank,只需在所有节点上指定相同的 --rdzv_id(任务ID)和 --rdzv_endpoint(主节点地址):

      1
      2
      # 所有节点统一执行相同命令(不需要区分不同节点使用不同命令,会自动分配node_rank)
      torchrun --nproc_per_node=4 --nnodes=2 --rdzv_id=123 --rdzv_backend=c10d --rdzv_endpoint="localhost:29500" DDP_demo.py
      • --rdzv_id:任务唯一ID(任意整数)
      • rdzv_backend:后端(默认c10d)
      • rdzv_endpoint:主节点IP:端口
      • **torchrun**还可以用 --max_restarts 等参数指定最大重启次数
    • (Mac 系统下成功)在 Mac 系统下,上面的命令执行会出现问题(上面命令在两个窗口分别打开)

      1
      2
      torchrun --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_test.py
      torchrun --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_test.py
      • 亲测上面的代码可以成功
  • 特别地,部分介绍文档会说 torch.distributed.launch 和 torchrun 对代码的使用有一些不同,主要是初始化不同:

    • 初始化方式有下面两种:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      # 方式一:
      rank = int(os.environ["RANK"])
      world_size = int(os.environ["WORLD_SIZE"])
      local_rank = int(os.environ["LOCAL_RANK"])
      dist.init_process_group(backend='gloo', world_size=world_size, rank=rank)

      # 方式二:
      dist.init_process_group(backend='gloo')
      rank = dist.get_rank()
      world_size = dist.get_world_size()
      local_rank = int(os.environ["LOCAL_RANK"])
    • 特别说明:部分文档介绍说 torchrun 只能用方式二, torch.distributed.launch 只能用方式一

    • 亲测:

      • torch.distributed.launch 可用方式一和方式二初始化两种方式均可用,具体使用命令为:

        1
        2
        3
        4
        5
        # 机器1
        python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_demo.py

        # 机器2
        python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_demo.py
      • torchrun 可用方式二定义,在使用下面的命令时,方式一方式二均可:

        1
        torchrun --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_test.py
  • 使用建议:

    • 优先使用 torchrun :它是 PyTorch 官方推荐的新一代工具,简化了分布式配置,支持弹性训练,且兼容未来的功能更新
    • 避免使用 torch.distributed.launch :该工具已逐步被废弃,不再添加新功能,仅为兼容性保留

附录:python -m torch.distributed.launch 命令具体在做什么?

  • 本节讲述 python -m torch.distributed.launch,实际上 torchrun 是 python -m torch.distributed.launch 的升级版本,做的事情差不多,但还多了些功能

启动多个进程

  • 根据 --nproc_per_node 参数指定的数量,为每个 GPU 启动一个独立的进程

    • 例如,如果 --nproc_per_node=4,则会在当前节点启动 4 个进程,每个进程绑定到一个 GPU 上
  • 每个进程会独立执行训练脚本(如 train.py),并通过 dist.init_process_group 初始化分布式环境

    • dist.init_process_group 会让当前进程于其他进程简历通信
  • 代码中,通过下面的命令执行即可使用进程自己的 GPU(实现 GPU 的分配):

    1
    2
    3
    4
    import os
    import torch
    local_rank = int(os.environ["LOCAL_RANK"])
    torch.cuda.set_device(local_rank)
  • 注:这也是在使用普通的 python 命令启动时,需要在代码里自己手动启动多进程的原因

设置环境变量(分布式环境所需的)

  • python -m torch.distributed.launch 命令会设置分布式训练所需的环境变量(如 WORLD_SIZE、RANK、LOCAL_RANK、MASTER_ADDR、MASTER_PORT 等)
  • 每个进程的环境变量不同,RANK、LOCAL_RANK 等环境变量均是有差异的

代码内部在做什么?(非启动命令的工作)

  • 代码内部通过 dist.init_process_group 初始化分布式通信后端(如 nccl 或 gloo),确保所有进程能够协同工作
    • backend:通信后端(如 nccl 用于 GPU,gloo 用于 CPU)
    • init_method:初始化方法(如 tcp:// 指定 master 地址和端口)
  • 代码示例 :
    1
    dist.init_process_group(backend='nccl', init_method='env://')

附录:init_process_group 的 store 参数详细用法说明

  • 在 torch.distributed.init_process_group() 函数中,store 参数是一个可选参数,用于指定分布式进程间通信所使用的键值存储后端
  • 该参数允许用户显式创建和配置存储实例,而不是依赖默认的自动创建机制
  • store 参数的作用:进程间协调,store 参数指定的存储系统用于:
    • 存储分布式训练过程中的元数据
    • 协调各个进程的初始化过程
    • 实现进程间的同步和通信
    • 管理进程组的状态信息

支持的 Store 类型

  • PyTorch 分布式包支持三种主要的键值存储类型,这些 Store 的核心功能一致(同步元数据),但实现方式不同,选择时需根据分布式环境的网络、存储和调度方式决定
  • 下面是最常见的三种 Store 的使用示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import torch.distributed as dist

    # 第一类:TCPStore
    # # server store 持有数据,client store 可以连接到 server store(by TCP)访问数据,
    store = dist.TCPStore(
    host_name="localhost", # 服务器(master)地址,所有参与分布式训练的进程必须能通过此地址访问到 master
    port=12345, # 需确保该端口在主机上未被占用,且所有进程可访问此端口
    world_size=4, # 指明 store users 数,默认为 None(表示没有固定的 store users 数量),注:一般与分布式环境的 world_size保持一致
    is_master=True, # 是否为master节点,只有主进程会启动 TCP 服务器,其他进程(非主进程)会作为客户端连接到主进程
    timeout=300, # 超时时间(秒)
    wait_for_workers=True, # (bool, optional), 默认为 True,是否等待所有 workers 连接到 store 服务器,只有当 world_size 为固定值时生效
    )
    # # 第二类:FileStore
    # store = dist.FileStore("/tmp/distributed_store", world_size=4)

    # # 第三类:HashStore(通常用于单机多进程)
    # store = dist.HashStore()

    # 使用自定义store初始化进程组
    dist.init_process_group(
    backend="nccl",
    world_size=4,
    rank=0,
    store=store
    )

使用 init_method 指定初始化 Store 类型

  • 在 PyTorch 的 dist.init_process_group 中,不指明 store 参数时,init_method 参数决定了分布式进程初始化时使用的 Store 类型
  • 不同的 init_method 对应不同的 Store 实现,用于在进程间同步元数据(如进程编号、通信地址等)
  • 不同初始化方法如下:
    • init_method='tcp://master_ip:port':(对应 TCPStore)
      • 基于 TCP 协议的集中式 Store,需要指定一个主节点(master)的 IP 和端口
      • 主进程会在指定地址创建 TCP 监听,其他进程通过该地址连接主进程,完成元数据交换
      • 适用于大多数分布式场景(单机多卡、多机多卡),无需依赖外部服务
    • init_method='file:///path/to/shared_file'(对应 FileStore)
      • 基于共享文件系统的 Store,所有进程通过读写同一个共享文件同步元数据
      • 要求所有进程可访问同一个共享文件系统(如 NFS、本地文件系统,单机多卡场景常用)
      • 无需网络通信,但依赖文件系统的可靠性和性能
    • init_method='env://'(由环境变量指定的 Store(通常是 TCPStore))
      • 不直接指定 Store 类型,而是通过环境变量(如 MASTER_ADDR、MASTER_PORT、WORLD_SIZE、RANK 等)配置初始化信息
      • 本质上仍会创建 TCPStore,但参数由环境变量而非函数参数传入
      • 常用于容器化环境(如 Kubernetes)或需要动态配置的场景
    • 第三方分布式框架集成(如 Slurm、MPI)(对应 SlurmStore、MPIStore 等(取决于框架))
      • 当使用 Slurm 或 MPI 启动分布式任务时,PyTorch 可自动检测并使用对应框架的 Store
      • 例如,init_method='slurm://' 会使用 SlurmStore,通过 Slurm 的环境变量和接口同步元数据,无需手动指定主节点
  • 总结核心差异总结
    init_method 类型 Store 实现 依赖条件 适用场景
    tcp://master_ip:port TCPStore 网络连通性 通用分布式场景(单机/多机)
    file:///path FileStore 共享文件系统 单机多卡或共享存储的多机场景
    env:// 通常为 TCPStore 环境变量配置 容器化、动态配置场景
    第三方框架(如 Slurm) 框架专属 Store 对应调度框架环境 集群管理系统(Slurm/MPI)

使用 PrefixStore 进行多进程组管理

  • 使用 PrefixStore 可以为不同的进程组创建隔离的命名空间 :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 创建基础store
    base_store = dist.TCPStore("localhost", 12345, world_size, is_master=True)

    # 为不同进程组创建前缀store
    pg1_store = dist.PrefixStore("group1_", base_store)
    pg2_store = dist.PrefixStore("group2_", base_store)

    # 使用不同的store初始化不同的进程组
    dist.init_process_group(backend="nccl", store=pg1_store, ...)

注意事项

  • 当同时指定了环境变量和 store 参数时,store 参数会优先使用:

    1
    2
    3
    4
    5
    6
    # 即使设置了环境变量,也会使用显式指定的store
    os.environ['MASTER_ADDR'] = 'localhost'
    os.environ['MASTER_PORT'] = '12355'

    custom_store = dist.TCPStore("192.168.1.100", 29500, world_size, is_master)
    dist.init_process_group(backend="nccl", store=custom_store) # 使用custom_store
  • 和 init_method 参数是互斥的,只能指定其中一个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 方法1:使用init_method
    dist.init_process_group(
    backend="nccl",
    init_method="tcp://192.168.1.100:29500"
    )

    # 方法2:使用store参数
    store = dist.TCPStore("192.168.1.100", 29500, world_size, is_master)
    dist.init_process_group(
    backend="nccl",
    store=store
    )
  • 可根据环境动态配置不同 store

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def create_store(backend, world_size, rank):
    if backend == "nccl":
    # GPU训练使用TCPStore
    return dist.TCPStore(
    host_name=os.environ.get('MASTER_ADDR', 'localhost'),
    port=int(os.environ.get('MASTER_PORT', '29500')),
    world_size=world_size,
    is_master=(rank == 0)
    )
    else:
    # CPU训练可以使用FileStore
    return dist.FileStore("/tmp/dist_store", world_size)

附录:init_process_group 的 device_id 参数详细用法说明

  • device_id 参数用于将进程”绑定”到单个特定设备,从而实现后端特定优化,是一个 torch.device 类型的可选参数,主要用于 GPU 训练场景

  • device_id 参数在 NCCL 后端下有特殊效果:在 NCCL 后端下,device_id 参数有两个重要影响

    • 1)立即形成通信器 :通信器会立即形成(直接调用ncclCommInit*而非延迟初始化)
    • 2)内存占用优化 :每个进程会在指定的 GPU 上占用显存,而不是在第一个可访问的 GPU 上
    • If you want to know NCCL initialization error early, you can also use this field
  • 使用 gloo 后端时,不需要设置 device_id

  • 基本用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import torch
    import torch.distributed as dist

    # 指定当前进程使用的GPU设备
    device_id = torch.device(f"cuda:{local_rank}")

    dist.init_process_group(
    backend="nccl",
    world_size=world_size,
    rank=rank,
    device_id=device_id # 不指定 device_id 默认会在可访问的第一个 GPU 上占用内存?
    )
  • 多 GPU 训练场景

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import torch
    import torch.distributed as dist
    import os

    def setup_distributed():
    # 获取本地rank
    local_rank = int(os.environ.get("LOCAL_RANK", 0))
    world_size = int(os.environ.get("WORLD_SIZE", 1))
    rank = int(os.environ.get("RANK", 0))

    # 设置当前进程的GPU设备
    torch.cuda.set_device(local_rank) # 根据不同 rank 获取不同的设备
    device_id = torch.device(f"cuda:{local_rank}")

    # 初始化进程组,绑定到特定GPU
    dist.init_process_group(
    backend="nccl",
    world_size=world_size,
    rank=rank,
    device_id=device_id # 绑定到特定设备
    )

    return device_id

附录:init_process_group 中 NCCL 的延迟初始化

  • NCCL 是 NVIDIA 集合通信库(NVIDIA Collective Communications Library)

    • 提供多 GPU / 多节点通信原语(如 all-reduce、broadcast、all-gather、reduce-scatter 等)
    • 针对 PCIe/NVLink 与 NVIDIA 网络优化,用于加速深度学习分布式训练
    • 开源地址:github.com/NVIDIA/nccl
  • 延迟初始化(Lazy Initialization)是指 NCCL 通信器不在 init_process_group() 调用时立即创建,而是推迟到 第一次实际需要进行集合通信操作时才创建

    • 注:若 init_process_group() 函数指定了 device_id 参数,则 NCCL 会立即初始化到当前设备上
  • 立即初始化(指定 device_id 时)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    device_id = torch.device(f"cuda:{local_rank}")
    dist.init_process_group(backend="nccl", device_id=device_id)

    # 在这一行执行时,NCCL立即:
    # 1. 调用 ncclCommInit* 系列函数
    # 2. 创建通信器对象
    # 3. 分配通信缓冲区
    # 4. 建立进程间的通信通道
    # 5. 进行通信拓扑优化
  • 延迟初始化(不指定 device_id 时)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    dist.init_process_group(backend="nccl")  # 没有device_id

    # 在这一行执行时,NCCL只是:
    # 1. 记录进程组的元信息
    # 2. 设置必要的环境变量
    # 3. 但不创建实际的通信器

    # 真正的初始化发生在第一次集合通信时:
    tensor = torch.randn(10).cuda()
    dist.all_reduce(tensor) # 在这里才真正初始化 NCCL 通信器!自动识别需要绑定的 GPU 并初始化,这里会很慢,因为要初始化 NCCL

    dist.all_reduce(tensor) # 这里就很快了,这次不需要初始化 NCCL

    # # 注:以下操作都会触发NCCL初始化:
    # dist.all_reduce(tensor) # 全规约
    # dist.all_gather([tensor]) # 全收集
    # dist.reduce(tensor, dst=0) # 规约
    # dist.broadcast(tensor, src=0) # 广播
    # dist.all_to_all([tensor], [tensor]) # 全到全
    • 延迟初始化可以允许灵活、动态、自动地选择需要的 GPU,但可能会造成一些误解
    • 注意:一旦 NCCL 初始化以后,就绑定了 GPU 了,再切换 GPU 可能会出现错误
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      def multi_device_lazy_init_issues():
      dist.init_process_group(backend="nccl")

      # 问题场景:进程需要使用多个GPU
      tensor_gpu0 = torch.randn(10).cuda(0)
      tensor_gpu1 = torch.randn(10).cuda(1)

      # 第一次通信在GPU 0上
      dist.all_reduce(tensor_gpu0) # NCCL在GPU 0上初始化

      # 尝试在GPU 1上通信
      try:
      dist.all_reduce(tensor_gpu1) # 可能失败或性能差
      except Exception as e:
      print(f"Multi-device issue: {e}")
  • 目前尚无代码可直接检测 NCCL 通信器是否已创建,只能通过第一次通信时间来大致判断

  • 生产环境推荐立即初始化,方便代码阅读;开发时可以使用延迟初始化以获得更快的启动

PyTorch——分布式编程框架总结

  • 参考链接:
    • 简单入门可参考:「指北」PyTorch分布式训练 - Will Lee的文章 - 知乎

整体说明

  • PyTorch 原生支持了 DP(DataParallel)和DDP(DistributedDataParallel)是常用的数据并行分布式训练工具
  • PyTorch 原生还支持了 FSDP 作为模型并行的分布式训练工具,通过分片模型参数、梯度和优化器状态到多个 GPU,显著降低单卡内存占用
  • HuggingFace Accelerate 是一个轻量级库,专为简化 PyTorch 模型在各种硬件配置上的训练和推理而设计,支持选择 DeepSpeed 和 FSDP 等
  • HuggingFace 的 Trainer 是 transformers 库中一个核心且功能强大的类,它为 PyTorch 模型提供了完整的训练和评估循环,极大地简化了训练过程,让用户可以专注于模型、数据集和训练参数的配置,而无需手动编写复杂的训练代码
    • Trainer 比 Accelerate 更高一级,把循环等也封装了,进需要用户配置参数数据集等即可
  • 其他相关的分布式封装框架有 Horovod、Ray 等
    • Horovod 是 Uber 开源的跨平台的分布式训练工具,名字来自于俄国传统民间舞蹈,舞者手牵手围成一个圈跳舞,与 Horovod 设备之间的通信模式很像
    • Ray 是更高层级的分布式训练框架(利用其他框架),目标是融合数据处理、模型训练、超参数调优和模型服务等各个阶段
  • 注意:篇幅有限,本文主要是记录一些简单的使用示例和说明,更详尽的使用细节需要去官网查看
  • PyTorch 分布式系统的启动方式见:/Notes/PyTorch/PyTorch——分布式程序启动方式汇总

DataParallel(DP) 使用示例

  • DP 是单进程多线程模式,简单易用,适合单机多 GPU 场景
  • DP 的每次前向过程都会进行一次从 GPU 间的参数复制(效率较慢)
  • 参考博客(有比较清晰的图片):Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU & Distributed setups
  • DP 的使用示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    import torch
    import torch.nn as nn
    from torch.utils.data import DataLoader, Dataset

    class DiyModel(nn.Module):
    def __init__(self):
    super().__init__()
    self.fc = nn.Linear(10, 2)
    def forward(self, x):
    return self.fc(x)

    class DiyDataset(Dataset):
    def __len__(self):
    return 1000
    def __getitem__(self, idx):
    return torch.randn(10), torch.randint(0, 2, (1,)).item()

    # 第一步:数据准备(与常规方式一致)
    dataset = DiyDataset()
    dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

    # 第二步:初始化模型、损失函数、优化器
    model = DiyModel()
    # 将模型放到DP中(自动分发到多个GPU),核心步骤,相对普通训练方式,仅需要修改这里
    model = nn.DataParallel(model) # 注意:DP 仅增加这一步即可
    model = model.cuda() # 或 .to('cuda')

    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

    # 第三步:训练循环
    for epoch in range(3):
    for inputs, labels in dataloader:
    inputs = inputs.cuda()
    labels = labels.cuda()

    # 注:model 是 DP封装过的模型,所以能执行 DP 的个性化操作
    # 在这里 model(inputs) 会执行四个流程:
    # * 数据分发
    # * 模型复制(主线程GPU到其他线程)
    # * 并行前向推理
    # * 输出汇总四个流程
    outputs = model(inputs)
    loss = criterion(outputs, labels)

    optimizer.zero_grad()

    # 注:loss 本质是从 Model 出来的(loss 的梯度是传到 outputs 上再进一步计算的,而 outputs 是模型的输出,执行梯度计算时能考虑分布式),所以也能执行 DP 的个性化操作
    # 这里 loss.backward 执行四个流程:
    # * 各GPU损失梯度计算:根据各GPU的outputs与总output的关系来计算各自的损失梯度(不同GPU不一样)
    # * 损失梯度分发(将各自的梯度分发到各自 GPU 上
    # * 并行后向推理(计算 GPU 自身的局部梯度)
    # * 梯度汇总
    loss.backward()
    optimizer.step() # 仅更新主线程GPU上的模型
    print(f"Epoch {epoch}, Loss: {loss.item()}")

nn.DataParallel(model) 发生了什么?

  • TLDR:model = nn.DataParallel(model) 的核心作用是通过包装模型实现多 GPU 数据并行 :
    • 执行这一行后,模型对象已经变了,需要通过 model.module.fc 才能访问原模型属性(访问 model.fc 会出错)
    • DP 中,仅需要这一步即可实现数据并行 ,后续的代码都无需修改,自动适配了;但代码后面做了很多数据分发和梯度合并的工作
    • 这一行后,程序会自动管理设备分配、模型复制、数据拆分与合并、梯度汇总与参数同步
  • 1)初始化:确定并行设备与包装模型,nn.DataParallel 实例化时会完成以下核心操作:
    • 检测可用GPU :默认情况下,DataParallel 会自动检测当前可见的GPU(通过 CUDA_VISIBLE_DEVICES 环境变量控制),并将其ID列表存储在 device_ids 属性中(默认值为 range(torch.cuda.device_count()))
    • 指定主GPU :主GPU(device[0])是默认的“主导设备”,负责汇总计算结果、更新参数,并作为数据/模型的初始落脚点。若未指定 device_ids,则第一个可见GPU(通常是 cuda:0)会被设为主GPU
    • 包装原始模型 :原始模型会被存入 DataParallel 的 module 属性中(这也是后续访问原模型属性需通过 model.module 的原因),同时 DataParallel 会接管模型的 forward 和 backward 逻辑
  • 2)模型的移动与复制:DataParallel 会自动处理模型在设备间的分布:
    • 主GPU加载模型 :若原始模型在CPU上,DataParallel 会先将其移动到主GPU(device_ids[0]);若模型已在某个GPU上,会检查是否与主GPU一致,不一致则移动
    • 副本同步到其他GPU :在首次执行前向传播时,DataParallel 会将主GPU上的模型参数复制到 device_ids 中的其他GPU,确保所有GPU上的模型初始参数完全一致
  • 3)前向传播:数据拆分与并行计算,当调用 output = model(input) 时,DataParallel 会按以下流程处理:
    • 数据校验与准备 :检查输入数据是否在主GPU上(若不在,会自动移到主GPU)。输入可以是Tensor、列表、字典等结构,只要包含需要拆分的批量数据(如 batch_size 维度的Tensor)
    • 数据拆分(Split) :沿批量维度(默认是第0维,即 batch_size 所在维度)将输入数据均匀拆分到 device_ids 中的所有GPU。例如,若 batch_size=8 且使用2个GPU,则每个GPU会收到 batch_size=4 的子数据
    • 并行计算 :每个GPU上的模型副本会独立处理分配到的子数据,执行各自的 forward 计算,得到子输出
    • 结果收集与合并(Gather) :所有GPU的子输出会被发送回主GPU,然后按拆分的逆过程合并(如拼接),最终形成与单GPU计算格式一致的输出(例如,将2个 batch_size=4 的子输出拼接为 batch_size=8 的完整输出)
  • 4)反向传播:梯度汇总与参数更新
    当调用 loss.backward() 时,梯度计算与参数更新流程如下:
    • 各GPU独立计算梯度 :每个GPU会基于自己处理的子数据和子输出,独立计算模型参数的梯度(存储在各自GPU的模型副本中)
    • 梯度汇总到主GPU :DataParallel 会自动将所有GPU上的梯度求和(sum)并汇总到主GPU的模型中(即 model.module 的参数梯度)
    • 主GPU更新参数 :优化器(如 optimizer.step())仅作用于主GPU上的模型参数,完成参数更新
    • 参数同步到其他GPU :主GPU更新后的参数会被自动广播(broadcast)到其他GPU的模型副本中,确保所有GPU的模型参数保持一致,为下一轮计算做准备
  • 5)特殊细节与注意事项
    • 模型属性访问 :由于原始模型被包装在 DataParallel 的 module 属性中,访问原模型的层或参数时需通过 model.module(例如,model.module.fc 而非 model.fc)。若仅使用单GPU,DataParallel 仍会包装模型,因此建议始终通过 model.module 访问原模型
    • 单GPU场景 :若只有1个可见GPU,DataParallel 不会进行拆分计算(本质上是单GPU运行),但仍会包装模型,此时 model.module 与原始模型等价
    • 数据类型与设备兼容 :输入数据必须与主GPU设备兼容(例如,若主GPU是 cuda:0,输入数据需为 cuda:0 上的Tensor),否则会触发设备不匹配错误
    • 局限性 :DataParallel 是单进程多线程模式,受Python GIL限制,多GPU效率可能不如多进程的 DistributedDataParallel(DDP),且不支持跨节点并行

DistributedDataParallel(DDP)使用示例

  • 更详细的使用说明见:官网说明文档
  • DDP是多进程模式,支持单机/多机多GPU,效率更高,是PyTorch推荐的分布式训练方式

第一步:编写训练脚本

  • 注:这一步只是定义脚本,这个脚本不能通过简单的 python 命令启动(需通过torch.distributed.launch启动)
  • DDP 的示例脚本如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    import os

    import torch
    import torch.nn as nn
    import torch.distributed as dist
    from torch.utils.data import DataLoader, Dataset
    from torch.utils.data.distributed import DistributedSampler # 用于DDP的数据采样

    class DiyModel(nn.Module):
    def __init__(self):
    super().__init__()
    self.fc = nn.Linear(10, 2)

    def forward(self, x):
    return self.fc(x)

    class DiyDataset(Dataset):
    def __len__(self):
    return 1000

    def __getitem__(self, idx):
    return torch.randn(10), torch.randint(0, 2, (1,)).item()

    # 初始化分布式环境
    dist.init_process_group(backend='gloo') # 多GPU推荐用'nccl'后端(NVIDIA专门优化过),CPU使用'gloo'或'mpi',亲测Mac上'gloo'可直接使用
    rank = dist.get_rank() # 全局进程编号(0,1,2...),也可以用 env_rank = int(os.environ["RANK"])
    world_size = dist.get_world_size() # 也可以使用 world_size = int(os.environ["WORLD_SIZE"])
    local_rank = int(os.environ["LOCAL_RANK"]) # 当前进程的local_rank

    # 定义设备并设置
    device = torch.device(f"cuda:{local_rank}" if torch.cuda.is_available() else "cpu")
    torch.cuda.set_device(device) # 为当前进程分配 GPU

    # 数据准备
    dataset = DiyDataset()
    sampler = DistributedSampler(dataset) # 重点:sampler确保各进程数据不重叠
    dataloader = DataLoader(dataset, batch_size=32, sampler=sampler) # sampler传入时,shuffle参数不生效

    # 模型初始化(每个进程单独初始化,再用DDP包装)
    model = DiyModel().to(device)
    model = torch.nn.parallel.DistributedDataParallel( # 核心步骤
    model,
    device_ids=[local_rank],
    output_device=local_rank
    )

    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

    # 训练循环
    for epoch in range(3):
    sampler.set_epoch(epoch) # 每个epoch打乱数据,避免每个epoch数据顺序一致(epoch内部顺序不随机时容易导致模型学到错误的样本顺序规律)
    for inputs, labels in dataloader:
    # 使用to(device)将数据移动到指定设备
    inputs = inputs.to(device)
    labels = labels.to(device)

    # 前向过程,会同时追踪需要的同步的数据
    outputs = model(inputs)
    loss = criterion(outputs, labels)

    optimizer.zero_grad()

    # 后向过程
    # 注:DDP 在初始化时会为模型参数注册特殊的钩子(hook),这些钩子会在 backward() 过程中自动触发
    # 当本地梯度计算完成后,钩子会启动 All-Reduce 操作,将所有进程的同一份参数的梯度进行汇总(默认求平均)
    # 完成上述流程后,每个进程上的同一份参数会拥有相同的梯度值,存储在 `grad` 属性中
    loss.backward()
    optimizer.step() # 普通的 optimizer 更新每个进程自己的参数

    # 只在主进程(rank=0)打印信息
    if rank == 0:
    print(f"Epoch {epoch}, Loss: {loss.item()}")

    dist.destroy_process_group() # 销毁进程组
附录:DDP 的 DP 间求平均的细节
  • DDP 中在 DP 间累积梯度后,做了平均,具体实现参见 github.com/pytorch/pytorch/blob/main/torch/csrc/distributed/c10d/reducer.cpp

    1
    2
    3
    4
    5
    // 取值与 DP_size 有关(注意: 这里的 size 就是 DDP 中的 world_size,也就是 DP_size)
    div_factor_ = process_group_->getSize();
    ...
    // 做除法
    bucket_view.div_(div_factor_);
  • 问题:为什么 DDP 中,loss.backward() 要对梯度求平均而不是求和?

    • 理解:本质应该是一样的,求和求平均都可以,目前实现本质也是先求和再做除法
  • 特别说明:如果是想做 Token 粒度的平均(每个样本的可学习 Token 数不一致),需要多维护一个 Token 数量的变量并执行一次 all_reduce 通信

    • 当然,为了实现与不做 DP 完全一致的效果,这里其实是应该对 Token 也做聚合,再做除法才行的

第二步:启动脚本(通过 torch.distributed.launch 命令)

  • 假设脚本名为ddp_demo.py

  • 单机4卡,使用4个GPU启动:

    1
    python -m torch.distributed.launch --nproc_per_node=4 ddp_demo.py
    • --nproc_per_node:指定单机的GPU数量
  • 多机多卡,2个机器,各有4个GPU启动:

    1
    2
    3
    4
    5
    # 机器1
    python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_demo.py

    # 机器2
    python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_demo.py
    • --nnodes:总节点数
    • --node_rank:当前节点编号
    • --master_addr:指定master节点的IP地址,默认值是”localhost”,但是显示给出来更明确
    • --master_port:指定master节点的端口号,默认值是 29500,但是显示给出来更明确

补充:torchrun 命令启动脚本(结论是在 Mac 系统下得到的,暂时没有在 Linux 上尝试)

  • torchrun 启动单机多卡(与 torch.distributed.launch 相同):

    1
    2
    3
    4
    5
    # 回顾 `torch.distributed.launch` 的启动方式
    python -m torch.distributed.launch --nproc_per_node=4 ddp_demo.py

    # torchrun 的启动方式:替换 `python -m torch.distributed.launch` 为 `torchrun` 即可
    torchrun --nproc_per_node=4 ddp_demo.py
  • torchrun 启动多机多卡(相对 torch.distributed.launch而言,torchrun 会自动推断部分参数(如节点数、进程数))

    • (待确认:Mac 系统下失败)**torchrun** 无需手动指定 node_rank,只需在所有节点上指定相同的 --rdzv_id(任务ID)和 --rdzv_endpoint(主节点地址):

      1
      2
      # 所有节点统一执行相同命令(不需要区分不同节点使用不同命令,会自动分配node_rank)
      torchrun --nproc_per_node=4 --nnodes=2 --rdzv_id=123 --rdzv_backend=c10d --rdzv_endpoint="localhost:29500" DDP_demo.py
      • --rdzv_id:任务唯一ID(任意整数)
      • rdzv_backend:后端(默认c10d)
      • rdzv_endpoint:主节点IP:端口
      • **torchrun**还可以用 --max_restarts 等参数指定最大重启次数
    • (Mac 系统下成功)在 Mac 系统下,上面的命令执行会出现问题(上面命令在两个窗口分别打开)

      1
      2
      torchrun --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr="localhost" --master_port=29500 DDP_test.py
      torchrun --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr="localhost" --master_port=29500 DDP_test.py
      • 亲测上面的代码可以成功
  • 更多启动详情见:/Notes/PyTorch/PyTorch——分布式程序启动方式汇总


HuggingFace Accelerate 介绍及使用示例

  • 详情见:/Notes/PyTorch/PyTorch——HF-Accelerate使用总结

HuggingFace Trainer 介绍及使用示例

  • 详情见:/Notes/NLP/NLP——HF-Trainer使用总结
1…178179180…352
San Ye

San Ye

Stay Hungry. Stay Foolish.

704 posts
53 tags
© 2026 San Ye
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4