1.7 分布式机器学习编程接口
在进入分布式机器学习世界之前,我们先来看现有分布式机器学习系统公开的一些可用编程接口(API),这些API旨在帮助用户将原始单节点代码转换为分布式版本,以此简化并行环境下的分布式机器学习编程。[3]我们首先给出原始机器学习迭代收敛算法如图1-15所示,接下来看看各种API如何对代码进行改造。
图1-15
图片来源:论文Machine Learning Parallelism Could Be Adaptive,Composable and Automated
1.7.1 手动同步更新
早期基于参数服务器的系统公开了一组API(push、pull、clock)。在Worker计算本地梯度之后,把梯度应用于模型之前,需要将这些API精确地插入训练循环来手动从参数服务器同步梯度。
MPI、OpenMPI等集合通信库及PyTorch都采用了这组接口。尽管它们的定义很直观,但使用这些API需要修改低层代码,这需要系统的专业知识,而且容易出错。下面是PyTorch代码示例。
1.7.2 指定任务和位置
TensorFlow等框架可以基于任务(Task-Based)进行分布式操作,用户将TensorFlow作为一组任务部署在集群上,这些任务是可以通过网络进行通信的命名进程,每个任务包含一个或多个加速器设备。这种设计允许在“任务:设备(Task:Device)”元组上手动放置操作或变量,如下面的代码所示。
这种手动放置操作为实现其他并行化策略提供了极大的灵活性。例如,可以启动一个名为parameter_server:cpu:0的任务,该任务在高带宽节点上放置一个可训练的变量,并在所有Worker任务中共享这个变量,从而形成一个参数服务器架构。
另一方面,“指定任务和位置”这种方式需要用户大量修改原始代码来进行变量布局(Placement Assignment),这假设开发人员了解分布式细节,并且能够将计算图元素正确分配给分布式设备,是个不小的挑战。
1.7.3 猴子补丁优化器
猴子补丁(Monkey Patch)优化器是避开手动平均梯度的一个改进接口。比如,Horovod提供了分布式优化器实现,该实现使用All-Reduce在Worker之间平均梯度。为了减少用户修改代码,Horovod修补了Host(宿主/主机)框架(如TensorFlow或PyTorch)的朴素(Naive)优化器接口,并将其重新链接到Horovod提供的分布式接口。通过从原生优化器切换到Horovod提供的分布式优化器,Horovod可以在一个训练step(步进,即完成一个批量数据的训练)中方便地把单机代码转换为分布式版本,如下面的代码所示。
该接口在开源社区中得到广泛的应用,然而,它需要将分布式策略的所有语义作为优化器来实现,导致对数据并行策略以外其他策略的支持十分有限。
1.7.4 Python作用域
TensorFlow Distribute提供了基于Python作用域的接口,如下面的代码所示。
该接口提供了一组分布式策略(如ParameterServerStrategy、CollectiveStrategy、MirroredStrategy)作为Python作用域,这些策略将在用户代码开始时生效。在后端,分布式系统可以重写计算图,并根据选择的策略(参数服务器或集合通信)来合并相应的语义。
这组接口用法简单,支持各种分发策略,并可以扩展到即时编译以自动生成分发策略。主要缺点是:它假设模型定义可以通过作用域完全准确地捕获,如果代码是用命令式编程(Imperative)来实现的,或者代码可以动态变化,则这种方法有时可能会产生错误结果。