本文整顿自同名线上分享,是 12 月份「百度百舸 – 云原生 AI」技术公开课的第二期。
这次分享将系统性的剖析在 AI 模型训练过程中的次要性能瓶颈,以及以后针对这些瓶颈的次要的减速计划和技术原理,并介绍百度智能云在这方面的一些实际成绩。
明天的分享,次要包含三个局部:
首先介绍咱们为什么须要做 AI 训练减速,也就是整体背景和出发点是什么;
第二局部咱们会系统性的剖析理论训练过程中的可能会遇到的性能瓶颈问题,而后针对这些问题,介绍目前次要的减速计划;
第三局部介绍百度百舸平台的 AI 训练减速套件 AIAK-Training 在一些模型训练减速上的实际成果。
一、为什么须要 AI 训练减速?
在 AI 零碎中,一个模型从生产到利用,个别包含离线训练和推理部署两大阶段。
离线训练阶段,就是产生模型的过程,用户须要依据本人的工作场景,筹备好训练模型所须要的数据集,以及神经网络算法。
算法能够了解为是一个高度简单的非凸数学函数,函数中包含很多变量以及参数。模型训练的过程其实就是在学习神经网络模型中的参数。
模型训练开始后,会读取数据,而后送入模型进行前向计算,并计算与实在值的误差。而后执行反向计算失去参数梯度,最初更新参数。训练会进行多轮的数据迭代。
训练实现之后,咱们会保留训练好的模型,而后将模型做上线部署,承受用户的实在输出,通过前向计算,实现推理。
因而,无论是训练还是推理,外围都是数据计算。为了减速计算效率,个别都是通过 GPU 等异构减速芯片来进行训练和推理。
另外,从深度学习模型倒退历程来看,为了可能继续冲破模型的精度下限,模型参数量其实在疾速的收缩。然而更大的参数量,就会带来更大的计算复杂度。
下图左侧是摘自一篇公开的论文,从这篇总结里,咱们看到在 2010 年之前,模型的计算量大概 20 个月翻一番。在 2010~2015 年,惯例模型计算每 5-6 个月翻一番。而在 2015 年之后,衍生了大模型训练的趋势,计算量增长 10~100 倍。
模型训练对算力以及基础设施的要求越来越高,训练须要更多的算力,也须要更长的工夫,这也导致了须要更多的资源老本。这里咱们列举了一些论文或钻研中公开的老本数据,反馈了模型训练的费用是十分昂扬的。
因而,如何稳固的进行模型训练,如何继续降本增效其实至关重要。
在这样的大背景下,百度智能云推出了百度百舸 · AI 异构计算平台,指标是为 AI 场景提供软硬一体化的解决方案。通过 AI 计算、AI 存储、AI 减速、AI 容器四层技术栈,满足下层业务场景的需要。
- AI 计算层,提供了包含高性能的 GPU、以及昆仑等异构芯片资源,以及高性能的 RDMA 或 IB 网络,以及自研的超级 AI 计算机 X-MAN 等;
- AI 存储层,包含对象存储 BOS 满足数据湖存储的需要、以及专为 AI 设计的高性能并行文件系统 PFS;
- AI 减速层,包含数据湖存储减速套件 RapidFS,AI 训练减速套件 AIAK-Training,AI 推理减速套件 AIAK-Inference;
- AI 容器层,也即是资源调度层,利用云原生的技术能力,满足 GPU、AI 作业等弹性调度的需要。云原生 AI 的内容在咱们上一期的技术公开课有专门分享。
当咱们思考做性能减速的时候,第一个想到的可能是应用更好的硬件。
这会带来肯定的性能晋升,然而大部分状况下可能并没有充分发挥出硬件的计算能力, 外围的起因就是训练代码的执行效率并没有调到最优或更优的状态 。
- 首先,框架为了兼容更多的需要,个别会提供一些较为通用的优化能力,更多的会关注易用性、生态建设等;
- 其次,算法工程师在设计算法时,外围的精力在如何进步模型的精度,性能效率关注有余,某些状况下框架提供的一些通用优化能力,也都没有利用起来。
因而,当咱们决定应用某种模型算法时,为了达到较好的资源效率和训练效率,咱们须要无意识的去优化。不过这里也有很多的技术挑战:
- 性能影响因素比拟多,须要依据不同模型本身的运行特点进行剖析,没有齐全固定的优化计划;
- 性能优化须要了解工程实现的原理,比方当咱们去做异构芯片计算的优化,须要业余的异构研发教训能力发展,技术门槛较高;
- 还有些状况,当咱们做分布式训练时,可能还须要联合集群硬件和网络拓扑,来优化分布式训练场景下的扩展性问题。
这些挑战极大地影响了模型训练性能的调优,因而咱们推出了 AIAK-Training 的减速套件,冀望通过形象易用性的接口升高优化老本,并通过软硬协同的优化伎俩,来充沛减速客户在百度智能云上的模型训练性能。
二、训练性能开销剖析和减速计划
在介绍 AIAK-Training 具体成果之前,咱们先介绍下训练减速这个话题下要害的技术思路和计划原理是什么样的。
因为模型训练优化自身是一个软硬件综合的工作,技术栈绝对比较复杂,明天的内容必定没方法涵盖全副细节,咱们尽量把要害思路讲到。
首先咱们看下以后的模型训练计划。过来的倒退阶段里,模型训练计划要害有两个档次的变动,一是从单卡训练到分布式训练的变动,二是从数据并行训练到多维混合并行训练的变动。 这里的外围驱动点一个是训练的数据量,一个是模型的参数量。
- 单卡训练形式:理论采纳这种模式,个别都是模型参数量和数据量绝对比拟少,单卡的训练工夫能够承受。模型参数规模须要保障在训练过程中,单张卡的显存可能满足存储的下限。依照新的 GPU 卡的显存容量配置,个别能够搁置的最大规模在 10 亿级参数量;
- 当数据集规模比拟大的时候,因为训练过程中须要屡次遍历全量的数据集,单卡训练就会须要比拟长的工夫,这时能够从单卡扩大到多卡,通过分布式的形式来放慢训练。
这里利用最宽泛的就是数据并行。在数据并行计划下,数据集会被均匀切分成多份,而后每张卡上保留残缺的模型,各自独立并行处理切分后的子数据集。
当模型参数量足够大的时候,比方参数量达到百亿、千亿级别,单卡放不下残缺的模型,这里又呈现了模型并行、或者同时应用数据并行和模型并行的混合并行计划。
模型并行,会将模型切分到不同的卡上,每个卡上搁置模型的一部分,这里又依据切分的形式不同,比方层内切分或层间切分,又细分了 Tensor 并行和流水线并行的形式。
因为在个别模型训练中,应用更多的还是数据并行,咱们上面重点还是以数据并行为例,来介绍性能优化的思路。
咱们从软硬件整体视角先了解下单卡训练过程中存在的性能开销。
下图右边是咱们从软件角度上的训练流程。单卡训练的过程,次要包含数据读取、数据预处理,前向计算输入并计算 loss,依据 loss 函数反向计算,失去每一层参数的梯度,最初依据梯度更新模型参数。继续该过程,直到训练收敛。
下图左边,是一个简化的节点硬件拓扑图。最下面是数据存储,能够是本地存储,也能够是网络存储。而后是 CPU、内存,CPU 下通过多个 PCIe Switch 连贯着 8 张 GPU 卡,编号从 0~7,8 张卡之间通过 NVSwitch 互联。不同的计算实例,硬件的拓扑构造会有不同。
- 当训练启动时,首先数据读取,波及从存储介质中读数据到内存中,次要是存储 I /O 开销。依据存储介质,要读取的数据量不同,这部分工夫开销也不同;
- 接下来是数据预处理,次要是对读入的数据进行一些数据加强的操作,比方对图片进行 resize,对比度、饱和度调整等等,这部分工作大多数状况下是在 CPU 上。当在 CPU 上实现数据预处理操作之后,须要从主机内存拷贝到 GPU 显存上,波及到主机和设施之间的内存拷贝开销;
- 而后开始前向计算、反向计算、参数更新,这部分更多操作次要是通过 GPU 来进行,次要工夫破费在 GPU 计算下面。这个过程里也可能会交叉一些 CPU 上的操作,可能也须要做主机和设施内存之间的拷贝。
因而,从单卡角度看,次要存在 I/O、CPU 预处理、CPU 和 GPU 之间数据拷贝,GPU 计算等方面的开销。
接着咱们看下数据并行的过程。
下图右边还是训练的主流程,左边展现了一个 3 机 24 卡的训练集群的硬件拓扑,3 台机器通过网络互联。
后面的局部咱们也介绍到了,在数据并行里每个设施并行独立地执行前向和反向计算过程,因而,每个训练过程也都会遇到后面讲的单卡训练中的性能开销问题。
数据并行为了保障和单卡训练在数学上等价,须要确保每张卡的模型参数在迭代过程中始终保持统一。这里一方面须要让各 GPU 卡的模型参数初始化状态统一,这个个别是在训练开始前,通过播送的形式将第一张卡上的参数状态播送到其余的卡。
而在训练期间,因为每个设施解决的数据不同,前向计算所失去的模型损失值也是不同的,因而还须要在每个设施反向计算出梯度之后,进行梯度的均匀,以均匀后的梯度值来更新模型参数,从而保障每张卡的模型参数在迭代过程中始终保持统一。
梯度均匀波及到通信的过程,包含节点外部卡之间的通信,以及跨节点的网络通信开销。这里的通信又包含同步通信和异步通信,不过为了保障模型训练的收敛,个别都是采纳同步通信的计划,后续的优化工作也都是基于同步通信的形式来开展。
由上可知,数据并行相比单卡训练,次要减少了额定的通信开销。
通过前述剖析,咱们晓得减速 AI 训练不单是某一方面的工作,须要从数据加载、模型计算、分布式通信等零碎维度综合思考。 这里说的数据加载,包含数据 I/O、预处理、内存拷贝等过程。
在具体的优化实际中,给定一个待优化的模型, 减速模型训练就是要一直晋升训练的总体吞吐(每秒能够训练的样本数)。这个过程,咱们个别能够先剖析单卡的训练吞吐,当单卡的训练吞吐晋升上来之后,咱们再看从单卡扩大到多卡,看看如何晋升多卡的训练减速比。
首先单卡的训练优化,极限优化指标是全副工夫都在 GPU 计算上,加速器利用率 100%。当然理论很难齐全达到这个状态,然而咱们能够按这个指标来牵引或掂量咱们的工作。
单卡性能优化要害包含两局部:
- 首先数据加载效率的优化。从存储系统上,咱们能够应用更高性能的存储介质,或者基于这些高速存储介质组成的并行文件系统,或者说一些缓存减速零碎。后面介绍到的,百度百舸也提供了相应的存储系统计划,比方 PFS、RapidFS 等。除此之外,还须要在框架 dataloader 中优化数据的读取过程。
- 其次就是模型计算效率的优化。次要思考如何优化计算实现,怎么晋升计算单元的利用效率,这块可能就须要联合模型具体分析。
而后从单卡扩大到多卡,指标是如何达到线性减速比。线性减速比这个指标,简略来说就是从 1 张卡扩到 2 张卡训练时,训练的性能是否是单卡的 2 倍。
这里外围就是优化分布式的通信效率,一方面是硬件层面的优化,另外一方面在理论通信中,须要思考怎么利用好网络的带宽资源,或者是否可能将通信过程进行暗藏等。
上面咱们别离从这几个方面具体开展。
首先是数据加载方面的优化。
当咱们实例化一个 dataloader 之后,咱们会继续迭代 dataloader 来读取一个 batch 的数据进行模型训练。
如果这里不做任何优化,如下图上半局部所示,每个 batch 的数据加载过程和每个 batch 的模型训练过程,实际上是串行进行的。从 GPU 视角来看,就会呈现因为数据加载导致的计算间隙,计算资源存在工夫上的节约。
除了后面讲到咱们用更好的硬件间接晋升数据读的效率之外,还能够怎么优化呢?
理论在 AI 训练过程中,数据拜访上有两个要害特色:
- 当数据集做完 shuffle 之后,每轮训练所须要的 batch 数据以及拜访程序是已知的;
- 任意两个 batch 的数据读能够并行,因为数据之间没有任何依赖关系。
因而,咱们在不依赖硬件层面改变的时候,能够做的一个优化工作就是数据预取,当训练第一个 batch 数据的时候,能够提前加载下一个 batch 数据,让 I/O 的过程和 GPU 上的计算充沛并行起来。
首先,咱们须要利用好 dataloader 中已有的优化计划,一是正当设置 num\_workers 超参数,通过多过程的形式读数据,这一步能够实现数据从存储系统中预取到主机内存。二是从主机内存拷贝到 GPU 显存,能够通过 pinned memory 的机制来减速。
大略介绍下 pinned memory 减速的次要原理:内存数据有两种类型 pageable memory 和 pinned memory,pageable memory 中的数据有被换出到磁盘上的可能。这种状况下,当执行 H2D 时,可能须要先从磁盘读到内存,而后从内存拷贝到显存。另外,pageable memory 数据拷贝到 GPU 显存时,须要先创立一个长期的 pinned memory 缓冲区,把数据从 pageable memory 拷贝 pinned memory,之后能力传输到 GPU 上,也多了额定的数据搬运的操作。
不过当咱们使能了上述计划后,咱们仅实现了从存储系统到主机内存的预取,放慢了主机到设施的数据拷贝速度。然而主机到设施的内存拷贝,和理论计算 kernel 在 GPU 上还是串行执行,也就是 GPU 上仍然存在大量的工夫间隙。
AIAK 针对这个问题,又做了进一步的优化,能够实现 H2D 和前向计算的 overlap。
在数据并行场景下,还须要留神的一个事件,数据须要平衡的切分。
如果每个训练过程调配的数据不平衡,计算量就会不同,也就导致每个过程前向计算和反向计算实现的工夫不同,那么先实现计算的过程,在反向过程中就会先进入到梯度通信环节中,然而因为 Allreduce 通信是同步通信操作,须要所有过程同时开始并同时完结,因而先开始通信的过程,会始终期待其余所有过程也发动了 AllReduce 后能力一起实现通信操作。这里就会呈现因为快慢不一导致的资源闲暇问题。
为了解决这种问题,须要每个过程应用雷同的 batchsize 来读取数据,另外每个 batch 的数据量要平衡。图像类数据个别会固定尺寸进行训练,而像 NLP 类模型须要解决变长的语句,可能须要进行非凡的解决,比方能够将数据 padding 到雷同的长度,或者通过样本长度排序的形式来平衡调配等。
上面介绍计算效率的优化。
计算包含前向、反向、参数更新。优化计算的指标,是为了可能充分发挥出异构硬件的算力,现实状况就是让 GPU 芯片理论计算时的性能达到实践峰值。
咱们先从一个单算子的角度剖析,当咱们筹备在 GPU 上执行一个计算操作的时候,简化的流程有四步。
- 首先在 CPU 上异步发射一个 GPU 计算 Kernel;
- 当 Kernel 调度执行时,须要先从 GPU 上的 Global Memory 读取计算所须要的数据;
- 开始执行计算;
- 当计算实现后,须要将计算的后果写回 Global Memory。
依据计算、访存的开销占比,个别会将算子分类为计算瓶颈或者访存瓶颈。
当从一个算子扩大到一个残缺的模型训练时,因为要间断执行十分多的计算 Kernel,那么 Kernel 计算之间就会呈现很多因为 Kernel Launch、两头后果的读写导致的计算间隙问题。
由上可知,优化模型计算效率,须要从访存优化、计算优化、其余开销的优化综合思考。
- 访存优化,次要思考如何缩小数据在显存和计算单元之间搬运的工夫。从算子实现角度上,须要利用好 GPU 存储档次架构,比方把数据搬运到更快的存储器上比方 share memory,缩小对 global memory 的拜访,从而节俭访存工夫。或者做好计算指令和访存指令的 overlap,来晋升计算访存比。而从单算子扩大到多算子时,还须要思考如何缩小两头后果的读写,这种问题个别能够通过算子交融的伎俩来优化;
- 计算优化,计算瓶颈问题多半应该是没有正确对工作进行分块,或者没有利用好 GPU 并行计算的劣势,导致并行度不高。还有可能没有应用合并指令集导致计算效率低下,或者没有应用 Tensor Core 等高性能计算单元,造成资源节约。不同的问题,也对应着不同的优化伎俩;
- Kernel Launch 等其余开销,大量工夫破费在访存或计算之外,能够思考的优化伎俩如算子交融、Cuda Graph 等。
首先是算子交融。算子在底层 GPU 执行时,会发动一次或者屡次 Kernel Launch,Kernel 之间交互数据也须要通过显存,而算子交融就是将多个 GPU Kernel 交融成一个大 Kernel,对立发动和执行。
- 因为缩小了须要执行的算子数量,从而能够缩小 Kernel 调度和发动的开销;
- 通过交融,能够通过寄存器等来传递两头后果,防止从 global memory 的来回搬运,极大升高了显存期待的工夫;
- 在某些场景中,能够通过算子交融,能够更充沛的利用计算资源,晋升资源和计算的效率。
算子交融具体如何实现呢?
一种形式,分析模型中的低效操作,专家教训手写交融算子。在 GPU 上次要就是 CUDA 算子研发,这里存在肯定的门槛。AIAK-Training 会针对典型的模型构造,或者客户需要,提供高效优化的算子实现。
另一种形式,就是编译优化的计划。通过编译的形式进行计算优化,以及代码主动生成,从而升高在不同硬件上的手工优化老本。不过以后很多编译计划更多还是针对推理优化,训练上的计划还在疾速演进过程中。不过从极致性能角度看,将来一段时间仍然离不开手写交融算子的工作。
上面介绍一些算子交融的理论案例。第一个是针对典型模型网络结构的优化。
下图展现了咱们在 SwinTransformer 模型中针对外围模块 WindowAttention 进行的计算交融优化。
WindowAttention 构造,外围操作公式如下图所示。计算过程中,须要顺次执行 7 个计算 Kernel。再加上一些 reshape 等转换操作,总共须要 launch 10 个 Kernel。通过性能剖析发现,理论执行过程中 launch kernel 的距离冗余开销占到了端到端 80% 以上的工夫,导致该模块存在着较大的优化空间。
通过将这些 Kernel 交融成一个,整个模块的执行工夫从 392 微秒缩小到 13 微秒,单算子减速了 30 倍。整个模型的训练效率,端到端减速了 20% 以上。
这个优化的外围思路,次要有三点:
- 是利用好 GPU 的三级访存流水:显存、share memory、寄存器;
- 通过分块策略,将 2 个矩阵乘法和 softmax 进行交融;
- 针对前向两头后果的优化,充分利用 Tensor Core,在反向计算中应用重计算代替重加载,使得访存开销极大升高。
下图是一个数据操作的交融举例,是在 FCOS3D 模型中对于坐标压缩操作的一个优化。
通过性能剖析发现,这个操作过程中存在大量 GPU 空隙,GPU 利用率较低。该操作次要的性能是依据 index 将 3D-Tensor 压缩成 2D-Tensor。在原生实现中会先在 host 端生成 index,再进行 H2D 拷贝,最初实现 Tensor 压缩,这会造成额定的拷贝和期待开销。
为此,咱们从新实现这部分操作,外围思路次要就是将操作全副迁徙至 GPU 上,间接在 GPU 上实现 index 的生成和 Tensor 的压缩,缩小 CPU 参加,同时防止了非必要的 CPU-GPU 之间的内存拷贝。
单算子执行工夫从 9.69 毫秒缩小到 32 微秒,减速了 300 倍,整个模型端到端训练晋升了 10% 以上。
上面咱们介绍另一个计算优化的思路,就是进步计算的并行度,充分利用 GPU 并行计算的劣势,同样借助一些理论案例来介绍。
咱们发现在一些模型中,有一些操作是串行执行的。比方在一些指标检测模型里,在 loss 计算过程中,有些操作并不是依照一个 batch 进行操作,而是 for-loop 每张图片或一个样本,这种状况下,当咱们去晋升 batchsize 的时候,因为这里的串行,可能没法达到咱们想要的性能成果。
以 YOLOv7 中的 SimOTA 操作举例,原生实现中,是通过 for-loop 遍历一个 batch 的每一张图片,而后为图片的 gtbox 执行 SimOTA 标签调配。这种串行的实现形式导致该局部操作的 GPU 利用率十分低效。
而每张图片解决时数据之间是没有依赖的。因而,咱们做的一项工作就是将串行计算改成 batch 并行计算,通过对一个 batch 的数据进行并行化的标签调配,从而减速这部分计算的效率。
最终成果上,SimOTA 操作的耗时从 384 毫秒降落到 69 毫秒,计算效率晋升 5.5 倍,整个模型的端到端训练效率晋升了 18% 以上。
模型训练过程中也有其余的相似场景,比如说参数更新。参数更新时,默认也是通过循环的形式,遍历每个参数,而后每个参数都会启动一个参数更新的 Cuda Kernel,而后顺次执行。
针对这种状况,AIAK 也减少了 FusedOptimizer 的优化,通过交融参数更新的算子,实现批量化更新参数,大幅缩小 Kernel Launch 次数。
上面介绍了另一个优化伎俩 CUDA Graph,次要是为了缩小 CPU Launch Kernel 的开销。
CUDA Graph 是在 CUDA 10 版本中引入的个性,能够将一系列 CUDA Kernel 封装成单个单元,能够通过一次 CPU Launch 操作来启动多个 GPU Kernel,从而缩小了 CPU Launch Kernel 的开销。
如下图所示,默认状况下 CPU 须要顺次发射多个 Kernel,如果 Kernel 计算工夫比拟短,Kernel 之间的 Launch 间隙可能成为性能瓶颈。通过 CUDA Graph,仅须要破费一些额定工夫构建 Graph,后续通过一次 Graph 的发射,即可大幅缩短理论执行时 Kernel 之间的间隙。
当初很多框架也都减少了对 CUDA Graph 的反对,通过插入一些代码来使 K 能这个性能。不过也有一些应用上的限度,比方不反对动静 shape、不反对动态控制流、过程中不能捕捉 CPU 操作等等,能够依据模型状况,尝试应用下这种优化能力。
上面介绍最初一个计算优化伎俩,充分利用 Tensor Core 计算单元。
一个 GPU 内个别蕴含多个 SM,每个 SM 包含不同数据类型的计算外围,以及各类存储资源等。下图右边所示,是一个 NVIDIA A100 SM 的示意图,一个 SM 蕴含 64 个 FP32 CUDA Core,以及 4 个 Tensor Core。
模型训练时,默认状况次要是用到 FP32 CUDA Core 计算,而 Tensor Core 是一块非凡的硬件执行单元,是从 Volta 系列 GPU 开始引入,次要是用来减速矩阵或卷积的操作效率。
相比 FP32 CUDA Core 一次只能执行两个标量的计算,Tensor Core 能够一次对两个矩阵执行计算,因而 Tensor Core 的计算吞吐远高于 FP32 CUDA Core。
在 A100 中,Tensor Core 反对了多种浮点数据类型,对于深度学习训练来说,可能波及到包含 FP16、BF16、以及 TF32 模式。
TF32 次要用于单精度训练场景,相比 FP32 训练,在放弃雷同的访存带宽需要下,实践计算吞吐量进步了 8 倍。
FP16/BF16 次要用于混合精度训练场景,相比 FP32 训练,访存需要缩小了一半,实践计算吞吐量进步了 16 倍。
应用 Tensor Core 的形式,能够应用底层的 cublas 或 cuda 接口进行编程,而对于算法开发者来说,更间接的就是应用框架中提供的 TF32 训练或混合精度训练计划。
首先是 TF32 训练模式,TF32 是 Ampere 开始引入。
TF32 在浮点数的表白中,有 8 个指数位,10 个尾数位和 1 个符号位。指数位与 FP32 雷同,即数据表示范畴雷同,然而尾数位低于 FP32,和 FP16 雷同。
须要留神的是,TF32 不是一个对外开放的数值类型,只是 Tensor Core 的一种计算模式,也就是用户不能去间接创立一个 TF32 类型的浮点数。
当使能 TF32 的时候,Tensor Core 计算矩阵或卷积操作时,会主动将 FP32 转换成 TF32,计算实现之后,输入的数据类型仍然是 FP32 类型。
TF32 训练在某些框架版本中是默认开启,某些框架版本中可能须要通过环境变量或者参数配置来手工开启,具体须要参考框架的用户手册。
不过因为 TF32 相比 FP32 来说,精度范畴升高了,理论训练时还须要关注对模型收敛精度的影响。
混合精度训练是指在尽可能减少模型精度损失的状况下,应用 FP32 和 FP16 混合精度进行训练。
混合精度训练的收益次要有:相比 FP32 训练,内存需要缩小,能够训练更大的网络或应用更大的 batchsize。应用更少的内存带宽,能够减速数据传输,半精度的计算也能够让数学运算效率更快;
不过因为 FP16 的指数位和尾数位的范畴都比 FP32 要少,因而数值示意范畴和精度都会有升高,在理论应用的时候,就可能呈现因为示意范畴狭隘导致的数值溢出问题,或者因为精度有余导致舍入误差。
为了优化相似问题,混合精度训练计划中有几个要害的技术工作:
- 算子黑白名单机制,框架应用黑白名单主动为算子抉择精度,模型训练过程中,会主动的插入 cast 操作进行类型转换,不须要开发者干涉。针对数值精度敏感的计算,仍然应用 FP32 来算,而对于数值平安的计算,比方矩阵乘,则会应用 FP16 来计算;
- 训练过程中,会存储一份 FP32 的权重参数,用于训练时候的参数更新,优化舍入误差的问题;
- 针对 FP16 容易溢出的问题,应用了 Loss scaling 的计划,比方对 Loss 放大 n 倍,依据链式法则,梯度也会随之放大 n 倍,从而使其落到 FP16 的示意范畴内,具体过程如下图左侧所示。
目前所有框架都曾经反对混合精度。AIAK-Training 组件进一步引入 NVIDIA Apex 中 AMP O2 混合精度模式,这个模式会更加激进的将更多计算转 FP16 来减速训练。相比默认 O1 模式,速度会有进一步晋升,不过精度可能会受影响,须要联合具体模型验证。
AIAK-Training 提供兼容 torch amp 原生用法的应用形式,不便使能 O2 模式。
上面介绍通信优化,这个也是一个十分大的话题,波及到内容也十分多。
后面也介绍了,通信次要是分布式训练中引入的,因为从单卡扩大到多卡,须要进行多卡之间的一些数据同步,这些同步操作就是通过通信来施行的。
下图列了一个通信优化的整体架构:
- 最底层就是网络协议层,包含传统的 TCP 网络、以及在训练场景用的越来越多的 RoCE 或 IB 的 高性能 RDMA 网络。这些底层网络计划,百度百舸也都提供了反对。显然通过改善硬件基础设施来晋升网络带宽、升高提早,是最间接无效的优化通信性能的办法。
- 而后是通信库层。因为须要应用底层的网络协议来进行通信,并且理论利用时可能波及多种通信原语,比方点对点通信、汇合通信等,因而也呈现一些高度封装并且优化的通信库。在 GPU 训练场景中,咱们个别都是应用 NCCL 通信库,性能比拟优。
- 基于底层通信库,下层框架能够比拟不便的构建分布式训练的通信架构。常见的包含参数服务器架构,汇合通信架构。目前 CV/NLP 等畛域的模型,次要都是采纳汇合通信的架构形式。
- 通信策略层,次要是在应用层上做一些通信效率的优化,比方通信暗藏、通信交融、通信压缩、通信降频等不同的思路,这些形式大部分也能够叠加一起应用。
咱们先看通信策略层面的优化思路,首先是通信暗藏优化。
在数据并行中,梯度同步通信是在训练的反向过程中进行的,当反向算出梯度之后,就能够进行全局的梯度均匀。
如果不做任何的机制优化,反向计算和通信就会串行的进行,就会存在计算上的工夫间隙。
因为反向过程中,上一个梯度的通信和下一个梯度的计算,两者之间没有任何数据依赖,因而能够让上一个梯度通信和下一个梯度计算并行起来,让两者的耗时互相重叠,从而能够暗藏局部的通信耗时。
在实现层面上,通常是将通信和计算算子调度到不同的 cuda 流上实现的,通信算子调度到通信流,计算算子调度到计算流,不同流上的算子能够并行发射执行,从而实现反向中梯度通信和计算的并行重叠。
目前这个优化能力,在框架中都是默认开启的。
其次,通信交融优化。
默认状况下,模型中的每个梯度都须要发动一次通信操作。如果单个梯度的 size 比拟小,那么小数据包在理论通信时,网络带宽的利用率就会非常低,通信性能较差。
通信交融,就是将将多个梯度交融到一起对立进行一次通信,从通信开销模型上剖析,既能晋升带宽利用率,又能缩小通信的初始化提早项。
当初很多分布式训练框架中,也默认都反对梯度交融的策略,不同框架实现形式有肯定的区别,有的实现须要先进行梯度协商确定通信程序,有的则是间接通过动态的通信分桶。
尽管框架中默认反对通信交融,然而梯度交融的大小个别能够通过参数来配置,用户能够依据物理环境和模型的需要,调节适合的交融阈值,应该能够获得更佳的收益。
如果在网络带宽较低的训练场景中,比方低带宽的 TCP 环境中,梯度同步的提早可能会成为训练的次要性能瓶颈。
- 这种状况下,能够思考的一个优化伎俩就是通信压缩。通信压缩,次要有三种不同的压缩思路:
- 量化压缩,比方应用更低精度来示意梯度,这种形式的压缩率较低,最大能从 32 位压缩到 1 位,也就是最大压缩 32 倍。
- 稠密化压缩,典型的算法比方 DGC 算法,核心思想是每轮迭代只传输重要的梯度,也就是梯度数值超过设定的某一个阈值,同时为了缩小信息损失,会把剩下不重要的梯度在本地进行累积,只有工夫足够,最终累积梯度就会超过所设定的阈值,再进行梯度替换。通过这种形式缩小通信的数据量,升高对网络带宽的需要。同时为了缩小对模型收敛的影响,还通过动量校对、梯度裁剪、动量因子掩蔽、预热训练等不同形式来缓解。这种计划目前次要反对 SGD 优化器。
- 低秩矩阵压缩形式,典型的算法比方 PowerSGD,外围思路是将一个大的梯度矩阵分解成多个小梯度矩阵,通过传输小矩阵来缩小通信量。
通信降频优化,最简略的思路就是增大 batchsize,每次迭代更多的数据,缩小了迭代次数,也就是缩小了通信量。
不过 batchsize 也不是越大越好,越大的 batchsize 可能会导致模型收敛精度降落或者收敛速度变慢。针对相似问题,业界也提出比方 LARS、LAMB 优化器算法,通过分层自适应调节学习率,缓解相似问题,AIAK-Training 也减少了反对。
为了增大 batchsize,如果显存比拟短缺,能够间接调整 batchsize 超参。如果显存比拟吃紧,还能够通过梯度累加的形式,跳过若干次梯度通信,理论也相当于增大了 batchsize。
上面介绍了一种针对通信拓扑的优化计划——分层拓扑通信,也是针对机间网络带宽比拟低的状况。
通过分层通信,能够充沛的利用机内的高的互联带宽,同时弱化机间低网络带宽的影响。
AIAK 中也实现了这种通信计划,在 25Gbps TCP 环境下,实测 4 机 32 卡 SwinTransformer 训练,通过分层 allreduce,性能能够减速 85%。
最初介绍一个底层通信库层面的优化,GPU Direct RDMA 通信技术,这个技术须要硬件环境上反对 RDMA 网络。
RDMA 通信,容许本地应用程序间接读写近程应用程序的用户态虚拟内存,整个通信过程,除了最一开始提交发送申请这一步须要 CPU 的参加,其余都是由网卡硬件实现的,不须要内存拷贝、零碎中断以及软件解决,因而能够实现极致的低时延和高带宽。
而在 GPU 场景中,GPU Direct RDMA 技术进一步减少了 RDMA 间接拜访 GPU 显存的反对,防止通信的时候,数据在 GPU 显存和主机内存中来回拷贝,跨机通信提早进一步升高。
不过在理论的案例中,咱们发现有些用户购买了 RDMA 的环境,然而理论并没有用起来 GDR 技术,导致通信效率不高。这里列出了几个要害的配置项,如果有相似问题的话,能够顺次进行排查和设置。
后面介绍了以后的一些次要的性能优化思路和计划,整体来看,无论 I/O 优化、计算优化、通信优化,最奢侈的优化思路次要就是如何优化操作自身、或者是否能够缩小操作产生的次数,或者是否能够操作和其余的过程并行起来从而暗藏开销等。
三、AIAK-Tranining 减速套件事件
后面介绍了很多的优化工作,要想正确的使能,须要每个用户对于框架的工程实现原理比拟清晰。为了简化训练优化的老本,咱们构建了 AIAK-Training 这个减速套件。
AIAK-Training 会围绕数据加载、模型计算、通信等方面,构建全链路优化能力,同时咱们会把这种优化能力,封装成简略易用的接口,用户插入几行代码,即可比拟不便的集成应用。同时,咱们也在构建自动化策略组合调优的机制,主动帮忙用户抉择无效的优化策略。
具体应用时,减速库组件能够独立的装置部署,也能够间接应用咱们提供的容器镜像。
上面是一些具体的利用案例。
下图所示,次要是针对 dataloader 的优化。这个场景中,模型比拟小,数据集规模也比拟小,纯计算的速度其实比拟快,然而跨 EPOCH 的数据加载工夫比拟长,导致 I/O 耗时成为了次要的瓶颈。
通过应用 AIAK 中提供的过程复用、以及充沛预取机制,整个模型训练减速了 166%。
下图是一个针对模型计算优化的案例。
Transformer 类模型训练场景,理论训练时通信扩展性靠近线性,I/O 耗时占比也非常低,计算是次要的性能瓶颈。
针对这个模型,AIAK-Training 进行了一系列计算层面的优化,包含次要构造的算子交融、混合精度、大 batch 调优等等,整个模型的训练效率晋升了 169%。
下图的案例次要是利用了通信层面的优化,在云上 TCP 环境中使能针对低带宽网络的优化策略,在一些经典模型上比方 resnet50、bert、vgg16,能够减速 26%~78%。
在主动驾驶场景中,咱们也针对典型的 2D 视觉、3D 视觉、激光雷达,以及前交融类的模型,做了一系列的模型训练性能优化,训练性能减速 49%~ 391%。如果对这一类算法的减速感兴趣,也欢送后续和咱们交换。
点击浏览原文,理解 云原生 AI 更多内容
—— END——
举荐浏览:
分布式系统要害门路提早剖析实际
百度工程师教你玩转设计模式(装璜器模式)
百度工程师带你体验引擎中的 nodejs
揭秘百度智能测试在测试定位畛域实际
百度工程师带你探秘 C ++ 内存治理(ptmalloc 篇)
为什么 OpenCV 计算的视频 FPS 是错的