0 前言:
在很多时候,数据集很大,单卡单节点不能满足我们的工程工作。这个时候我们需要考虑多卡多机器上运行模型的张量并行化运算。
1 模型并行Model Parallel
将模型的计算图放入不同的计算节点,然后不同的节点并行计算图的不同的部分。
优点:
较容易容纳大模型,把很大的模型,分散置于不同节点了。
缺点:
并行计算较复杂,因为需要考虑如何有效控制数据在不同的节点的计算顺序,有条不紊的高速计算保证计算资源的利用率。
2 数据并行Data Parallel
每个节点有一个计算图,在不同的节点输入不同迷你批次的数据来对节点进行前向后反向计算,最后归约反向计算的梯度,并对模型进行优化和权重更新。
2.1 优点:
并行化算法较简单,只要考虑各个节点的张量再进行归约就好了
2.2 缺点:
大模型不适用,他对模型的显存上限还是各自节点上限。
Pytorch框架主要支持的却也是数据并行化。主要有DP和DDP俩种实现方式
2.3 DP:
DataParallel,这个是最简单的并行方式,部需要任何前置工作,代码会自动对一个迷你批次进行分割,讲其均匀分布到多个GPU上进行计算,最后讲结果归约到某一个GPU上。
torch.nn.DataParallel(module,device_ids=None,output_device=None,dim=0)
虽然这个用起来很简单,但是不太建议使用。
torch.nn.DataParallel能够很快对模型进行并行化,充分利用多GPU的优势,但他是基于Python线程的 ,通过不同的线程在部通的GPU上 异步运行模型来得到最终的结果。由于Python 自带全局解释器(GIL),任何时候,python解释器只能运行一个线程,对于多GPU来说就是资源浪费了。所以推荐下面这种方法
2.4 DDP:
分布式数据并行化,DISTRIBUTED DATA PARALLEL
关于分布式框架的后端主要有Gloo,MPI,NCCL
Pytorch 里,如果是GPU分布式优先推荐,NCCL,CPU分布式优先推荐Gloo
这个方法就是有些前置工作了。
2.4.1
为了启动不同的进程,我们需要对所有的计算进程进行初始化。
torch.distributed.init_process_group(backend,init_method=None,timeout=datatime.timedelta(0,1800),world_size=-1,rank=-1,store=None,group_name='')
backend参数就是前面提到的分布式框架后端
2.4.2
为了让多进程训练的每个进程能够分配到不同的输入数据,我们可以定义个数据载入器
train_dataset = datasets.ImageFolder(
traindir,
transform.Compose([
transform.RandomResizedCrop(224),
...
normalize,
})
)
分布式采样器对数据进行分割,给每个进程分配独立的数据
train_sampler = torch.utils.data.distributed.DistributedSampler=(train_dataset)
train_loader = torch.utils.data.DataLoader(
train_dataset,batch_size =args.batch_size,
shuffle=(train_sampler is None),
num_workers=args.workers,pin_memory =True,
sampler=train_sampler
)
tips1,如果设置数据采样器,shuffle需要设置为False。
tips2,为了让每个迭代器所有的进程能够合理进行分割和随机排列,需要固定一个随机种子,因此次迭代开始的时候需要先运行train_sampler.set_epoch(epoch)
2.4.3
在分布式进程启动和数据载入后,就是并行模型的构建运行了,这个比较简单
torch.nn.parallel.DistributedDataParallel(
modle,device_ids=NOne,output_device=None,dim=0,broadcast_buffers=True,process_group=None,bucket_cap_mb=25,find_unused_parameters=False,check_reduction=False)
2.4.4
在分布式计算时,如果使用IO函数,输出时候进程会有竞争输出报错的可能,因此,需要具体设置
if not args.multiprocessing_distributed or(args.multiprocessing_distributed
and args.rank %ngpus_per_node ==0
#可看到只有当GPU里的进程id=0时候才进行保存 ):
save_checkpoint({
'epoch':epoch+1,
"atch":args.arch,
'state_dict':model.state_dict(),
'best_accl':best_accl,
'optimizer':optimizer.state_dict(),
},is_best)