天天看点

无服务器计算的机器学习,出路在哪里?(一)

一、机器学习和无服务器学习

1.1、机器学习(ML)在应用场景中遇到了什么问题?

近年来,机器学习(Machine Learning,ML)在图像识别、文本和语音处理等领域中广泛应用,改变了人们工作、生活的方式,带来了巨大的便利性。但同时,ML 用户也面临着几个巨大的挑战,这些挑战极大地阻碍了 ML 的生产力和效率。首先,用户通常需要手动配置许多系统级参数,例如工作服务器 / 参数服务器的数量、内存分配、cpu 数量、物理拓扑等。其次,用户需要指定大量与 ML 相关的参数,如学习率、学习算法、神经网络结构等,这些参数与系统级参数之间还存在各种交互作用。第三,ML 工作流通常由多个阶段组成,包括预处理、训练、超参数搜索等等,每个阶段都有 ML 用户必须考虑的不同计算需求。

由于 ML 的这些特点,在实际应用中经常会导致两个问题:

  • 一是,ML 工作流中不同任务的异构性导致了训练工作流执行过程中资源的严重不平衡。ML 用户需要单独考虑每个阶段的异构资源需求,常常会导致资源过度配置(Resource Overprovisioning)。当前的 ML 框架通常是基于粗粒度的 VM 集群的,而这些集群并不具备 ML 相关工作负载所需的灵活性。CPU 总利用率低至 20% 的情况并不少见[1]。在实践中,开发人员在工作流的不同阶段反复使用不同的 ML 参数进行实验会进一步加剧资源过度配置的问题;
  • 二是,ML 用户需要应对复杂的管理问题,他们面临着为每个 ML 工作负载提供、配置和管理这些资源的挑战。利用 VMs 进行机器学习工作负载的系统通常需要用户重复执行一系列繁重的任务,表 1 中展示了一些任务。这种管理复杂性阻碍了交互和迭代用例,降低了用户生产力和模型的有效性。

在实践中,过度资源调配和显式资源管理负担这两个问题是紧密耦合的:ML 用户在遇到工作流不同阶段所需资源精确分配所带来的难度和人工成本的问题时,通常会采用过度资源调配的方式来应对。

那究竟用什么办法应对 ML 在实践中应用的这些问题呢?在这篇文章中我们一起来探讨一个目前广泛应用且获得了非常好效果的办法:无服务器计算(Serverless Computing)。

表 1. ML 用户在使用 VM 集群时遇到的任务挑战。

无服务器计算的机器学习,出路在哪里?(一)

1.2、无服务器计算(Serverless Computing)

无服务器计算是云原生计算模型的一种落地状态。云计算的发展在经历了基础设施即服务(Infrastructure as a Service-IaaS)、平台即服务(Platform as a Service-PaaS)、软件即服务(Software as a Service-SaaS)几个阶段后,逐渐进入了无服务器计算的阶段。从与之前几个阶段所能提供的服务进行比较的角度分析,无服务器计算可以提供以下一种或两种服务:

  • 1.  函数即服务 (Functions-as-a-Service-FaaS)。开发人员使用由事件(event) 或 HTTP 请求触发的函数运行和管理应用程序代码,开发人员将这些小的代码单元部署到 FaaS 中,FaaS 按需执行和扩展,开发人员则无需管理服务器或任何其他底层基础设施。
  • 2. 后端即服务(Backend-as-a-Service-BaaS)。提供第三方的基于 API 的服务用于替换应用程序中的核心功能子集。对于开发人员来说,这些 API 是作为一个自动扩缩容和透明操作的服务提供的,所以对于开发人员来说,这种服务方式也是无服务器的。

从技术实现的角度分析,无服务器计算依靠云基础设施而不是用户来自动解决资源调配和管理的挑战。这种方法依赖于一个更受限制的计算单元,例如 AWS Lambda 的无状态 Lambda 函数(the Stateless Lambda Function),该计算单元由开发人员提交,并由云基础设施安排执行。因此,用户无需手动配置、部署和管理长期计算单元(例如 VM)。无服务器模式的优势促进了数据中心、云提供商和开放源代码平台的快速应用。

无服务器计算所提供的服务包括:一种有时间限制的无状态函数作为执行程序逻辑的服务 API,以及,一种管理程序状态的对象存储系统。通过使用服务 API,用户可以运行代码函数 (也称为操作) 并返回每个函数的结果。无服务器计算还提供 HTTPS 终端,允许开发人员检索函数结果,开发人员可以通过 HTTPS 终端输入参数后生成相关函数的触发事件(或链接)。对于能够清晰地分离程序状态和逻辑的应用程序设计人员来说,无服务器计算平台提供了对大型计算能力的即时访问,使得程序设计人员无需进行复杂的集群部署。

在无服务器计算平台中,云服务提供商提供了按需执行函数的能力,并对最终用户隐藏了集群配置和管理开销。除了可用性方面的好处外,这种模式还提高了效率:云提供商可以以比传统集群计算更精细的粒度复用资源,并且用户不需要为空闲资源付费。然而,为了有效地管理资源,云服务提供商对每种资源的使用进行了限制。

  • 计算(computation)。无服务器计算平台中提供的计算资源通常仅限于一个 CPU 核和一个较短的计算窗口。例如,AWS Lambda 在单个 AVX 内核上提供 900 秒的计算时间,可以访问高达 3GB 的内存和 512MB 的磁盘存储。用户可以执行许多并行函数,并且这些执行的聚合计算性能几乎呈线性扩展。函数执行中的线性可伸缩性只在单个 worker 之间没有通信的情况下对并行计算有用。在实际应用中,由于单个 worker 只是瞬时存在的,他们的启动时间可能是错开的,因此传统的类似 MPI 的点对点通信模型无法在这种环境中工作。我们可以考虑利用存储作为 worker 之间的间接通信通道。
  • 存储(Storage)。云服务提供商提供了多种存储选项,从键值存储到关系型数据库。有些服务不完全是弹性的,因为它们需要预先提供资源。然而,像 Amazon S3 或 Google Cloud Storage 这样的分布式对象存储系统提供了无限存储,用户只需按存储的数据量付费。我们可以考虑潜在地将计算期间的中间状态存储在分布式对象存储中,并且仍然可以获得与从其他节点的 RAM 访问时相同的带宽。
  • 控制面(Control Plane)。除了存储服务,云服务提供商还提供发布 - 订阅服务,如 Amazon SQS 或 Google Task Queue。这些服务通常不支持高数据访问带宽,但提供一致性保证,如至少一次传递,并且可以用于 “控制平面” 状态:所有无服务器函数调用之间共享的任务队列。云服务提供商还提供一致的键值存储(例如 DynamoDB),可用于跨无服务器函数调用存储和操作控制平面状态。

由于无服务器计算存在上述约束条件,在实际应用中,无服务器计算也不是 “完美无缺” 的,应用无服务器计算也面临很多问题。以 AWS Lambda 为例,利用无服务器计算的主要挑战是与 Lambda 函数相关联的非常小的本地资源约束(内存、cpu、存储、网络),这是无服务器计算的基础,正因为这些细粒度的计算单元实现了可伸缩性和灵活性。具体的,无服务器计算面临着如下问题:

  • 本地内存和存储空间小(Small local memory and storage)。由于存在计算资源限制,阻止了使用任何未使用这些资源设计的计算框架。例如,我们无法在 AWS Lambda 或具有此类资源受限配置的 VM 上运行 Tensorflow 或 Spark。
  • 低带宽以及缺乏 P2P 通信(Low bandwidth and lack of P2P communication)。与常规 VM 相比,Lambda 函数的可用带宽有限。我们发现,最大的 AWS Lambda 只能维持 60MB/s 的带宽,即使在中型 VM 中,也远远低于 1GB/s 的可用带宽。此外,无服务器计算对通信拓扑施加了进一步的限制。诸如 AWS Lambda 之类的无服务器计算单元不允许对等通信。因此,传统的用于数据中心 ML 的通用通信策略,例如树结构或环结构 AllReduce 通信等等,在这样的环境中都无法有效实现。
  • 短暂且不可预测的加载时间(Short-lived and unpredictable launch times)。Lambda 函数的寿命很短,且启动时间非常多变。例如,AWS Lambda 在加载后可能需要几分钟的时间来启动。这意味着在训练过程中,Lambda 会在不可预知的时间开始,并且有可能在训练中途结束。这就要求 Lambda 的 ML 运行时能够容忍 worker 的频繁离开和到达。
  • 缺乏快速共享存储(Lack of fast shared storage)。因为 Lambda 函数之间不能连接,所以需要使用共享存储。由于 ML 算法有严格的性能要求,这种共享存储需要低延迟、高吞吐量,并针对 ML 工作负载中的通信类型进行优化。然而,到目前为止,还没有能够为云提供所有这些属性的快速无服务器存储。

不过,目前已经有不少无服务器计算的落地应用案例。其中,有代表性的公有云无服务器平台有:

  • AWS Lambda。亚马逊的 AWS Lambda,借助 Lambda,几乎可以为任何类型的应用程序或后端服务运行代码,而且完全无需管理。只需上传代码,Lambda 会处理运行和扩展高可用性代码所需的一切工作。开发人员可以将代码设置为自动从其他 AWS 服务触发,或者直接从任何 Web 或移动应用程序调用。 https://aws.amazon.com/cn/lambda/
  • Microsoft Azure Functions。微软的 Azure 是一个事件驱动(Event-drive)的无服务器计算平台,可以解决复杂的编排问题。本地构建和调试,无需额外设置,在云中大规模部署和操作,并使用触发器和绑定集成服务。 https://azure.microsoft.com/en-us/services/functions/
  • Google Cloud Functions。Google 的 Cloud Functions 是一种事件驱动的计算服务。它具有自动扩展、运行代码以响应事件的能力,仅在代码运行时付费的能力,并且不需要任何服务器管理。用例包括无服务器应用程序后端,实时数据处理和智能应用程序,如虚拟助手,聊天机器人和情绪分析。 https://cloud.google.com/functions/
  • 阿里云函数计算(Function Compute)。阿里的函数计算是一个事件驱动的全托管无服务器计算服务,无需管理服务器等基础设施,只需编写代码并上传,函数计算会准备好计算资源,并以弹性、可靠的方式运行代码。所有客户,函数计算都将提供每月 100 万次函数调用、400,000 个函数实例资源的免费无服务器算力支持。 https://www.aliyun.com/product/fc?spm=5176.10695662.1112509.1.3b6768bc2OOWFL

有代表性的私有云无服务器框架有:

  • Fission 。Fission 使用 Kubernetes 构建函数。它允许程序员使用任何编程语言编写函数,并将其与任何事件触发器 (如 HTTP 请求) 进行映射。 https://fission.io/
  • Funktion 。Funktion 是一个开源的容器本地化服务器平台,使用 Kubernetes 构建函数。它允许程序员用任何编程语言编写函数,可以在任何地方、任何云上或在本地运行。 https://github.com/funktionio/funktion
  • Kubeless 。是一个 kubernets 原生的无服务器计算框架。它利用 Kubernetes 资源提供自动缩放、API 路由、监控、故障恢复等功能。 https://github.com/kubeless/kubeless
  • Apache OpenWhisk 。OpenWhisk 使用 Docker 构建函数,它允许程序员使用 Scala 语言编写函数,允许在任何规模的事件响应中执行代码。框架响应类似 HTTP 请求这样的触发事件,然后运行 JavaScript 或 Swift 代码片段。 https://openwhisk.apache.org/
  • Iron Functions 。Iron 使用 Docker、Swarm、Kubernetes 构建函数,它允许程序员使用 Go 语言编写函数。 https://github.com/iron-io/functions
  • OpenLambda。OpenLambda 是一个 Apache 许可的无服务器计算项目,用 Go 编写,基于 Linux 容器。OpenLambda 的主要目标是探索无服务器计算的新方法。 https://github.com/open-lambda/open-lambda
  • OpenFaas 。OpenFaaS 是一个使用 Docker 构建无服务器 (Serverless) 功能的框架,它拥有对指标的一级支持。任何流程都可以打包为一个函数,使你能够使用一系列 web 事件,而无需重复的样板化编码。 https://www.oschina.net/p/openfaas?hmsr=aladdin1e1

有代表性的无服务器平台的包装框架有:

  • Zappa(Python,AWS)。Zappa 极大的简化了在 AWS Lambda + API 网关上发布所有 Python WSGI 应用。相当于是无服务器的部署运行 Python Web 应用。这意味着无限伸缩、零宕机、零维护。 https://www.oschina.net/p/python-zappa
  • Chalice(Python,AWS)。Chalice 允许开发者快速创建和部署应用,采用 Amazon API 网关和 AWS Lambda 。 https://www.oschina.net/p/chalice?hmsr=aladdin1e1
  • Claudia.js(Node,AWS)。方便快速部署 Node.js 项目到 AWS Lambda 和 API 网关。它自动化了所有容易出错的部署和配置任务,并按照 JavaScript 开发人员所期望的开箱即用的方式设置了一切。开发人员可以轻松地开始使用 Lambda 和 API 网关,并专注于解决重要的业务问题,而不是处理 AWS 部署工作流。 https://github.com/claudiajs/claudia

二、引入 ML 的无服务器计算最新研究情况介绍

由上一节的介绍我们知道,目前已经有很多公有云、私有云无服务器计算平台,也有一些无服务器平台的包装框架。可以说,我们想在日常的应用实践中尝试无服务器化,已经是比较容易的一件事了。不过,具体到机器学习的问题,这些无服务器计算平台在 ML 应用场景下都或多或少存在一些问题。

由第一章中的介绍我们可以看到,无服务器计算非常适用于离散化数据中心(Disaggregated Datacenters),但对许多性能关键型应用(Performance critical applications)却不是非常适用,因为无服务器计算方式切断了传统的性能优化途径,例如利用数据局部性进行优化或分层通信等,因此会直接影响性能关键型应用的效果。目前无服务器平台主要用于简单的事件驱动应用程序,如物联网自动化、前端 web 服务和日志处理等等。

最近,一些研究人员将无服务器计算应用在更广泛的场景中,如并行数据分析和分布式视频编码。然而,这些工作负载要么只能简单并行,要么只能跨函数使用简单的通信模式。复杂的通信模式和工作负载如何有效地适应无服务器计算仍然是一个有待研究的问题。我们这篇文章中重点关注的是用于 ML 的无服务器计算。我们知道,ML 包含大量的参数、复杂的处理流程,是典型的 “性能关键型应用”,我们将在这一节中介绍最新的关于“如何将 ML 引入无服务器计算” 这一问题的研究进展。

2.1、A Case for Serverless Machine Learning [2]

无服务器计算的机器学习,出路在哪里?(一)

本文是来自 Berkeley 的研究人员发表在 NIPS2018 中的一篇文章,具体分析了 ML 工作负载环境下的资源管理问题,探讨了利用无服务器基础设施实现 ML 工作流资源管理自动化的研究方向。作者提出了一个无服务器机器学习框架,该框架专门用于无服务器基础设施和 ML 工作流。

本文所讨论的无服务器计算依赖于 Amazon S3 的无状态 Lambda 函数,这些函数由开发人员提交,并由云基础设施自动调度。因此,它们避免了开发人员显式配置、部署和管理长期计算单元(例如 VM)的需要。与一般的无服务器计算平台不同,无服务器机器学习框架需要满足三个关键目标。首先,它的 API 需要支持广泛的 ML 任务:数据预处理、训练和超参数优化。为了简化从现有 ML 系统的转换所涉及的工作量,应该用 Python 之类的高级语言开发这样的 API。第二,为了为无状态工作者之间的中间数据和消息传递提供存储,它需要提供一个具有丰富接口的低延迟可伸缩数据存储。第三,要在资源受限的 Lambda 上高效运行,它的 Runtime 必须是轻量级和高性能的。

为了满足这些条件,作者构建了一个专门用于 ML 的无服务器框架。

  • 首先,该框架为 ML 工作流的所有阶段提供了一个 API,该 API 实用且易于更广泛的 ML 社区使用。(1)API 完全包含在 Python 包中,允许 ML 开发人员轻松调用。(2) API 提供了一个抽象底层系统级资源的高级接口。(3) Python 包提供了一个用户界面,开发人员可以通过该界面可视化工作进度。
  • 然后,该框架包含 Python 前端提供到客户端后端的接口。这个后端负责管理临时计算资源和调度任务。在这个后端中,不同的子模块为 ML 工作流的每个特定阶段的逻辑(例如预处理)进行编码处理。这些子模块启动 Lambda 上的 worker,跟踪计算进度,并在计算完成后将结果返回到 Python 前端。客户端后端使用内部低级调度程序,该调度程序封装了与启动、终止和重新生成在无服务器 Lambda 上运行的任务相关的所有逻辑。这个调度程序还跟踪所有任务的状态。
  • 第三,该框架提供一个轻量级 Runtime,它封装了系统支持的不同计算之间共享的所有函数,从而简化了新算法的开发。Worker runtime 提供两个接口。首先,它提供了一个智能迭代器来训练存储在 S3 中的数据集。这个迭代器在 Lambda 的本地内存中预取和缓冲 mini-batch,与 worker 的计算并行,以减轻访问 S3 的高延迟(>10ms)。它为分布式数据存储提供了一个 API。
  • 最后,该框架为 workers 之间的中间数据和通信提供具有丰富接口的共享存储。此接口有两种类型的 API:(1)用于一般消息传递、中间数据存储和数据缩减的键值存储,以及(2)参数服务器接口。为了达到所需的低延迟,将该数据存储部署在云 VMs 上。为了有效地利用稀缺的网络资源,对数据存储接口进行优化处理,例如:数据压缩、稀疏数据结构、异步通信等。

为了实现简化机器学习工作流执行的目标,理想的系统应该提供一个简单但足够通用的 API。这个 API 需要让用户在一个单一的、集成的框架内执行 ML 任务,例如:(1)数据集加载,支持常用的数据格式,(2)数据预处理,(3)模型训练,(4)大规模的超参数调整。

作者给出了一个例子来展示这个 API 的功能——图 1 中给出基于 Criteo Kaggle 竞争开发模型的过程,该模型用于预测用户点击显示广告数据集的广告的概率。工作流的第一步是加载数据集并将其上载到 Amazon S3。例如,用户可以调用 load_libsvm 方法来加载以 LIBSVM 格式存储的数据集,解析数据后自动为其创建分区,然后将其上载到 Amazon S3。第二步,一旦数据加载到 Amazon S3 中,就可以立即进行预处理。系统应该提供一些开发人员常用的预处理方法。例如,用户可以通过使用 Amazon S3 中数据集的路径调用 normalize 函数来规范化数据集。一旦加载了数据,用户就可以通过查看系统的测试损失来训练不同的模型并查看它们的性能。一旦用户对某个特定的模型获得了合理的损失,他们就可以通过超参数搜索对其进行微调。此外,作者设想这样一个系统允许用户在每个阶段的执行过程中进行多次交互。例如,当超参数搜索任务正在运行时,用户应该能够监视每个单独实验的测试损失。对于表现不好的实验(例如,测试损失发散(test loss is diverging)),用户应该能够终止它们。这个特性可以通过交互环境(比如 Jupyter)中的用户界面来实现。

无服务器计算的机器学习,出路在哪里?(一)

图 1. API 示例。无服务器 ML 的 API 应该支持 ML 开发工作流的不同阶段:(a)预处理,(b)训练,和(c)超参数调优。

为了评估对 ML 无服务器框架的需求,作者引入两个框架进行性能比对:PyWren[3]和 Bosen[4]。PyWren 是一个专门用于无服务器架构的 Map-reduce 框架。PyWren 提供了可缩放到数千个 workers 的 map 和 reduce 原语。Bosen 是一个分布参数框架,专门用于基于 VM 的 ML 算法。为了进行评估,作者在 PyWren 上实现了一个异步 SGD 训练算法。在 PyWren 基线实现的基础上,作者还进行了一组优化。作者使用了来自 Criteo Kaggle 竞赛的 Criteo 展示广告数据集进行实验。作者在 10 个最大的 AWS Lambda(3GB 内存)上运行 PyWren,在单个 VM(m5.2xlarge1)中的 8 个内核上运行 Bosen。

作者通过记录随时间变化的测试损失来测量这两个系统的性能(图 2)。对于 PyWren,作者在实现每个优化之后报告这个值。作者累计实现了以下优化:(1)跨迭代重用 Lambda;(2)使用异步 SGD 进行小批量预取;(3)使用低延迟存储(Redis)代替 Amazon S3;(4)使用具有多 get 操作的稀疏数据传输。我们观察到这些优化显著改善了 Pyren 在 600 秒后实现的最终测试损失(从 0.61 到 0.57)。尽管有了这些改进,PyWren 仍然比 Bosen 慢得多。进一步的性能分析表明,PyWren 存在一些开销,例如序列化 / 反序列化数据,以及使用接口不适合 ML 工作负载的远程存储(例如 Redis 或 S3)。这一结果表明,在设计无服务器计算框架的早期,需要仔细考虑 ML 工作负载的性能需求。

无服务器计算的机器学习,出路在哪里?(一)

图 2. PyWren 和 Bosen 在 Criteo-Kaggle 逻辑回归任务中的表现。PyWren 基线通过重用 Lambda、添加预取、切换到异步计算、用更高性能的 Redis 存储后端替换 S3 以及支持在单个 RPC 上获取多个密钥而得到了增量改进。

此外,作者还构建了本文所提出的框架的原型,包括:(1)具有参数服务器接口的高性能数据存储,(2)mini-batch 数据的循环缓冲区预取,(3)逻辑回归 SGD 训练算法。为了充分验证这种设计的好处,作者在相同的逻辑回归任务中对其进行了评估。作者测量了每个 worker 的平均 SGD 迭代时间(见图 3)。这个时间是 worker 性能的一个指标;较低的迭代时间意味着更频繁的模型更新和更快的收敛。作者还将这一次的 SGD 算法分解为四个主要步骤:(1)从数据存储中获取最新模型,(2)从远程存储(例如 S3)中获取一个 minibatch,(3)计算 SGD 梯度,以及(4)将梯度发送到数据存储。作者发现,尽管无服务器计算具有固有的开销,本文所提出的框架原型还是实现了较低的每次迭代时间( 500 μs)--- 与 Bosen 这样的系统不相上下。这种性能源于两种机制:(1)远程 mini-batch 的有效预取和缓冲,以及(2)尽可能与数据存储通信。首先,minibatch 预取机制通过与计算并行进行,有效地掩盖了从 S3 获取 minibatch 所需的时间。实际上,对于中型 / 大型 Lambda,在新的 minibatch 上开始计算所需的时间可以忽略不计,因为大多数情况下,这些数据都是在 worker 需要之前缓存在内存中的。即使从 S3 获取一个 mini-batch 需要 10ms 也是这样的。其次,作者发现与数据存储的通信是有效的(例如,发送梯度的时间可以忽略不计)。由于能够与数据存储异步通信,进一步提升了该框架的性能。

无服务器计算的机器学习,出路在哪里?(一)

图 3. 本文所提出原型每次 SGD 迭代的时间。具体细分为四个主要步骤:(1)将梯度发送到数据存储,(2)计算梯度,(3)从数据存储获取模型,(4)从 S3 获取 minibatch。