那如果现在向你推举一款神器,可以实现演习速率翻倍,访存效率翻倍,你心动吗?心动不如行动(这可不是电视直销,别焦急换频道),来和我一起看看这款神器——基于PaddlePaddle核心框架的自动稠浊精度(Automatic Mixed Precision) 技能,简称飞桨 AMP 技能。
飞桨 AMP 技能仅仅通过一行代码即可帮助用户简便快速的将单精度演习的模型修正为自动稠浊精度演习。同时通过黑白名单和动态 Loss Scaling 来担保演习的稳定性,避免涌现 INF 或者 NAN 问题。PaddlePaddle AMP 可以充分发挥新一代 NVIDIA GPU 中 Tensor Core 的打算性能上风,ResNet50、Transformer 等模型的演习速率与单精度演习比较可以提升到 1.5~2.9 倍。
那么它是怎么实现的呢?我们先从什么是自动稠浊精度技能讲起。

顾名思义,自动稠浊精度是一种自动将半精度和单精度稠浊利用,从而加速模型演习的技能。个中单精度(Float Precision32,FP32)好理解,是打算机常用的一种数据类型。那么半精度是什么呢?如图 1 所示,半精度(Float Precision16,FP16)是一种相对较新的浮点类型,在打算机中利用 2 字节(16 位)存储,在 IEEE 754-2008 中,它被称作 binary16。与打算中常用的单精度和双精度类型比较,Float16 更适于在精度哀求不高的场景中利用。
不言而喻,在深度学习领域,如果利用 Float16 代替 Float32 来存储数据,那么开拓者就可以演习更大更繁芜的模型,利用更大的 batch size。因此对付那些恨不得挖掘出 GPU 里每一个晶体管全部潜力的科学家们怎么能放过它呢?同时由于 NVIDIA 推出了具备 Tensor Core 技能的 Volta 及 Turing 架构 GPU,使半精度打算趋向成熟。在相同的 GPU 硬件上,Tensor Core 的半精度打算吞吐量是单精度的 8 倍。
但显而易见,利用 Float16 肯定会同时带来打算精度上的丢失。但对深度学习演习而言,并不是所有打算都哀求很高的精度,一些局部的精度丢失对终极演习效果影响很微弱,仅须要某些分外步骤保留 Float32 的打算精度即可。因此稠浊精度打算的需求应运而生。我们可以将演习过程中一些对精度丢失不敏感且能利用 Tensor Core 进行加速的运算利用半精度处理,最大限度的提升访存和打算效率。
但是对每个详细模型,人工去设计和考试测验精度稠浊的方法,是非常繁琐的,我们急迫须要一种更简洁的办法,高效地实现稠浊精度的演习。AMP,顾名思义,便是让稠浊精度演习自动化,因此利用大略是它的主要特色。详细咋用,咱们往下看!
下面以 MNIST 为例先容如何利用飞桨 AMP 技能。MNIST 网络定义的代码如下所示。个中 conv2d、batch_norm(bn)和 pool2d 的数据布局须要提前设置为'NHWC',这样有利于加速稠浊精度演习,并且 conv2d 的输出通道数须要设置为 4 的倍数,以便利用 Tensor Core 技能加速。
import paddle.fluid as fluiddef MNIST(data, class_dim): conv1 = fluid.layers.conv2d(data, 16, 5, 1, act=None, data_format='NHWC') bn1 = fluid.layers.batch_norm(conv1, act='relu', data_layout='NHWC') pool1 = fluid.layers.pool2d(bn1, 2, 'max', 2, data_format='NHWC') conv2 = fluid.layers.conv2d(pool1, 64, 5, 1, act=None, data_format='NHWC') bn2 = fluid.layers.batch_norm(conv2, act='relu', data_layout='NHWC') pool2 = fluid.layers.pool2d(bn2, 2, 'max', 2, data_format='NHWC') fc1 = fluid.layers.fc(pool2, size=50, act='relu') fc2 = fluid.layers.fc(fc1, size=class_dim, act='softmax') return fc2
为了演习 MNIST 网络,还须要定义丢失函数来更新权重参数,此处利用的优化丢失函数是 SGDOptimizer。为了简化解释,这里省略了迭代演习的干系代码,仅表示丢失函数及优化器定义干系的内容。
import paddle.fluid as fluidimport numpy as npdata = fluid.layers.data( name='image', shape=[None, 28, 28, 1], dtype='float32')label = fluid.layers.data(name='label', shape=[None, 1], dtype='int64')out = MNIST(data, class_dim=10)loss = fluid.layers.cross_entropy(input=out, label=label)avg_loss = fluid.layers.mean(loss)sgd = fluid.optimizer.SGDOptimizer(learning_rate=1e-3)sgd.minimize(avg_loss)
那么如何将上面的示例改造成利用 AMP 演习的办法呢?用户仅须要利用飞桨供应的 AMP 函数 fluid.contrib.mixed_precision.decorate 将原来的优化器 SGDOptimizer 进行封装,然后利用封装后的优化器(mp_sgd)更新参数梯度,代码如下所示:
sgd = fluid.optimizer.SGDOptimizer(learning_rate=1e-3)mp_sgd = fluid.contrib.mixed_precision.decorator.decorate(sgd)mp_sgd.minimize(avg_loss)
如上即为最大略的飞桨 AMP 功能利用方法。
但是大家可能有些疑问,模型是如何感知哪些算子(Op)须要被转换呢?是不是还须要手工指定呢?算子那么多,我怎么知道哪个算子可以被转换呢?别焦急,PaddlePaddle已经帮你定制好了,这也是这门技能被称为「自动」的缘故原由之一,且请往下看!
为了让开发者可以方便快捷的利用稠浊精度打算,PaddlePaddle的工程师们利用了大量模型在不同运用处景中反复验证,然后根据半精度数据类型打算的稳定性和加速效果,梳理出一系列适宜转换为半精度打算的算子,并将这些算子定义到了一份白名单文件中。同时对付一些经由验证创造不适宜转换的算子,也便是利用半精度打算会导致数值禁绝确的算子将被记录到黑名单文件中。此外一些对半精度打算没有多少影响的算子归类于灰名单。在利用 AMP 演习过程中,系统会自动读取黑白名单,从而感知到哪些算子须要被转换为半精度打算。
对付某些分外场景,如果开拓者希望利用自定义的黑白名单,则可以利用 AutoMixedPrecisionLists 类设置,代码示例如下所示。
sgd = SGDOptimizer(learning_rate=1e-3)# 指定自定义的黑白名单,个中 list1 和 list2 为包含有算子名称的列表amp_list = AutoMixedPrecisionLists(custom_white_list=list1,custom_black_list=list2)mp_sgd = fluid.contrib.mixed_precision.decorator.decorate(sgd, amp_list)mp_sgd.minimize(avg_loss)
那么自动稠浊精度技能被称为「自动」的缘故原由之二呢?那便是下面的自动调度 Loss Scaling 功能。
自动调度 Loss ScalingAMP 技能在提升访存和打算效率的同时,伴随的副浸染也是很明显的。那便是由于半精度数据类型的精度范围与转换前的单精度比较过窄,导致随意马虎产生 INF 和 NAN 问题。为了避免此类问题,AMP 技能实现了自动调度 Loss Scaling 功能,即在 AMP 演习过程中,为了避免精度下溢,每演习一定数量批次的数据,就将 Loss 放大指定倍数。如果 Loss 在放大过程中发生上溢,则可以再缩小一定倍数,确保全体演习过程中,梯度可以正常收敛。
fluid.contrib.mixed_precision.decorate 函数携带了自动调度 Loss Scaling 功能干系的参数,这些参数都带有默认值,如下面代码所示。这些默认值都是经由飞桨工程师多次验证后定义的。常日情形下,用户可以直策应用,无需重新设置。
sgd = SGDOptimizer(learning_rate=1e-3)mp_sgd = fluid.contrib.mixed_precision.decorator.decorate(sgd, init_loss_scaling=215, incr_every_n_steps=2000, use_dynamic_loss_scaling=True)mp_sgd.minimize(avg_loss)
多卡 GPU 演习的优化
在新发布的PaddlePaddle核心框架 1.7 版本上,AMP 技能深度优化了多卡 GPU 演习。如图 2 所示,在优化之前的参数梯度更新过程中,梯度打算时虽然利用的是半精度数据类型,但是不同 GPU 卡之间的梯度传输数据类型仍为单精度。
为了降落 GPU 多卡之间的梯度传输带宽,我们将梯度传输这个过程提到 Cast 操作之前,而每个 GPU 卡在得到对应的半精度梯度后再实行 Cast 操作,将其转变为单精度类型,如图 3 所示。这一优化在演习网络繁芜度较大的模型时,对减少带宽占用方面非常有效,如多卡演习 BERT-Large 模型。
演习性能比拟(AMP VS FP32)
飞桨 AMP 技能在 ResNet50、Transformer 等模型上演习速率相对付 FP32 演习来说有非常大的上风,下面以 ResNet50 模型为例,从下图中可以看出,ResNet50 的 AMP 演习相对与 FP32 演习,单卡加速比可达 2.9 倍,八卡加速比可达 2.8 倍。
干系资料Mixed Precision Training:http://arxiv.org/abs/1710.03740利用自动稠浊精度加速 PaddlePaddle 演习:http://on-demand-gtc.gputechconf.com/gtcnew/sessionview.php?sessionName=cn9312-利用自动稠浊精度加速+paddlepaddle+演习