回顾chatgpt的发展历程,我们可以总结出大语言模型(llm)取得惊艳效果的要点(重要性从高到低排序):
愿意烧钱,且接受“烧钱 != 好模型”的现实
高质量的训练语料
高效的分布式训练框架和充沛优质的硬件资源
算法的迭代创新
在大模型训练这个系列里,我们将一起探索学习几种经典的分布式并行范式,包括流水线并行(pipeline parallelism),数据并行(data parallelism)和张量并行(tensor parallesim)。微软开源的分布式训练框架fastspeed,融合了这三种并行范式,开发出3d并行的框架,实现了千亿级别模型参数的训练。
本篇文章将探索流水线并行,经典的流水线并行范式有google推出的gpipe,和微软推出的pipedream。两者的推出时间都在2019年左右,大体设计框架一致。主要差别为:在梯度更新上,gpipe是同步的,pipedream是异步的。异步方法更进一步降低了gpu的空转时间比。虽然pipedream设计更精妙些,但是gpipe因为其“够用”和浅显易懂,更受大众欢迎(torch的pp接口就基于gpipe)。因此本文以gpipe作为流水线并行的范例进行介绍。内容包括:
1、优化目标
2、模型并行
3、流水线并行
切分micro-batch
re-materialization (active checkpoint)
4、实验效果
推荐阅读: chatgpt技术解析系列之:训练框架instructgpt chatgpt技术解析系列之:gpt1、gpt2与gpt3 chatgpt技术解析系列之:赋予gpt写代码能力的codex
一、优化目标
当你从单卡穷人变成多卡富翁时,你做分布式训练的总体目标是什么呢?(虽然手握一张a100怎么能是穷呢)
能训练更大的模型。理想状况下,模型的大小和gpu的数量成线性关系。即gpu量提升x倍,模型大小也能提升x倍。
能更快地训练模型。理想状况下,训练的速度和gpu的数量成线性关系。即gpu量提升x倍,训练速度也能提升x倍。
这是目标,也是难点,难在于:
训练更大的模型时,每块gpu里不仅要存模型参数,还要存中间结果(用来做backward)。而更大的模型意味着需要更多的训练数据,进一步提高了中间结果的大小。加重了每块gpu的内存压力。我们将在下文详细分析这一点。(对应着gpu中的内存限制)
网络通讯开销。数据在卡之间进行传输,是需要通讯时间的。不做设计的话,这个通讯时间可能会抹平多卡本身带来的训练速度提升。(对应着gpu间的带宽限制)
明确这两个训练目标后,我们来看并行范式的设计者,是如何在现有硬件限制的条件下,完成这两个目标的。
二、模型并行
当你有一个单卡装不下的大模型时,一个直接的解决办法是,把模型隔成不同的层,每一层都放到一块gpu上,如下图:
此时,模型做一轮forward和backward的过程如下
其中下标表示gpu编号,例如表示在gpu0上做foward,表示在gpu0上做backward。图中的横轴表示timestep。
这张图的含义是:我在gpu0上做完一次forward,然后将gpu0上最后一层的输入传给gpu1,继续做forward,直到四块gpu都做完forward后,我再依次做backward。等把四块gpu上的backward全部做完后,最后一个时刻我统一更新每一层的梯度。
这样做确实能训更大的模型了,但也带来了两个问题:
(1)gpu利用度不够。
如图,阴影部分所表示的时间段里,总有gpu在空转。在gpipe中,将阴影部分定义为bubble。我们来计算一下bubble。假设有块gpu,而单块gpu上做一次forward和backward的时间为:。则:
图中灰色长方形的整体面积为:(宽=,长=)
图中实际在做forward和backward的面积为:
图中阴影部分的面积为:
图像阴影部分的占比为:
则我们定义出bubble部分的时间复杂度为:,当k越大,即gpu的数量越多时,空置的比例接近1,即gpu的资源都被浪费掉了。因此这个问题肯定需要解决。
(2)中间结果占据大量内存
在做backward计算梯度的过程中,我们需要用到每一层的中间结果z。假设我们的模型有l层,每一层的宽度为d,则对于每块gpu,不考虑其参数本身的存储,额外的空间复杂度为。从这个复杂度可以看出,随着模型的增大,n,l,d三者的增加可能会平滑掉k增加带来的gpu内存收益。因此,这也是需要优化的地方。
三、训练数据与训练方法
朴素的模型并行存在gpu利用度不足,中间结果消耗内存大的问题。而gpipe提出的流水线并行,就是用来解决这两个主要问题的。
3.1 切分micro-batch
流水线并行的核心思想是:在模型并行的基础上,进一步引入数据并行的办法,即把原先的数据再划分成若干个batch,送入gpu进行训练。未划分前的数据,叫mini-batch。在mini-batch上再划分的数据,叫micro-batch。
图例如下:
其中,第一个下标表示gpu编号,第二个下标表示micro-batch编号。假设我们将mini-batch划分为m个,则流水线并行下,bubble的时间复杂度为(推导过程略,可参照第二部分的bubble推导流程)。gpipe通过实验证明,当$m>=4k时,bubble产生的空转时间占比对最终训练时长影响是微小的,可以忽略不计。
将batch切好,并逐一送入gpu的过程,就像一个流水生产线一样(类似于cpu里的流水线),因此也被称为pipeline parallelism。
3.2 re-materialization(active checkpoint)
解决了gpu的空置问题,提升了gpu计算的整体效率。接下来,就要解决gpu的内存问题了。
前文说过,随着模型的增加,每块gpu中存储的中间结果也会越大。对此,gpipe采用了一种非常简单粗暴但有效的办法:用时间换空间,在论文里,这种方法被命名为re-materalization,后人也称其为active checkpoint。
具体来说,就是几乎不存中间结果,等到backward的时候,再重新算一遍forward,图例如下:
每块gpu上,我们只保存来自上一块的最后一层输入z,其余的中间结果我们算完就废。等到backward的时候再由保存下来的z重新进行forward来算出。
现在我们来计算每块gpu峰值时刻的内存:
每块gpu峰值时刻存储大小 = 每块gpu上的输入数据大小 + 每块gpu在forward过程中的中间结果大小
每块gpu上固定需要保存它的起始输入,我们记起始输入为(即mini-batch的大小)。
每个micro-batch是流水线形式进来的,算完一个micro-batch才算下一个。在计算一个micro-batch的过程中,我们会产生中间变量,它的大小为(其中m为micro-batch个数)。
因此,每块gpu峰值时刻的空间复杂度为
将其与朴素模型并行中的gpu空间复杂度比较,可以发现,由于采用了micro-batch的方法,当l变大时,流水线并行相比于朴素模型并行,对gpu内存的压力显著减小。
如果你使用pytorch提供的pp接口,其中有一个参数叫checkpoint,就是用来做这一项的。
最后,再提一点,在micro-batch的划分下,我们在计算batch normalization时会有影响。gpipe的方法是,在训练时计算和运用的是micro-batch里的均值和方差,但同时持续追踪全部mini-batch的移动平均和方差,以便在测试阶段进行使用。layer normalization则不受影响。
四、实验效果
回顾第二部分的两个目标,gpipe真的实现了吗?如果实现不了,又是因为什么原因呢?我们来看下实验效果。
4.1 gpu数量 vs 模型大小
gpipe分别在amoebanet(图像)和transformer(自然语言)两个大模型上做了实验。
naive表示单卡
pipeline-n表示re-materalization + n卡。
ameobanet-d和trasformer-l一行表示超参数的量
of model parameter表示模型的参数量
total model parameter memory表示模型参数所占内存大小
peak activation memory表示峰值时中间结果大小。可以发现,中间结果占据的内存大小是相当可观的。
从实验结果里,我们可以发现:
在transformer上,gpipe基本实现了模型大小(参数量)和gpu个数之间的线性关系。例如从32卡增到128卡时,模型的大小也从21.08b增加到82.9b,约扩4倍
对amoebanet而言,却没有完全实现线性增长。例如从4卡到8卡,模型大小从1.05b到1.8b,不满足2倍的关系。本质原因是amoebanet模型在切割时,没有办法像transformer一样切得匀称,保证每一块gpu上的内存使用率是差不多的。因此对于amoebanet,当gpu个数上升时,某一块gpu可能成为木桶的短板。
gpu数量 vs 训练速度
(1)关掉nvlinks
为了验证gpipe框架带来的收益,实验中关掉了nvlinks(gpu间快速通信的桥梁。估计是通过强迫gpu先连cpu然后再连别的gpu做到的)。关掉的意义在于说明,不靠硬件本身的高效通讯带来的收益,gpipe一样能做的很好。实验效果如下:
m=32表示micro-batch的数量为32,k表示gpu数量。从实验结果可知,在关掉nvlinks的情况下,gpipe一样也能实现随着gpu数量的增加,训练速度也增加的效果。虽然这两者间不是线性的。同样,因为模型切割不均的原因,amoebanet的表现不如transformer。
(2)开启nvlinks,并寻找最佳m
当重新开启nvlinks后,我们来看m的大小(即流水线的核心)对训练速度的影响。
当m=1的时候,如前文所说,gpu的空置率太高,因此两个模型都没有实现训练速度和gpu个数间的线性关系
当m=4时,表现明显好转。
当m=32时,表现最佳,且transformer基本实现了训练速度和gpu个数的线性关系。
4.3 gpipe下时间消耗分布
对每块gpu来说,约2/3的时间,是真正花在计算上的。
其余1/3的时间,大部分花在re-materalization策略下的重计算上。因为采用流水线的方法,bubble的时间也被压缩到很短,可以忽略不计。
集成的数字电源控制和监控IC MAX8688
刘作虎:5G将分为三个阶段,一加本季度推5G手机
苹果新iPad采用Retina显示屏,分辨率提升4倍
Intel重启晶圆代工业务,Intel计划成为代工产能的主要提供商
恒温恒湿箱的作用和意义分别是怎样的
以Gpipe作为流水线并行的范例进行介绍
永磁同步电机和感应异步电机特点和区别
白天电价竟然比夜间低?
什么是CBDC,世界各地政府为什么还要发行CBDC
老邢点评:工信部支持新材料产业发展 关注前沿新材料
硅片半导体制造工艺详细图文版科普
iQOO 7预热:搭配高动态独立Hi-Fi芯片 旗舰级听感
Xilinx FPGA开发工具总结
明智选择电源架构
ntc热敏电阻的阻值如何变化?
西部数据携全平台游戏存储解决方案亮相ChinaJoy 2023
智能魔镜是一款可以人镜互动的智能镜面显示屏
对于电池容量的过充和过放,它们的定义是怎样的
平板电脑市场日渐衰落,苹果试图依靠iPadPro拯救iPad
阿里云ET医疗大脑预测个体的发病概率准确率能达到83%