world_size = torch.cuda.device_count()
这一步就是设定一个组,这个组里面设定你有几个进程(world_size),现在是卡几(rank)。让pycharm知道你要跑几个进程,包装在组内,进行通讯这样模型参数会自己同步,不需要额外操作了。
import os
import torch.distributed as dist
def ddp_setup(rank,world_size):
os.environ['MASTER_ADDR'] = 'localhost' #rank0 对应的地址
os.environ['MASTER_PORT'] = '6666' #任何空闲的端口
dist.init_process_group(
backend='nccl', #nccl Gloo #nvidia显卡的选择nccl
world_size=world_size,
init_method='env://',
rank=rank
) #初始化默认的分布进程组
dist.barrier() #等到每块GPU运行到这再继续往下走
rank是自动分配的。怎么分配呢?这里用的是spawn也就一行代码。
import torch.multiprocessing as mp
def main (rank:int,world_size:int,args):
pass
#训练代码 主函数
mp.spawn(main,args=(args.world_size,args), nprocs=args.world_size)
注意,调用spawn的时候,没有输入main的其中一个参数rank,rank由代码自动分配。将代码复制两份在两张卡上同时跑,你可以print(rank),会发现输出 0 1。两份代码并行跑。
另外,nprocs=args.world_size。如果不这么写,代码会卡死在这,既不报错,也不停止。
one of the variables needed for gradient computation has been modified by an inplace operation
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.cuda.FloatTensor [2048]] is at version 4; expected version 3 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True).
经过调试发现,当使用nn.DataParallel并行训练或者单卡训练均可正常运行;另外如果将两次模型调用集成到model中,即通过out1, out2 = model(input0, input1) 的方式在分布式训练下也不会报错。
在分布式训练中,如果对同一模型进行多次调用则会触发以上报错,即nn.parallel.DistributedDataParallel方法封装的模型,forword()函数和backward()函数必须交替执行,如果执行多个(次)forward()然后执行一次backward()则会报错。
解决此问题可以聚焦到nn.parallel.DistributedDataParallel接口上,通过查询PyTorch官方文档发现此接口下的两个参数:
model = nn.parallel.DistributedDataParallel(model,
device_ids=[args.local_rank],
broadcast_buffers=False,
find_unused_parameters=True)
RuntimeError: NCCL error in: ../torch/lib/c10d/ProcessGroupNCCL.cpp:859, invalid usage, NCCL version 21.1.1
ncclInvalidUsage: This usually reflects invalid usage of NCCL library (such as too many async ops, too many collectives at once, mixing streams in a group, etc).
*这是因为model和local_rank所指定device不一致引起的错误。
model.cuda(args.local_rank)
model = torch.nn.parallel.DistributedDataParallel(model,
device_ids=[args.local_rank],
broadcast_buffers=False,
find_unused_parameters=True)
使用distributed加载数据集,需要使用DistributedSampler自动为每个gpu分配数据,但需要注意的是sampler和shuffle=True不能并存。
train_sampler = DistributedSampler(trainset)
train_loader = torch.utils.data.DataLoader(trainset,
batch_size=args.train_batch_size,
num_workers=args.train_workers,
sampler=train_sampler)