『技术』一行代码让训练速度提升2倍,飞桨自动混合精度技术详解


机器之心发布
机器之心编辑部
飞桨自动混合精度技术 , 让你的训练速度飞起来 。
随着生活节奏的加快 , 「等待」已经越来越成为人们希望远离的事情 。 但是在深度学习领域 , 模型的参数、数据集的规模等等动辄就是以亿为单位 , 甚至更大 , 因此当模型训练成功之时 , 放一首张靓颖的「终于等到你」作为背景音乐实在是太应景了 。
那如果现在向你推荐一款神器 , 可以实现训练速度翻倍 , 访存效率翻倍 , 你心动吗?心动不如行动(这可不是电视直销 , 别着急换频道) , 来和我一起看看这款神器——基于飞桨核心框架的自动混合精度(Automatic Mixed Precision) 技术 , 简称飞桨 AMP 技术 。
飞桨 AMP 技术仅仅通过一行代码即可帮助用户简便快速的将单精度训练的模型修改为自动混合精度训练 。 同时通过黑白名单和动态 Loss Scaling 来保证训练的稳定性 , 避免出现 INF 或者 NAN 问题 。 飞桨 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 更适于在精度要求不高的场景中使用 。
『技术』一行代码让训练速度提升2倍,飞桨自动混合精度技术详解
本文插图
图 1 半精度和单精度数据示意图
不言而喻 , 在深度学习领域 , 如果使用 Float16 代替 Float32 来存储数据 , 那么开发者就可以训练更大更复杂的模型 , 使用更大的 batch size 。 因此对于那些恨不得挖掘出 GPU 里每一个晶体管全部潜力的科学家们怎么能放过它呢?同时由于 NVIDIA 推出了具备 Tensor Core 技术的 Volta 及 Turing 架构 GPU , 使半精度计算趋向成熟 。 在相同的 GPU 硬件上 , Tensor Core 的半精度计算吞吐量是单精度的 8 倍 。
但显而易见 , 使用 Float16 肯定会同时带来计算精度上的损失 。 但对深度学习训练而言 , 并不是所有计算都要求很高的精度 , 一些局部的精度损失对最终训练效果影响很微弱 , 仅需要某些特殊步骤保留 Float32 的计算精度即可 。 因此混合精度计算的需求应运而生 。 我们可以将训练过程中一些对精度损失不敏感且能使用 Tensor Core 进行加速的运算使用半精度处理 , 最大限度的提升访存和计算效率 。
【『技术』一行代码让训练速度提升2倍,飞桨自动混合精度技术详解】但是对每个具体模型 , 人工去设计和尝试精度混合的方法 , 是非常繁琐的 , 我们迫切需要一种更简洁的方式 , 高效地实现混合精度的训练 。 AMP , 顾名思义 , 就是让混合精度训练自动化 , 因此使用简单是它的重要特色 。 具体咋用 , 咱们往下看!
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


推荐阅读