并行计算的量化模型及其在深度学习引擎中的应用如何更快地训练深度学习模型一直是业界关注的焦点。业内玩家或开发专用硬件,或开发软件框架,各显神通。当然,这些规律可以在计算机体系结构的教科书和文献中看到,比如这篇《计算机体系结构:量化研究方法(Computer Architecture: a Quantative Approach)》,但本文的价值在于有针对性地选取了最基本的规律,并结合深度学习引擎去理解。1关于计算量的假设在研究并行计算的量化模型之前,我们先做一些假设。对于一个具体的深度学习模型训练任务,假设总的计算量V是固定的,可以粗略地认为只要计算完V的量级,深度学习模型就完成了训练。这个GitHub页面(https://github.com/albanie/convnet-burden)列出了常见的CNN模型处理一张图片所需的计算量。需要注意的是,该页面列出了前向阶段所需的计算量,在训练阶段也需要进行后向阶段的计算。通常,后向计算量大于正向计算量。这篇论文(https://openreview.net/pdf?id=Bygq-H9eg)给出了训练阶段处理一张图片的计算量的直观可视化结果:以ResNet-50为例,在训练阶段处理一张图片训练阶段一张224X224x3的图片需要8G-Ops(约80亿次计算)。整个ImageNet数据集大约有120万张图片。训练过程需要处理整个数据集90次(Epochs)。粗略估计,训练过程需要(8*10^9)*(1.2*10^6)*90=0.864*10^18次操作,那么ResNet-50训练过程的总计算量约为10亿次1billion操作,我们可以简单的认为,只要这些计算完成,模型操作就完成了。深度学习计算引擎的目标就是在最短的时间内完成这个给定的计算。2关于计算设备的假设本文仅限于下图所示的以处理器为中心的计算。存储设备中的处理已经在业界进行了探索,但还不是主流。.上图所示的计算设备中的ComputingUnit可以是CPU、GPGPU等通用处理器,也可以是TPU等专用芯片。如果ComputingUnit是通用芯片,通常程序和数据都存储在MemoryUnit中,也就是最流行的冯诺依曼结构计算机。如果计算单元是专用芯片,通常只有数据存储在内存单元中。CommunicationUnit负责将MemoryUnit中的数据传输到ComputingUnit,完成数据加载(load)。ComputingUnit接收到数据后负责完成计算(数据格式转换),然后CommunicationUnit将计算结果传送给MemoryUnit完成数据存储。(店铺)。CommunicationUnit的传输能力通常用内存访问带宽β表示,即每秒可以传输的字节数,通常与电缆的数量和信号的频率有关。ComputingUnit的计算能力通常用吞吐率pi来表示,即每秒可以完成的浮点运算(flops)的数量,通常与集成在其上的逻辑运算设备的数量有关计算单元和时钟频率。深度学习引擎的目标是通过软硬件协同设计,使计算设备具有最强的数据处理能力,即在最短的时间内完成给定的计算量。3RooflineModel:描述实际计算性能的数学模型。计算设备在执行任务时所能达到的实际计算性能(每秒完成的操作数)不仅与计算单元的内存访问带宽beta和理论峰值pi有关,还与计算能力有关当前任务本身的强度(算术强度,或运算强度)。任务的计算强度定义为每字节数据所需的浮点计算次数,即Flopsperbyte。通俗地说,一个计算强度低的任务,就是计算单元需要对通信单元携带的一个字节进行较少的操作。为了让计算单元在这种情况下保持忙碌,通信单元必须频繁地携带数据;计算强度高的任务意味着计算单元需要对通信单元携带的一个字节进行更多的操作,通信单元不需要如此频繁地携带数据来保持计算单元忙碌。首先,实际计算性能不会超过计算单元的理论峰值pi。其次,如果内存访问带宽beta很小,1秒内只能将beta字节从内存传输到ComputingUnit,让I表示当前计算任务中每个字节需要的操作次数,则beta*I表示1秒一分钟内传输数据实际需要的操作次数。如果beta*Ipi。ComputingUnit的计算单元增加s倍后,理论计算峰值为s*pi,假设任务的计算强度I足够高,这样理论峰值增加s后计算量仍然有限次,即I*beta>s*pi;(2)假设不使用流水线,CommunicationUnit和ComputingUnit总是顺序执行的(后面会讨论流水线的影响)。我们来计算任务执行效率提高了多少倍。在理论峰值为pi的初始情况下,通信单元在1秒内传输beta字节的数据,计算单元需要(I*beta)/pi秒来完成计算。即I*beta的计算在1+(I*beta)/pi秒内完成,则可以在单位时间内完成(I*beta)/(1+(I*beta)/pi)的计算,假设总的计算量为V,所以一共需要t1=V*(1+(I*beta)/pi)/(I*beta)秒。通过提高并行度将理论计算峰值增加s倍后,通信单元传输beta字节的数据仍然需要1秒,需要(I*beta)/(s*pi)秒计算单元完成计算。假设总计算量为V,则完成任务需要t2=V*(1+(I*beta)/(s*pi))/(I*beta)秒。计算t1/t2得到加速比:1/(pi/(pi+I*beta)+(I*beta)/(s*(pi+I*beta))),公式丑陋请见谅,读者可以自己推导一下,比较简单。当理论峰值为pi时,移动数据需要1秒,计算需要(I*beta)/pi秒,则计算时间的比例为(I*beta)/(pi+I*beta),我们设p表示这个比值,等于(I*beta)/(pi+I*beta)。将p代入t1/t2的加速度比,可以得到加速度比为1/(1-p+p/s),这就是著名的Amdahl定律(https://en.wikipedia.org/wiki/Amdahl%27s_law)。其中,p表示原始任务可以并行化的比例,s表示并行化的倍数,1/(1-p+p/s)表示得到的加速比。我们用一个简单的数值计算,假设通信单元需要1秒来移动数据,计算单元需要9秒来计算,那么p=0.9。假设我们增加ComputingUnit的并行度使其理论峰值增加3倍,即s=3,那么ComputingUnit只需要3秒就可以完成计算,那么加速比是多少呢?利用Amdahl定律可知加速比为2.5倍,2.5的加速比小于计算单元3的并行度倍数。我们已经尝到了提高计算单元并行度的甜头。我们能否通过进一步增加并行度s来获得更好的加速比?能。比如让s=9,那么我们可以得到5倍的加速,可以看到增加并行度带来的好处越来越小。我们可以通过无限增加s来增加加速比吗?是的,但是越来越不划算了。想象一下,如果s趋于无穷大(即计算单元的理论峰值无穷大),p/s趋于0,那么最大加速比为1/(1-p)=10。只要系统中存在不可并行化的部分(CommunicationUnit),加速比就不能超过1/(1-p)。实际情况可能比加速比上限1/(1-p)还要差,因为上面的分析假设计算强度I是无穷大,当ComputingUnit的并行度增加时,传输带宽为CommunicationUnit通常会减小,这使得p变小,因此1/(1-p)变大。这个结论是非常悲观的。即使通信开销(1-p)只有0.01,也意味着无论使用多少并行单元,几万个,我们最多也只能获得100倍的加速比。有没有办法让p尽可能接近1,即1-p趋近于0,从而提高加速比?有灵丹妙药:流水线。5流水线:灵丹妙药在推导阿姆达尔定律时,我们假设通信单元和计算单元将串行工作。我们总是命令通信单元移动数据,计算单元进行计算。计算完成后,我们命令通信单元移动数据,然后重新计算。反复。通信单元和计算单元可以同时工作,边移动数据边计算吗?如果ComputingUnit在计算完一条数据后可以立即开始计算下一批数据,那么p就几乎为1,无论并行度s增加多少倍,都可以获得线性加速比。让我们研究一下在什么条件下可以获得线性加速。图4:(与图1相同)屋顶线模型中的I_1图4是通信受限任务。通信单元1秒内可以携带beta字节的数据,计算单元处理这个beta字节所需的计算量为beta*I_1次运算,理论计算峰值为pi,需要(beta*I_1)/pi秒完成计算。对于通信受限的任务,我们有beta*I_1pi,所以ComputingUnit的计算时间大于1秒。这意味着需要几秒才能计算出需要1秒传输的数据,并且在计算时间内有足够的时间传输下一批数据,即计算时间可以覆盖数据传输时间,并且p最大为1,只要I无穷大,加速比就可以无穷大。使通信单元和计算单元重叠工作的技术称为流水线(Pipeling:https://en.wikipedia.org/wiki/Pipeline_(computing))。是一种有效提高ComputingUnit利用率,提高加速比的技术。6.并行计算量化模型对深度学习引擎的启发上面讨论的各种量化模型也适用于深度学习引擎的开发。使用相同的硬件设备,采用不同的并行方式(数据并行、模型并行或流水线并行)都会影响计算强度I,从而影响实际计算性能;分布式深度学习引擎包含大量的通信开销和运行时开销,如何减少或屏蔽这些开销对于加速效果至关重要。读者可以站在以处理器为中心的计算设备的角度,通过理解基于GPU训练的深度学习模型,思考如何设计深度学习引擎以获得更好的加速比。在单机单卡的情况下,只需要做好数据处理和计算流水线,就可以实现GPU的100%利用率。实际的计算性能最终取决于底层矩阵计算的效率,即cudnn的效率。理论上,各种深度学习框架在单卡场景下应该没有性能差距。如果想通过在同一台机器上增加GPU来获得加速,与单卡场景相比,增加了GPU之间数据传输的复杂度,不同的任务切分方式可能会产生不同的计算强度I(例如,体积乘积层适合数据并行,全连接层适合模型并行)。除了通信开销,运行时调度开销也会影响加速比。在多机多卡场景下,GPU之间数据传输的复杂度进一步增加。机器之间通过网络传输数据的带宽一般低于机器内部通过PCIe传输数据的带宽,这意味着并行度提高,数据传输带宽的降低意味着斜率Roofline模型中的线变小了。适合数据并行场景的CNN,通常意味着比较高的计算强度I,而一些模型如RNN/LSTM,计算强度I要小很多,这也意味着管道中的通信开销更难隐藏。7小结用过分布式深度学习引擎的读者应该对软件框架的加速比有切身体会。基本上,卷积神经网络这种适合数据并行(计算强度I比较高)的模型,可以通过加GPU来加速。效果还是比较满意的。但是,仍然有一大类神经网络使用模型并行来具有更高的计算强度,即使使用模型并行,其计算强度也远低于卷积神经网络。对于这些应用程序如何通过增加GPU并行度来获得加速是业界尚未解决的问题。在之前的深度学习评测中,甚至出现过使用多个GPU训练RNN比单个GPU慢的情况(https://rare-technologies.com/machine-learning-hardware-benchmarks/)。不管用什么技术来解决深度学习引擎的效率问题,都是一样的。为了提高加速比,就是降低运行时开销,选择合适的并行方式提高计算强度,通过管道覆盖通信开销。在本文所述的基本法所涵盖的范围内。