Tensorflow的多卡训练:原理和实践
当我们拥有大量的数据后,尤其是大规模文本、多模态、视频序列数据及其使用大型预训练模型等等情况下,训练好一个模型不得不借助分布式策略来提高计算资源的使用效率,进而缩短模型的训练时间。文本总结一下Tensorflow的分布式多卡训练,包括单机多卡训练与多机的分布式训练。
Tensorflow提供API包括tf.distribute.Strategy
方便我们实现多卡或多记训练策略。
基本理论
首先介绍若干个概念。
模型并行与数据并行:
- 模型并行,不同的GPU上各自运行模型的不同部分,如多个层分别运行于不同的GPU
- 数据并行,不同的GPU上运行同一个模型的副本,但是使用不同的数据来计算梯度
数据并行由于不同GPU使用不同的数据来计算,这就涉及到参数更新时同步的问题。方法有同步更新与异步更新:
- 同步更新,每个batch内所有GPU都计算完成后,整合好各个GPU上的计算结果统一计算新权重,接着更新所有GPU上的副本模型参数后进行下一轮计算。同步更新需要所有GPU协调,更新速度取决于最慢的GPU,即受到木桶效应约束。
- 异步更新,每个GPU计算完梯度后,不需要协调其他GPU,马上更新整体权重与同步。这种同步方式无需等待,但过程涉及复杂的更新方法。
实践中,参数同步更新方法常见有Parameter Server与Ring All Reduce:
- Parameter Server,Reducer服务器负责把batch数据分发到各个GPU上,各个GPU计算梯度后send到Reducer上累加,得到该batch的准确梯度后更新参数,参数再同步到各个GPU上。这种方法由于各个GPU都需要与Reducer进行数据、梯度和模型参数的通信,每个batch的计算通信开销很大,且和GPU数量线性相关。此外,Reduce过程需要等待多有GPU,受到木桶效应约束。Google的第一代分布式机器学习框架DistBelief,其多卡训练也是使用Parameter Server架构。
- Ring All Reduce,分为两个步骤Scatter Reduce与All Gather。Ring All Reduce的通信成本与GPU数量无关且恒定。
Scatter Reduce与All Gather两个过程可以参考 https://andrew.gibiansky.com/blog/machine-learning/baidu-allreduce/,这里简单叙述一下:
- Scatter Reduce,GPU间构成一个环,假设参数分成N份,相邻的GPU传递与接收不同的参数k($1 \le k \le N$),在传递N-1次后,可以得到分散到不同GPU上的N份参数的累积。
- All Gather,由于这些参数的累积是分散到不同GPU上,于是再同步一下就能够让参数的累积到同步到不同GPU上。于是每个GPU都获得最新的完整的参数更新。
环境准备
首先我们需要准备单机多卡环境或多机多卡环境。单主机情况下,可以使用如下方式查看CPU或GPU设备的具体信息。tf.config.list_physical_devices
可以查看本地GPU、CPU设备,
1 | >>> gpus = tf.config.list_physical_devices(device_type="GPU") |
如果不具备多卡环境,可以使用单GPU来模拟多个GPU环境,例如一个8G显存的显卡,虚拟化为两个4G显存的GPU,具体如下,
1 | # 环境下只有一个GPU |
然后我们就可以看到两个逻辑GPU,
1 | >>> tf.config.list_logical_devices("GPU") |
单机多卡训练
单机多卡训练是指多个GPU在同一台主机上训练,在Tensorflow中,tf.distribute.MirroredStrategy
提供该训练策略的实现,
1 | # 创建MirroredStrategy的实例,并指定参与对卡训练的GPU,这里指定0~2GPU参与单机多卡训练 |
MirroredStrategy策略,即镜像策略,每个GPU上都有一份模型的副本。
那么MirroredStrategy下的单机多卡训练的原理是怎么样的?
- 首先是在训练前,context下的模型会被复制完整的模型到MirroredStrategy所有指定的K个计算设备上
- 对于每个batch的数据,会平分成K份,分别送到K个设备上,并使用模型的本地变量计算当前获得数据的梯度
- 各个设备互相交换梯度数据(即All-reduce操作,默认是使用NVIDIA NCCL),使得每个设备都具有该batch所有K份数据的梯度的和
- 根据梯度的和结果更新各个设备的本地变量
- 接着继续进行下一轮训练
多机训练
多机训练是指多个GPU在不同的主机上,联合多个GPU(或其他计算设备)的分布式训练。它的原理和单机多卡训练中的tf.distribute.MirroredStrategy
,只不过这里换成tf.distribute.MultiWorkerMirroredStrategy
。MultiWorkerMirroredStrategy其实是单机多卡的镜像策略推广到多节点后的情况。
此外,还需要设置TF_CONFIG
环境变量,其包括各个节点的元数据,如IP地址、端口号、角色等,例如下面两个节点的情况,
1 | # 第一台机器设置为0 |
这些配置直接写到Python代码中即可,以便修改后在多个节点间同步。这些配置好后,使用MultiWorkerMirroredStrategy
即可实现分布式训练,
1 | strategy = tf.distribute.MultiWorkerMirroredStrategy() |
接着每个脚本单独运行,然后会根据配置的元数据信息建立各个节点互连状态,当所有节点都建立连接后,即可进行多机训练。参数更显策略,多机训练与单机多卡训练是类似的。
实现
由于Tensorflow本身已经对多卡训练做了抽象和实现,因此实现上并不困难。源码地址:https://github.com/allenwind/tensorflow-in-large-dataset 。
总结
本文总结了多卡训练的基本原理,包括参数同步算法Parameter Server与Ring All Reduce,然后讲述Tensorflow对卡训练的实践。
参考
[1] https://tensorflow.google.cn/guide/distributed_training
[2] 《TensorFlow 内核剖析》刘光聪
[3] https://andrew.gibiansky.com/blog/machine-learning/baidu-allreduce/
转载请包括本文地址:https://allenwind.github.io/blog/16113
更多文章请参考:https://allenwind.github.io/blog/archives/