标签:SBP oneflow placement nn 训练 flow sbp 多机 环境变量
多机训练时的环境变量
通过设置环境变量配置分布式训练,仅仅是为了在交互式 Python 环境下,方便查看实验效果。如果不是学习、试验目的,而是生产需求,可以直接通过 oneflow.distributed.launch 启动分布式训练,该模块内部根据命令行参数,自动设置了必要的环境变量。
1)MASTER_ADDR:多机训练的第0号机器的 IP。
2)MASTER_PORT:多机训练的第0号机器的监听端口,不与已经占用的端口冲突即可。
3)WORLD_SIZE:整个集群中计算设备的数目,因为目前还不支持各个机器上显卡数目不一致,因此 WORLD_SIZE 的数目实际上是机器数目每台机器上的显卡数目,机器数目每台机器上的显卡数目机器数目×每台机器上的显卡数目。如这个例子中,是单机2卡的情况,因此 WORLD_SIZE=2。
RANK 和 LOCAL_RANK 都是对计算设备的编号,不同的是 RANK 是全局视角的编号,LOCAL_RANK 某个特定机器上的局部视角的编号。当是单机训练(单机单卡或单机多卡)时,两者是没有区别的。以上的例子中,有两个显卡,分别是0号和1号。
当多机训练时,每台机器上的 LOCAL_RANK 的上限,就是每台机器上的计算设备的数目;RANK 的上限,就是所有机器上所有计算设备的总和,它们的编号均从0开始。(因为编号从0开始,所以不包含上限)
以两台机器、每台机器上有两张显卡为例,可以整理出每张显卡的 LOCAL_RANK 与 RANK 对应情况,见表5-1。
表5-1 整理出每张显卡的 LOCAL_RANK 与 RANK 对应情况
|
RANK
|
LOCAL_RANK
|
机器0的第0张显卡
|
0
|
0
|
机器0的第1张显卡
|
1
|
1
|
机器1的第0张显卡
|
2
|
0
|
机器1的第1张显卡
|
3
|
1
|
1. Boxing(自动转换 SBP)
已经通过以上代码的例子,知道一个算子会根据输入张量的 SBP 属性以及算子内置的 SBP签名,自动设置输出张量的SBP。
如果上游算子输出张量的 SBP,与下游算子输入的需要不一致时,怎么办呢?
比如,假设在模型并行中,有2层矩阵乘法,在第一层和和第二层都做模型并行。如图5-11所示。
图5-11假设在模型并行中,有2层矩阵乘法,在第一层和和第二层都做模型并行
因为第一层的输出的 SBP(split(1)),并不是第二层输入所期待的(broadcast),这时候,OneFlow 会自动在上一层的输出和下一层的输出之间,插入对抗操作,利用集合通信进行必要的数据转换。
从 split(1) 转换为广播,相当于做了一次 AllGather 操作。如图5-12所示。
图5-12 从 split(1) 转换为广播,相当于做了一次 AllGather 操作
因为有对抗机制的存在,使得用户只用关心少数关键地方(如源算子)的 SBP 设置,剩下的全部都可以交给 OneFlow 框架。
2. 2D SBP
介绍了
集群的全局视角和
全局张量 后,相信已经掌握了 SBP 和 SBP签名的基本概念,并且能够上手相关的编程任务。实际上,以上资料中涉及都是 1D SBP。
现在介绍 2D SBP,它能够更灵活地应对更复杂的分布式训练场景。
3. 2D 设备阵列
已经熟悉 1D SBP 的位置配置,在 1D SBP 的场景下,通过
oneflow.placement 接口配置集群,比如使用集群中的第 0~3 号 GPU 显卡:
>>> placement1 = flow.placement("cuda", ranks=[0, 1, 2, 3])
以上的 cuda指定了设备类型,ranks=[0, 1, 2, 3] 指定了集群中的计算设备。其实,ranks 不仅可以是一维的int list,还可以是多维的int数组:
placement2 = flow.placement("cuda", ranks=[[0, 1], [2, 3]])
当 ranks 是 ranks=[0, 1, 2, 3] 这种一维列表的形式时,集群中的所有设备组成了一个 1D 设备向量,这也是 1D SBP 名称的由来。
当 ranks 是多维数组的形式时,集群中的设备被分组为一个多维的设备阵列。ranks=[[0, 1], [2, 3]] 表示集群中的四个计算设备被划分为了 2×2 的设备阵列。
2D SBP
构造全局张量时,需要同时指定位置与 SBP。当位置中的集群是 2 维的设备阵列时;SBP 也必须与之对应,是一个长度为 2 的元祖,这个元祖中的第 0 个、第 1 个 元素,分别描述了全局张量张量在设备阵列第 0 维、第 1 维的分布。
以下代码,配置了 2×2 的设备阵列,并且设置 2D SBP 为 (broadcast, split(0))。
>>> a = flow.Tensor([[1,2],[3,4]])
>>> placement = flow.placement("cuda", ranks=[[0, 1], [2, 3]])
>>> sbp = (flow.sbp.broadcast, flow.sbp.split(0))
>>> a_to_global = a.to_global(placement=placement, sbp=sbp)
逻辑上的数据,在整个设备阵列上,在第 0 维度(竖着看)做 广播;在第 1 维度(横着看)做 split(0)。如图5-13所示。
图5-13 在第 0 维度(竖着看)做 广播;在第 1 维度(横着看)做 split(0)
此图的最左边是全局视角的数据,最右边是设备阵列上各个设备的数据。可以看到,从第 0 维的角度看,它们都是 broadcast 的关系:
1)(group0, device0) 与 (group1, device0) 中数据一致,互为 broadcast 关系
2)(group0, device1) 与 (group1, device1) 中数据一致,互为 broadcast 关系
从第 1 维的角度看,它们都是 split(0) 的关系:
1)(group0, device0) 与 (group0, device1) 互为 split(0) 关系。
2)(group1, device0) 与 (group1, device1) 互为 split(0) 关系。
直接理解逻辑数据和最终的设备阵列中的物理数据,对应关系可能有一定难度,在思考 2D SBP 时,可以假想一个中间状态(图5-13中灰色部分),以 (broadcast, split(0)) 为例:
1)原始逻辑张量,先经过广播,广播到 2 个 group 上,得到中间的状态。
2)在中间状态的基础上,继续在各自的 group 上,做 split(0),得到最终设备阵列中各个物理张量的状态。
4. 2D SBP签名
类似 1D SBP 有 SBP签名的概念,算子也有 2D SBP签名,在掌握了 1D SBP 及其 签名的基础上,2D SBP签名非常简单,只需要遵循一条原则:在各自的维度上独立推导即可。
以矩阵乘法为例,先回顾 1D SBP 的情况,假定有
标签:SBP,
oneflow,
placement,
nn,
训练,
flow,
sbp,
多机,
环境变量
From: https://www.cnblogs.com/wujianming-110117/p/18419188