在这篇文章中展示了 如何将 tensor ,pipeline, data 并行组合,扩展到数千个GPU上。
提出了一个新的交错流水线调度,可以提升10%的吞吐量。propose a novel interleaved pipelining schedule that can improve throughput by 10+% with memory foot-print comparable to existing approaches
训练transformer 语言模型等这类模型,模型规模超大,挑战:
利用数据并行进行规模化,通常表现很好但是存在两方面的限制:
a) 除了一个节点的情况,每个GPU的batch size 太小会将带GPU的利用率,增加通信的损耗
b) 可用设备的最大数是batch的size,限制了加速器的数量。
一些模型被提出来解决这两个挑战
tensor (intra-layer) model parallelism,层内并行模型。transformer每一层内的矩阵乘被切分到多态GPU上。在更大的模型上表现不好;
更大的模型被被割到多个· 多GPU的服务器上,导致两个问题:
流水线模型并行,将模型的各层分不到CPU上。 将一个batch 分成多个更小的 microbatches,通过流水线执行。
为了达到高效,需要更大的batch size
本文中介绍了一种新的流水线调度方式,可以提升小的batch size的效率。
本文解决如下问题:
在保证严格的优化器语义的同时,如何 结合并行化技术来最大化给定 batch size的大模型训练吞吐效率。(strict optimizer semantics???)
How should parallelism techniques be combined to maximize the training throughput of large models given a batch size while retaining strict optimizer semantics?
PTD-P:将 pipeline,tensor,data parallesim 进行结合的技术。
研究了不同组件之间相互作用对吞吐量的影响。
在这些研究的基础上,我们提供了如何配置分布式训练的指导原则:
Different forms of parallelism interact in non-trivial ways:不同方式的并行以非平凡的方式交互。 tensor并行最好在 多GPU的sever内部使用,才更高效。
不同的并行化策略会影响: 通信量、 kernel的计算效率、worker的空闲时间、 pipeline 气泡。提出交错式的流水线调度,寄生吞吐量,限制内存占用。
超参数,例如micro batch的大小会影响内存占用、计算效率、流水线气泡大小
分布式训练是通信密集型的,如果 inter-node 通信慢,或者在通信密集处分区隔断,将严重影响效率
PTD-P : 我们将管道模型并行性和张量模型并行性(组合如图2所示)与数据并行性相结合,并称之为 PTD-P
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-neZO7MOh-1669727514425)(assets/markdown-img-paste-20220712113741693.png)]
每个工作节点都有完整的模型,输入的数据被切分,worker 定期聚合梯度,保证所有worker 看到版本一致的权重。
对于太大的模型,无法适配到单个工作节点上。
模型的层被分割到多个设备上。
当应用到有相同的transformer模块重复的模型时,每个设备分得相同数量得transformer 层。 我们不考虑更多非对称得模型结构,分配策略会更难。
一个batch 被分成更小得 microbatches, 然后在 microbatches 上执行流水线。
流水线模式需要保证:流水线方案需要确保输入在前向和后向传递中看到一致的权重版本,保证是严格优化的。
为准确保留严格优化器语义(strict optimizer semantics),我们引入了流水线周期性刷新,以便优化步骤跨设备同步。
在设备空闲时,空闲时间被称为流水线气泡(pipeline bubble),要使得它足够小。
Asychromous (异步) 和 bounded-staleness (边界陈旧)的方式比如:PipeMare,PipeDream 和 PipeDram-2BW 完全消除了 flushes ,但是松弛了权重更新语义。
有几种可能的 microbatch 跨设备前推和后推调度策略。对 bubble size, 通信,内存占用进行 trade off
GPipe 第一次执行将一个batch的所有 microbatches 向前传递,然后再将所有的 microbatches 向后传递。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-80mfGana-1669727514427)(https://raw.githubusercontent.com/novaCoder-zrk/Picture-Bed/master/img[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YyQKcTFF-1669727514773)(…assetsmarkdown-img-paste-20220709213408346.png)]].png)
理想的处理时间(不包含气泡的时间)
t i d = m ∗ ( t f + t b ) t_{id} = m*(t_f + t_b) tid=m∗(tf+tb)
m 是一个batch 分成的 microbatch 的数量
B u b b l e t i m e f r a c t i o n ( p i p e l i n e b u b b l e s i z e ) = t p b t i d = p − 1 m Bubble \,time \, fraction(pipeline \,bubble\, size) = \frac{t_{pb}}{t_{id}} = \frac{p-1}{m} Bubbletimefraction(pipelinebubblesize)=tidtpb=mp−1
要使的气泡时间分数小,使得 m 远大于 p,对于 大的m会导致 搞得内存占用率,因为需要将中间激活值保存在内存中,在 m microbatch的生命中周期中,都需要保存
PipeDream-Flush
因为,在处理一个batch时,如果让所用的microbatch 都处于处理中的状态(in-flight microbatches),内存占用会很大,因此需要限制管道中还没处理完的microbatch的数量。
PipeDream-Flush 调度,首先进入热身步骤,worker进行一定次数的向前传递,没有一下子将所有的forward都进行完,等有microbtach可以进行backward的时候立刻进行backward,
这个调度限制了处理中的 microbatch的数量,限制在了管道深度depth,而不是batch中microbatch数。
在经过了热身阶段后,每个worker进入了一种稳定状态,向前和向后传递交替进行(1F1B)。
这个新的调度的气泡时间是相同的,但是需要更少的激活被存储,不需要保存 m 个中间激活,只需要depth个。
当m远大于p的时候,PipeDream-Flush 比 Gpipe 内存效率更优。
本文提出的新方法
为了减少流水线的气泡,设备可以为层的多个子集进行计算(model chunk),而不是计算一组连续的层。也就是说一个设备可以执行多股效地chunk,而不是一整个连续的chunk,相当于流水线的细分。
例如,一个机器可以计算4层,设备1 计算1,2,9,10;设备2 计算3,4,11,12;每个设备计算两个模型块,每个模型块有2层。
每个设备承担更多计算阶段,每个阶段的计算任务却更少。
使用1F1B 和 交错阶段调度;
要求microbatch的数量,是流水线并行度的整数倍。
将流水线的每个阶段减小,气泡就小了。但是数据之间的传输通信速度又会造成代价。
以前每个device只有一个stage(或者说 model chunk),如今每个device都有 v 个model chunk
因此气泡的时间减少为 t p b i n t . = ( p − 1 ) ( t f + t b ) v t_{pb}^{int.} = \frac{(p-1)(t_f + t_b)}{v} tpbint.=v(p−1)(tf+tb)
B u b b l e t i m e f r a c t i o n ( p i p e l i n e b u b b l e s i z e ) = t p b i n t . t i d = 1 v ⋅ p − 1 m Bubble \,time \, fraction(pipeline \,bubble\, size) = \frac{t_{pb}^{int.}}{t_{id}} = \frac{1}{v}\cdot\frac{p-1}{m} Bubbletimefraction(pipelinebubblesize)=tidtpbint.=v1⋅mp−1
将流水线细分,可以提高流水线的并行度,会减少流水线气泡的大小,但代价是增加了机器之间的通信量
Tensor Model 会将单独的层,划分到多个机器上
Megatron 划分策略:
multi-head attention 的划分策略
并行化配置的性能分析
不同维度的并行化都进行了内存占用,设备利用率和通信量的tradeoff
流水线气泡大小:
p − 1 m = n / t − 1 m \frac{p-1}{m} =\frac{n/t-1}{m} mp−1=mn/t−1
m 是确定的,当t增加,流水线的气泡减少。
s 是序列长度 (sequence length),s怎么理解?每个样本序列的长度? h是 (hidden size)
大小为 bsh的tensor需要在t个设备之间进行all-reduce,forward和backward个进行一次,每个设备每层的总传输量为 8 b s h ( t − 1 t ) 8bsh(\frac{t-1}{t}) 8bsh(tt−1)
每个设备有 l s t a g e l^{stage} lstage层,每个设备总的传输量为 l s t a g e ⋅ ( 8 b s h ( t − 1 t ) ) l^{stage}\cdot(8bsh(\frac{t-1}{t})) lstage⋅(8bsh(tt−1))
tensor 并行增加了设备之间的通信量。
当t大于单个接单的GPU数量时,需要节点之间进行通信。更慢的节点之间的链路的影响是很大的。
要点#1
当使用 g-GPUS服务器时,将tensor 并行度控制在g之内,使用pipeline 并行来跨服务器扩展模型
B u b b l e t i m e f r a c t i o n ( p i p e l i n e b u b b l e s i z e ) = p − 1 m = n / d − 1 b ′ / d = n − d b ′ Bubble \,time \, fraction(pipeline \,bubble\, size) = \frac{p-1}{m} = \frac{n/d - 1}{b'/d} = \frac{n-d}{b'} Bubbletimefraction(pipelinebubblesize)=mp−1=b′/dn/d−1=b′n−d
因为 tensor 模型并行, 每个microbatch 都需要执行 all-reduce 通信。
因为数据并行,每个batch 都要进行代价较大的 all-reduce 通信。
GPU负责计算一个层的一部分,如果模型的层不够大划分给一个GPU的矩阵计算太小,GPU的利用率会不高
tensor 的通信代价比 data并行的通信代价大得多。
要点#2
data 并行,只需要在 batch 层面进行 all-reduce。 但是tensor 并行,需要在 每个micro batch 层面进行all-reduce。
总的模型并行度 M = tp,tensor 和 pipeline 主要是为了 将大模型切分以适应GPU的主存,但是数据并行,主要是为了将训练扩展至更多的GPU。
microbatch 的大小 b 同样也可以影响模型训练的吞吐率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHdRNdP8-1669727514434)(https://raw.githubusercontent.com/novaCoder-zrk/Picture-Bed/master/img[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2lmtSckd-1669727514735)(…assetsmarkdown-img-paste-20220710215139274.png)]].png)
要点#3
对于 microbatch size b的优化,要考虑吞吐率,内存占用,pipeline深度 ,data 并行度 d,batch大小B
激活值的重新计算是计算量和内存占用的 tradeoff
只存给定流水线阶段输入的激活值, 而不是保存整个集合上的大得多的激活值
在训练相当大的模型时,重新计算激活值,保证内存占用在一个较低的可接受的水平。
激活值检查点的数量不影响吞吐率,但是影响内存占用。
大多数情况下,每一到两 transformer层设置一个检查点最优
其他技术,比如 (激活分区)activation partitioning 可与和 tensor model 并行一起使用以降低 激活值得内存占用
使用A100时,配备了8 个IB网卡,但是在两个sever之间,只能进行一对GPU之间的通信,这就很难充分利用8个卡。
scatter/gather communication optimization
每个卡只需要发送 output的一部分,每个卡发送的数据量相等,在接收端通过NVLink 执行all-gather
将发送端的tensor1 划分成大小相等的块,每个rank 只发送一块到下个设备,通过 InfiniBand card
有 8 个tensor 并行,每个块是原先的 1/8大。 在接受端,使用 NVLink 上的 all-gather 来得到完整的tensor。
通过 scatter-gather 通信优化,在两个阶段之间的总的通信量减少到了 b s h t \frac{bsh}{t} tbsh, t 是tensor模型并行的大小, s是序列长度, h 是隐藏层大小
三种特定模型的优化
首先 : 更改transformer 层的数据布局,来避免内存占用较多的转置操作(transpose) , 并且可以使用 strided batched GEMM ,将数据布局从[b,s,a,h] 和修改为[s,b,a,h] 。
其中 b,a,s,h, 分别为 batch 大小,序列长度, attention-head ,hidden-size 的维度??为什么原先的数据布局是这样??
其次,使用 PyTorCH JIT 为一系列元素级操作生成融合核 ,将几个kernel 融合在一起
第三, 创建了两个自定义核,来实现 scale, mask, and softmax (reduction) 的融合。 一个用来支持一般的mask ,另一个用于 隐含式因果mask (implicit causal masking)
回答如下问题:
实验使用的是适当大小的GPT模型
pipeline 并行度从 1 变化到8,在增加pipeline 并行度的同时,改变模型的大小。比如 p = 1,使用3层transformer的模型,15 billion 个参数;p = 8,使24层transformer的模型,121 billion 个参数
batch 越大,将 pipeline bubble摊到更多得到 microbatch中;batch越大,pipeline 并行度增加对计算雄安绿的影响越小。
交错流水线调度 和非交错流水线调度之间的 差距 gap 会随着batch增大而减小,主要有两个原因:
讨论了不同为维度的并行度结合的tradeoff和性能
流水线模型主要用来切分模型,数据并行主要用来扩大训练规模
data 并行度越高,性能越高。
data 并行无法总是应用
batch 大小固定,microbatch大小增加,m减小,气泡变大。但是 microbatch增大,GPU利用率提升了
激活值重计算,可以减少内存占用,因此可以增加训练时batch的大小,来减小流水线中的气泡,会随着batch增大,提高效率
这篇文章中,展示了PTD-P 可以组合在一起,来实现更高效的吞吐量