type
status
date
slug
summary
tags
icon
password

一、 为什么要做分布式训练?

分布式训练,就是使用多台机器共同完成训练任务。分布式训练常在训练数据量较大或模型参数规模太大导致单卡不可训练的场景下使用。

二、 一些前提知识

2.1 什么是梯度?

梯度是一个向量,是一个n元函数关于n个变量的偏导数,梯度会指向各点处的函数值降低的方向,也就是说梯度指示的方向是各点处的函数值减少最多的方向。

2.2 为什么要进行梯度更新?

深度学习中, 神经网络的主要任务是在学习时找到最优的参数(权重和偏置),这个最优参数也就是损失函数最小时的参数。但是一般情况下,损失函数比较复杂,参数也很多,无法确定在哪里取得最小值。所以通过梯度来寻找最小值(或者尽可能小的值)的方法就是梯度法。

2.3 如何进行梯度更新?

梯度下降的基本过程就和下山的场景很类似。首先,我们有一个可微分的函数。这个函数就代表着一座山。我们的目标就是找到这个函数的最小值,也就是山底。根据之前的场景假设,最快的下山的方式就是找到当前位置最陡峭的方向,然后沿着此方向向下走,对应到函数中,就是找到给定点的梯度 ,然后朝着梯度相反的方向,就能让函数值下降的最快,所以重复利用这个方法就可以找到最优的参数

三、 分布式训练的技术原理

在分布式训练技术中,集合通信训练模式和参数服务器训练模式是两种最主要的训练模式。对于网络复杂、参数稠密特点的计算机视觉(CV)\ 自然语言处理(NLP)等类型的模型训练场景,使用集合通信训练模式;对于拥有庞大的 Embedding 层(Embedding的作用是弄到合适的关联性)模型和超大数据量的搜索、推荐模型训练场景,使用参数服务器训练模式。

3.1 集合通信训练模式

适用于模型结构复杂、参数稠密。每个节点都可以称为Worker节点,每个Worker负责模型训练还要掌握最新的全局梯度信息。所以会对计算芯片的算力和网络互联的要求较高。所以很适合参数稠密、计算密集的训练任务。
图1 集合通信训练模式过程图
图1 集合通信训练模式过程图

3.1.1 流水线并行

先上一个图,流水线并行的原理过程可以通过这个图看的清楚。
图2 流水线并行过程图
图2 流水线并行过程图
流水线并行是将模型的不同层放置到不同的计算设备,降低单个计算设备的显存消耗。如上图所示例,这个模型包含四个模型层(四列圆代表四个模型层),模型被切割成了三个部分,并将这三个部分分别放到不同的计算设备上(Device 0、Device 1、Device 2)。 1、传统的:在向前计算(forward)的过程中,输入数据首先在设备0上通过第1层的计算得到中间结果,并将中间结果传输到设备1,然后在设备1上计算得到第2层和第3层的输出,并将模型第3层的输出结果传输到设备2,在设备2上经由最后一层的计算得到前向计算结果。反向传播(backward)过程类似。可以看到有大片的空白,说明利用率不是很高。
图3 传统流水式图
图3 传统流水式图
 
2、朴素流水线并行(同步),先前向计算再反向计算(F-the-B)模式:先将一个mini-batch切分为若干个micro-batch,来提升流水线并行的并发度。在向前阶段,每个设备依次计算单个micro-batch的结果(并且保存下来),增加了设备间的并发度。
图4 朴素流水线图
图4 朴素流水线图
3、向前计算和反向计算交叉进行的方式(异步),1F1B模型:前向计算和反向计算交叉进行,可以及时释放不必要的中间变量。对比朴素流水线并行峰值显存明显下降,设备资源利用率显著提升。
1F1B流水线过程:
  • 把一个 batch 分成多个mini batches,比如把一个 batch 分成 1,2,3,4 这4个mini batches。
  • 把多个 mini batches 逐一插入到流水线。
  • Machine 1 先计算 蓝色 1 的前向传播,然后把蓝色 1 发送给 Machine 2 继续计算。
  • Machine 2 接着计算 蓝色 2 的前向传播,然后把蓝色 1 发给 Machine 2 继续计算。
  • 当蓝色 1 由上至下遍历了 Machine 1 ~ 4,则完成了全部前向传播,于是开始进行反向传播,对应了第一个绿色 1,然后逆向传递到 Machine 3 ~ 1。(以蓝色1为最先优先级)。
  • 当数据 1 完成了全部反向传播,即绿色 1 来到了 Machine 1。
  • 每个机器在完成自己 mini batch 的反向传播之后,会在本地进行梯度更新。
  • Machine 和 Machine 之间只传送模型的一个子集,这样计算和通讯可以并行。
图5 异步流水线图
图5 异步流水线图

3.1.2 数据并行

数据并行的核心思想是:在各个GPU上都拷贝一份完整模型(相同),各自吃一份数据(不同),算一份梯度,最后对梯度进行累加来更新整体模型。
  1. 经典的数据并行(同步),DP(Data Parallelism):一般采用参数服务器这种框架。
图6 经典数据并行图
图6 经典数据并行图
  • 在每块计算GPU(Worker)上都拷贝一份完整的模型参数。
  • 把一份数据X(例如一个batch)均匀分给不同的计算GPU。
  • 每块计算GPU做一轮FWD和BWD后,算得一份梯度G。
  • 每块计算GPU将自己的梯度push给梯度收集GPU(Server),做聚合操作。这里的聚合操作一般指梯度累加。当然也支持用户自定义。
  • 梯度收集GPU聚合完毕后,计算GPU从它那pull下完整的梯度结果,用于更新模型参数W。更新完毕后,计算GPU上的模型参数依然保持一致。
图7 同步并行计算时间图
图7 同步并行计算时间图
可以看出同步数据并行计算有以下问题:
  • Worker 节点会有先后、快慢之分,不会恰好在同一时刻完成任务。
  • 同步要求每一轮都必须等待所有节点完成计算,这势必导致“短板效应”,即任务所需时间取决 于最慢的节点。
  • 同步会造成很多节点处于空闲状态,无法有效利用集群的算力。
 
 
2.异步数据并行计算:在异步并行计算中,一个Worker节点无需等待其余节点完成计算或者通信。过程如下:
  • 当Worker正常计算完梯度,立马向Server发送push&和pull梯度请求
  • Worker并不会实际等到把聚合梯度拿回来,更新完参数W后再做计算。而是直接拿旧的W,吃新的数据
但是也不会一直计算梯度,可以设置一个延迟的步数。比如在设置步数为1的延迟时,就意味着在做第12轮的运算时,用的模型参数就是第10轮和第11轮计算结果更新之后的模型参数。
图8 异步数据并行计算时间图
图8 异步数据并行计算时间图
3.分布式数据并行(DDP)-解决的的是通信负载不均:分布式数据并行去Server,留Worker。几个Worker构成一个环(Ring)。设计的核心思想就是:我们将所有设备安排在一个逻辑环中,每个GPU应该有一个左邻和一个右邻,设备只会从它的右邻居发送数据,并从它的左邻居接收数据,整个计算过程通过Scatter-reduce和Allgather两个通信原语完成。
图9 Ring-Allreduce示意图
图9 Ring-Allreduce示意图
Scatter-Reduce过程为:
  • 定义网络拓扑关系,使得每个GPU只和其相邻的两块GPU通讯。每次发送对应位置的数据进行累加。每一次累加更新都形成一个拓扑环,因此被称为Ring。
图10 第一轮的更新示意图
图10 第一轮的更新示意图
• 一次累加完毕后,红色位置的数据块被更新,被更新的数据块将成为下一次更新的起点,继续做累加操作。
图11 第二轮更新示意图
图11 第二轮更新示意图
  • All-Gather的过程为:
直到到最后每一个chunk都是同列几个chunk之和。最后的波浪框就要广播到其他的几个chunk里面
图12 最后一轮的示意图
图12 最后一轮的示意图

四、 参考资料

编译优化编译选项
Loading...
JucanaYu
JucanaYu
干饭人,干饭魂🍚
最新发布
Python练习-类型转换
2025-4-9
Python练习-count、remove、append、extend
2025-4-8
Python练习-set和sorted
2025-4-6
Python练习-insert和del
2025-4-6
Python练习-range
2025-4-6
Python练习-双指针法
2025-4-6