本文为数盟原创译文,转载请注明出处为“数盟社区”。
介绍
Arimo的日益增长的数据科学团队包括研究和开发机器学习和深入学习新的方法和应用。
我们正在调查的一个主题是分布式的深度学习。当数据集和模型非常大时,我们就会发现与深度学习相融合的模式和预测值的价值和品种。然而,如果数据或模型不适合机器的内存,培养大型模型可能就会很慢或很难。我们高兴地看到谷歌开源了TensorFlow深度学习库,这样我们能够在分布式Spark环境中运行TensorFlow。
这篇文章是对Arimo的演讲题目的概述:Spark的分布式TensorFlow:2016年Spark东部峰会上提出了调试谷歌深度学习库。
TensorFlow
2015年后期,谷歌大张旗鼓的开源了其深度学库TensorFlow。在谷歌,TensorFlow生产从搜索到地图、翻译的各种产品应用,因此库可以广泛的开展大规模测试。一个谷歌白皮书介绍了进入TensorFlow设计的各种系统的考虑。该开源库仅包含单机实现,可能是由于全分布式版本对谷歌的基础设施的依赖。 (注:谷歌现在已经开源其分布式代码)
为了研究TensorFlow的分布式版本的功能,我们将单机版TensorFlow改编到Apache
Spark。 Apache Spark带来了内存计算和可扩展的分布式计算。我们的目标是要回答:“TensorFlow和Apache Spark能不能协同工作去提供一个强大的、可扩展的分布式深度学习平台。”
体系架构
我们怎么做?
TensorFlow有一个Python API用来构建计算图表,然后再执行C++和用Scala编写的Spark,并具有Java和Python的API,所以把Python作为实现语言是自然选择。
对于Spark上的TensorFlow基本问题是如何分配的Spark神经网络训练。对于迭代map-reduce问题Spark是极好的解决方案,但训练神经网络不是map-reduce问题(见并行计算的13个矮人)。从谷歌中找一个线索,我们模仿他们的DownpourSGD架构。该DownpourSGD架构是一个数据并行设置,这意味着每个worker具有一个完整的模型,并且运行与其他worker不同的数据(数据并行),而不是在不同的机器上运行模型的不同部分(型号平行)。
图1:左边是来自谷歌的Distbelief纸图,右边是我们实现的示意图
从本质上讲,我们采取梯度下降法,并把它分为两类:“计算梯度”并行“应用梯度(下降)”和在它们之间插入一个网络边界。
图2:示意图为driver和worker扮演的角色。
driver和工作人员发送并通过WebSockets接收数据。
Spark worker用不同步的计算梯度,周期性地发送它们的梯度回驱动程序(参数服务器),这个结果是结合了所有worker的梯度并把所得到的参数按worker的要求发送回给worker。
“DownpourSGD”这个名字来自于直觉,如果我们把梯度下降视为从山上流下来的水,那么worker的异步性就代表许多临近的水都沿着不同的路径通向相同的山谷。
实验装置
我们通过下列型号测试执行的伸缩能力
1.数据集大小
2.型号规格
3.计算面积
4.CPU VS GPU
数据集大小
Dataset Name | Rows | Columns | Cells | Description |
MNIST | 60,000 | 784 | >39.2M | 60K 28×28 images |
Higgs | 10,000,000 | 28 | 280M | https://archive.ics.uci.edu/ml |
Molecular | 150,000 | 2,871 | 430M | https://www.kaggle.com/c/ |
注意,大的两个数据集之间,希格斯数据集中的行数大得多,而分子集是在数据点(行×列)的总数目比较大。这使模型在训练过程中产生了一些有趣的结果。
型号规格
我们为每一个模型采用前馈网络,主要用于简单计算模型的大小,即重量和偏见的变量总数。
Dataset | Hidden Layers | Hidden Units | Total Parameters |
MNIST | 2 | 1,024 | 1.8M |
Higgs | 3 | 2,048 | 14.3M |
Molecular | 3 | 2,048 | 8.5M |
计算面积
我们单一机器的配置有12个CPU内核,以及24个节点的有4个CPU没有GPU的一个AWS集群。我们使用Docker容器,以确保所有的节点具有相同的结构,各节点的硬件如下:
每台机器4核
每台机器1个执行人
每个执行人10GB内存
Spark 1.6.0
CPU VS GPU
最后,我们有8个节点的集群具有上述相同配置,但另外每个有一个GPU。 12核单一机器的配置也有4个GPU,可以根据需要被导通,但是TensorFlow默认只使用一个GPU。
结果
数据并行有益处,但网络通信开销迅速限制了其可扩展性。
扩展集群
我们证明了TensorFlow在Spark可以利用增加集群大小,但是集群型号所给予的更高的性能只能达到一个点。似乎网络通信瓶颈性能和作用需要被明确,以确定瓶颈是否可以克服。
在一个侧面说明,我们发现,只要数据足够小,就适合在一台GPU性能优于其它架构的机器上运行,因为它不具备处理网络开销问题的功能,可以充分利用GPU的优势为数学运算。
下图总结了我们运行的各种模型的关键测试结果。一些关键的趋势是明显的:
关键信息:worker同步性有影响:使worker异步有帮助,但如果他们太不同步,就可能会减缓甚至阻止聚合。
关键信息:增加模型的大小减少了从数据并行中取得的成果。
关键信息:网络通信与模型的大小成正比例变化。这使得随着模型尺寸的增大训练速度可预见的比例增速放缓。
关键信息:直到超过16个worker(在这里64个内核)被介绍,它会成为网络绑定系统。
总的来说,我们的实现提供有限的可扩展性因素。我们正在研究方法来减轻这些瓶颈,从而使训练性能能够与扩展并行。这仍然是一个有趣的研究方向,利用使用商业级Spark技术的优势来分发TensorFlow。
GPU性能测试
图4:培训速度(行/分钟)横跨每个数据集的架构
在以上图中,我们比较了各种实现的速度,包括本地和分布式的GPU实现。这里基本上有两种相互竞争力,本地实现没有数据并行且没有网络开销。当我们看向右边,数据集的行数方面变得更大,我们看到当地的GPU击败当地的CPU和GPU的集群,最终击败了没有GPU实现的集群。这证实了我们已经知道的GPU加速数学运算。但是随着数据库变得更大,地方GPU实现使所有其他实现大大逊色。由于所有的数据集足够小,可以适应内存,网络成为了分布式实现的瓶颈,而当地GPU没有这样的瓶颈,得到了两全其美。
总结
我们的研究目标是开发一个可扩展、分布式计算实现的深度学习。目前的项目证明, TensorFlow上的Apache Spark有实现的可能性,但我们还没有达到我们的目标。
这种实现最适合于大型数据集、小型号,因为模型大小与网络开销线性相关。大型数据集让我们充分利用了系统中的数据并行的优势。
对于适合单台计算机上的数据,单机GPU提供了最快的速度。
对于大型数据集,使用有GPU的Spark集群包揽了速度和规模。
该项目是开源:https://github.com/adatao/tensorspark
未来的工作
l对于较大的模型,我们正在研究模型的并行和型号的压缩,以提高性能。
l我们正在研究用更聪明的方式来更新计算参数,例如使用坐标下降或共轭梯度。
l更有效地扩展这个架构的一个重要的方法是,使用多个参数服务器分裂模型,该模型事实上是在TensorFlow以及DistBelief白皮书中所讲述过的。我们当前的实现很容易造成驾驶员瓶颈。
附录
在这个项目中,我们遇到了许多挑战,我们用研究、试验和错误以及纯粹的执着克服了这些挑战。我们分享我们所发现的,如果你愿意的话就可以加入我们的工作。
GPU很快,但它不是万能药
GPU可以轻松获得本地配置几乎10倍的加速,同时由于网络开销GPU在Spark获取2-3倍的速度提升。因此,一旦数据不再适用于单台机器的内存容量,Spark配置就变得有意义。
然而,GPU有自己的内存,如果矩阵大到无法适应有限的典型GPU内存,GPU内存就可能成为瓶颈。如果你的模型不适合于GPU内存,型号并行可能比数据并行更有意义。
我们使用AWS GPU实例(NVIDIA K680卡添加的内存 – 1536支持CUDA核心,4GB内存)。该GPU实例使用NVIDIA计算环境3.0,这个环境TensorFlow本身并不支持。为的是支持AWS的GPU从源代码编译TensorFlow,并把一组TF_UNOFFICIAL_SETTING旗帜放在配置阶段。此版本技术显著增加TensorFlow在某些系统要求的虚拟内存的数量,因此被警告。当试图设置-executor-核心=8,而我们得到“虚拟内存不足”的错误时,我们才发现了这一点。 [https://github.com/adatao/tensorspark/blob/master/gpu_install.sh# L17]
序列化
l由于Tensorflow只有一个Python和C API,我们不得不使用PySpark去使Spark实施工作。这就导致对象在Scala、Java、Python和C ++中转换,而造成不是最佳的性能。
lTensorFlow的对象不能挑选,所以我们建议把它们排序为numpy数组。
l解析大JSON非常缓慢,所以避免它。
错误三岔口
我们的第一个执行涉及每个worker用随机重量和偏见创造一个新的TF模型,然后开始与driver沟通。一个问题很快出现了。
图5:意味着测试集平方误差VS可见训练样本数
我们可以看到测试误差只在14000样本后开始爆发。由于误差表面有多个局部最优,每个worker从不同的起点开始训练可能导致参数从不聚集和错误的建立。回到我们DownpourSGD类比,在山上出现降雨,有些水滴从一边流下,其他水滴下从另一边流下(见下图),所以正确的参数集没有聚集。
图6:不同的局部最优间的尖端的可视化
我们发现,我们可以通过热身阶段来降低这个影响,其中一些单独的数据驱动整个火车,有效地将其汇集到山的一侧。其结果是,由于山的尖端导致分散的概率下降,因为有多个这样的尖端而有限的热身只消除了其中的一些。 [https://en.wikipedia.org/wiki/Morse_theory]
原文链接:https://arimo.com/machine-learning/deep-learning/2016/arimo-distributed-tensorflow-on-spark/