-
Notifications
You must be signed in to change notification settings - Fork 37
Global Tensor 分布式并行策略 #512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
c91a86d
d54086b
b95ba9a
bc3f5da
d0400a5
a66f63f
e06da9f
0414a7d
a30f737
d48a6a3
e972610
3c3691a
9d15744
b8f4c9d
2e774f3
b2d43ff
e0186f7
a9b2c2f
9e8f7c5
cb83a4e
8042d0b
a9dc8f0
9b4b20b
d63ff11
513c095
0ff15ae
dd9d374
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
# 使用 Global Tensor 进行多机多设备编程:分布式并行策略 | ||
|
||
> 首先简单介绍分布式训练的重要性 | ||
|
||
深度学习是通过神经网络学习样本数据的内在规律和表现层次的一种复杂机器学习算法。计算过程主要涉及数据和模型两部分。 | ||
|
||
随着深度学习的广泛应用,模型规模不断扩大,对硬件(算力、内存)的需求也在不断提高。然而,受限于物理定律,持续提高芯片的集成越来越困难,单一设备的算力及容量难以跟上模型扩大的需求。 | ||
|
||
为解决算力增速不足的问题,多节点集群的分布式训练方式逐渐受到重视,高效易用的分布式并行策略的提出势在必行。 | ||
|
||
|
||
## 并行策略 | ||
|
||
值得注意的是,简单的设备堆叠并不一定会带来算力的增长。因为神经网络的训练并不是单纯的“把原来一个设备做的事情,现在分给多个设备各自做”,它不仅需要多个设备进行计算,还涉及到设备之间的数据传输,只有协调好集群中的计算与通信,才可以实现高效的分布式训练。 | ||
|
||
> 对三种并行方式进行简要概括 | ||
|
||
常见的并行策略包括**数据并行**、**模型并行**和**流水并行**,特点如下: | ||
|
||
- 数据并行:对**数据**进行切分,不同设备数据不同,但模型相同 | ||
- 模型并行:对**模型**进行切分,不同设备数据相同,但模型不同 | ||
- 流水并行:将**模型**分为多个阶段,分发到不同设备,各个设备之间以“流水线”的方式完成训练 | ||
|
||
除上述三种策略外,**混合并行**也是一种常见的并行策略,通过上述两种或三种方式的混合使用完成训练目的。 | ||
|
||
> 这里考虑加一段 Global Tensor 实现并行的优势介绍(简单、高效、……) | ||
|
||
待定 | ||
|
||
> matmul 基础代码和示例,后续所有示例都以这个为基础修改 | ||
|
||
本文以矩阵乘法为例,解释并行策略间的区别,以及如何利用 Global Tensor 实现不同的并行方式。 | ||
|
||
假设神经网络中的某一层是进行矩阵乘法计算,其中,输入 $x$ 的形状为 $4\times5$,模型参数 $w$ 的形状为 $5\times8$,那么,矩阵乘法输出形状为 $4\times8$。 | ||
|
||
基础代码: | ||
|
||
```python | ||
import oneflow as flow | ||
|
||
x = flow.randn(4, 5) | ||
w = flow.randn(5, 8) | ||
out = flow.matmul(x, w) | ||
print(out.shape) # (4, 8) | ||
``` | ||
|
||
示意图如下: | ||
|
||
 | ||
|
||
单设备的训练中,以上矩阵乘法计算得到 $out$ 后会传递到下一层,并最终计算得到 $loss$。然后,在反向传播过程中,得到 $\frac{\partial loss}{\partial w}$,用于更新 $w$。 | ||
|
||
### 数据并行 | ||
|
||
> 接下来以以例子和图片结合的方式,分别介绍各种并行策略 | ||
|
||
数据并行是将数据进行切分输入不同设备,而每个设备上的模型保持完整和一致。 | ||
|
||
OneFlow 特有的 Global Tensor 采用 `placement` 与 `sbp` 结合的方式完成分布。其中 `placement` 表示 Global Tensor 分布的物理设备,`sbp` 表示 Global Tensor 分布的方式(详情可见:[创建 Global Tensor](./global_tensor.md/#global-tensor_2))。 | ||
|
||
以两卡数据并行为例,Global Tensor 的设计方式使得上述矩阵乘法案例的修改非常简单: | ||
|
||
1. 数据 $x$ 按第 0 维度切分(`sbp=flow.sbp.split(dim=0)`),分布在两卡设备上(`placement=flow.placement(type="cuda", ranks=[0, 1])`) | ||
2. 模型 $w$ 保持完整(`sbp=flow.sbp.broadcast`),分布在两卡设备上(`placement=flow.placement(type="cuda", ranks=[0, 1])`) | ||
|
||
修改后,完整代码如下: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里只提供了代码,没有谈如何启动,不符合“重实践”。可能用户直接复制这类代码去跑,会发现跑不起来。 (这些代码已经跑过吗) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 代码还没有测试,所以启动部分没写,我想的是代码量也不大,就放上来展示一下 |
||
|
||
```python | ||
import oneflow as flow | ||
|
||
placement = flow.placement(type="cuda", ranks=[0, 1]) | ||
x = flow.randn(4, 5, placement=placement, sbp=flow.sbp.split(dim=0)) | ||
w = flow.randn(5, 8, placement=placement, sbp=flow.sbp.broadcast) | ||
out = flow.matmul(x, w) | ||
print(out.shape) # (4, 8) | ||
``` | ||
|
||
数据并行示意图: | ||
|
||
 | ||
|
||
|
||
> 这里在考虑要不要放数据并行的其他介绍,例如: | ||
>> 数据并行策略下,在反向传播过程中,需要对各个设备上的梯度进行 AllReduce,以确保各个设备上的模型始终保持一致 | ||
|
||
>> 当数据集较大,模型较小时,由于反向过程中为同步梯度产生的通信代价较小,此时选择数据并行一般比较有优势,常见的视觉分类模型,如 ResNet50,比较适合采用数据并行。 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这些介绍又太偏 tutorial 了。其实感觉可以不放。或者说放的话,也要用 how to 的风格,体现实践和面向解决工作中的问题。比如,列出 ResNet50 这类模型用数据并行的线性加速比(效果好),再列出 BERT 那类模型用数据并行的线性加速比(效果不好) |
||
|
||
### 模型并行 | ||
|
||
当神经网络非常巨大时,数据并行同步梯度的代价很大,此时可以考虑采用模型并行策略。 | ||
|
||
与数据并行相反,模型并行是将模型进行切分输入不同设备,而每个设备上的数据保持完整和一致。 | ||
|
||
同样以两卡为例,模型并行修改方式为: | ||
|
||
1. 数据 $x$ 保持完整(`sbp=flow.sbp.broadcast`),分布在两卡设备上(`placement=flow.placement(type="cuda", ranks=[0, 1])`) | ||
2. 模型 $w$ 按第 1 维度切分(`sbp=flow.sbp.split(dim=1)`),分布在两卡设备上(`placement=flow.placement(type="cuda", ranks=[0, 1])`) | ||
|
||
修改后,完整代码如下: | ||
|
||
```python | ||
import oneflow as flow | ||
|
||
placement = flow.placement(type="cuda", ranks=[0, 1]) | ||
x = flow.randn(4, 5, placement=placement, sbp=flow.sbp.broadcast) | ||
w = flow.randn(5, 8, placement=placement, sbp=flow.sbp.split(dim=1)) | ||
out = flow.matmul(x, w) | ||
print(out.shape) # (4, 8) | ||
``` | ||
|
||
模型并行示意图: | ||
|
||
 | ||
|
||
### 流水并行 | ||
|
||
当神经网络过于巨大,无法在一个设备上存放时,可以选择流水并行策略。 流水并行将网络切分为多个阶段,并分发到不同的计算设备上,各个计算设备之间以“流水线”的方式完成训练。 | ||
|
||
以两卡流水并行为例,构造两阶段示例程序: | ||
|
||
```python | ||
import oneflow as flow | ||
|
||
P0 = flow.placement(type="cuda", ranks=[0]) | ||
P1 = flow.placement(type="cuda", ranks=[1]) | ||
BROADCAST = flow.sbp.broadcast | ||
|
||
# 模型第一阶段分布在第 0 卡 | ||
w0 = flow.randn(5, 8, placement=P0, sbp=BROADCAST) | ||
# 模型第二阶段分布在第 1 卡 | ||
w1 = flow.randn(8, 3, placement=P1, sbp=BROADCAST) | ||
|
||
# 随机生成数据模拟输入 | ||
x = flow.randn(4, 5) | ||
|
||
# 利用 to_global 将第一阶段的数据分布在第 0 卡 | ||
in_stage0 = x.to_global(placement=P0, sbp=BROADCAST) | ||
out_stage0 = flow.matmul(in_stage0, w0) | ||
print(out_stage0.shape) # (4, 8) | ||
|
||
# 利用 to_global 将第二阶段的数据分布在第 1 卡 | ||
in_stage1 = out_stage0.to_global(placement=P1, sbp=BROADCAST) | ||
out_stage1 = flow,matmul(in_stage1, w1) | ||
print(out_stage1.shape) # (4, 3) | ||
``` | ||
|
||
以上程序采用矩阵乘法,模拟了一个两阶段神经网络。与数据并行和模型并行不同,流水并行中的数据和模型均未被切分,而是分别将两个阶段分布在不同的设备上进行计算。 | ||
|
||
Global Tensor 的设计,使得计算过程中,只需通过 `to_global` 方法调整上一阶段的输出数据的分布策略,作为下一阶段的输入数据即可。 | ||
|
||
> 这里要不要写“ Stage ID 及梯度累积设置” | ||
|
||
### 混合并行 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 混合并行这里考虑知识跨度,可以把三个例子组合起来就好,而不写复杂而具体的case。 实际使用中,混合并行需要用到 2D sbp,可以开单独的一篇文章。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 三个例子组合起来是用 matmul 再给一个程序吗?混合并行这里两两混合和三种全用应该总共有四种混合方式,需要全放上例子吗? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 可以不用做复杂组合,一个case就可以了。需要放上例子。 |
||
|
||
> 这里想的是直接放 GPT-3 示例 | ||
|
||
在网络的训练中,也可以将多种并行策略混用,以 GPT-3 为例,以下是它训练时的设备并行方案: | ||
|
||
首先将模型分为 64 个阶段,进行流水并行。每个阶段都运行在 6 台 DGX-A100 主机上。在 6 台主机之间,进行的是数据并行训练;每台主机有 8 张 GPU 显卡,同一台机器上的 8 张 GPU 显卡之间是进行模型并行训练。 | ||
|
||
 | ||
|
||
## 结语 | ||
|
||
并行策略的选择影响着训练效率,框架对并行训练的接口支持程度,决定了算法工程师的开发效率。 | ||
|
||
本文介绍了数据并行、模型并行、流水并行以及混合并行这些分布式并行策略,通过示例展示了 OneFlow 针对分布式训练所做的系统级设计和创新,以便于用户轻松上手分布式训练。 |
Uh oh!
There was an error while loading. Please reload this page.