精通-AWS-机器学习-三-

65 阅读28分钟

精通 AWS 机器学习(三)

原文:annas-archive.org/md5/837d819f8bef8ba992e2d85e9b0ed5bc

译者:飞龙

协议:CC BY-NC-SA 4.0

第十五章:为机器学习调整集群

许多数据科学家和机器学习实践者在尝试在大数据上运行 ML 数据管道时都会遇到规模问题。在本章中,我们将主要关注弹性 MapReduceEMR),这是一个运行非常大的机器学习作业的非常强大的工具。配置 EMR 有许多方法,并不是每个设置都适用于每个场景。在本章中,我们将概述 EMR 的主要配置以及每种配置如何针对不同的目标工作。此外,我们将介绍 AWS Glue 作为我们的大数据管道结果编目工具。

在本章中,我们将涵盖以下主题:

  • EMR 架构简介

  • 为不同应用调整 EMR

  • 使用 Glue 管理数据管道

EMR 架构简介

在第四章 使用基于树的预测用户行为 中,我们介绍了 EMR,这是一个 AWS 服务,允许我们运行和扩展 Apache Spark、Hadoop、HBase、Presto、Hive 和其他大数据框架。这些大数据框架通常需要运行特定软件的机器集群,这些机器配置正确,以便机器能够相互通信。让我们看看 EMR 中最常用的产品。

Apache Hadoop

许多应用程序,例如 Spark 和 HBase,都需要 Hadoop。Hadoop 的基本安装包含两个主要服务:

  • Hadoop 分布式文件系统HDFS):这是一个允许我们在多个服务器上存储大量数据(例如,无法存储在单个机器上的文件)的服务。NameNode 服务器负责索引哪个文件的哪些块存储在哪个服务器上。每个文件的块在集群中复制,这样如果一台机器出现故障,我们不会丢失任何信息。DataNode 服务器负责在每个机器上保持和提供数据。许多其他 EMR 服务,如 Apache HBase、Presto 和 Apache Spark,能够使用 HDFS 来读取和写入数据。当您使用长期运行的集群时,HDFS 运行良好。对于仅为了执行单个作业(例如训练作业)而启动的集群,您应考虑使用 S3 进行数据存储。

  • MapReduce:这个框架多年来一直是大数据处理的基础。通过允许用户指定两个函数(一个 map 函数和一个 reduce 函数),许多大数据工作负载得以实现。map 函数负责将数据块取出来并以一对一的方式进行转换(例如,获取每笔交易的价格)。reduce 函数接收 map 函数的输出并以某种方式聚合它(例如,找出每个地区的平均交易价格)。MapReduce 被设计成在存储 HDFS 文件块的同一台机器上执行处理,以避免在网络中传输大量数据。这种数据本地性原则被证明对于在通用硬件上运行大数据作业以及有限的网络速度下运行大数据作业非常有效。

EMR 允许您创建包含三种类型节点的集群:

  • 主节点:这是集群中唯一的节点,通常负责协调集群中其他节点的作业。

  • 核心节点:这类节点将托管 HDFS 块并运行 DataNode 服务器,因此在这些节点上运行的作业可以利用数据本地性。

  • 任务节点:这些节点不托管 HDFS 块,但可以运行任意作业任务。在这些节点上运行的作业将需要从其他机器上托管(例如,核心节点或 S3 服务器)的文件系统中读取数据。

Apache Spark

Apache Spark 是最受欢迎的大数据框架之一。它通过允许用户在数据之上指定额外的函数来扩展 MapReduce 的概念。它不仅可以执行 map 和 reduce 函数,还支持过滤、分组、连接、窗口函数以及许多其他操作。此外,正如我们在整本书中看到的那样,我们可以使用 SQL 操作来执行 ETL 和分析。Apache Spark 被设计用来在内存中缓存大量数据以加速需要多次遍历数据的算法。例如,需要多次迭代 梯度下降 的算法如果数据集在内存中缓存,可以运行得快得多。

Apache Spark 还附带了一些非常实用的库,用于流处理、图操作以及我们在本书中使用的机器学习库。我们鼓励你探索这些额外的库,因为它们质量极高且非常有用。Spark 的独特之处在于它无缝地整合了许多成熟的库,例如 TensorFlow 和 scikit-learn。你可以使用这两个工具构建出色的模型,但它们目前不允许我们像 Spark 那样通过在集群中并行化工作来读取和准备数据。换句话说,Apache Spark 提供了从数据摄入到模型生成的完整堆栈的包。有些人将 Spark 称为大数据的操作系统。通常,数据科学家和工程师使用 Spark 进行大规模的数据准备,然后使用其他工具,如 TensorFlow 和 SageMaker 来构建和部署专门的模型。在 第五章 中,我们看到了如何通过使用 SageMaker Spark 估计器来平滑地整合 Apache Spark 和 SageMaker。

Apache Hive

Apache Hive 最初作为一个从 SQL 到 MapReduce 作业的翻译器诞生。你可以指定 数据定义语言 (DDL) 和 数据操作语言 (DML) 语句,并像使用 Apache Hive 一样在标准数据库管理系统上工作。当 Hive 首次出现时,许多了解 SQL 的非技术用户能够进行大规模的分析,这是其受欢迎的原因之一。Hive(以及 Spark SQL)内部发生的事情是,SQL 语句被解析,并动态构建一系列 MapReduce 作业,在集群上运行以执行 SQL 语句描述的声明性操作。

Presto

Presto 是由 Facebook 开发的一个产品,它也把 SQL 转换为大数据工作负载,但专为交互式分析而定制。它非常快,并且特别针对当你有一个大型事实表和几个小维度表(如交易和其他连接表,如产品和客户)时进行了优化。AWS 提供了一个基于 Presto 的无服务器替代方案,称为 Athena,当你的数据在 S3 上时,它非常出色。Athena 查询的收费基于扫描的数据量。因此,它已成为大数据分析中非常受欢迎的工具。

Apache HBase

HBase 是一个类似于 Google Bigtable 的产品。从概念上讲,它可以被视为一个巨大的分布式键值存储。由于 AWS DynamoDB 等技术的出现,HBase 的受欢迎程度已经不再那么高,后者是无服务器的,根据我们的经验,更加可靠。然而,当你需要通过键访问数据时,它可能是一个成本效益高的存储数据的方式。例如,你可以使用 HBase 存储每个用户的自定义模型(假设你有数十亿用户来证明这一点)。

另一个资源协商者

Apache Hadoop 还开发了另一个资源协调器YARN),这是 EMR 调度和协调不同应用的基本工具。YARN 实际上是 EMR 背后的集群管理器,负责在不同的机器上启动必要的守护进程。当你通过 EMR 配置集群时,你可以指定你想要运行的不同应用。这类应用的例子包括 Spark、HBase 和 Presto。YARN 负责启动必要的进程。在 Spark 的情况下,YARN 将根据需要启动 Spark 执行器和驱动器。这些进程将必要的内存和 CPU 消耗报告给 YARN。这样,YARN 可以确保集群负载得到适当管理,不会过载。

调整 EMR 以适应不同的应用

在本节中,我们将考虑调整我们用于机器学习的集群所涉及到的方面。当你启动一个 EMR 集群时,你可以指定你想要运行的不同应用。

以下截图显示了 EMR 版本 5.23.0 中可用的应用:

在启动 EMR 集群后,以下是需要配置的最相关项目:

  • 应用: 例如 Spark 应用。

  • 硬件: 我们在第十章中介绍了这一点,在 AWS 上创建集群

  • 使用 Glue 数据目录: 我们将在本章的最后部分介绍,使用 Glue 管理数据管道)。

  • 软件配置: 这些是我们可以指定以配置特定应用属性的属性。在下一节,配置应用属性中,我们将展示如何通过特定属性来定制 Spark 的行为。

  • 引导操作: 这些是用户特定的脚本(通常位于 S3),在集群启动时会运行在每个节点上。引导操作在例如你希望在集群启动时在所有机器上安装特定软件包时非常有用。

  • 步骤: 这些是在应用启动后用户想要运行的不同作业。例如,如果我们想要启动一个运行 Spark 训练作业的集群,然后我们想要关闭集群,我们就会指定一个 Spark 作业步骤,并在最后一步完成后选择自动终止集群选项。这种用例在通过 AWS API 程序化启动集群时是相关的。计划或事件驱动的 AWS Lambda 函数可以使用boto3等库在事件发生或定期计划时程序化地启动集群。有关 AWS Lambda 的更多信息,请参阅docs.aws.amazon.com/lambda/

配置应用属性

在前面的屏幕截图中,您可能已经注意到有一个名为 软件设置 的空间,用于自定义不同应用程序的配置。有不同的配置类别,称为 分类,允许您通过更改所选属性集的值来覆盖不同应用程序的默认配置。

在以下代码块中,我们提供了一组非常有用的属性来配置 Spark,用于两个目的:最大化资源分配并启用 AWS Glue 元数据存储:

classification=spark,properties=[maximizeResourceAllocation=true]
classification=spark-defaults,properties=[spark.sql.catalogImplementation=hive]
classification=spark-hive-site,properties=[hive.metastore.connect.retries=50,hive.metastore.client.factory.class=com.amazonaws.glue.catalog.metastore.AWSGlueDataCatalogHiveClientFactory]

让我们看看每个这些配置的效果。

最大化资源分配

当您启用 maximizeResourceAllocation 时,EMR 和 Spark 将确定如何配置 Spark 以使用所有可用资源(例如,内存和 CPU)。另一种选择是手动配置属性,如执行器的数量、每个执行器的 Java 堆空间以及每个执行器的核心数(即线程数)。如果您选择手动进行此操作,您需要非常小心,不要超过集群的可用资源(并且也不要未充分利用可用硬件)。我们建议始终默认设置此设置。

AWS Glue 目录

AWS Glue 提供了一种称为 Hive 元数据存储的服务。此服务的目的是通过定义描述数据的表来跟踪我们数据湖中的所有数据。数据湖通常托管在 S3 或 HDFS 上。任何位于这些分布式文件系统上的数据,且具有表格格式,如 Parquet 或 CSV,都可以添加到元数据存储中。这不会复制或移动数据;它只是保持所有数据目录的一种方式。通过在集群配置中配置 hive.metastore.client.factory.class 属性,我们允许 Spark 使用 Glue 目录中注册的所有表。此外,Spark 还可以通过 Spark SQL 语句创建新表或修改目录。在下一节中,我们将展示 Glue 如何有用的具体示例。

使用 Glue 管理数据管道

数据科学家和数据工程师运行不同的作业来转换、提取和加载数据到系统,如 S3。例如,我们可能有一个每日作业处理文本数据,并存储一个包含我们第二章中看到的词袋表示法的表,即 使用朴素贝叶斯分类 Twitter 流。我们可能希望每天更新该表以指向最新的可用数据。上游过程可以仅依赖于表名来查找和处理数据的最新版本。如果我们没有正确地编目这些数据,将非常难以合并不同的数据源,甚至不知道数据在哪里,这就是 AWS Glue 元数据存储发挥作用的地方。Glue 中的表被分组到数据库中。然而,不同数据库中的表可以连接和引用。

使用 Glue 创建表

您可以通过访问console.aws.amazon.com/glue/home?region=us-east-1#catalog:tab=databases来访问 AWS 上的 Glue 控制台。

在控制台中,创建一个新的数据库,如下面的屏幕截图所示:

图片

一旦创建了数据库,您就可以切换到 Athena AWS 服务,并开始从 S3 中的数据创建表以运行查询分析。AWS Athena 控制台可以通过console.aws.amazon.com/athena/home访问。

让我们在 S3 中为我们在第三章,“使用回归算法预测房价”中工作的波士顿房价数据集创建一个表。

在下面的屏幕截图中,我们可以看到创建表的 SQL 语句将指定来自 S3 中 CSV 数据的表名、格式和字段:

图片

注意,位置指定了一个文件夹(而不是一个文件)。在我们的例子中,我们在s3://mastering-ml-aws/chapter3/linearmodels/train/training-housing.csv有一个单独的CSV文件夹。然而,我们可以在同一个文件夹中有许多 CSV 文件,并且所有这些都会链接到我们刚刚创建的house_prices表。一旦我们创建了表,由于数据在 S3 上,我们就可以开始如下查询我们的表:

图片

注意数据是如何正确分表的。这是因为我们告诉 Glue 了数据的正确格式和位置。现在我们可以通过 Athena 使用 Presto-as-a-service 通过 SQL 进行超快速分析。

我们刚刚执行了一个创建表的操作;然而,通常我们想要执行更改表命令来将表背后的数据切换到更近的版本。执行添加分区操作以增量地向表中添加数据(如新批次或日期)也是非常常见的。分区也有助于查询引擎更有效地过滤数据。

在 Spark 中访问 Glue 表

一旦创建的表在 Glue 中,它也将可在每个 EMR Spark 集群中可用(只要我们配置了前述章节中描述的 hive.metastore.client.factory.class,即 调整 EMR 以适应不同应用)。让我们启动一个启用了 JupyterHub 应用的 EMR 集群。JupyterHub 应用是 第二章,使用朴素贝叶斯分类 Twitter 流,到 第六章,分析访问模式以生成推荐 中使用的 EMR 笔记本功能的替代品。当您有一组数据科学家在重用同一集群并运行不同的笔记本时,请考虑使用 JupyterHub。您可以在 docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-jupyterhub.html 上了解更多关于 JupyterHub 的信息。

以下截图显示了启用 Glue 元数据存储和 JupyterHub 作为应用的我们创建的集群:

图片

如果您点击 JupyterHub 链接,它将带您到一个认证页面,如下所示:

图片

JupyterHub 的默认配置有一个默认用户账户,用户名为 jovyan,密码为 jupyter。如果需要,可以通过 EMR 配置自定义认证。

认证后,我们可以像使用 EMR 笔记本一样开始创建笔记本。在这种情况下,我们将创建一个 PySpark3 笔记本:

图片

现在,笔记本可以使用 SparkMagic 在 Python 和 SQL 中交错段落。让我们看看以下笔记本示例:

图片

第一段通过 Glue/Athena 通过 SparkMagic 的 %%sql 魔法在刚刚创建的表上运行 SQL(有关 SparkMagic 的更多信息,请参阅 github.com/jupyter-incubator/sparkmagic)。第二段通过一个简单的 SQL 语句从我们的表中选择两个字段来构建 Spark DataFrame。第三段在我们的 Spark DataFrame 上运行 Spark 作业(即 describe 命令)。您将欣赏到,一旦我们在 Glue 元数据存储中正确编目了数据,处理、集成和处理数据是多么容易。

摘要

在本章中,我们探讨了 EMR 的主要配置参数以及它们如何帮助我们运行许多大数据框架,如 Spark、Hive 和 Presto。我们还探讨了 AWS 服务 Athena 和 Glue,作为在数据湖中编目数据的方式,以便我们能够正确同步我们的数据管道。最后,我们展示了 Glue 如何在 EMR 中使用,以及 JupyterHub 与 SparkMagic 的无缝集成。

在下一章* 在 AWS 中构建的模型部署*中,我们将介绍如何在不同的环境中部署机器学习模型。

第十六章:在 AWS 中构建的模型部署

到目前为止,我们在 AWS 中构建了模型,并希望将它们部署到生产环境中。我们知道模型应该部署在不同的环境中。在某些情况下,这就像生成一个 CSV 文件,其中包含将被输入到某个系统中的操作一样简单。通常我们只需要部署一个能够进行预测的 Web 服务。然而,在某些特殊情况下,我们需要将这些模型部署到复杂、低延迟或边缘系统中。在本章中,我们将探讨将机器学习模型部署到生产环境的不同方法。

在本章中,我们将涵盖以下主题:

  • SageMaker 模型部署

  • Apache Spark 模型部署

SageMaker 模型部署

在第二章,使用朴素贝叶斯分类 Twitter 流,我们使用 SageMaker 部署了我们的第一个模型。在那个阶段,我们已经使用BlazingText训练了我们的分类器,并将其存储在一个名为bt_model的变量中。要部署模型,我们只需调用deploy方法,并指定要使用的机器数量和类型:

bt_model.deploy(initial_instance_count = 1,instance_type = 'ml.m4.xlarge')

SageMaker 可以在实例数量之间平衡对端点的请求,并根据服务负载自动扩展或缩减。详细信息请参阅docs.aws.amazon.com/sagemaker/latest/dg/endpoint-auto-scaling.html

一旦我们调用deploy方法,一个端点应该出现在 AWS SageMaker 控制台中的console.aws.amazon.com/sagemaker。以下截图显示了我们的 BlazingText 示例端点:

通过在控制台中点击端点,我们可以找到更多详细信息:

特别是,我们可以看到端点有一个特定的 URL,服务就在那里托管。如果我们尝试通过 HTTP 工具,如curl直接调用此 URL,我们会得到以下结果:

curl -X POST \
> https://runtime.sagemaker.us-east-1.amazonaws.com/endpoints/blazingtext-endpoint-2019-01-04-01/invocations \
> -H 'cache-control: no-cache' \
> -H 'content-type: application/json' \
> -H 'postman-token: 7hsjkse-f24f-221e-efc9-af4c654d677a' \
> -d '{"instances": ["This new deal will be the most modern, up-to-date, and balanced trade agreement in the history of our country, with the most advanced protections for workers ever developed"]}'

{"message":"Missing Authentication Token"}

这是因为向 SageMaker 端点发出的每个请求都必须正确签名以确保身份验证。只有具有调用 Amazon SageMaker InvokeEndpoint API 角色权限的用户才能允许调用 SageMaker 端点。为了让 SageMaker 背后的 HTTP 服务能够识别和验证调用者,HTTP 请求需要正确签名。有关签名请求的更多信息,请参阅docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html。如果我们想公开我们的模型端点,请求签名的替代方案是创建一个 AWS 中的 lambda 函数,并通过 API 网关公开它。有关如何做到这一点的更多信息,请参阅docs.aws.amazon.com/sagemaker/latest/dg/getting-started-client-app.html

幸运的是,如果我们从 AWS 实例内部调用端点,我们可以通过使用sagemaker库来避免手动签名请求。让我们回顾一下如何进行此类调用。

如同往常,我们首先导入必要的 Python 库:

import sagemaker
from sagemaker import get_execution_role

sess = sagemaker.Session()
role = get_execution_role()

接下来,如果我们知道端点的名称,我们可以创建一个RealTimePredictor实例来进行实时预测:

from sagemaker.predictor import json_serializer, RealTimePredictor

predictor = RealTimePredictor(endpoint='blazingtext-endpoint-2019-01-04-01', serializer=json_serializer)

在这个例子中,我们使用的是json_serializer,这是一种方便且易于阅读的格式。要调用端点,我们只需调用predict()方法:

predictor.predict({"instances": ["This new deal will be the most modern, up-to-date, and balanced trade agreement in the history of our country, with the most advanced protections for workers ever developed"]})

这里是输出:

b'[{"prob": [0.5000401735305786], "label": ["__label__1"]}]'

你可以回到第二章,使用朴素贝叶斯分类 Twitter 流,来解释这个输出,但这里的重要点是RealTimePredictor实例代表我们完成了所有适当的身份验证、请求签名和端点调用。

除了端点的 URL 和基本信息外,AWS 控制台还显示了端点配置:

图片

通过配置,我们可以跟踪从这个端点起源的模型和训练作业。让我们点击链接来检查起源模型。然后我们得到以下屏幕:

图片

在模型描述中,我们可以找到诸如模型的 S3 位置等详细信息。这种模型序列化针对每种类型的模型都是特定的。在第四章使用基于树的预测用户行为中,我们看到了这种模型的格式方便地采用了xgboost pickle 序列化兼容格式。

你可能也注意到了,这个模型关联了一个图像。SageMaker 在 Amazon 弹性容器注册库ECR)中创建了一个托管此模型的机器的镜像。通常这些是底层的 Docker 镜像。

以下链接是一个关于部署内部工作原理以及 SageMaker 中容器化工作方式的优秀资源:sagemaker-workshop.com/custom/containers.html

Apache Spark 模型部署

Apache Spark 没有像 SageMaker 那样提供直接将模型作为端点暴露的现成方法。然而,有简单的方法可以使用 Spark ML 包的序列化和反序列化功能在标准网络服务上加载 Spark 模型。在本节中,我们将展示如何部署我们在第三章中创建的模型,即使用回归算法预测房屋价值,通过一个简单的端点提供预测。为此,我们将保存一个训练好的模型到磁盘,以便我们可以将该模型发送到通过端点提供模型的机器。

我们首先开始训练我们的模型。在第三章中,使用回归算法预测房屋价值,我们将房屋数据加载到一个 dataframe 中:

housing_df = sql.read.csv(SRC_PATH + 'train.csv', 
                          header=True, inferSchema=True)

为了简化这个例子,我们将使用一组减少的特征来构建一个作为端点公开的模型。在所有特征中,我们将只选择三个训练特征(crimznindus):

reduced_housing_df = housing_df.select(['crim', 'zn', 'indus', 'medv'])

你可能还记得medv是实际房屋价值(这是我们试图预测的值)。现在我们有了我们的 dataframe,我们可以创建一个pipeline,就像我们之前做的那样:

from pyspark.ml import Pipeline
from pyspark.ml.regression import LinearRegression
from pyspark.ml.feature import VectorAssembler

training_features = ['crim', 'zn', 'indus']
vector_assembler = VectorAssembler(inputCols=training_features,           
               outputCol="features")
linear = LinearRegression(featuresCol="features", labelCol="medv")
pipeline = Pipeline(stages=[vector_assembler, linear])
model = pipeline.fit(reduced_housing_df)

使用模型实例,我们可以通过调用save()方法将其保存到磁盘:

model.save("file:///tmp/linear-model")

这种序列化模型表示可以发送到我们想要提供预测的位置(例如,一个网络服务器)。在这种情况下,我们可以通过调用PipelineModel.load()静态方法来重新加载模型,如下所示:

from pyspark.ml import PipelineModel
loaded_model = PipelineModel.load('/tmp/linear-model')

让我们使用这个模型来获取我们减少的数据集的前几行的预测:

loaded_model.transform(reduced_housing_df.limit(3)).show()

前述命令的输出如下:

+-------+----+-----+----+-------------------+------------------+
| crim  | zn |indus|medv| features          | prediction       |
+-------+----+-----+----+-------------------+------------------+
|0.00632|18.0| 2.31|24.0|[0.00632,18.0,2.31]|27.714445239256854|
|0.02731| 0.0| 7.07|21.6| [0.02731,0.0,7.07]|24.859566163416336|
|0.03237| 0.0| 2.18|33.4| [0.03237,0.0,2.18]| 26.74953947801712|
+-------+----+-----+----+-------------------+------------------+

看看pipeline模型是如何从原始 CSV 文件开始,应用管道中的所有转换步骤,最终完成预测。当然,从我们的训练数据集中获取预测并不那么有趣。从现实的角度来看,在提供预测的端点上,我们希望接收我们三个特征的所有可能值并获得预测。在撰写本文时,Apache Spark 只能根据 dataframe 获取预测。因此,每次我们想要为几个值获取预测时,我们需要构建一个 dataframe,即使我们只需要找到单行数据的预测。

假设我们想要找到以下特征组合的预测:crim=0.00632zn=18.0indus=2.31。第一步是定义我们特征的架构,因为 Spark 期望 dataframe 的格式与训练时使用的格式完全相同。

我们定义了以下模式:

from pyspark.sql.types import *

schema = StructType([StructField('crim', DoubleType(), True),
                    StructField('zn', DoubleType(), True),
                    StructField('indus', DoubleType(), True)])

在前面的模式定义中,我们放置了每个字段的名称和类型。有了这个模式,我们可以构建一个包含我们感兴趣的特征值的单行 dataframe:

from pyspark.sql import Row

predict_df = 
sql.createDataFrame([Row
(crim=0.00632, zn=18.0,
indus=2.31)],
schema=schema)

这就是 dataframe 的样式:

+-------+----+-----+
| crim  | zn |indus|
+-------+----+-----+
|0.00632|18.0| 2.31|
+-------+----+-----+

使用这个简短的 dataframe 和加载的模型,我们可以为我们任意特征获得预测:

loaded_model.transform(predict_df).show()

以下是前一个命令的输出:

+-------+----+-----+-------------------+------------------+
| crim  | zn |indus| features          |        prediction|
+-------+----+-----+-------------------+------------------+
|0.00632|18.0| 2.31|[0.00632,18.0,2.31]|27.714445239256854|
+-------+----+-----+-------------------+------------------+

因此,考虑到前面的想法,我们如何构建一个能够提供这个模型的服务端点?最简单的方法是使用允许我们轻松在任何选择的机器上暴露端点的包,例如 Flask。有关 Flask 的详细信息,请参阅flask.pocoo.org。要运行一个 flask 网络服务,我们只需编写一个 Python 文件,该文件知道如何响应不同的端点请求。在我们的案例中,我们将只创建一个端点,根据我们三个特征的值提供预测。我们将实现一个简单的GET端点,其中三个特征将作为 URL 参数传递。

在本地主机上运行时调用服务的命令如下:

curl 'http://127.0.0.1:5000/predict?crim=0.00632&zn=18.0&indus=2.31'

这是服务的输出:

27.71

要在机器上启动 flask 服务,执行以下三个步骤:

  1. 创建一个 Python 文件,指定如何响应端点。我们将把这个文件命名为deploy_flask.py

  2. FLASK_APP环境变量设置为指向我们刚刚创建的 Python 文件。

  3. 运行flask run命令。

deploy_flask.py中,我们将关于如何加载模型和为预测构建 dataframe 的前面想法整合在一起:

from flask import Flask
from flask import request
from pyspark.ml import PipelineModel
from pyspark.sql import Row
from pyspark.sql.types import *
from pyspark.sql import SQLContext
from pyspark.context import SparkContext

sc = SparkContext('local', 'test')
sql = SQLContext(sc)
app = Flask(__name__)
loaded_model = PipelineModel.load('/tmp/linear-model')

schema = StructType([StructField('crim', DoubleType(), True),
                    StructField('zn', DoubleType(), True),
                    StructField('indus', DoubleType(), True)])

@app.route('/predict', methods=['GET'])
def predict():
   crim = float(request.args.get('crim'))
   zn = float(request.args.get('zn'))
   indus = float(request.args.get('indus'))
   predict_df = sql.createDataFrame([Row(crim=crim, zn=zn, indus=indus)],schema=schema)
   prediction = loaded_model.transform(predict_df).collect()[0].prediction
   return str(prediction)

deploy_flask.py文件中,唯一的新部分是 flask 应用的初始化和predict方法的定义,其中我们提取了作为 URL 参数授予的三个特征。接下来,我们设置提到的环境变量并运行服务:

export FLASK_APP=deploy_flask.py
flask run

在日志中,你可以看到服务和 Spark 的初始化过程,以及调用服务的操作:

* Serving Flask app "deploy_flask.py"
* Environment: production
  WARNING: Do not use the development server in a production environment.
  Use a production WSGI server instead.
* Debug mode: off
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Setting default log level to "WARN".
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [13/Apr/2019 19:13:03] "GET /predict?crim=0.00632&zn=18.0&indus=2.31 HTTP/1.1" 200 -

如 flask 日志所述,如果你在考虑严肃的生产负载,考虑在 WSGI 服务器后面运行 flask。更多关于这方面的信息可以在 flask 文档中找到。

SageMaker 也能够托管任何任意模型。为此,我们需要创建一个响应两个端点的 Docker 镜像:/ping/invocations。就这么简单。在我们的案例中,/invocations端点将使用加载的 Spark 模型来响应预测。一旦 Docker 镜像创建完成,我们需要将其上传到 AWS ECR。一旦它被加载到 ECR 上,我们只需提供 ECR 镜像标识符就可以创建一个 SageMaker 模型。

在 AWS 控制台(或通过 API)中,选择创建模型:

图片

一旦你提供了基本模型详情,输入你自定义推理端点的 ECR 位置:

图片

与任何 SageMaker 模型一样,您可以使用常规方法将其部署到端点。在本章中,我们不会介绍 Docker 镜像创建的过程,但您可以在我们的 GitHub 仓库中找到笔记本,网址为 github.com/mg-um/mastering-ml-on-aws,在 第十六章,在 AWS 中构建的模型部署,其中解释了如何进行部署。

即使您的生产环境不在 AWS 内,SageMaker 和 EMR 中的 Spark 也可以非常有用,因为模型可以在 AWS 离线训练并发送到不同的环境。此外,AWS 创建的模型工件通常可以离线获取和使用(例如 xgboost 模型)。如果您需要将 Spark ML 模型移植到无法实例化本地 Spark 会话的环境或需要一个非常低延迟的预测器,请考虑使用以下工具:github.com/TrueCar/mleap

摘要

在本章中,我们探讨了如何通过 SageMaker 部署模型,并介绍了端点的定义和调用方法。通过使用 Spark 的模型序列化和反序列化,我们展示了模型如何被发送到其他环境,例如 flask 中的自定义 Web 服务实现。最后,我们概述了如何通过在 AWS ECR 中注册自定义 Docker 镜像,将您的 Spark 模型(或任何其他任意模型)通过 SageMaker 提供服务。

练习

  1. 为什么当您尝试直接访问服务时,SageMaker 端点会响应一个缺少身份验证令牌的消息?

  2. 列出两种解决上述问题的替代方案。

  3. 提供两种将基于 Apache Spark 构建的模型部署到端点的方法。

  4. 以我们的 flask 示例为基础,构建一个 Docker 镜像,该镜像提供 /invocations/ping 端点,然后通过 SageMaker 部署一个模型。

附录:AWS 入门

由于我们将重点关注 AWS 上的机器学习,因此您在尚未创建账户的情况下,通过创建账户开始使用 AWS 非常重要。请访问portal.aws.amazon.com/billing/signup#/start。您需要提供一些信用卡信息,但只有在您有效使用不同服务后才会被收费。考虑许多服务都有免费层,您可以免费开始使用。一旦您注册,下一步就是在平台上创建一个用户,您将使用该用户进行程序性访问。

导航到console.aws.amazon.com/iam/home并创建用户:

图片

一旦您创建了用户,请授予一些权限(在我们的例子中,我们将授予完全访问权限):

图片

您可以选择设置标签以更好地跟踪成本,如果您有多个用户的话,但在这本书中我们不会重点讨论这一点。一旦您创建了用户,您就可以导航到该用户并创建密钥:

一旦您生成了这些密钥,您可以将它们存储在您的机器上的 ~/.aws/credentials 中,如docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html中所述。通过将凭证存储在该文件中,您在机器上运行的代码将知道如何与 AWS 进行身份验证。