一行代码让训练速度提升2倍,飞桨自动混合精度技术详解
机器之心发布
机器之心编辑部
飞桨自动混合精度技术,让你的训练速度飞起来。
随着生活节奏的加快,「等待」已经越来越成为人们希望远离的事情。但是在深度j F ` d V M ( P学习领域,模型的参O l T . t F数、数据集的规模等等动辄就是以亿为单位,甚至更大,因此当模型训练成功之时,放一首张靓颖的「终于等到你」作为背景l y B F ` ? 5音乐实在是太应景了。
那如果现在向你推荐一款神器,可以实现训练速度翻倍,访存效率W K y U u D `翻倍,你心动吗?心动不如行动(这可不是电视直销,: Z w h ! ~ H T别着急换频道),来和我一起看看这款神器——基于飞桨核心框架的自动混合精度_ N w [ L R 1 s存储,在 IEEE 7* @ ! ` ] m I ^ ?54-2008 中,它被称作 binary1* m n 6 P ] . x6。与计算中常用的单精度和双精度类型相比,N & $ & ) &Float16 更适于在精度要求不高的场景中使用。
图 1 半精度和单精度数据示意图
不言而喻,在深度学习领域,如果使用 Float16 代替 Float32 来存储数据,那么开发者就可以训练更大更复杂的模型,U Z J p B 8 P使用更大的x . q U R & 6 batch size。因此对于那些恨不得挖掘出 Gx i Q E pPU 里每一个晶体管全部潜1 y c 2 v力的科学家们怎么能放过它呢?同时由于 NVIDIA 推出C C W的精2 @ 0 7 3 # Q w -度损失对最终训练效果影响很微弱,仅需要某些特殊步骤保留= ] n O ] , 9 u h Float32 的计算精度即可。因此混合精度计算的需求应运而生。我们可以将训练过程中一些对精度损失不敏感且能使用 Tensor Core 进行加速的运算使用半精度处w | [ k ^ u @ ,理,最大限度的提升访存和计算效率。
但是对p R t ) k A每个具体模型,人工去设计和尝试精度混合的方法,是非常繁琐的,我们迫切需要一种更简洁的方式,高效地实现混合精度的训练s s a n 2 -。K } b 3AMP,顾名思义,就是* K Z K x N _ l Y让混合精度训练自动化,因此使用简单是它的重要特色。具体咋用,咱们往下看!
AMP 的使用方法
下面以 MNIST 为例介绍如何使用飞桨G v z AMPB } K W z _ 技术。MNIST 网络定义的代码如下所示。其中 conv2d、batc{ 4 i 5 }h_norm(bn)和 pool2d 的数据布局需要提前设置为\'NHWC\',这样有利P - . i l Z ,于加速混合精度训练,并且 conv2d 的输出通m 3 | ^ N S K道数需要设置为 4U _ M g T -data, class_dim): conv1 = fluid.layers.conv2d(data, 16, 5, 1, act=None, datJ Y & w n $ $a_formatX @ s=\'NHWC\') bn1 = fluid.A m g Tlayers.batch_norm(conv1, act=\'relu\', data_layout=\'NHWC\E { V { K Q L {') pool1 = fluid.laye8 H % M 4 m I d krs.pool2d(bn1,e n m ? o g q a ) 2, \'max\', 2, data_N G 4 j Bformat=\'NHWC\') conv2 = fluid.layers.conv2d(pool1, 64, 5, 1,R | ; L F K % _ act=None, data_format=\'NHWC\') bn2 = fluid.) X ^ d r + #layers.batchs F ) | B_norm(conv2, act=\'rB d s & L 7 e O Xelud I C\', data_layout=\'NHWC\') pool2 = fluid.layers.pool2d(bn2, 2, \'max\', 2, data_format=\'NHWC\') fc1 = fluid.layers.fc(pooA 9 u Q S x Zl2, size=50, act=\'relu\') fc2 = fluid.layers.fc(fc1, size=class_dim, act=\'softmaxR ^ x L @\') return fc2
为了训练 MNIST 网络,z N S g x T 0还需要定义损失函数来更新权重参数,此处使r Y Y用的优化损失函数是 SGDOptimizerq ! V g ` y )。为了简化说明,这里省略了迭代训练的相关代码,仅体现损失函数及优化器定义相关的内容。
imA k ]port paP 1 P _d6 E % l 0 m J @ dle.fluid as fluidimport nu4 P m V o ^ Smpy as npdata = fluid.layers.data( name=\'imag] t de\', sha~ Z x Y gpe=, dti J { _ b S ~ype=\'float3K ? _ L2\')label = fluid.layers.data(name=\'label\', shape=, dtype=\'int64\')out = MNIST(data, class_dim=10)loss = fluid.layers.cross_entropy(input=out, label=label)avg_loss = fluid.layers.mean(loss)sgd =O S + ? = L 3 | fluid.optimizerP l Q B + +.SGDOptimizer(learm m h $ning_rate=1e-3)sgd.minimize(avg_loss)那么如何将上面的示例改造成使用 AMP 训练的方式呢?用户仅需要使用飞桨提供的 AMP 函数 fluid.cM ) I . h ] Aontrib.mixed_precie t Asion.decorate 将原来的优化器 SGDOptimiz! A r x V l +er 进行封装,然后使用封装后的优化器(mp_sgd)更新参数梯度,代码如下所示:
sgd = fluid.optimizer.SGDOptimD 1 ^izer(learning_rateY 9 M m=1e-3)mp_sgd = fluid.contrib.mixed_precision.decorator.dec# u | W % m x Q Aorate(sgd)mpv Z ( ]_sgd.minimize(avg_loss)如上即为最简单的飞桨 AMP 功能使用方法。
但是大家可能有些疑问,模型是如何感知哪些算子(Op)需要被转换呢?是不是还需要手工指定呢?算子那么多,我怎么知道哪个算子可以被y } T $ & _转换呢?别着D i @ u ] # T急,飞桨已经帮你定制好了,这也1 P H %是这门技术被称为「自动」的原因之一,且请往下看!
黑白名单功能a g c
为了让开发者可以方便快捷的使用混合精度计算,飞桨的工程师们使用了大量模型在不同应用场景中反复验证,然后根据半精度数据类型计算的稳定性M ^ T t D .和加速效果,梳理出一系列适合转换为半精度计算的算子,并将这些算子定义到了一份白名单文件, u W J {中。同时对于一些经过验证发现不u 5 3 : z u q U } 4用自定义的黑白名单,则可以使用 Auo * Q X Y WtoMixeR : [ r pdPrecisionLists 类设置,代码示例如下所示。
sgd = SGDOptimizer(learning_rate=1e-3)# 指定自定义的黑白名单,其中 list1 和 liA = tst2 为包含有算子名称的列表amp_list = AutoMixedPrecisionLists(custom_white_lisu } } ! 5 ` w h t=list1,custom_black_lis| 7 o X 7 V mt=list2)mp_sgd = fluid.contrib.mixed_preT L u % U xcision.decorator.decorate(sgd, amp_list)mp_sgd.mP z , % = Winimize(avg_loss)那么自动混合精度技术被j V G f b ( U D称为「自动」的原因之二呢?那就是下面的自动调整 Y R t U 6 L I g倍数。如果 LoH U f x & $ ? rss 在放大过程中发生上溢,则可以再缩小一定倍数,确保} 0 X 7 E f 6 E整个训练过程中,梯度可以正常收敛。
fluid.contrib.mixed_precision.decorate 函数携带了自动调整 Loss Scaling 功能相关的参G J l F数,这些参数都带有b z m u I m默认值,如下面代码所示。这些默认值都是经5 x c q $ K 9 ^过飞桨工程师多次验证后定义的。通常情况下,用户可以直接使用r % O y G,无需重新设置。
sgd = SGDOptimizer(learning_rate=1e-3)mp_sgd = fluid.contrib.mixed_preciR Z T - asion.decorator.dD . & 9 5 . j necorate(sgd, init_loss_scaling=2**15, incr_every_n_steps=2000, use_dynamic_loss_scaling=True)mp_sgd.minimize(avg_loss)多卡 GPU 训练的优化
在新发布的飞桨核心框架 1.7 版本上,AMP 技术深度优化了多卡 GPU 训练。如图 2 所示,在优化之前的参数梯度更新过程中,梯度计算时虽然使用的是半精度数据类型,但是不同 GPU 卡之间的梯度传输数据类型仍为单精度。
图 2 1.7 版本之前的参数4 5 + v Q @ R梯度更新过程示意图
为了降低 GPU 多卡之间的梯度传b Y 6 u %输带宽,我们将梯度传输这个过程提到 Cast 操作之前,而每个 GPU 卡在得到对应的半精度梯度后再执行r s } ? M / Cast 操作,将其转变为单精度类型,如图 3 所示。这一优化在训练网络复杂度较大的模型时,对减少带宽占用方面非常有效,如多卡训练 BERT-Large 模型。t ( m r
图9 , & : x G u C f 3 1.7 版本的参数梯度更新过程示j Q G { C意图
训练性能对比(AMP VS FP32)
飞桨 AMP 技术在 ResNet50、Transformer 等模型上训练速度相对于 FP32 训练来说有非常大的优势,下面以 ResNet50 模型为例,从下图中可以看出,ResNet50 的 AMP 训练相对与 FP32 训练,单卡D e y l ~ T 6加速比可达 2.9 倍,八卡加速比可达 2.8 倍。
相关资料
- M^ _ = c m )ixed Precision Training:https://arxiv.org/abs/1710.03740
- 使用自动混合精度加速 PaddlePaddle 训练:https://on-demand-gtc.gputechconf.com/gtcnew/sessionview.php?sessionName=cn9312-使用自动混合精度加速+paddlepaddle+训练
如果您加入官方 QQ 群,您将遇上大批志m d [ ) Z &同道合的深度学习同学。官方 QQ 群:703252161。
如果您想详细= L / z t F ^了解更多飞桨的相关内容,请参阅以下文档。
官网地址:https://www.ph W g n taddlepaddle.org.cn
飞桨核心框架项目地址:https://g, = / 3 Kithub.com/PaddlePaddle/Par p -ddle