pytorch分布式训练(⼆init_process_group)backend str/Backend 是通信所⽤的后端,可以是"ncll" "gloo"或者是⼀个torch.distributed.Backend类(Backend.GLOO)init_method str 这个URL指定了如何初始化互相通信的进程
world_size int 执⾏训练的所有的进程数
rank int this进程的编号,也是其优先级
timeout timedelta 每个进程执⾏的超时时间,默认是30分钟,这个参数只适⽤于gloo后端
group_name str 进程所在group的name
def init_process_group(backend,
init_method=None,
rank函数怎么用timeout=default_pg_timeout,
world_size=-1,
rank=-1,
store=None,
group_name=''):
pytorch分布式训练通信的后端使⽤的是gloo或者NCCL,⼀般来说使⽤NCCL对于GPU分布式训练,使⽤gloo对CPU进⾏分布式训
练,MPI需要从源码重新编译。
默认情况下通信⽅式采⽤的是Collective Communication也就是通知体,当然也可以采⽤Point-to-Point Communication即⼀个⼀个通知。
在分布式多个机器中,需要某个主机是主要节点。⾸先主机地址应该是⼀个⼤家都能访问的公共地址,主机端⼝应该是⼀个没有被占⽤的空闲端⼝。
其次,需要定义world-size代表全局进程个数(⼀般⼀个GPU上⼀个进程),rank代表进程的优先级也是这个进程的编号,rank=0的主机就是主要节点。
同时,rank在gpu训练中,⼜表⽰了gpu的编号/进程的编号。
每个流程维护⾃⼰的优化器,并在每次迭代中执⾏完整的优化步骤。
虽然这可能看起来是多余的,因为梯度已经收集在⼀起并跨进程平均,
因此每个进程都是相同的,这意味着不需要参数⼴播步骤,从⽽减少节点之间传输张量的时间。
每个进程都包含⼀个独⽴的Python解释器,消除了额外的解释器开销和“GIL-thrashing”,
即从单个Python进程中驱动多个执⾏线程、模型副本或gpu。
这对于⼤量使⽤Python运⾏时的模型(包括具有循环层或许多⼩组件的模型)尤其重要。
分布式其它api:
_backend(group=group)# group是可选参数,返回字符串表⽰的后端 group表⽰的是ProcessGroup类
_rank(group=group)# group是可选参数,返回int,执⾏该脚本的进程的rank
_world_size(group=group)# group是可选参数,返回全局的整个的进程数
torch.distributed.is_initialized()# 判断该进程是否已经初始化
torch.distributed.is_mpi_avaiable()# 判断MPI是否可⽤
torch.distributed.is_nccl_avaiable()# 判断nccl是否可⽤
初始化⽅式:
使⽤TCP
import torch.distributed as dist
dist.init_process_group(backend, init_method='tcp://10.1.1.20:23456',
rank=args.rank, world_size=4)
共享⽂件系统
import torch.distributed as dist
dist.init_process_group(backend, init_method='file:///mnt/nfs/sharedfile',
world_size=4, rank=args.rank)
环境变量
默认情况下使⽤的都是环境变量来进⾏分布式通信,也就是指定init_method="env://",这个进程会⾃动从本机的环境变量中读取如下数据:
MASTER_PORT: rank0上机器的⼀个空闲端⼝
MASTER_ADDR: rank0机器的地址
WORLD_SIZE:这⾥可以指定,在init函数中也可以指定
RANK:本机的rank,也可以在init函数中指定
在pytorch的官⽅教程中提供了以下这些后端
根据官⽹的介绍, 如果是使⽤cpu的分布式计算, 建议使⽤gloo, 因为表中可以看到 gloo对cpu的⽀持是
最好的, 然后如果使⽤gpu进⾏分布式计算, 建议使⽤nccl, 实际测试中我也感觉到, 当使⽤gpu的时候, nccl的效率是⾼于gloo的. 根据博客和官⽹的态度, 好像都不怎么推荐在多gpu的时候使⽤mpi
对于后端选择好了之后, 我们需要设置⼀下⽹络接⼝, 因为多个主机之间肯定是使⽤⽹络进⾏交换, 那肯定就涉及到ip之类的, 对于nccl和gloo ⼀般会⾃⼰寻⽹络接⼝, 但是某些时候, ⽐如我测试⽤的服务器, 不知道是系统有点古⽼, 还是⽹卡⽐较多, 需要⾃⼰⼿动设置. 设置的⽅法也⽐较简单, 在Python的代码中, 使⽤下⾯的代码进⾏设置就⾏:
# 以下⼆选⼀, 第⼀个是使⽤gloo后端需要设置的, 第⼆个是使⽤nccl需要设置的
我们怎么知道⾃⼰的⽹络接⼝呢, 打开命令⾏, 然后输⼊ifconfig, 然后到那个带⾃⼰ip地址的就是了, 我见过的⼀般就是em0, eth0,
esp2s0之类的, 当然具体的根据你⾃⼰的填写. 如果没装ifconfig, 输⼊命令会报错, 但是根据报错提⽰安装⼀个就⾏了.
dist.init_process_group(backend, init_method='file:///mnt/nfs/sharedfile',
rank=rank, world_size=world_size)
rank/world_size:
这⾥其实没有多难, 你需要确保, 不同机器的rank值不同, 但是主机的rank必须为0, ⽽且使⽤init_method的ip⼀定是rank为0的主机, 其次world_size是你的主机数量, 你不能随便设置这个数值, 你的参与训练的主机数量达不到world_size的设置值时, 代码是不会执⾏的.
应⽤举例:
import os
import re
import torch
as nn
import torch.distributed as dist
parallel import DistributedDataParallel as DDP
# 1. 获取环境信息
rank =viron['SLURM_PROCID'])
world_size =viron['SLURM_NTASKS'])
local_rank =viron['SLURM_LOCALID'])
node_list =viron['SLURM_NODELIST'])
# 对ip进⾏操作
node_parts = re.findall('[0-9]+', node_list)
host_ip ='{}.{}.{}.{}'.format(node_parts[1], node_parts[2], node_parts[3], node_parts[4])
# 注意端⼝⼀定要没有被使⽤
port ="23456"
# 使⽤TCP初始化⽅法
init_method ='tcp://{}:{}'.format(host_ip, port)
# 多进程初始化,初始化通信环境
dist.init_process_group("nccl", init_method=init_method,
world_size=world_size, rank=rank)
# 指定每个节点上的device
torch.cuda.set_device(local_rank)
model = model.cuda()
# 当前模型所在local_rank
model = DDP(model, device_ids=[local_rank])# 指定当前卡上的GPU号
input=input.cuda()
output = model(input)
# 此后训练流程与普通模型⽆异
参考⽂章:
初始化,blog.csdn/m0_38008956/article/details/86559432 blog.csdn/weixin_38278334/article/details/105605994