在使用 GPU 进行深度学习时,务必使用集群管理器

阅读数:4227 2019 年 1 月 23 日

话题:大数据开源AI最佳实践

本文最初发布于Logical Clocks AB 的官方博客,经原作者授权由 InfoQ 中文站翻译并分享。

如果你正在雇用一个数据科学家团队或使用深度学习,那么可以在团队之间共享 GPU 的集群管理器将可以最大化 GPU 的利用率,并使数据科学家保持愉快的心情。

共享 GPU 的反模式

在 Logical Clocks,我们与许多客户讨论过他们如何在团队之间共享 GPU,令人惊讶的是,许多公司仍然使用谷歌日历或固定的时间表来共享 GPU。许多公司甚至不跨业务部门共享 GPU,这更糟糕。不用说,这些方法不利于充分利用你的 GPU 投资,也不利于开发人员——因为他们可能无法在需要时使用所有可用的 GPU。

GPU 即资源(GPUs-as-a-Resource)

资源管理器用于管理数据中心或组织中可用的计算和 GPU 资源。开发人员可以通过向集群提交应用程序请求来使用资源管理器运行应用程序:请使用 X 容器启动我的应用程序,其中每个容器都有 Y 个 CPU 和 X GB 内存。然后,当资源管理器可以将这些资源分配给应用程序时,它就会调度应用程序执行。对于深度学习,我们需要 GPU,有一些现代化的资源管理器支持“GPU 即资源”,你可以请求在容器中加入 N 个 GPU。深度学习应用程序可以对资源管理器提出具体的要求。对于分布式训练(使用 1 个以上的 GPU),应用程序会同时请求所有的 GPU——即所谓的分组调度(Gang Scheduling)。但是,对于超参数优化,应用程序开始时可以只使用 1 个 GPU,并在资源管理器增量分配GPU 时使用更多的 GPU。为了让分组调度和增量分配正常工作,需要应用程序软件和资源管理器都支持。

image

分布式训练需要资源管理器提供分组调度支持,以便可以同时提供 GPU。如果不支持或不完全支持分组调度,则分布式训练可能会无限期地中断,或者导致资源管理器死锁。

image

超参数优化可以使用资源管理器增量分配的 GPU。它可以仅使用 1 个 GPU,也可以使用更多的 GPU 获得更快的速度。

机器学习工作流

当机器学习从研发转移到生产时,模型训练通常会成为一个更长的机器学习工作流程中的一个阶段,其中包括(1)收集和准备训练数据,(2)训练 / 验证模型,以及(3)部署模型以提供服务。如果数据量很大,阶段(1)可能需要许多容器以及许多 CPU,用于 ETL 和 / 或特征工程。Spark/PySpark 是用于这个阶段的流行框架。对于阶段(2),可以使用 PyTorch 或 Keras/TensorFlow 等框架进行训练。分布式训练可以在HopsML等框架的帮助下完成。最后,阶段(3)涉及到将模型部署到生产环境中以提供服务。这可以在不同的集群或相同的集群上完成。Kubernetes 是一个流行的模型服务框架,因为它支持负载平衡和伸缩性。

image

机器学习工作流包含 DataPrep 阶段、训练阶段和模型服务阶段,每个阶段需要集群提供不同的资源集。DataPrep 通常需要 CPU,训练需要 GPU,服务需要 CPU(低延迟模型服务可能也需要 GPU)。

YARN、Mesos、Slurm、Kubernetes

有多种数据中心资源管理器支持 GPU 即资源:

  • YARN
  • Kubernetes
  • Mesos (DC/OS)
  • Slurm

YARN 是本地数据湖的主要资源调度程序,自 Hadoop 3.1 版以来,它就对 GPU 即资源提供了全面支持。Hops 的 YARN 是 Hadoop 的一个分支,自 2017 年 10 月以来一直支持 GPU 即资源。这两个版本都不支持分组调度,但是 Hops 在 YARN 上提供的层次分组调度语义使用了 PySpark 和 HopsML API。实际上,训练应用程序是在单个 map 操作中运行的,而该操作由 HopsML 在 PySpark 执行器上进行分组调度。在 Spark 2.4 中,有一种新的障碍执行模式,也支持分布式训练的分组调度。

Mesos 不支持分组调度,但是,与 HopsML 使用 Spark 向 YARN 添加分组调度支持类似,Uber 在一个名为 Peleton 的平台上添加了对使用 Spark 进行分布式训练的分组调度支持。遗憾的是,Peleton 目前不是开源的,Uber 正在讨论将 Peleton 迁移到 Kubernetes。Kubernetes 目前正致力于支持分组调度(或者他们称之为协同调度),并且有望在 2019 年晚些时候将其包含在 KubeFlow 这样的框架中。传统上,Slurm 用于 HPC 集群,在云或数据湖集群中没有得到广泛的应用,但提供了原生分组调度支持。

我们现在讨论使用以下两个开源框架的数据科学家如何使用资源管理器:KubeFlow on Kubernetes 和 Hopsworks on Hops YARN。

使用 KubeFlow 的数据科学家的经验

Kubernetes 支持使用 YAML 中的集群规范创建包含 GPU 的集群,例如:

复制代码
apiVersion: v1
kind: Pod
metadata:
name: cuda-vector-add
spec:
restartPolicy: OnFailure
containers:
name: cuda-vector-add
# https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
image: "k8s.gcr.io/cuda-vector-add:v0.1"
resources:
limits:
nvidia.com/gpu: 1 # requesting 1 GPU

数据科学家通常不直接使用 Kubernetes,因为这涉及太多 DevOps:YAML 规范、安装 Python 库和其他包的 Dockerfile。相反,Kubeflow 通常用于配置集群,并在命令行中使用 GPU 训练深层神经网络。

首先,数据科学家可以使用命令行,通过以下命令检查集群中 GPU 的可用性:

复制代码
$ kubectl describe nodes | grep -B 3 gpu
Capacity:
cpu: 8
memory: 2879772Ki
nvidia.com/gpu: 2
Allocatable:
cpu: 8
memory: 2777372Ki
nvidia.com/gpu: 2

然后,假设你已经安装了 Kubeflow(比如使用本教程),那么你可以通过以下命令使用 Kubeflow 在 GPU 上训练深度神经网络:

复制代码
ks generate tf-job mnist –name=mnist –namespace=mykubeflow
# examine, then set the cluster configuration parameters
ks param list
COMPONENT PARAM VALUE
========= ===== =====
mnist args "null"
mnist image "null"
mnist image_gpu. "null"
mnist name "mnist"
mnist namespace "mykubeflow"
mnist num_gpus 0
mnist num_masters 1
mnist num_ps 0
mnist num_workers 0
IMAGE=docker.io/raddaoui/tfjob_mnist_image:2.0
ks param set mnist image ${IMAGE}
ks param set mnist num_ps 2
ks param set mnist num_workers 3
ks param set mnist num_masters 0
ks param set mnist args — python,/opt/mnist_replica.py
# start training
ks apply default -c

使用 Hopsworks 的数据科学家的经验

Hopsworks 是我们面向机器学习和数据分析的扩展平台,它基于下一代 Hadoop 平台 Hops。在 Hopsworks UI 中,数据科学家可以快速查看集群中可用 GPU 的数量:

image

启动一个有多个 GPU 的集群非常简单,只需确定要分配给应用程序主程序和执行程序的 GPU 数量和内存量即可:

image

最后,数据科学家可以使用pipconda安装 Python 库(不需要编写 Dockerfile):

image

Hopsworks 中的机器学习工作流

在 Hopsworks,我们为(1)DataPrep 和(2)训练阶段和(3)模型服务 Kubernetes 提供 YARN 支持,如下图所示。通常,DataPrep 是在 PySpark 或 Spark 上完成的,该阶段的输出是将训练数据写入我们的分布式文件系统 HopsFS。训练通常通过使用 PySpark 启动 PyTorch 或 TensorFlow/Keras 应用程序来完成,经过训练的模型存储在 HopsFS 上。最后,在 Kubernetes 中通过从 HopsFS 读取模型来提供服务。

image

HopsML 中的机器学习工作流可以在 PySpark 上运行 DataPrep 阶段,在 TensorFlow/PyTorch 上进行(分布式)训练,在 Kubernetes 上提供模型服务。一个分布式文件系统 HopsFS 用于集成不同的阶段,YARN 用于为 PySpark 阶段分配 CPU 以及为训练阶段分配 GPU。

有集群管理器就够了吗?

2018 年 Spark 欧洲峰会上的演讲中,我们指出,集群管理器本身不足以最有效地利用 GPU。数据科学家可以在 Jupyter 笔记本上编写 Python 程序,在那里他们可以使用相同的资源进行训练和可视化。例如,开发人员可以在 Jupyter 中编写一个 cell 在一个或多个 GPU 上训练网络,然后编写后续 cell 来评估该模型的性能,然后会有一个或多个 cell 来可视化或验证经过训练的模型。当数据科学家可视化地分析训练好的模型时,她不必使用宝贵的 GPU 资源。

GPU 应该在训练 / 评估完成后立即释放——与如何实现分组调度无关。你可以使用(1)规则和分布式文件系统或(2)HopsML 确保 GPU 立即被释放。对于(1),开发人员可以将他们训练过的模型和评估数据集写入分布式文件系统,并在训练完成后关闭用于训练模型的容器。然后打开一个新的 Python 笔记本,访问相同的分布式文件系统,以直观地检查经过训练的模型。对于(2),在 HopsML 中,开发人员将他们的训练代码放在 Python 中的一个函数里,这个函数在 GPU 上运行,当函数返回时,与其关联的 GPU 会在几秒钟的不活动状态后释放。HopsML 使用 PySpark 中的动态执行器实现了这种行为——要了解更多细节,请阅读这篇博文。下面的例子展示了如何在 HopsML 中构造代码:

复制代码
def train_fn():
# training code goes here
from hops import experiment
experiment.launch(train_fn)

小结

集群管理器(例如 YARN for Hopsworks)将帮助你最大化 GPU 的价值,并通过模型的分布式训练和超参数优化提高数据科学家的工作效率,从而使他们保持愉快的心情。

查看英文原文:When Deep Learning with GPUs, use a Cluster Manager