1.数据并行(DP)
数据并行是最常用的并行训练方式,主要分为DataParallel(DP)和DistributedDataParallel(DDP)两种。
DP是早使用的数据并行方案,通过torch.nn.DataParallel()来调用,代码如下:
# 设置可见的GPU
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1,2,3"# 将模型放到GPU 0上,必须先把模型放在GPU上,后面才可以调用DP
model.cuda()# 构建DataParallel数据并行化
model=torch.nn.DataParallel(model)
DP核心思想是将一个大的batch数据分割成多个子batch,并将子batch分配给不同的GPU进行并行计算。如下图将训练过程分为前向传播和反向传播详细分析:
「前向传播:」
模型和完整的mini-batch数据被放置在Master GPU(例如GPU:0)上。
GPU:0将mini-batch数据分割成若干个子batch,并将这些子batch分发(scatter)到其它GPU上。
GPU:0将自身的模型参数复制到其它GPU,确保每个GPU上的模型参数完全相同。
每个GPU在单独的线程上对其sub-mini-batch的数据前向传播,计算出各自的输出结果。
GPU:0收集所有GPU的输出结果。
「反向传播:」
GPU:0基于收集的输出结果和真实label计算总损失loss,并得到loss的梯度。
GPU:0将计算出的loss梯度分发(scatter)到所有GPU上。
每个GPU根据收到的loss梯度反向传播,计算出所有模型参数的梯度。
所有GPU计算出的参数梯度被汇总回GPU:0。
GPU:0基于汇总的梯度更新模型参数,完成一次迭代的训练。
DP里面只有一个优化器Optimizer,这个优化器Optimizer只在Master GPU上进行参数更新,当环境不再不在改变的时候,其它GPU选择了躺平,当GPU:0忙前忙后去分发数据、汇总梯度,更新参数的时候,其它GPU就静静躺着。
DataParallel采用的是Parameter Server并行架构,在实现多GPU或多节点并行训练时,存在一些固有的局限性:
通信开销大:每个 「计算节点」 在每次迭代中都需要与参数服务器进行多次通信,以获取最新的参数更新并将计算的梯度发送回去。这种频繁的通信会导致网络带宽成为瓶颈,尤其是当模型参数量大且GPU数量众多时,通信延迟和带宽消耗会显著影响整体训练速度。
负载不均衡:其中一个GPU被指定为Master GPU,负责汇总梯度和广播更新等,Master GPU可能会承担额外的通信和计算负担,导致负载不均衡。这不仅会影响该GPU的计算效率,也可能拖慢整个训练过程的速度。同时导致GPU利用率不足。
「仅支持单机多卡模式,无法实现多机多卡训练。」
2.DDP
DDP采用多进程架构,赋予了每个GPU更多的自由,支持多机多卡分布式训练。每个进程都拥有自己的优化器Optimizer,可独立优化所有步骤。每个进程中在自己的GPU上计算loss,反向计算梯度。
在DDP中,不存在所谓的Master GPU,所有GPU节点地位平等,共同参与训练过程的每一个环节。每个GPU都会计算loss和梯度,然后通过高效的通信协议(如AllReduce)与其它GPU同步梯度,确保模型参数的一致性。
前向传播流程:
- 数据分配:每个进程从分布式采样器(DistributedSampler)获取其对应的数据子集。这些数据子集是按照进程的编号分配的,确保每个进程获得不同的数据。
- 模型复制:在所有进程中复制模型,并用DDP封装。每个进程的模型运行在其指定的GPU上。
- 计算输出:每个进程在自己的GPU上计算其数据子集的模型输出。这些输出是独立计算的,没有跨进程的通信。
反向传播流程:
- 计算损失:每个进程根据自己的输出和目标标签计算损失函数(loss)。
- 反向传播:调用
loss.backward()
在每个进程中独立进行反向传播,计算梯度。- 梯度同步:DDP使用AllReduce算法在所有进程间同步梯度。这个步骤确保了所有进程的模型参数梯度是一致的。
- Bucket机制:为了提高通信效率,DDP采用基于Bucket的方式组织和管理模型参数。模型参数被映射到不同的Bucket中,这样可以在反向传播过程中直接对Bucket中的参数计算梯度。
- Autograd Hook:为每个参数的梯度累加器注册Autograd Hook,当反向传播过程中参数的梯度更新完成后触发。
优化器更新:每个进程使用自己的优化器根据同步后的梯度更新模型参数。由于所有进程的梯度是一致的,因此模型参数的更新也是一致的