亚马逊机器学习高效指南-全-

亚马逊机器学习高效指南(全)

原文:annas-archive.org/md5/6b6457e6a024266a78a39c9504226f74

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

大数据和人工智能是我们日常生活中的普遍部分,催生了蓬勃发展的、价值数十亿美元的基于云的机器学习即服务(MLaaS)行业。

在目前市场上众多的机器学习即服务(Machine Learning as a Service, MLaaS)平台中,亚马逊机器学习因其简单性而脱颖而出。亚马逊机器学习于 2015 年 4 月推出,其明确的目标是通过提供一种无需高度技术资源的公司也能访问的服务,降低预测分析门槛,同时平衡性能和成本。当与 AWS 生态系统的深度结合时,亚马逊机器学习平台使预测分析成为业务数据管道的自然元素。

本书遵循亚马逊机器学习的简化方法,有两个目标:一是为您提供使用该服务充分发挥其潜力的核心数据科学知识,二是为您提供构建以预测分析为中心的完整数据管道的钥匙,有效地使预测分析成为您的数据驱动应用程序的驱动因素。

本书涵盖内容

第一章,机器学习和预测分析简介,这一章是对亚马逊机器学习服务及其可以解决的预测分析问题类型的一般介绍。我们展示了该服务如何使用简单的线性模型来解决回归和分类问题,并介绍了使用亚马逊机器学习进行成功预测的背景。

第二章,机器学习定义和概念,本章解释了使用亚马逊机器学习服务以及全面理解其工作原理所需的机器学习概念。处理原始数据时使用了哪些准备技术?我们如何评估预测模型?有哪些策略可以用来补救较差的预测性能?

第三章,亚马逊机器学习工作流程概述,本章是对一个简单的亚马逊机器学习项目的概述。读者将学习如何在亚马逊机器学习平台上开始,如何设置账户并确保其安全。我们通过一个基于经典数据集的简单数值预测问题进行说明。我们描述了如何准备数据、训练和选择模型以及进行预测。

第四章,加载数据集和准备数据,亚马逊机器学习提供了强大的功能,通过食谱来转换数据。使用一个经典数据集,我们在 S3 上上传数据,实现交叉验证,创建模式,并检查可用的数据统计信息。然后,我们通过使用最近推出的 AWS SQL 服务 Athena,扩展亚马逊机器学习特征工程和数据清洗功能。

第五章,模型创建,在本章中,我们探讨了 Amazon ML 数据转换及其如何通过食谱应用。我们训练和调整模型,通过分析不同的预测指标来选择最佳模型。我们深入探讨了随机梯度下降算法,以及不同类型正则化的使用。最后,我们分析训练日志,以更好地理解在模型训练过程中 Amazon ML 内部发生了什么。

第六章,预测与性能,我们将我们新训练的模型应用于对先前未见数据的预测,并对它们的性能和鲁棒性进行最终评估。我们展示了如何在给定数据集上执行批量预测,以及如何设置实时端点以进行流式预测。

第七章,命令行和 SDK,使用 AWS 网络界面来管理和运行项目是耗时的。在本章中,我们远离网络界面,开始通过 AWS 命令行界面(AWS CLI)和 Python SDK(Boto3 库)通过命令行运行我们的项目。我们利用我们的新能力来实现交叉验证和递归特征选择。

第八章,从 Redshift 创建数据源,在本章中,我们将利用 SQL 查询的力量来处理非线性数据集。在 Redshift 中创建数据源为我们提供了在数据源创建之前基于 SQL 的特征工程潜力。我们探讨了如何将数据从 S3 上传到 Redshift,访问数据库,运行查询,并导出结果。

第九章,构建流式数据分析管道,在本书的最后一章中,我们通过与其他 AWS 服务的集成扩展了 Amazon ML 的功能。我们构建了一个功能齐全的流式数据处理流程,整合了 AWS Kinesis、Lambda、Redshift 和机器学习,以实现实时推文的分类。

您需要为本书准备什么

本书需要 AWS 账户,以及运行 Python 3(3.5)代码示例的能力。我们使用最新的 Anaconda 发行版(conda 4.3)。读者还应在终端中能够运行基本的 shell 命令。请注意,Amazon 机器学习服务不属于免费层 AWS 服务。

本书面向对象

本书面向希望构建现实世界人工智能应用的 Python 开发者。本书对 Python 初学者友好,但熟悉 Python 将有助于与代码互动。它也将对希望在其现有技术堆栈中使用人工智能技术的经验丰富的 Python 程序员有所帮助。

术语约定

在这本书中,您会发现许多不同的文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。

文本中的代码单词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称如下所示:"将原始重量估计设置为w_0 = 100g以初始化,并设置计数器。"

代码块设置如下:

# Create datasource for training
resource = name_id_generation('DS', 'training', trial)
print("Creating datasources for training (%s)"% resource['Name'] )

任何命令行输入或输出都如下所示:

$ aws s3 cp data/titanic.csv s3://aml.packt/data/ch9/

新术语重要词汇以粗体显示。屏幕上看到的单词,例如在菜单或对话框中,在文本中如下所示:"强化学习应用的例子包括AlphaGo,谷歌的世界冠军围棋算法,自动驾驶汽车和半自主机器人。”

警告或重要注意事项以如下框中的形式出现。

技巧和窍门如下所示。

读者反馈

我们始终欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢或不喜欢的地方。读者反馈对我们来说很重要,因为它帮助我们开发出您真正能从中获得最大收益的标题。

要向我们发送一般反馈,请简单地发送电子邮件至feedback@packtpub.com,并在邮件主题中提及书籍的标题。

如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在您已经是 Packt 图书的骄傲拥有者,我们有一些东西可以帮助您从您的购买中获得最大收益。

下载示例代码

您可以从您的账户中下载此书的示例代码文件www.packtpub.com。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接发送给您。

您可以通过以下步骤下载代码文件:

  1. 使用您的电子邮件地址和密码登录或注册我们的网站。

  2. 将鼠标指针悬停在顶部的 SUPPORT 标签上。

  3. 点击代码下载与勘误。

  4. 在搜索框中输入书籍名称。

  5. 选择您想要下载代码文件的书籍。

  6. 从下拉菜单中选择您购买此书的来源。

  7. 点击代码下载。

文件下载完成后,请确保使用最新版本的以下软件解压缩或提取文件夹:

  • WinRAR / 7-Zip(适用于 Windows)

  • Zipeg / iZip / UnRarX(适用于 Mac)

  • 7-Zip / PeaZip(适用于 Linux)

书籍的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Effective-Amazon-Machine-Learning。我们还有其他来自我们丰富图书和视频目录的代码包可供选择,网址为github.com/PacktPublishing/。请查看它们!

勘误

尽管我们已经尽一切努力确保我们内容的准确性,但错误仍然可能发生。如果您在我们的书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告它们。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题勘误部分的现有勘误列表中。

要查看之前提交的勘误,请访问www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将在勘误部分显示。

盗版

互联网上版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现任何形式的我们作品的非法副本,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。

请通过copyright@packtpub.com与我们联系,并提供涉嫌盗版材料的链接。

我们感谢您在保护我们作者和我们为您提供有价值内容的能力方面的帮助。

问题

如果您在这本书的任何方面遇到问题,您可以通过questions@packtpub.com与我们联系,我们将尽力解决问题。

第一章:机器学习和预测分析简介

随着人工智能和大数据已成为我们日常生活的普遍部分,基于云的机器学习服务已成为一个崛起的百亿美元产业。在市场上目前提供的多种服务中,亚马逊机器学习因其简单性而脱颖而出。亚马逊机器学习于 2015 年 4 月推出,其明确的目标是通过提供一项无需高度技术资源的公司即可访问的服务,降低预测分析的门槛。

这章是亚马逊机器学习服务的一般介绍,以及它可以解决的预测分析问题类型。亚马逊机器学习平台以其简单性和直接性而区别于其他平台。然而,简单性往往意味着做出了艰难的选择。我们解释了牺牲了什么,为什么这些选择是有意义的,以及如何通过 AWS 丰富的数据导向生态系统中的其他服务来扩展这种简单性。

我们探讨了亚马逊机器学习平台可以解决哪些类型的预测分析项目,以及它是如何使用简单的线性模型来解决回归和分类问题的。在开始一个预测分析项目之前,了解适当的上下文和构成良好结果的因素非常重要。我们介绍了使用亚马逊机器学习Amazon ML)进行成功预测的上下文。

读者将了解亚马逊机器学习可以解决哪些类型的问题,以及关于底层数据的假设。我们展示了亚马逊机器学习如何使用简单的线性模型解决线性回归和分类问题,以及为什么这样做是有意义的。最后,我们介绍了该平台的局限性。

本章讨论以下主题:

  • 机器学习即服务(MLaaS)是什么,为什么它很重要?

  • 亚马逊机器学习如何成功利用线性回归,一个简单而强大的模型

  • 预测分析是什么,它可以解决哪些类型的回归和分类问题?

  • 数据必须满足的必要条件,以获得可靠的预测

  • 亚马逊机器学习服务缺少了什么?

介绍亚马逊机器学习

在新兴的机器学习即服务(MLaaS)行业中,亚马逊机器学习在多个方面脱颖而出。其简单性与 AWS 生态系统的强大功能相结合,降低了公司进入机器学习的门槛,同时平衡了性能和成本。

机器学习即服务

亚马逊机器学习是亚马逊网络服务(Amazon Web ServicesAWS)提供的在线服务,用于进行预测分析的监督学习。

2015 年 4 月在 AWS 峰会上推出的 Amazon ML,加入了云计算机器学习服务日益增长的名单,例如 Microsoft Azure、Google 预测、IBM Watson、Prediction IO、BigML 以及许多其他服务。这些在线机器学习服务共同构成了一个通常被称为机器学习即服务MLaaS的提供方案,遵循其他基于云服务的类似命名模式,如SaaSPaaSIaaS,分别代表软件、平台或基础设施即服务。

研究表明,MLaaS 是一个潜在的巨大商业趋势。商业智能咨询公司 ABI Research 估计,基于机器学习的数据分析工具和服务收入将在 2021 年达到近 200 亿美元,正如本商业报告中概述的那样,MLaaS 服务将起飞:iotbusinessnews.com/2016/08/01/39715-machine-learning-iot-enterprises-spikes-advent-machine-learning-service-models/

ABI Research 的研究分析师 Eugenio Pasqua 表示以下内容:

"机器学习即服务(MLaaS)模型的出现对市场来说是个好消息,因为它降低了实施机器学习的复杂性和时间,从而打开了增加其采用率的门户,尤其是在中小企业领域。"

增加的可访问性是使用基于 API 的基础设施来构建机器学习模型而不是从头开发应用程序的直接结果。提供高效的预测分析模型,无需编码、托管和维护复杂的代码库,降低了门槛,使 ML 对小型企业和机构变得可用。

Amazon ML 通过显著简化预测分析过程及其实施,比该领域的其他参与者更进一步地推进了这种民主化方法。这种简化围绕四个嵌入到平台中的设计决策:

  • 有限的任务集:二元分类、多分类和回归

  • 单一的线性算法

  • 有限的指标选择来评估预测的质量

  • 基于底层预测算法的一组简单的调整参数

这个相对受限的环境在解决与商业相关的多数预测分析问题时足够简单。它可以被应用于众多不同的行业和用例中。

利用完整的 AWS 集成

AWS 数据生态系统,包括管道、存储、环境和人工智能(AI),也是选择 Amazon ML 作为业务平台以解决其预测分析需求的有力论据。尽管 Amazon ML 很简单,但一旦集成到 AWS 数据相关服务的更大结构中,该服务就会向更复杂的复杂性和更强大的功能发展。

AWS 已经是云计算的主要参与者。以下是《经济学人》2016 年 8 月的一篇摘录对 AWS 的评价 (www.economist.com/news/business/21705849-how-open-source-software-and-cloud-computing-have-set-up-it-industry):

AWS 在向云计算广阔的天空全面主导的进程中没有显示出放缓的迹象。据咨询公司 Gartner 称,AWS 的计算能力是下一个 14 家云服务提供商总和的十倍。AWS 过去一个季度的销售额大约是其最接近的竞争对手微软 Azure 的三倍。

这使得 Amazon ML 在许多使用云服务的公司中具有优势,因为这些公司很可能已经在使用 AWS。将简单高效的机器学习工具添加到产品组合中,预示着预测分析功能作为网络服务标准组件的兴起。与其他 AWS 服务无缝集成是使用 Amazon ML 的一个强有力的论据,尽管它看起来很简单。

以下架构是一个案例研究,摘自 AWS 于 2016 年 1 月发布的名为 *Big Data Analytics Options on AWS (d0.awsstatic.com/whitepapers/Big_Data_Analytics_Options_on_AWS.pdf) 的白皮书,展示了社交媒体情感分析潜在的 AWS 架构。它展示了 Amazon ML 如何成为 AWS 服务更复杂架构的一部分:

比较性能

保持系统和应用程序简单始终是困难的,但这对商业来说通常值得。例如,过载的用户界面会导致用户体验下降,而具有简单、优雅界面和最少功能的产物却受到广泛欢迎。"简单至上"的格言在预测分析这样的环境中,性能至关重要的背景下,更是难以坚持。这正是亚马逊在它的 Amazon ML 服务中面临的挑战。

典型的预测分析项目是一系列复杂的操作:获取数据、清理数据、选择、优化和验证模型,最后进行预测。在脚本方法中,数据科学家使用机器学习库(如 Python 的 scikit-learn 库或 R 包)开发代码库,以处理从数据收集到生产中的预测的所有步骤。随着开发者将必要的步骤分解成模块以提高可维护性和可测试性,Amazon ML 将预测分析项目分解成不同的实体:数据源、模型、评估和预测。正是这些步骤的简单性使得 AWS 在实施成功的预测分析项目中如此强大。

工程数据与模型多样性

对于预测来说,拥有大量算法始终是一件好事,但最终,领域知识和从干净数据中提取有意义特征的能力往往是赢得比赛的关键。

Kaggle 是一个知名的预测分析竞赛平台,全球最优秀的数据科学家在这里竞争,对复杂数据集进行预测。在这些预测竞赛中,预测分数上增加几个小数点就能区分获奖者或只是成千上万名竞争者中公共排行榜上的额外一行。Kagglers 很快就会学到,选择和调整模型只是战斗的一半。特征提取或如何从数据集中提取相关预测因子往往是赢得比赛的关键。

在现实生活中,当处理与业务相关的问题时,数据处理阶段的质量和从原始数据中提取有意义信号的能力是构建高效预测模型最重要的和耗时最多的部分。众所周知,“数据准备占数据科学家工作的约 80%” (*www.forbes.com/sites/gilpress/2016/03/23/data-preparation-most-time-consuming-least-enjoyable-data-science-task-survey-says/)。模型选择和算法优化仍然是工作的一个重要部分,但在实施方面往往不是决定性因素。

一个坚实且稳健的实现,易于维护且无缝连接到您的生态系统,通常比内部开发和编码的过于复杂的模型更受欢迎,尤其是当脚本模型与基于服务的实现相比只能带来微小收益时。

亚马逊的专业知识和梯度下降算法

亚马逊一直在其商业的零售方面使用机器学习,并在预测分析方面建立了深厚的专业知识。这种专业知识转化为选择驱动亚马逊机器学习服务的算法。

随机梯度下降SGD)算法是驱动亚马逊机器学习线性模型的算法,并最终负责服务生成的预测的准确性。SGD 算法是最稳健、最具有弹性和最优化算法之一。自 20 世纪 60 年代以来,它已在许多不同的环境中使用,从信号处理到深度学习,以及各种问题,都取得了巨大的成功。SGD 还催生了许多高度高效的变体,适应了广泛的数据环境。我们将在后面的章节中回到这个重要的算法;在此阶段,只需说 SGD 算法是所有可能的预测分析算法中的瑞士军刀即可。

亚马逊机器学习服务的多个基准测试和评估可以在网络上找到(亚马逊、谷歌和 Azure:blog.onliquid.com/machine-learning-services-2/ 和亚马逊与scikit-learn的对比:lenguyenthedat.com/minimal-data-science-2-avazu/)。总体结果表明,亚马逊机器学习的表现与其他 MLaaS 平台相当,同时也与基于流行机器学习库(如scikit-learn)的脚本解决方案相当。

在特定情境下的特定问题,以及可用的数据集和特定的评分指标选择,可能使用适当的库编写预测模型,并可能获得比使用亚马逊机器学习更好的性能。但亚马逊机器学习提供的是稳定性、无需编码、非常坚实的基准记录,以及与亚马逊云服务生态系统无缝集成的优势,该生态系统已经为互联网的很大一部分提供动力。

定价

与其他机器学习即服务(MLaaS)提供商和 AWS 服务一样,亚马逊机器学习只对您使用的部分收费。

成本分解如下:

  • 用于构建预测模型所使用的计算时间的每小时费用

  • 每千个预测样本的预测费用

  • 在实时(流式)预测的背景下,基于模型预先分配的内存的费用

计算时间随着以下因素增加:

  • 模型的复杂性

  • 输入数据的大小

  • 属性的数量

  • 应用转换的数量和类型

在撰写本文时,这些费用如下:

  • 每小时 0.42 美元的数据分析和模型构建费用

  • 批量预测每 1000 次预测 0.10 美元

  • 实时预测每次预测 0.0001 美元

  • 为您的模型提供的每 10 MB 内存每小时 0.001 美元

这些价格不包括与数据存储(S3、Redshift 或 RDS)相关的费用,这些费用将单独收费。

在创建您的模型时,亚马逊机器学习根据所选的数据源为您提供成本估算。

亚马逊机器学习服务不属于 AWS 免费层,这是一个为期 12 个月的优惠,在某些条件下,某些 AWS 服务可以免费使用。

理解预测分析

数据科学、预测分析、机器学习——这些术语被广泛使用,有时甚至相互重叠。它们实际上所指的内容并不总是显而易见。

数据科学是最受欢迎的技术领域之一,其趋势在 2012 年 10 月发布的经常引用的《哈佛商业评论》文章《数据科学家:21 世纪最性感的工作》发表后爆发(hbr.org/2012/10/data-scientist-the-sexiest-job-of-the-21st-century)。数据科学可以看作是数据挖掘和数据分析的一种演变。数据挖掘是关于探索数据以发现可能导致商业层面决策和行动的模式。数据科学涵盖了数据分析和重新组合更广泛的领域,如统计学、数据可视化、预测分析、软件工程等,在非常广泛的领域下。

预测分析是基于过去观察预测未来事件的艺术。它要求你的数据以某种方式组织,具有明确的预测变量和结果。正如丹麦政治家卡尔·克里斯蒂安·斯坦克曾说过,“预测是困难的,尤其是关于未来的预测。”(这句话也被归功于尼尔斯·玻尔、尤吉·贝拉和其他人,参见quoteinvestigator.com/2013/10/20/no-predict/)。预测分析应用广泛,包括预测消费者行为、自然事件(天气、地震等)、人们的行为或健康、金融市场、工业应用等。预测分析依赖于监督学习,其中数据和标签被提供给训练模型。

机器学习包括计算机用于预测分析或其他目标的模型优化所需的工具、方法和概念。

机器学习的范围比预测分析要大得多。通常考虑三种不同的机器学习类型:

  • 监督学习:假设有已知结果的训练数据可用,并可用于训练模型。预测分析是监督学习的一部分。

  • 无监督学习:关于在不知道结果的情况下在现有数据中寻找模式。例如,聚类客户行为或为了可视化目的降低数据集的维度是无监督学习的例子。

  • 强化学习:是机器学习的第三种类型,其中代理在给定一组规则和特定的奖励方案后,学会自行行动。强化学习应用的例子包括AlphaGo,谷歌的世界冠军围棋算法,自动驾驶汽车和半自主机器人。AlphaGo 从数千场过去的比赛中学习,并在 2016 年 3 月击败了世界围棋冠军(www.wired.com/2016/03/go-grandmaster-lee-sedol-grabs-consolation-win-googles-ai/)。经典的强化学习实现遵循以下方案,其中代理根据产生的奖励在其环境中调整其行为:

图片

在二元分类和聚类的背景下,监督学习和无监督学习之间的区别在下述两个图中得到说明:

  • 对于监督学习,原始数据集由两个类别(正方形和圆形)组成,并且我们从一开始就知道每个样本属于哪个类别。将此信息提供给二元分类算法允许对两个类别进行某种优化的分离。一旦知道了分离前沿,模型(即线)就可以用来根据样本最终落在哪一侧来预测新样本的类别:

图片

  • 无监督学习中,不同的类别是未知的。没有基准事实。数据被提供给算法,并附带一些参数,例如要找到的类别数量,算法根据定义的标准或指标在原始数据集中找到最佳聚类集。结果可能非常依赖于初始化参数。没有事实,没有准确性,只是对数据的解释。以下图显示了要求在原始数据中找到三个类别的聚类算法得到的结果:

图片

读者此时会注意到,这本书的标题是《亚马逊机器学习》而不是《亚马逊预测分析》。这有点误导,因为机器学习涵盖了除了预测分析之外许多应用和问题。然而,将服务称为机器学习为亚马逊推出未来不专注于预测分析的服务留下了空间。以下图展示了数据科学术语之间的关系:

图片

构建最简单的预测分析算法

预测分析可以非常简单。我们介绍了一个基于简单阈值的二元分类背景下的预测模型的一个非常简单的例子。

想象一下,一辆运输小橙子和大葡萄柚的卡车偏离了道路;所有的水果箱都打开了,所有的水果都混合在一起。装备了简单的秤和将水果从卡车中滚出来的方法,你希望能够根据它们的重量自动将它们分开。你有一些关于小橙子平均重量(96 克)和大葡萄柚平均重量(166 克)的信息。

根据美国农业部(USDA)的数据,中等大小橙子的平均重量为 131 克,较大的橙子重量约为 184 克,较小的橙子约为 96 克。

  • 大葡萄柚(直径约 4-1/2 英寸)166 克

  • 中等葡萄柚(直径约 4 英寸)128 克

  • 小葡萄柚(直径约 3-1/2 英寸)100 克

你的预测模型如下:

  • 你随意设置了一个 130 克的阈值

  • 你称量每个水果

  • 如果水果重量超过 130 克,则是葡萄柚;否则是橙子

好了!你已经有了一个健壮、可靠的预测模型,可以应用于所有混合在一起的水果,以将它们分开。注意,在这种情况下,你是通过有根据的猜测来设置阈值的。这里没有涉及机器学习。

在机器学习中,模型是自行学习的。你不需要自己设置阈值,而是让你的程序自行进化,并计算水果的重量分离阈值。

为了这个目的,你需要留出一部分橙子和葡萄柚。这被称为训练数据集。重要的是这个训练数据集中橙子和葡萄柚的数量大致相同。

你让机器自行决定阈值值。一个可能的算法可能是这样的:

  1. 将原始重量估计设置为w_0 = 100g以初始化,并设置计数器k = 0

  2. 对于训练数据集中每个新的水果,根据以下方法调整权重估计:

        For each new fruit_weight:
            w(k+1) = (k*w(k) + fruit_weight)/ (k+1)
            k = k+1

假设你的训练数据集代表了所有剩余的水果,并且你有足够的水果,阈值在一定的条件下会收敛到所有水果重量的最佳平均值。这个值用于根据你估计的阈值将其他水果分开,判断它们是重于还是轻于这个阈值。以下图表显示了该粗略算法估计水果平均重量的收敛情况:

图表

这个问题是一个典型的二元分类模型。如果我们有两种以上的水果类型(柠檬、橙子和葡萄柚),那么我们就会有一个多类分类问题。

在这个例子中,我们只有一个预测因子:水果的重量。我们可以添加另一个预测因子,例如直径。这将导致所谓的多元分类问题。

在实践中,机器学习使用更复杂的算法,如 SGD(随机梯度下降),这是 Amazon ML 使用的线性算法。其他经典的预测算法包括支持向量机、贝叶斯分类器、随机森林等等。每种算法都有其优势和针对数据集的假设。

回归与分类

Amazon ML 进行两种类型的预测分析:分类和回归

如前一段所述,分类是关于预测给定样本集的有限标签或类别的。

  • 在两个类别的情况下,问题被称为二元分类。

  • 当有超过两个类别且类别互斥时,问题是一个多类分类问题。

  • 如果样本可以同时属于多个类别,我们谈论多标签分类问题。

简而言之,分类是对有限集合的类别、标签、类别的预测。

  • 二元分类的例子包括:购买结果(是/否)、生存结果(是/否)、异常检测(垃圾邮件、机器人)等等。

  • 多类分类的例子包括:在图像中分类对象(水果、汽车等)、根据智能手机传感器识别音乐流派或运动、文档分类等等。

在回归问题中,结果具有连续值。预测年龄、体重、股价、薪水、降雨量、温度等等都是回归问题。当有多个预测因子时,我们谈论多重回归;当预测为每个样本预测多个值时,我们谈论多元回归。Amazon ML 进行单变量回归和分类,包括二元和多类,但不进行多标签分类。

使用逻辑回归将回归扩展到分类

Amazon ML 使用线性回归模型进行回归、二元和多类预测。使用逻辑回归模型将连续回归扩展到分类问题。

具有一个预测因子的简单回归模型建模如下:

图片

在这里,x是预测因子,y是结果,(a, b)是模型参数。每个预测值y是连续的且无界。我们如何使用该模型来预测那些按定义是分类值的类别呢?

以二元预测为例。方法是将连续的、无界的预测转换为概率,这些概率都在 0 和 1 之间。然后我们使用一个预定义的阈值将这些概率与两个类别之一关联。这个模型被称为逻辑回归模型——名称误导,因为逻辑回归是一个分类模型,而不是回归模型。

为了将连续的无界值转换为概率,我们使用以下定义的sigmoid函数:

图片

此函数将任何实数转换为[0,1]区间的值。因此,其输出可以解释为概率:

图片

总之,使用回归模型进行二元分类的方法如下:

  1. 建立回归模型,并估计实值结果 y

  2. 使用预测值 y 作为 sigmoid 函数的参数。结果 f(y) 是属于两个类别之一的概率度量。

  3. 设置一个阈值 T 在 [0,1] 范围内。所有预测样本中概率 f(y) > T 的属于一个类别,其他属于另一个类别。默认值 T = 0.5

逻辑回归本质上是一种二元分类器。有几种策略可以将二元分类器转换为多类分类器。

一对所有(OvA) 技术在于选择一个类别作为正类,所有其他类别作为负类,以回到二元分类问题。一旦对第一个类别进行了分类,就选择第二个类别作为正类,所有其他类别作为负类。当有 N 个类别需要预测时,这个过程重复 N-1 次。以下一系列图表显示:

  • 原始数据集和所有样本的类别

  • 第一次二元分类(圆形与其他所有类别)

  • 第二次分类的结果,将正方形和三角形分开

图片

提取特征以预测结果

可用数据需要易于访问且具有意义,以便算法能够提取信息。

让我们考虑一个简单的例子。想象一下,我们想要预测一个特定城市的房屋市场价格。我们可以想到许多可能成为房屋价格预测指标的变量:房间数量或浴室数量、社区、面积、供暖系统等等。这些变量被称为特征、属性或预测指标。我们想要预测的值被称为结果或目标。

如果我们想要我们的预测是可靠的,我们需要几个特征。仅基于房屋面积预测房价将不会非常有效。许多其他因素会影响房价,并且我们的数据集应该尽可能包含这些因素(满足条件)。

通常可以向模型添加大量属性以尝试提高预测的准确性。例如,在我们的房价预测中,我们可以添加房屋的所有特征(浴室、面积、供暖系统、窗户数量)。其中一些变量会向我们的定价模型提供更多信息,从而提高预测的准确性,而其他变量只会增加噪声并混淆算法。向预测模型添加新变量并不总是能提高预测的准确性。

为了做出可靠的预测,你带到模型中的每个新特征都必须带来一些有价值的信息。然而,这并不总是如此。正如我们将在第二章“机器学习定义和概念”中看到的,相关的预测变量可能会损害模型的性能。

预测分析建立在几个假设和条件之上:

  • 你试图预测的值是可以预测的,而不仅仅是随机噪声。

  • 你可以访问与目标有一定关联的数据。

  • 可用的数据集足够大。从太小的数据集中无法推断出可靠的预测。(例如,你可以定义并因此预测一条直线,但你不能从只有两个点中推断出遵循正弦曲线的数据。)

  • 你将基于的新数据与你参数化和训练模型的数据相似。

你可能有一个很好的数据集,但这并不意味着它对预测是有效的。

这些数据条件非常通用。在 SGD 的情况下,条件更为约束。

深入探讨用于预测的线性建模

Amazon ML 基于线性建模。回想一下平面上一条直线的方程:

图片

这个具有系数 (a, b) 的线性方程可以解释为一个预测线性模型,其中 x 是预测变量,y 是结果。在这个简单的情况下,我们有两个参数 (a, b) 和一个预测变量 x。一个例子可以是预测孩子的身高与体重之间的关系,并找到一些 a 和 b 使得以下方程成立:

图片

让我们考虑经典的 Lewis Taylor (1967) 数据集,其中包含 237 个孩子的年龄、体重、身高和性别样本 (v8doc.sas.com/sashtml/stat/chap55/sect51.htm),并关注孩子的身高与体重之间的关系。在这个数据集中,最佳回归线遵循以下方程:

图片

下图展示了身高与体重数据集及其相关的线性回归:

图片

现在假设我们不止有一个预测变量,而是有多个,让我们将前面的线性方程推广到 N 个预测变量,用 {x[1], . . . , x[n]} 表示,以及 N +1 个系数或 {w[o], w[1], . . ., w[n]} 权重。线性模型可以写成以下形式:

图片

在这里,ŷ 表示预测值,(y 将对应于要预测的真实值)。为了简化符号,我们将假设本书的其余部分系数 w[o] = 0

该方程可以重写为以下矢量形式:

图片

其中 T 是转置算子,X = {x[1], . . ., x[n]} 和 W= {w[1], . . .,w[n]} 分别是预测变量和模型权重的向量。在特定条件下,系数 w[i] 可以精确计算。然而,对于大量样本 N,这些计算在计算量上非常昂贵,因为它们涉及到 N 维矩阵的求逆,对于大数据集来说,这是昂贵且缓慢的。随着样本数量的增加,通过迭代过程估计这些模型系数变得更加高效。

随机梯度下降算法迭代地估计模型的系数 {w[o], w[1], . . ., w[n]}。在每次迭代中,它使用训练数据集的一个随机样本,其中已知实际结果值。SGD 算法通过最小化预测误差的函数来工作:

将预测误差作为参数的函数也称为损失函数。不同的损失函数导致不同的算法。凸损失函数有一个唯一的极小值,这对应于回归问题的最优权重集。我们将在后面的章节中详细介绍 SGD 算法。现在只需说,SGD 算法特别适合处理大数据集。

有许多理由可以证明选择 SGD 算法用于通用预测分析问题的合理性:

  • 它是鲁棒的

  • 其收敛性质已被广泛研究,并且众所周知

  • 它非常适合优化技术

  • 它有许多扩展和变体

  • 它的计算成本较低

  • 它可以应用于回归、分类和流数据

一些弱点包括以下内容:

  • 需要正确初始化其参数

  • 依赖于称为学习率的参数的收敛速率

验证数据集

并非所有数据集都适合线性建模。对于您的线性模型有意义,样本必须满足几个条件。有些条件是严格的,而有些则可以放宽。

通常,线性建模假设以下条件(www.statisticssolutions.com/assumptions-of-multiple-linear-regression/):

  • 归一化/标准化:线性回归可能对表现出非常不同尺度的预测变量敏感。这对于所有依赖于样本间距离度量或样本标准差的损失函数都是正确的。具有更高平均值和标准差的预测变量对模型的影响更大,可能潜在地掩盖了具有更好预测能力但值域更受限制的预测变量。预测变量的标准化将所有预测变量置于同一水平。

  • 独立同分布(i.i.d.):假设样本之间相互独立,并且遵循相似的分布。即使样本之间并不那么独立,这个属性也经常被假设。在时间序列的情况下,样本依赖于先前值,使用样本到样本的差异作为数据通常足以满足独立性假设。正如我们将在第二章,“机器学习定义和概念”中看到的,混淆因素和噪声也会对线性回归产生负面影响。

  • 无多重共线性:线性回归假设数据中几乎没有多重共线性,这意味着一个预测变量不是其他预测变量的线性组合。可以通过其他预测变量的线性组合来近似预测变量的预测将使模型混淆。

  • 异方差性:预测变量的标准差在其所有值范围内是恒定的。

  • 残差的正态分布:这不仅仅是事后验证线性回归的有效性。残差是真实值与其线性估计之间的差异。如果这些残差遵循正态分布,则认为线性回归是相关的。

这些假设在现实生活中的数据集中很少完美满足。正如我们将在第二章,“机器学习定义和概念”中看到的,有一些技术可以检测线性建模假设是否没有得到尊重,并随后转换数据以更接近理想的线性回归环境。

亚马逊机器学习(Amazon ML)中缺少的功能

亚马逊机器学习提供监督学习预测,用于分类(二分类和多分类)和回归问题。它提供了一些原始数据的基本可视化,并有一个预设的数据转换列表,例如分箱或数据归一化。它是高效且简单的。然而,一些对数据科学家来说非常重要的功能却不幸地缺失在这个平台上。缺少这些功能可能不是决定性的,但它确实限制了亚马逊机器学习可以应用的问题范围。

亚马逊机器学习不提供的一些常见机器学习功能如下:

  • 无监督学习:无法对数据进行聚类或降维。

  • 除线性模型之外的选择模型:非线性支持向量机、任何类型的贝叶斯分类、神经网络以及基于树的算法(决策树、随机森林或提升树)都是缺失的模型。所有预测、所有实验都将建立在基于 SGD 的线性回归和逻辑回归之上。

  • 数据可视化功能仅限于直方图和密度图。

  • 指标的选择:Amazon ML 使用 F1 分数和 ROC-AUC 指标进行分类,使用 MSE 进行回归。无法使用任何其他指标来评估模型性能。

  • 你不能下载你的训练模型,在其他地方(除了 Amazon ML)使用它。

最后,尽管在 Amazon ML 平台上无法直接使用自己的脚本(R、Python、Scala 等),但使用其他 AWS 服务,如 AWS Lambda,来预处理数据集是可能的,也是推荐的。如果你的数据存储在 AWS 启用了 SQL 的服务之一(Athena、RDS、Redshift 等),还可以使用 SQL 进行超出 Amazon ML 中可用转换的数据操作。

统计方法与机器学习方法

在 2001 年,Leo Breiman发表了一篇题为《统计建模:两种文化》(http://projecteuclid.org/euclid.ss/1009213726)的论文,强调了关注数据中底层过程验证和解释的统计方法与更关注结果的机器学习方法之间的差异。

简而言之,经典统计分析遵循以下步骤:

  1. 提出了一个称为零假设的假设。这个零假设通常表明观察结果是随机的。

  2. 然后计算零假设下事件的概率(或 p 值)。

  3. 如果这个概率低于某个阈值(通常 p < 0.05),则拒绝零假设,这意味着观察结果不是随机事件。

p> 0.05并不意味着零假设是真的。它仅仅意味着你不能拒绝它,因为观察结果偶然发生的概率并不大。

这种方法旨在解释和发现现象的影响因素。目标是在这里建立一个相对静态且完全已知的模型,使其尽可能符合观察结果,因此能够预测未来的模式、行为和观察。

在机器学习方法中,在预测分析中,模型的显式表示并不是重点。目标是构建适合预测期的最佳模型,模型从观察中自行构建。模型的内部结构并不明确。这种机器学习方法被称为黑盒模型。

通过消除对数据进行显式建模的需求,机器学习方法在预测方面具有更强的潜力。机器学习专注于通过最小化模型的预测误差来做出尽可能准确的预测,而牺牲可解释性。

摘要

在本章中,我们介绍了 Amazon ML 服务所使用的技巧。尽管 Amazon ML 提供的功能比其他机器学习工作流程要少,但 Amazon ML 建立在坚实的基础之上,一个简单而非常高效的算法驱动其预测。

亚马逊机器学习(Amazon ML)并不提供解决任何类型的自动化学习问题,并且在某些情境和某些数据集中可能不够充分。然而,其简单的方法和设计对于许多预测分析项目来说是足够的,前提是初始数据集经过适当的预处理,并包含可以进行预测的相关信号。

在第二章《机器学习定义与概念》中,我们将进一步探讨在预测分析中使用的技巧和概念。

更具体地说,我们将介绍用于提高原始数据质量的最常见技术;我们将识别并纠正数据集中常见的异常;我们将学习如何训练和验证预测模型,以及如何在预测性能不佳时改进预测。

第二章:机器学习定义和概念

本章提供了使用 Amazon 机器学习(Amazon ML)服务所需的高级机器学习概念的定义和解释,并全面理解其工作原理。本章有三个具体目标:

  • 列出在处理原始数据时用于提高预测质量的主要技术。你将学习如何处理最常见的数据问题。其中一些技术可在 Amazon ML 中使用,而其他则不行。

  • 展示预测分析工作流程并介绍交叉验证的概念,即如何分割你的数据以训练和测试你的模型。

  • 展示如何检测模型的较差性能并介绍提高这些性能的策略。

读者将学习以下内容:

  • 如何在给定数据集中发现常见问题和异常

  • 如何从数据集中提取最多信息以构建稳健的模型

  • 如何检测并改进较差的预测性能

算法是什么?模型是什么?

在我们深入数据清洗之前,让我们花一点时间解释算法和模型之间的区别,这两个术语我们至今尚未给出正式定义。

考虑我们在第一章“机器学习与预测分析导论”中看到的简单线性回归示例,只有一个预测因子的线性回归方程:

在这里,x 是变量,ŷ 是预测值,而不是真实值,而 (a,b) 是线性回归模型的参数:

  • 概念或理论模型是数据表示,它最适合实际数据集。数据科学家在开始时选择它。在这种情况下,概念模型是线性回归模型,其中预测是变量的线性组合。其他概念模型包括决策树、朴素贝叶斯、神经网络等。所有这些模型都有需要调整到实际数据的参数。

  • 算法是计算过程,将计算概念模型的最佳参数。在我们的简单线性回归案例中,算法将计算最佳参数 ab。这里的最佳意味着它给出了给定可用数据集的最佳预测。

  • 最后,预测模型对应于与可用数据集找到的最佳参数相关联的概念模型。

在现实中,没有人明确区分概念模型和预测模型。两者都被称为模型。

简而言之,算法是学习的方法,模型是学习阶段的结果。模型是算法在训练数据集上训练的概念模型(树、SVM、线性)。

处理杂乱的数据

随着数据集的增长,不一致性和错误也随之增加。这些错误可能是由于人为错误、系统故障或数据结构演变造成的,真实世界数据中充满了无效、荒谬或缺失的值。即使数据集本身很干净,某些变量的性质也需要适应模型。我们将在 Amazon ML 线性模型的背景下查看最常见的数据异常和特征,这些特征需要被纠正。

经典数据集与真实世界数据集的比较

数据科学家和机器学习从业者经常使用经典数据集来展示某些模型的行为。由 150 个三种鸢尾花样本组成的Iris数据集,是用于演示或教授预测分析的常用数据集之一。它自 1936 年以来一直存在!

Boston housing数据集和Titanic数据集是预测分析中其他非常流行的数据集。对于文本分类,Reuters20 newsgroups文本数据集非常常见,而图像识别数据集用于评估深度学习模型。这些经典数据集用于评估算法和模型性能时建立基线。它们的特征是众所周知的,数据科学家知道可以期待什么样的性能。

这些经典数据集可以下载:

然而,经典数据集可能是真实数据集的弱等价物,这些真实数据集是从各种来源提取和汇总而来的:数据库、API、自由格式文档、社交网络、电子表格等等。在现实情况下,数据科学家必须经常处理含有缺失值、荒谬的异常值、人为错误、奇怪的格式、奇怪的输入和偏斜分布的杂乱数据。

预测分析项目的第一个任务是清理数据。在下一节中,我们将探讨原始数据的主要问题以及可以应用的战略。由于我们最终将使用线性模型进行预测,我们将以此为目标处理数据。

多类线性模型的假设

为了使线性模型提供可靠的预测,预测变量必须满足一定数量的条件。这些条件被称为多元线性回归的假设(www.statisticssolutions.com/assumptions-of-multiple-linear-regression/):

  • 线性关系:预测变量应与结果有一定的线性关系

  • 多元正态性:预测变量应遵循高斯分布

  • 无或低多重共线性:预测变量之间不应相互关联

  • 同方差性:每个预测变量的方差应在整个值域内保持大致恒定

当然,这些假设很少被验证。但有一些方法可以将数据转换为接近这些理想条件。

缺失值

数据聚合、提取和合并往往并不完美,有时会导致缺失值。在数据集中处理缺失值有几种常见的策略:

  • 从数据集中移除所有包含缺失值的行。这种方法简单易行,但你可能会丢失大量对模型有价值的信息。

  • 使用本质上不受缺失值影响的模型,例如基于决策树的模型:随机森林、提升树。不幸的是,线性回归模型以及由此扩展的 SGD 算法不能处理缺失值(facweb.cs.depaul.edu/sjost/csc423/documents/missing_values.pdf)。

  • 用替换值填充缺失数据;例如,用所有现有值的中位数、平均值或调和平均值替换缺失值,或使用聚类或线性回归预测缺失值。将这些值最初缺失的信息添加到数据集中可能很有趣。

最后,正确的策略将取决于缺失数据的类型以及当然,上下文。虽然在一个患者的医疗记录中用平均值替换缺失的血压数值在医疗保健环境中可能不可接受,但在数据科学竞赛中用泰坦尼克号数据集中的平均年龄替换缺失的年龄值无疑是适用的。

然而,Amazon ML 的文档并没有 100%清晰地说明处理缺失值所使用的策略:

如果记录中存在目标属性,但另一个数值属性缺失,那么 Amazon ML 会忽略这个缺失值。在这种情况下,Amazon ML 会创建一个替代属性,并将其设置为 1,以表示该属性缺失。

在缺失值的情况下,创建了一个新的布尔标志列,以指示原始值缺失。但并不清楚是整行或样本被丢弃或忽略,还是只是单元格被删除。没有提到任何类型的插补。

归一化

机器学习算法通过最小化真实值与使用上次迭代参数预测的值之间的误差来增量更新模型参数。为了衡量这种预测误差,我们引入了损失函数的概念。损失函数是预测误差的度量。对于某种算法,使用不同的损失函数将创建算法的变体。最常见的损失函数使用L2L1范数来衡量误差:

● L2 范数:

图片

● L1 范数:

图片

其中 y[i]ŷ 分别是样本的真实值和预测值。

当不同的预测变量相差一个数量级时,预测误差的度量可能会变得偏斜。大预测变量会掩盖小值变量的重要性,从而使得推断每个预测变量在模型中的相对重要性变得困难。这会影响线性模型各自的权重收敛到最优值,进而影响算法的性能。具有最高幅度的预测变量最终会主导模型,即使该预测变量对真实结果值几乎没有预测能力。通过强制所有预测变量处于相同的尺度,数据归一化是一种减轻该问题的方法。

有两种常见的归一化类型;数据可以被归一化或标准化:

  • min-max 归一化,或归一化,将所有值设置为[0,1]之间:

图片

  • z 分数标准化,或标准化,根据标准差进行归一化。所有预测变量都将具有 0 均值和 1 标准差:

图片

基于树的算法(决策树、随机森林、提升树)是唯一一种其性能不会因归一化或标准化而提高的机器学习模型。所有其他基于距离/方差预测算法都可能从归一化中受益。已经证明,标准化对于随机梯度下降(SGD)特别有用,因为它确保所有权重将以相同的速度适应。

高效反向传播 Yann A. LeCun 等人在神经网络中技巧贸易 pp. 9-48,Springer Verlag

亚马逊机器学习(Amazon ML)提供 z 分数标准化作为可用数据转换的一部分。

不平衡数据集

处理不平衡数据集是一个非常常见的分类问题。

考虑一个二元分类问题。你的目标是预测正类与负类。两个类别之间的比例高度偏向正类。这种情况在以下实例中经常遇到:

  • 在医疗环境中,正类对应于在大量随机人群中存在癌细胞的人群

  • 在营销环境中,正类对应于购买保险的潜在客户,而大多数人并没有购买

在这两种情况下,我们希望检测少数类别的样本,但它们在多数(负)类别的样本中占绝对少数。大多数预测模型都会高度偏向多数类别。

在高度不平衡的类别存在的情况下,一个总是预测多数类别而从不预测少数类别的非常简单的模型将具有极高的准确率,但永远不会检测到重要且有价值的类别。例如,考虑一个由 1,000 个样本组成的数据集,其中包含 50 个我们想要检测或预测的正样本和 950 个无趣的负样本。这个简单模型有 95%的准确率,这显然是一个不错的准确率,尽管这个模型完全无用。这个问题被称为准确率悖论(en.wikipedia.org/wiki/Accuracy_paradox)。

一个直接的解决方案是收集更多数据,重点关注收集少数类别的样本,以便平衡两个类别。但这并不总是可能。

处理不平衡数据集有许多其他策略。我们将简要介绍其中一些最常见的方法。一种方法是通过欠采样或过采样现有数据来重新采样数据:

  • 欠采样包括丢弃多数类别的多数样本,以便将少数/多数类别的比例调整为50/50。这种策略的明显问题是会丢弃大量数据,以及随之而来的对模型有意义的信号。这种技术在存在足够大的数据集时可能是有用的。

  • 过采样包括复制属于少数类别的样本。与欠采样相反,该策略不会丢失数据。然而,过采样会给少数类别的某些模式添加额外的权重,这可能不会给模型带来有用的信息。过采样会给模型添加噪声。当数据集较小且无法承受丢失一些数据时,过采样是有用的。

下采样和过采样是两种简单且易于实现的方法,在建立基线时很有用。另一种广泛使用的方法是从现有数据中创建合成样本。一种流行的样本创建技术是 SMOTE 方法,代表 合成少数类过采样技术。SMOTE 通过选择与某些距离度量相似的少数类样本,并在选定的属性上添加扰动来工作。然后,SMOTE 在现有少数样本的簇内创建新的少数样本。在存在高维数据集的情况下,SMOTE 的解决方案作用较小。

Python 中的 不平衡库 (http://github.com/scikit-learn-contrib/imbalanced-learn) 或 R 中的 不平衡包 (cran.r-project.org/web/packages/unbalanced/index.html) 都在所提及的技术之上提供了一组大量的高级技术。

注意,在数据不平衡的背景下,选择用于评估模型性能的指标尤为重要。准确率,定义为正确预测样本数与总样本数的比率,是分类问题中最直接的指标。但正如我们所见,这种准确率在存在高度倾斜的类别分布的情况下,并不是模型预测能力的良好指标。

在这种情况下,建议使用两个指标:

F1 分数是 Amazon ML 用于评估分类模型质量的指标。我们在本章末尾的“评估模型性能”部分给出了 F1 分数的定义。

解决多重共线性问题

以下是根据维基百科的定义的多重共线性 (en.wikipedia.org/wiki/Multicollinearity):

多重共线性是多个回归模型中两个或多个预测变量高度相关的现象,这意味着一个可以从其他变量中线性预测出来,具有相当高的准确性。

基本上,假设你有一个包含三个预测因子的模型:

图片

其中一个预测因子是一个线性组合(完美多重共线性)或者近似为两个其他预测因子的线性组合(近多重共线性)。

例如: 图片

这里, 图片 是一些噪声变量。

在这种情况下,的变化将驱动的变化,因此,将与相关联。中已经包含的信息将与第三个预测因子共享,这将在模型中引起高度不确定性和不稳定性。预测因子值的小幅变化将导致系数的大幅变化。回归可能不再可靠。在更技术性的术语中,系数的标准误差会增加,这会降低其他重要预测因子的显著性。

有几种方法可以检测多重共线性。计算预测因子的相关矩阵是第一步,但这只会检测预测因子对之间的共线性。

多重共线性的一个常用检测方法是计算每个预测因子的方差膨胀因子VIF

方程式图片

在这里,是第一步中回归方程的确定系数,其中在左侧,所有其他预测变量(所有其他变量)在右侧。

VIF(方差膨胀因子)中的一个值很大表明,特定系数的方差(标准误差的平方)比该预测因子与其他所有预测因子完全不相关时的方差要大。例如,VIF 为 1.8 表示预测因子的方差比不相关情况下的方差大 80%。

一旦确定了高度共线性的属性,以下选项将减少共线性:

  • 从数据集中移除高度共线性预测因子

  • 使用偏最小二乘回归(PLS)主成分分析(PCA),回归方法(en.wikipedia.org/wiki/Principal_component_analysis);PLS 和 PCA 将预测因子的数量减少到一组不相关的变量

不幸的是,从 Amazon ML 平台无法检测和移除多重共线性变量。

检测异常值

给定一个变量,异常值是该变量中与其他值非常不同的值。异常值相当常见,通常由人为或测量错误引起。异常值可能会严重破坏模型。

为了演示,让我们看看两个简单的数据集,并观察它们的平均值是如何受到异常值存在的影响的。

考虑两个样本量都很少的数据集:A = [1,2,3,4]B = [1,2,3,4, 100]。B 数据集中的第 5 个值,100,显然是一个异常值:mean(A) = 2.5,而mean(B) = 22。异常值可以对指标产生重大影响。由于大多数机器学习算法基于距离或方差测量,异常值可以对模型的性能产生重大影响。

多元线性回归对异常值效应敏感,如下面的图表所示,添加一个单独的异常值点会将坚实的回归线破坏成虚线:

移除与异常值相关的样本是最简单的解决方案。

另一种解决方案是对预测变量应用分位数分组,通过将值分成N个有序的区间或分组,每个分组大约包含相等数量的样本。这将把一个数值(连续)预测变量转换为分类变量。例如,[1,2,3,4,5,6,7,8,9,10,11,100]分成三个大小相等的分组变为[1,1,1,1,2,2,2,2,3,3,3,3];异常值 100 被包含在第三个分组中并隐藏。

分位数分组的缺点是在过程中会丢失一些信息粒度,这可能会降低模型的性能。

分位数分组在 Amazon ML 中作为数据变换过程可用,也用于量化原始数据集中的非线性。

事实上,分位数分组QB)是 Amazon ML 默认应用于所有与结果没有直接线性关系的连续变量的。在我们所有的试验中,与我们的先前假设相反,我们发现 QB 在 Amazon ML 环境中是一种非常高效的数据变换。

接受非线性模式

线性回归模型意味着结果可以通过预测变量的线性组合来估计。当然,这并不总是情况,因为特征通常表现出非线性模式。

考虑以下图表,其中Y轴依赖于X轴,但关系显示出明显的二次模式。将线(y = aX + b)作为Y作为X的函数的预测模型并不适用:

一些模型和算法能够自然地处理非线性,例如,基于树的模型或具有非线性核的支持向量机。线性回归和 SGD 则不行。

变换:在线性回归的背景下处理这些非线性模式的一种方法是对预测变量进行变换。在前面的简单例子中,将预测变量 X 的平方添加到模型中会得到更好的结果。现在的模型将具有以下形式:

如以下图表所示,新的二次模型更好地拟合了数据:

我们不仅限于二次情况,可以使用更高阶的幂函数来转换现有属性并创建新的预测因子。其他有用的转换可能包括取对数、指数、正弦和余弦等。在此处值得提及的是Box-Cox 转换(onlinestatbook.com/2/transformations/box-cox.html)。它是一种有效的数据转换,可以减少变量分布的偏度和峰度。它将变量分布重塑为更接近高斯分布的形状。

样条曲线是多项式插值的优秀且更强大的替代品。样条曲线是分段多项式,它们在平滑处连接。在其最简单层面上,样条曲线由在不同点连接的线条组成。样条曲线在 Amazon ML 中不可用。

分位数分组是 Amazon ML 解决非线性问题的方法。通过将数据分成 N 个分组,你消除了分组区间中的任何非线性。尽管分组有几个缺点(biostat.mc.vanderbilt.edu/wiki/Main/CatContinuous),主要缺点是信息在过程中被丢弃,但它已经在 Amazon ML 平台上显示出优秀的预测性能。

添加特征?

通常,添加与结果以某种方式相关的新的特征会带来信息并提高模型。然而,添加太多预测能力很小的特征可能会使同一个模型变得混乱,最终降低其性能。

当样本量相对于特征数量较小时,通过移除最不有趣的特征进行特征选择是值得尝试的;这会导致观察值过少或特征过多。有不同策略(machinelearningmastery.com/an-introduction-to-feature-selection/)来识别和移除弱特征。根据特征与结果的相关性选择特征,并丢弃与结果相关性很小或没有相关性的特征,通常会提高你的模型。

预处理回顾

下表总结了在原始数据中可能遇到的不同问题,以及 Amazon ML 提供处理这些问题的方法:

线性模型敏感性 在 Amazon ML 上可用
缺失值 自动处理
标准化 z 分数标准化
异常值 分位数分组
多重共线性
不平衡数据集 使用正确的指标 F1 分数,没有采样策略(可能存在于后台)
非线性 分位数分组

预测分析工作流程

我们一直在谈论训练模型。这在实践中意味着什么?

在监督学习中,数据集通常被分成三个不等的部分:训练、验证和测试:

  • 你训练模型的训练集。它必须足够大,以便尽可能多地给模型提供关于数据的信息。这个数据子集被算法用来估计模型的最佳参数。在我们的情况下,SGD 算法将使用这个训练子集来找到线性回归模型的最优权重。

  • 验证集用于评估训练模型的性能。通过测量训练模型在未用于其训练的子集上的性能,我们可以对其性能进行客观评估。这样我们就可以用不同的元参数训练不同的模型,并查看哪个模型在验证集上的表现最好。这也被称为模型选择。请注意,这会创建一个反馈循环,因为验证数据集现在对你的模型选择有影响。另一个模型可能在那个特定的验证子集上表现较差,但总体上在新数据上表现更好。

  • 测试集对应于在你完全优化了特征和模型之后保留的数据。测试子集也称为保留集

在现实生活中,你的模型将面临之前未预见的数据,因为模型的最终存在理由是预测未见数据。因此,评估模型在之前未曾遇到的数据上的性能是很重要的。保留的数据集是未见数据的代理。在最后之前,绝对不能使用这个数据集来优化模型或数据属性。

这三个子集应该足够大,以准确代表真实数据。更精确地说,所有变量的分布应该在三个子集中是等效的。如果原始数据集以某种方式排序,确保在训练、验证和测试分割之前对数据进行洗牌是很重要的。

如前所述,基于其在验证集上的性能而选择的模型可能对该特定数据集有积极的偏差。为了最小化这种依赖性,通常会对具有相同参数设置的多个模型进行训练和评估,并对多个训练验证数据集对的模型性能进行平均。这减少了模型选择对验证数据集中变量特定分布的依赖性。

这种三分法是基本的,正如我们所看到的,模型最终可能会依赖于验证子集的一些特定性。交叉验证是一种标准方法,可以减少这种依赖性并改进我们的模型选择。交叉验证包括执行多个训练/验证分割,并对不同验证子集上的模型性能进行平均。最常用的交叉验证技术是 k 折交叉验证,它包括将数据集分割成 K 个块,递归地使用每个部分作为验证集,其余 k-1 个部分作为训练集。其他交叉验证技术包括蒙特卡洛交叉验证,其中不同的训练和验证集是从初始数据集中随机采样的。我们将在后面的章节中实现蒙特卡洛交叉验证。交叉验证不是 Amazon ML 服务中包含的功能,需要通过编程实现。在 Amazon ML 中,模型的训练和评估仅在一个训练-验证分割上完成。

在 Amazon ML 中的训练和评估

在 Amazon ML 的上下文中,模型是线性回归,算法是随机梯度下降SGD)算法。该算法有一个主要元参数,称为学习率,通常表示为 ,它决定了每个迭代更新权重时考虑新样本的程度。较大的学习率使算法收敛更快,但离最佳权重更远,而较小的学习率会导致收敛较慢,但回归系数集更精确。

给定训练集和验证集,这是 Amazon ML 调整和选择最佳模型的方法:

  • Amazon 训练了多个模型,每个模型具有不同的学习率。

  • 对于给定的学习率:

    • 训练集允许 SGD 通过寻找最佳回归系数来训练模型。

    • 模型在验证集上用于进行预测。

  • 通过比较不同模型在该验证集上的预测质量,Amazon ML 能够选择最佳模型及其相关的最佳学习率。

  • 保留集被用作最终确认模型是否可靠。

常用的训练、验证和保留子集分割比例如下:

  • 训练:70% 验证和保留 15% 各自

  • 训练:60% 验证和保留 20% 各自

洗牌: 确保预测值和结果在所有三个子集中遵循相同的分布非常重要。在分割数据之前对数据进行洗牌是创建可靠训练集、验证集和保留集的重要部分。

在训练数据集上定义数据转换,并在验证和保留子集上应用转换参数,以确保验证和保留子集不会将信息泄露回训练集。

以标准化为例:预测变量的标准差和平均值应在训练数据集上计算。然后应用这些值来标准化验证集和保留集。如果你使用整个原始数据集来计算平均值和随机梯度下降(SGD),你将从保留集中泄露信息到训练集中。

一个常见的监督预测分析工作流程遵循以下步骤 - 假设我们已经提取了数据集,并且我们已经选择了一个指标来评估预测质量:

  1. 构建数据集

    • 清理和转换数据以处理噪声数据问题

    • 创建新的预测变量

    • 打乱并分割数据为训练、验证和保留集

  2. 选择最佳模型

    • 选择一个模型(线性、基于树的、贝叶斯等)

    • 对元参数的几个值进行重复:

      • 在训练集上训练模型

      • 在验证集上评估模型性能

  3. 使用新数据、新预测变量和其他模型参数重复步骤 1 和 2,直到你对模型的性能满意。保留最佳模型。

  4. 在保留的子集上对模型的最终测试。

在 Amazon ML 的上下文中,除了线性回归模型(分类时为逻辑回归)之外,没有其他选择模型(步骤 2)的可能性。

识别和纠正不良性能

一个性能良好的预测模型是指在新数据上产生可靠和令人满意的预测。模型无法持续产生良好预测的情况有两种,并且两者都取决于模型是如何训练的。一个训练不良的模型会导致欠拟合,而一个过度训练的模型会导致过拟合。

欠拟合

欠拟合意味着模型训练不良。要么是训练数据集没有足够的信息来推断强预测,要么是训练数据集上训练模型的算法不适合该上下文。算法参数设置不当或简单地不适合数据。

如果我们在验证集和训练集上测量预测误差,当模型欠拟合时,预测误差会很大。这是有道理的:如果模型无法预测训练数据,它将无法预测之前未见过的验证集的输出。欠拟合基本上意味着你的模型不起作用。

缓解此问题的常见策略包括:

  • 获取更多数据样本 - 如果问题来自一个太小或信息不足的数据集,获取更多数据可能会提高模型性能。

  • 添加更多特征,原始的或通过特征工程 - 通过取对数、平方、分箱、使用样条或幂函数。添加许多特征并观察这如何提高预测。

  • 选择另一个模型 - 支持向量机、随机森林、提升树、贝叶斯分类器在不同的上下文中都有不同的优势。

过拟合

过拟合发生在模型训练得如此之好,以至于它完美地拟合了训练数据,却无法处理新数据。

假设你有一个结果的唯一预测变量,并且数据遵循二次模式:

  1. 你在那些数据上拟合了线性回归![img/B05028_2_18_eqn.png],预测效果较弱。你的模型未能很好地拟合数据。训练误差和验证数据集上的误差水平都很高。

  2. 你在模型中添加了预测变量的平方![img/B05028_2_19_eqn.png],并发现你的模型做出了良好的预测。训练集和验证数据集上的误差都相当于更简单的模型,且更低。

  3. 如果你增加多项式特征的数量和幂,使得模型现在是![img/B05028_2_20_eqn.png],你最终会过于紧密地拟合训练数据。模型在训练数据集上的预测误差非常低,但无法对新数据进行预测。验证数据集上的预测误差仍然很高。

这是一个过拟合的例子。

下面的图表显示了关于先前二次数据集的一个过拟合模型示例,通过设置多项式回归的高阶(n = 16)。多项式回归拟合训练数据如此之好,以至于它将无法对新数据进行任何预测,而二次模型(n = 2)将更加稳健:

![img/image_02_035.png]

因此,检测过拟合的最佳方式是对比训练集和验证集上的预测误差。两个误差之间存在显著差距意味着过拟合。防止这种过拟合发生的一种方法是在模型上添加约束。在机器学习中,我们使用正则化。

线性模型上的正则化

随机梯度下降算法(SGD)通过最小化 N 个训练样本上真实值和预测值之间的误差来找到模型的最佳权重 {w[i]}

![img/B05028_2_22_eqn.png]

其中![img/image_02_038.png]是预测值,ŷ[i]是待预测的真实值;我们有N个样本,每个样本有n个维度。

正则化包括向先前方程中添加一个项,并最小化正则化误差:

![img/B05028_2_24_eqn.png]

![img/image_02_042.png]参数有助于量化正则化的程度,而R(w)是依赖于回归系数的正则化项。

通常考虑两种类型的权重约束:

  • L2 正则化作为系数平方的和:

![img/B05028_2_25_eqn.png]

  • L1 正则化作为系数绝对值的和:

![img/B05028_2_26_eqn.png]

正则化项R(w)引入的系数约束防止模型过度拟合训练数据。系数通过正则化相互关联,不能再紧密地与预测因子绑定。每种正则化都有其特征,并导致 SGD 算法的不同变体,我们现在介绍:

L2 正则化和岭

L2 正则化防止权重 {w[i]} 过度分散。对于非相关但可能具有意义的特征,上升的小权重不会与重要的相关特征关联的权重相比变得不重要。L2 正则化将强制执行相似的权重缩放。L2 正则化的直接后果是减少共线性对负面的影响,因为权重不能再相互分离。

带有 L2 正则化的随机梯度下降算法被称为岭算法

L1 正则化和 Lasso

L1 正则化通常会导致模型预测能力的某些损失。

L1 正则化的一种特性是迫使最小的权重为 0,从而减少模型中考虑的特征数量。当特征数量(n)与样本数量(N)相比很大时,这是一种期望的行为。L1 更适合具有许多特征的集合。

带有 L1 正则化的随机梯度下降算法被称为最小绝对收缩和选择算子(Lasso)算法。

在这两种情况下,模型的超参数如下:

  • SGD 算法的学习率 

  • 一个参数 来调整添加到模型中的正则化量

一种称为ElasticNet的第三种正则化方法,包括在模型中添加 L2 和 L1 正则化项。这以增加一个额外的超参数为代价,带来了两种正则化方案的优点。

在其他情况下,尽管专家们对哪种正则化更有效有不同意见(www.quora.com/What-is-the-difference-between-L1-and-L2-regularization),但共识似乎更倾向于 L2 正则化优于 L1 正则化。

在 Amazon ML 中,L2 和 L1 正则化都是可用的,而 ElasticNet 不可用。可用的正则化量限于三个值:温和的(10(-6))、中等(*10(-4))和激进(10^(-2))。

评估模型的性能

评估模型的预测性能需要定义其预测质量的一个度量。对于回归和分类都有几个可用的度量。在 Amazon ML 上下文中使用的度量如下:

  • RMSE 回归:均方根误差定义为真实结果值与其预测值之间差异的平方:

  • F-1 分数和 ROC-AUC 用于分类:Amazon ML 使用逻辑回归来解决二元分类问题。对于每个预测,逻辑回归返回一个介于 0 和 1 之间的值。这个值被解释为样本属于两个类别之一的概率。小于 0.5 的概率表示属于第一个类别,而大于 0.5 的概率表示属于第二个类别。因此,决策高度依赖于阈值值。我们可以修改这个值。

  • 表示一个类别为正,另一个为负,我们有以下表格中的四种可能性:

预测为是 预测为否
真实值:是 真正例(TP) 假负例(FN)(或第二类错误)
真实值:否 假正例(FP) 真负例
  • 这个矩阵被称为混淆矩阵(en.wikipedia.org/wiki/Confusion_matrix)。它定义了分类模型性能的四个指标:

    • TP:有多少个“是”被正确预测为“是”

    • FP:有多少个“否”被错误地预测为“是”

    • FN:有多少个“是”被错误地预测为“否”

    • TN:有多少个“否”被正确预测为“否”

  • 从这四个指标中,我们可以定义以下度量:

    • 召回率:这表示预测为正的实际正例数量。召回率也称为真正例率TPR)或灵敏度。它是检测的概率:

召回率 = (TP / TP + FN)

    • 精度作为所有预测为正的实际正例的分数:

精度 = (TP / TP + FP)

    • 假正例率是所有真实负例中错误预测为正例的数量。它是误报的概率:

FPR = FP / FP + TN

    • 最后,F1 分数定义为召回率和精度的加权平均值,其计算公式如下:

F1 分数 = 2 TP / ( 2 TP + FP + FN)

    • F1 分数总是在 0 到 1 之间,1 是最佳值,0 是最差值。

如前所述,这些分数都依赖于最初用于解释逻辑回归结果并决定预测属于哪个类别的初始阈值。我们可以选择改变这个阈值。这就是 ROC-AUC 发挥作用的地方。

如果你将真正例率(召回率)与不同决策阈值下的假正例率进行比较,你会得到一个如图所示的图形,称为接收者操作特征ROC 曲线

  • 对角线表示属于一个类别或另一个类别的概率相等。曲线越接近右上角,你的模型性能越好。

  • ROC 曲线自二战以来被广泛使用,当时它首次被发明出来用于在雷达信号中检测敌机。

  • 一旦您有了 ROC 曲线,您就可以计算曲线下面积AUC

  • AUC 将为您提供一个独特的分数,考虑了从 0 到 1 的所有可能的概率阈值。AUC 值越高,表示模型越好。

摘要

在本章中,我们关注了预测分析项目中的两个重要元素:数据和模型预测能力的评估。我们首先列出了与原始数据最常见的问题,它们对线性回归模型的影响以及解决这些问题的方法。现在,读者应该能够识别和处理缺失值、异常值、不平衡数据集和归一化问题。

我们还介绍了预测分析中最常见的两个问题:欠拟合和过拟合。L1 和 L2 正则化是 Amazon ML 平台的一个重要元素,它有助于克服过拟合,并使模型更加稳健,能够处理以前未见过的数据。

现在,我们已准备好在下一章深入探讨 Amazon 机器学习平台。

第三章:亚马逊机器学习工作流程概述

本章概述了一个简单亚马逊机器学习(Amazon ML)项目的流程,它包括三个主要阶段:

  1. 准备数据

  2. 训练和选择模型

  3. 进行预测

读者将学习如何在亚马逊机器学习平台上开始,如何设置账户,以及如何保护账户。在第二部分,我们将通过一个基于经典数据集的简单数值预测问题进行讲解。我们描述了上述三个步骤中每个步骤发生的情况、预期结果以及如何解释最终结果。

在本章中,我们将研究以下内容:

  • 开设亚马逊网络服务(AWS)账户

  • 设置账户

  • 标准亚马逊机器学习工作流程概述

开设亚马逊网络服务账户

在 AWS 账户注册非常简单。访问aws.amazon.com/,并选择创建 AWS 账户。如果您还没有 AWS 账户,可以利用免费层访问。新免费层账户在前 12 个月内可以享受一定限制内的免费资源。这些免费资源适用于许多 AWS 服务,如 EC2、S3、RDS 或 Redshift 等。不幸的是,亚马逊机器学习不包括在 AWS 免费层中。您将因使用亚马逊 ML 而付费。然而,由于亚马逊 ML 需要将您的数据存储在 S3 或另一个 AWS 资源(如 RedShift)上,而这些资源包括在免费层提供中,因此从免费层账户开始仍然是有利的。按照说明开设免费层账户。您将被要求提供您的姓名、电子邮件、地址、电话号码和支付信息。

如果您已经是亚马逊零售客户,通过使用不同的电子邮件地址来区分您的 AWS 账户和个人零售亚马逊账户将会更容易。这尤其适用于您计划为了专业目的从亚马逊机器学习(Amazon ML)中恢复成本的情况。使用相同的电子邮件地址同时为您的个人零售账户(如亚马逊 Prime、Echo 等)和 AWS 账户可能会造成混淆。

安全性

您用于开设 AWS 账户的电子邮件和密码被称为您的根凭证。它们为您提供了对每个 AWS 服务的根访问权限,这意味着对无限资源的无限访问。如果有人未经您的知晓获取了您的根凭证,他们可能会产生高额账单,并且他们可以在您的名义下通过您的账户执行所有类型的活动。强烈建议不要在日常的 AWS 操作中使用这种根访问权限,并尽可能设置最高安全级别的账户。

幸运的是,AWS 提供了许多通过用户、组、角色、策略、密码和多因素认证来控制和隔离对您的 AWS 账户访问的方法,以降低非法访问和欺诈的风险。配置和管理对您账户的访问是通过 AWS 身份和访问管理 (IAM) 服务完成的。IAM 服务是免费的。

IAM 服务允许您创建和配置您计划使用的所有 AWS 服务的访问点。这种级别的粒度非常重要。您可以通过用户、服务、角色来限制访问,甚至可以通过令牌启用临时访问,这些令牌有时间限制。启用多因素认证是另一个强烈建议您启用的功能,以防止未经授权访问您的 AWS 账户。

在本书的上下文中,我们将创建一个具有对仅两个服务(Amazon ML 和 S3)无限制访问的单个用户。随着我们在后续章节中需要时,我们将扩展此用户的访问权限到其他 AWS 服务。

我们不会在这里详细介绍 IAM 提供的所有功能,但我们强烈建议您熟悉 IAM 文档和最佳实践(docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html)。

设置账户

当您第一次设置账户时,您将获得访问您的根公共和秘密访问密钥的权限。这些密钥在您通过命令行界面(AWS CLI)管理 S3 上的数据以及 Amazon ML 中的模型时将非常有用。这两个密钥在创建时将仅对您可用,以便查看和复制。一旦浏览器中的页面关闭,您将无法再访问它们,并将需要创建新的。创建新的根密钥是通过访问“我的账户”|“安全凭证”完成的。值得注意的是,没有人可以访问您的密钥,即使是您账户的管理员也不例外。

我们不会在这里详细介绍您可以在 IAM 中采取的所有操作来配置、管理和保护对您账户的访问,因为 IAM 的全面介绍超出了本书的范围。您的访问管理需求和策略将取决于您组织的规模和安全限制。我们假设您是该账户的唯一个人用户,并且您不需要为其他用户、组或角色设置密码策略。然而,我们强烈建议您熟悉 IAM 文档并实施 IAM 最佳实践(aws.amazon.com/documentation/iam)。

我们将创建一个名为AML@Packt的新用户,并将在此书的其余部分使用此访问权限。唯一需要使用根访问(即您最初创建 AWS 账户时使用的密码)的情况是我们需要向AML@Packt用户添加或删除服务,例如,当我们需要用户使用 Amazon Athena 进行数据处理或Amazon Lambda进行脚本编写时。

IAM 仪表板可在console.aws.amazon.com/iam找到。它显示了您已创建多少个 IAM 资产(用户、角色、组等),以及如以下截图所示的安全状态:

此截图显示了以下三项已实施的内容:

  • 删除您的根访问密钥:这些密钥是在您创建账户时分配给您的。由于它们提供了对账户的无限制访问,您应该删除它们,并仅使用基于用户的访问密钥来访问您的账户。

  • 在您的根账户上激活多因素认证:登录并使用您的用户名和密码后,多因素认证(MFA)要求您输入一个六位数的代码。此代码可以通过短信或电子邮件发送给您,或者通过安装在您手机上的认证器应用程序提供。MFA 是一种易于实施且高效的账户访问安全保障方式。

  • 创建单个 IAM 用户:通过创建单个用户,您可以限制、管理他们的访问级别,并轻松地停用他们的账户。

您还可以创建组来分配用户权限并定义密码策略。

创建用户

让我们从创建您的用户开始。转到 IAM 仪表板console.aws.amazon.com/iam/,然后在左侧侧边栏中单击“用户”。用户创建过程很简单:

  1. 点击“创建新用户”。

  2. 输入您的用户名。保持为每个用户生成访问密钥的复选框选中。

到那时,您的用户已创建,您可以选择查看和下载用户安全凭证。一旦您离开此页面,这两个密钥中的一个,即秘密访问密钥将不再可用。请确保现在复制或下载这两个密钥。如果您丢失了密钥,您始终可以从 IAM > 用户仪表板为该用户重新创建一对新密钥。此截图是我们刚刚创建的AML@Packt用户的两个用户密钥的示例。您的密钥显然与这些不同:

就这样!您的AML@Packt用户已创建。您可以使用AML@Packt的访问密钥通过 API 和命令行访问和管理 AWS 服务。在此阶段,AML@Packt用户对所有 AWS 服务具有无限访问权限。为了限制该用户的访问范围,您需要将策略附加到该用户。

定义策略

策略声明用户或组可以访问哪些服务以及访问级别(只读、完全访问等)。您可以定义同时照顾几个服务的全局策略,并将它们附加到用户组,或者您可以将特定的单服务策略附加到您的用户。这就是我们现在要做的。

一旦您创建了用户并下载了其凭证,您将结束在 IAM 用户仪表板上,列出了您创建的所有用户。选择AML@Packt用户和权限选项卡。检查用户将需要访问的所有已知服务。在我们的例子中,我们选择了两个具有完全访问权限的服务,这将足够探索 Amazon 机器学习服务:

  • Amazon 机器学习完全访问

  • Amazon S3 完全访问

我们将在需要时为这些用户添加其他策略,以启用其他服务(Athena、RedShift、RDS 等)。

创建登录凭证

最后但同样重要的是,如果我们想使用AML@Packt用户登录 AWS 控制台,我们必须为该用户创建登录凭证。如图所示,安全凭证选项卡是您管理用户访问密钥、登录凭证和 SSH 密钥的地方:

图片

SSH 密钥与访问密钥不同。SSH 密钥将允许您 SSH 进入某些资产,例如EC2服务器。许多服务,包括机器学习服务,都不需要 SSH 密钥。另一方面,访问密钥用于程序化管理 AWS 服务。访问密钥对于设置使用命令行界面(AWS CLI)所需的凭证是必要的。

点击管理密码并为用户设置密码。这是用户AML@Packt的权限看起来是这样的:

图片

到目前为止,我们的 IAM 仪表板看起来是这样的:

图片

之前的截图显示了以下内容:

  • 我们已删除 root 访问密钥。我们不能再通过命令行或 API 以无限的方式程序化访问所有 AWS 服务。我们仍然可以以 root 身份登录 AWS 控制台以创建和管理人员的访问权限,但这些将取决于我们提供的策略和访问级别。

  • 我们已激活多因素认证(MFA),这是一种简单且非常有效的方式来保护从 root 访问凭证访问 AWS 服务。

  • 我们已创建了AML@Packt用户,我们将使用该用户登录 AWS,并在使用 API 或命令行时。

我们没有创建组或密码策略来进一步限制 root 访问,因为我们打算只通过AML@Packt用户访问 Amazon ML。

这里是您可以访问和使用 AWS 服务的不同方式的总结:

  • 使用您的 root 密码登录 AWS 控制台,并使用多因素认证(MFA)登录。

  • 使用带有AML@Packt用户的登录名和密码以及多因素认证(MFA)登录。AML@Packt用户只能使用 S3 和亚马逊 ML 服务。这相当限制性,但对于该用户来说,正是所需的访问量,不多也不少。

  • 通过命令行界面或 AWS SDK 使用 S3 和亚马逊 ML 访问密钥以编程方式访问 S3 和亚马逊 ML。

我们最初从一个用户开始,即根用户,可以以编程方式和通过控制台访问 AWS 提供的一切。新的设置更加安全,并且值得花费设置它的时间。我们对 IAM 角色和策略的新理解也将有助于我们开始使用不同的 AWS 服务时,因为服务将需要相互之间适当的访问权限。

在我们深入介绍标准亚马逊 ML 工作流程之前,我们需要简要谈谈区域问题。

选择区域

AWS 目前在全球 14 个区域运营数据中心。全球各地正在频繁地开设更多区域(aws.amazon.com/about-aws/global-infrastructure/)。大多数 AWS 服务要求您选择一个操作区域。一般规则是选择离您或访问您资源的最终用户最近区域。

选择区域可能取决于其他因素,这些因素可能因区域而异,包括:

  • 延迟

  • 定价

  • 安全和合规性规则

  • 服务的可用性

  • SLA–服务级别协议

  • 可再生能源的使用。在撰写本文时,AWS 提供两个碳中和区域(aws.amazon.com/about-aws/sustainability/),并正在积极创建更多区域。

亚马逊 ML 仅在弗吉尼亚北部和爱尔兰提供。您可以通过以下页面查看 AWS 在每个区域的可用性:aws.amazon.com/about-aws/global-infrastructure/regional-product-services/

政府云:根据 AWS 文档:AWS 政府云(美国)是一个隔离的 AWS 区域,旨在在云中托管敏感数据和受监管的工作负载,帮助客户支持其美国政府的合规性要求。IAM 是区域无关的,但有以下例外:IAM 在美国东部(弗吉尼亚北部)区域和政府云区域可用。在美国东部区域创建的用户和角色可以在除政府云区域以外的所有其他区域中使用。政府云 IAM 用户不能在政府云区域之外使用。

标准亚马逊机器学习工作流程概述

亚马逊机器学习服务可在console.aws.amazon.com/machinelearning/访问。亚马逊 ML 工作流程紧密遵循标准的科学数据工作流程,步骤包括:

  1. 提取数据并进行清理。使其可供算法使用。

  2. 将数据分为训练集和验证集,通常采用 70/30 的比例,每个部分预测因子的分布相等。

  3. 通过在训练数据集上训练多个模型并在验证数据集上比较它们的性能来选择最佳模型。

  4. 使用最佳模型对新数据进行预测。

如以下 Amazon ML 菜单所示,该服务围绕四个对象构建:

  • 数据源

  • 机器学习模型

  • 评估

  • 预测

数据源和模型也可以通过创建新的数据源和机器学习模型在同一流程中进行配置和设置。让我们更详细地看看每个步骤。

数据集

在本章的其余部分,我们将使用简单的通过身高和年龄预测体重数据集(来自Lewis Taylor (1967)),该数据集包含 237 个儿童的年龄、体重、身高和性别样本,可在v8doc.sas.com/sashtml/stat/chap55/sect51.htm找到。

此数据集由 237 行组成。每一行有以下预测因子:性别(F,M)、年龄(以为单位)、身高(以英寸为单位),我们试图预测这些儿童的体重(以为单位)。没有缺失值和异常值。变量范围足够接近,不需要归一化。简而言之,我们不需要对原始数据集进行任何预处理或清理。年龄、身高和体重是数值变量(实值),性别是分类变量。

我们将随机选择 20%的行作为保留子集,用于对先前未见过的数据进行预测,并保留其余 80%作为训练和评估数据。这种数据拆分可以在 Excel 或其他电子表格编辑器中完成:

  • 通过创建一个包含随机生成数字的新列

  • 按该列对电子表格进行排序

  • 选择 190 行用于训练,47 行用于预测(大约 80/20 的比例)

让我们命名训练集为LT67_training.csv,我们将用于预测的保留集命名为LT67_heldout.csv,其中LT67代表Lewis and Taylor,这是 1967 年创建此数据集的人。

与本书中提到的所有数据集、脚本和资源一样,训练和保留文件可在 GitHub 存储库github.com/alexperrier/packt-aml中找到。

注意,在年龄、性别、身高和体重方面,两个子集的分布应相似。我们希望用于预测的数据显示出的模式与我们将用于训练和优化模型的数据相似。

在本节的其余部分,我们将执行以下操作:

  1. 在 S3 上加载数据。

  2. 让 Amazon ML 推断模式并转换数据。

  3. 创建模型。

  4. 评估模型的性能。

  5. 在保留的测试数据集上进行预测。

在 S3 上加载数据

按以下步骤在 S3 上加载训练集和保留集:

  1. 前往您的 S3 控制台console.aws.amazon.com/s3

  2. 如果尚未创建,请创建一个存储桶。存储桶基本上是跨所有 S3 唯一命名的文件夹。我们创建了一个名为aml.packt的存储桶。由于该名称已被占用,如果您正在跟随此演示,您将不得不选择另一个存储桶名称。

  3. 点击您创建的存储桶名称,并通过从操作下拉菜单中选择“上传”来上传LT67_training.csvLT67_heldout.csv文件:

图片

这两个文件都很小,只有几 KB,因此在此练习中的托管成本应该微乎其微。

注意,对于每个文件,通过在右侧选择“属性”选项卡,您可以指定如何访问您的文件,哪些用户、角色、组或 AWS 服务可以下载、读取、写入和删除文件,以及它们是否应该可以从公开网络访问。当在 Amazon ML 中创建数据源时,您将被提示授予 Amazon ML 访问您的输入数据的权限。您现在可以在 S3 中指定这些文件的访问规则,或者稍后简单地授予访问权限。

我们的数据现在存储在云中的 S3 存储桶中。我们需要通过创建数据源来告诉 Amazon ML 在哪里可以找到这些输入数据。我们将首先为训练文件ST67_training.csv创建数据源。

声明数据源

前往 Amazon ML 仪表板,点击“创建新... | 数据源和 ML 模型”。我们将使用默认的快速流程:

图片

如以下截图所示,您需要指定LT67_training.csv文件的路径{S3://bucket}{path}{file}。请注意,S3 位置字段会自动填充您用户可用的存储桶名称和文件名称:

图片

指定数据源名称有助于组织您的 Amazon ML 资产。通过点击“验证”,Amazon ML 将确保它有权访问该文件。如果需要授予文件访问权限,您将如以下截图所示被提示进行操作:

图片

只需点击“是”以授予访问权限。此时,Amazon ML 将验证数据源并分析其内容。

创建数据源

一个 Amazon ML 数据源由以下组成:

  • 数据文件的位置:数据文件在 Amazon ML 中不会被复制或克隆,而是从 S3 访问

  • 包含 CSV 文件中变量类型信息的模式:

    • 分类

    • 文本

    • 数值(实值)

    • 二进制

如我们将在第四章“加载数据集和准备数据”中看到,您可以为 Amazon ML 提供自己的模式或修改由 Amazon ML 创建的模式。

此时,Amazon ML 对您的训练数据集中的数据类型有了相当好的了解。它已识别不同类型的变量,并知道它有多少行:

图片

通过点击“继续”进入下一步,查看 Amazon ML 从数据集中推断出的模式,如下一张截图所示:

图片

到那时,Amazon ML 需要知道你正在尝试预测哪个变量。务必告诉 Amazon ML 以下信息:

  • CSV 文件的第一行包含列名

  • 目标是 重量

我们看到 Amazon ML 正确推断出以下信息:

  • 性别 是分类的

  • 年龄, 身高体重 是数值型(连续的实数值)

由于我们选择了一个数值型变量作为目标,Amazon ML 将使用线性回归作为预测模型。对于二元或分类值,我们会使用逻辑回归。这意味着 Amazon ML 将尝试找到最佳的 a, b, 和 c 系数,以便预测的重量尽可能接近数据中观察到的实际重量:

预测重量 = a * 年龄 + b * 身高 + c * 性别

Amazon ML 将询问你的数据是否包含行标识符。在我们的案例中,没有。行标识符在你想了解每行的预测结果或在你项目的后期添加额外的列时很有用。行标识符仅用于参考,服务不会使用它们来构建模型。

你将被要求审查数据源。你可以返回到之前的每个步骤并编辑模式、目标和输入数据的参数。现在数据已告知 Amazon ML,下一步是设置训练模型的算法参数。

模型

我们为训练和评估设置选择了默认参数。Amazon ML 将执行以下操作:

  • 根据从数据集中推断出的统计属性创建数据转换的食谱

  • 将数据集 (ST67_training.csv) 分为训练部分和验证部分,比例为 70/30。分割策略假设数据已经打乱,可以顺序分割。

在 第四章,加载数据集和准备数据,我们将走更长的路,直接在食谱、模式和验证分割上工作。在下一页,你将被要求审查你刚刚创建的模型。

该食谱将用于以类似的方式转换训练集和验证集的数据。Amazon ML 建议的唯一转换是将分类变量 性别 转换为二元变量,例如 m = 0f = 1。不需要其他转换。

模型的默认高级设置如下截图所示:

图片

我们可以看到 Amazon ML 会遍历数据 10 次,每次遍历都会打乱数据分割。它将使用基于回归系数平方和的 L2 正则化策略来防止过拟合。我们将在稍后使用我们的LT67_heldout.csv数据集来评估模型的预测能力。

正则化有 3 个级别,有温和(10 的^-6 次方)、中等(10 的^-4 次方)或激进(10 的^-2 次方)的设置,每个值比前一个值更强。默认设置是温和最低的,正则化常数为0.00001(10 的^-6 次方),这意味着 Amazon ML 不预期在这个数据集上有太多的过拟合。当预测器的数量(在我们的案例中是三个)远小于样本数量(训练集为 190 个)时,这很有意义。

点击创建 ML 模型按钮将启动模型创建。这需要几分钟的时间来解析,具体取决于数据集的大小和复杂性。你可以通过刷新模型页面来检查其状态。在此期间,模型状态保持待定。

在那个点上,Amazon ML 将我们的训练数据集分成两个子集:一个训练集和一个验证集。它将使用数据的训练部分来训练算法的几个设置,并根据其在训练数据上的性能选择最佳设置。然后,它将相关的模型应用于验证集,并为该模型返回一个评估分数。默认情况下,Amazon ML 将按顺序取前 70%的样本用于训练,剩余的 30%用于验证。

值得注意的是,Amazon ML 不会创建两个额外的文件并将它们存储在 S3 上,而是从我们之前定义的初始数据源中创建两个新的数据源。每个新的数据源都是通过以下数据重排JSON 配方从原始数据源获得的:

{
  "splitting": {
    "percentBegin": 0,
    "percentEnd": 70
  }
}

你可以在数据源仪表板中看到这两个新的数据源。现在有三个数据源可用,而最初只有一个,如下面的截图所示:

图片

在模型训练过程中,Amazon ML 会在训练数据上多次运行随机梯度算法,并使用不同的参数:

  • 以 10 的幂次递增的方式调整学习率:0.01,0.1,1,10 和 100。

  • 在每次路径之前,对训练数据进行多次遍历并打乱样本。

  • 在每次遍历中,计算预测误差,即均方根误差 (RMSE),以估计相对于上一次遍历的改进程度。如果 RMSE 的下降并不真正显著,则认为算法已经收敛,不应再进行进一步的遍历。

  • 在遍历结束时,具有最低 RMSE 的设置获胜,并且相关的模型(回归的权重)被选为最佳版本。

一旦模型完成训练,Amazon ML 就会在验证数据源上评估其性能。一旦评估本身也准备好了,你就可以访问模型的评估结果。

模型的评估

Amazon ML 使用标准的 RMSE 指标进行线性回归。RMSE 定义为真实值与预测值之间差异的平方和:

图片

在这里,ŷ是预测值,y是我们想要预测的真实值(在我们的例子中是孩子的体重)。预测值与真实值越接近,RMSE 就越低。较低的 RMSE 意味着更好的、更准确的预测。

与基线比较

RMSE 是一个相对量,而不是一个绝对量。在某个特定情况下,RMSE 为 100 可能很好,而在另一个情况下,RMSE 为 10 可能表明预测不佳。因此,有一个基线来比较我们的模型是很重要的。每个 Amazon ML 评估都提供了一个基线,这个基线根据问题的性质(回归、二分类或多分类分类)而不同。基线是我们使用最简单和最明显的模型所能获得的分数:

  • 回归的基线是由一个总是预测目标均值模型的

  • 二分类的基线是 AUC 为0.5,这是随机分配 0 或 1 的模型的分数

  • 对于多分类分类问题,基线是预测最常见类别的模型的宏平均F1分数

在我们当前的情况下,当预测学生的体重时,我们的训练数据集的基线 RMSE 为18.71,而我们的模型给出的 RMSE 为14.44。我们的模型比简单地预测所有学生体重的平均值好 22.8%。

在评估中还需要关注的是残差的分布,残差定义为ŷy之间的差异:

图片

如我们在第一章,“机器学习与预测分析简介”中看到的,线性回归被认为是有效的条件之一是残差是独立、同分布的,并且它们的分布遵循高斯分布钟形曲线。Amazon ML 显示了残差的直方图,这有助于我们直观地评估残差分布的高斯性质。

当形状向右(左)弯曲时,这意味着更多的预测值大于(小于)它们的靶值。在这两种情况下,结论是数据中仍有未被模型捕捉到的信号。当残差的分布以 0 为中心时,线性回归被认为是有效的。以下图表显示了当前回归模型的残差分布:

图片

我们可以看到,我们的预测通常比目标值要大,这表明我们的模型可能还需要改进。数据中存在一些模型尚未利用的信息。

注意,可能无法使用线性回归模型提取这些模式中的信息。其他更复杂的模型可能更适合这个特定的数据集。将数据转换为创建新变量也可能是构建更好模型的关键。残差直方图是我们模型质量的良好简单诊断工具,因为它表明模型在某些方面可以改进。

进行批量预测

现在我们有一个经过适当训练并在其他模型中选出的模型。我们可以用它来对新数据进行预测。

记住,在本章的开头,在“在 S3 上加载数据”部分,我们上传了两个数据集到 S3,即训练数据集和保留数据集。我们已经使用训练数据集创建了最佳模型。现在我们将将该模型应用于保留数据集。

批量预测包括将模型应用于数据源以对该数据源进行预测。我们需要告诉亚马逊机器学习我们想在哪些数据上应用哪个模型。

批量预测与流式预测不同。在批量预测中,所有数据已经作为数据源提供,而对于流式预测,数据将随着其可用性被输入到模型中。数据集在事先并不完整可用。

在主菜单中选择批量预测以访问仪表板预测,然后点击创建新的预测:

第一步是选择模型仪表板中可用的一个模型。你应该选择具有最低均方根误差(RMSE)的那个:

下一步是将数据源与您刚刚选择的模型关联起来。在本章的开头(在“在 S3 上加载数据”部分),我们已经将保留数据集上传到 S3,但尚未用它来创建数据源。我们现在将这样做。

当在下一屏幕上被要求选择数据源时,请确保勾选“我的数据在 S3 中,我需要创建数据源”,然后选择应该已经存在于您的 S3 存储桶中的保留数据集:

不要忘记告诉亚马逊机器学习,文件的第一行包含列。

在我们当前的项目中,我们的保留数据集还包含学生体重的真实值。在现实世界的项目中,这不会是“真实”数据的情况,因为在现实世界的项目中,真实值是真正未知的。然而,在我们的情况下,这将允许我们计算预测的均方根误差(RMSE)分数并评估这些预测的质量。

最后一步是点击验证按钮,等待几分钟:

  • 亚马逊机器学习将在新的数据源上运行模型,并将预测结果以 CSV 文件的形式生成。

  • 与评估和模型构建阶段相反,我们现在有了真实的预测。也不再给出与这些预测相关的分数。

  • 几分钟后,您会在您的 S3 存储桶中注意到一个新的批量预测文件夹。此文件夹包含一个manifest文件和一个结果文件夹。manifest 文件是一个 JSON 文件,包含初始数据源的路径和结果文件的路径。结果文件夹包含一个 gzip 压缩的 CSV 文件:

图片

未压缩的 CSV 文件包含两列,trueLabel,来自保留集的初始目标,以及score,对应预测值。我们可以通过以下步骤直接在电子表格中轻松计算这些结果的 RMSE:

  1. 创建一个新列,该列包含两个列之间差异的平方。

  2. 求和所有行。

  3. 对结果取平方根。

下图展示了我们如何创建第三个列C,作为trueLabel列 A 和分数(或预测值)列 B 之间的平方差:

图片

如下截图所示,计算列C的平均值并取平方根得到 RMSE 为 11.96,这甚至比我们在评估阶段获得的 RMSE(RMSE 14.4)要好得多:

图片

保留集上的 RMSE 比验证集上的 RMSE 好,这意味着我们的模型没有过度拟合训练数据,因为它在新数据上的表现甚至比预期更好。我们的模型是健壮的。

下图的左侧显示了保留集中所有样本的 True(三角形)和 Predicted(圆形Weight值。右侧显示了残差的直方图。与我们在验证集上观察到的残差直方图类似,我们观察到残差没有集中在0上。我们的模型有高估学生权重的倾向:

图片

摘要

在本章的第一部分,我们介绍了亚马逊账户的创建以及如何正确设置和确保对您的 AWS 账户的访问安全。通过结合多因素认证和用户创建,我们能够迅速达到令人满意的水平的安全性。AWS 是一个功能强大的平台,拥有强大的工具,实施最佳访问保护至关重要。

在第二部分,我们介绍了简单线性回归预测中涉及的不同步骤,从将数据加载到 S3,通过数据源使数据可访问给亚马逊机器学习,创建模型,解释评估,以及在新的数据上做出预测。

亚马逊机器学习流程顺畅,促进了固有的数据科学循环:数据、模型、评估和预测。

在接下来的章节中,我们将进一步探讨数据准备和数据转换。这次我们将使用一个经典的二分类问题,即泰坦尼克号上的生存,它基于一个非常有趣的 数据集。

第四章:加载数据集并准备数据

数据准备涉及数据清洗和特征工程。这是机器学习项目中耗时最长的一部分。Amazon ML 提供了强大的功能来转换和切片数据。在本章中,我们将创建 Amazon ML 用于训练和选择模型的所需数据源。创建数据源涉及三个步骤:

  1. 在 AWS S3 上使数据集可用。

  2. 使用模式向 Amazon ML 告知数据的性质。

  3. 使用特征工程的配方转换初始数据集。

在第二部分,我们将扩展 Amazon ML 数据修改功能,以便通过使用 Amazon SQL 服务 Athena 来执行强大的特征工程和数据清洗。Athena 是一个无服务器的基于 SQL 的查询服务,非常适合预测分析环境中的数据处理。

处理数据集

没有数据集,你无法进行预测分析。尽管我们被数据包围,但找到适合预测分析的数据集并不总是那么简单。在本节中,我们介绍了一些免费可用的资源。然后,我们将重点介绍我们将用于几章的特定数据集。Titanic数据集是预测分析的入门级经典数据集。

寻找开放数据集

在线有大量的数据集存储库,从地方到全球的公共机构,从非营利组织到数据驱动的初创公司。以下是一些适合预测分析的开放数据集资源的小列表,这绝对不是详尽的列表:

这个 Quora 帖子指向了许多其他有趣的数据源:www.quora.com/Where-can-I-find-large-datasets-open-to-the-public。你还可以在 Reddit 上请求特定的数据集,www.reddit.com/r/datasets/

以下初创公司以数据为中心,并提供对丰富数据仓库的开放访问:

AWS 公共数据集:AWS 托管了各种公共数据集,如百万歌曲数据集、人类基因组映射、美国人口普查数据以及天文学、生物学、数学、经济学等多个领域的许多其他数据集。这些数据集大多可以通过 EBS 快照访问,尽管一些数据集可以直接在 S3 上访问。这些数据集很大,从几个吉字节到几个太字节不等,它们不是用于在本地机器上下载的,而是仅通过 EC2 实例访问(有关更多详细信息,请参阅docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-public-data-sets.html)。AWS 公共数据集可通过aws.amazon.com/public-datasets/访问。

介绍泰坦尼克号数据集

在本章以及第五章“模型创建”和第六章“预测和性能”中,我们将使用经典的Titanic数据集。数据包括 1309 名泰坦尼克号乘客的人口统计和旅行信息,目标是预测这些乘客的生存情况。完整的泰坦尼克号数据集可以从范德比尔特大学医学院生物统计学系(Department of Biostatistics)(biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic3.csv)以多种格式获取。Encyclopedia Titanica网站(www.encyclopedia-titanica.org/)是关于泰坦尼克号的参考网站。它包含了关于泰坦尼克号的所有事实、历史和数据,包括乘客和船员的完整名单。泰坦尼克号数据集也是 Kaggle 上入门竞赛的主题(www.kaggle.com/c/titanic,需要通过 Kaggle 开户)。您也可以在本书的 GitHub 仓库github.com/alexperrier/packt-aml/blob/master/ch4中找到 CSV 版本。

泰坦尼克号数据包含文本、布尔、连续和分类变量的混合。它表现出有趣的特征,如缺失值、异常值和适合文本挖掘的文本变量,这是一个丰富的数据集,将使我们能够展示数据转换。以下是 14 个属性的简要总结:

  • pclass: 乘客等级(1 = 一等;2 = 二等;3 = 三等)

  • survival: 一个布尔值,表示乘客是否幸存(0 = 否;1 = 是);这是我们目标

  • name: 一个信息丰富的字段,因为它包含头衔和姓氏

  • sex: 男性/女性

  • age: 年龄,其中很大一部分值是缺失的

  • sibsp: 船上兄弟姐妹/配偶的数量

  • parch: 船上父母/孩子的数量

  • ticket: 票号。

  • fare: 乘客票价(英镑)。

  • cabin: 舱位。舱位的位置是否会影响生存的机会?

  • embarked: 上船港口(C = Cherbourg;Q = Queenstown;S = Southampton)

  • boat: 救生艇,许多值缺失

  • body: 身体识别号码

  • home.dest: 家/目的地

查看更多关于这些变量的详细信息,请参阅campus.lakeforest.edu/frank/FILES/MLFfiles/Bio150/Titanic/TitanicMETA.pdf

我们有 1309 条记录和 14 个属性,其中三个我们将丢弃。home.dest属性现有的值太少,boat属性仅适用于幸存乘客,而body属性仅适用于未幸存的乘客。我们将在使用数据模式时稍后丢弃这三个列。

准备数据

现在我们有了初始的原始数据集,我们将对其进行洗牌,分割成训练集和保留子集,并将其加载到 S3 存储桶中。

分割数据

如我们在第二章,“机器学习定义和概念”中看到的,为了构建和选择最佳模型,我们需要将数据集分成三部分:训练集、验证集和测试集,通常的比例是 60%、20%和 20%。训练集和验证集用于构建多个模型并选择最佳模型,而保留集用于对先前未见数据进行的最终性能评估。我们将在第六章,“预测和性能”中使用保留子集来模拟使用我们在第五章,“模型创建”中构建的模型进行的批量预测。

由于 Amazon ML 负责将用于模型训练和模型评估的数据集分割成训练集和验证集,我们只需要将我们的初始数据集分成两部分:用于模型构建和选择的全球训练/评估子集(80%),以及用于预测和最终模型性能评估的保留子集(20%)。

在分割前打乱顺序:如果您从范德堡大学网站下载原始数据,您会注意到它按pclass(乘客等级)和name列的字母顺序排序。前 323 行对应头等舱乘客,其次是二等舱(277)和三等舱(709)乘客。在分割数据之前打乱数据非常重要,以确保所有不同的变量在每个训练和保留子集中都有相似的分布。您可以直接在电子表格中通过创建一个新列,为每一行生成一个随机数,然后按该列排序来打乱数据。

您可以在github.com/alexperrier/packt-aml/blob/master/ch4/titanic.csv找到本书的已打乱顺序的titanic.csv文件。除了打乱数据外,我们还从“姓名”列中移除了标点符号:逗号、引号和括号,这些在解析 CSV 文件时可能会引起混淆。

我们最终得到两个文件:包含 1047 行的titanic_train.csv和包含 263 行的titanic_heldout.csv。下一步是将这些文件上传到 S3,以便 Amazon ML 可以访问它们。

在 S3 上加载数据

AWS S3 是 AWS 主要服务之一,专注于托管文件和管理其访问权限。S3 中的文件可以是公开的,对互联网开放,或者仅限于特定用户、角色或服务访问。S3 还被 AWS 广泛用于存储日志文件或结果(预测、脚本、查询等)的操作。

S3 中的文件是围绕“桶”的概念组织的。桶是具有类似域名名称的唯一占位符。S3 中的一个文件将有一个唯一的定位 URI:s3://bucket_name/{文件夹路径}/filename。桶名在 S3 中是唯一的。在本节中,我们将为我们的数据创建一个桶,上传 titanic 训练文件,并打开其访问权限以供 Amazon ML 使用。

我们将在第七章,命令行和 SDK中展示,S3 中的文件可以通过命令行完全管理。现在,我们将使用 S3 在线界面。前往console.aws.amazon.com/s3/home,如果您还没有账户,请创建一个 S3 账户。

S3 定价: S3 根据您托管文件的总容量以及文件传输量(取决于文件托管区域)进行收费。在撰写本文时,对于小于 1TB 的容量,AWS S3 在美国东部地区的月费为每 GB $0.03。所有 S3 价格信息可在aws.amazon.com/s3/pricing/找到,同时也可在calculator.s3.amazonaws.com/index.html的 AWS 成本计算器中查看。

创建桶

一旦您创建了 S3 账户,下一步就是为您的文件创建一个桶。点击“创建桶”按钮:

图片

  1. 名称和区域:由于 S3 中的存储桶名称是唯一的,您必须选择一个尚未被占用的存储桶名称。我们为我们的存储桶选择了名称aml.packt,并且我们将在整个书中使用此存储桶。至于区域,您应该始终选择一个与访问文件的人或应用程序最接近的区域,以减少延迟和价格。

  2. 设置版本控制、日志记录和标签:版本控制将保留每个文件版本的副本,这可以防止意外删除。由于版本控制和日志记录会引发额外的成本,我们选择禁用它们。

  3. 设置权限

  4. 审查并保存

下面的截图说明了这些步骤:

图片

加载数据

要上传数据,只需单击上传按钮并选择我们之前创建的titanic_train.csv文件。此时,您应该已经将训练数据集上传到您的 AWS S3 存储桶。我们在aml.packt存储桶中添加了一个/data文件夹来隔离我们的对象。当存储桶也包含由 Amazon ML 创建的文件夹时,这将会很有用。

在此阶段,只有存储桶的所有者(即您)能够访问和修改其内容。我们需要授予 Amazon ML 服务读取数据和向存储桶添加其他文件的权限。在创建 Amazon ML 数据源时,我们将被提示通过 Amazon ML 控制台授予这些权限。我们还可以提前修改存储桶的策略。

授予权限

我们需要编辑aml.packt存储桶的策略。为此,我们必须执行以下步骤:

  1. 单击您的存储桶。

  2. 选择“权限”选项卡。

  3. 在下拉菜单中,选择如图所示的“存储桶策略”。这将打开一个编辑器:

图片

  1. 将以下 JSON 文件粘贴进去。确保将{YOUR_BUCKET_NAME}替换为您的存储桶名称并保存:
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "AmazonML_s3:ListBucket",
              "Effect": "Allow",
              "Principal": {
                "Service": "machinelearning.amazonaws.com"
              },
              "Action": "s3:ListBucket",
              "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}",
              "Condition": {
                "StringLike": {
                  "s3:prefix": "*"
                }
              }
            },
            {
              "Sid": "AmazonML_s3:GetObject",
              "Effect": "Allow",
              "Principal": {
                "Service": "machinelearning.amazonaws.com"
              },
              "Action": "s3:GetObject",
              "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}/*"
            },
            {
              "Sid": "AmazonML_s3:PutObject",
              "Effect": "Allow",
              "Principal": {
                "Service": "machinelearning.amazonaws.com"
              },
              "Action": "s3:PutObject",
              "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}/*"
            }
          ]
        }

关于此政策的更多详细信息可在docs.aws.amazon.com/machine-learning/latest/dg/granting-amazon-ml-permissions-to-read-your-data-from-amazon-s3.html找到。再次强调,此步骤是可选的,因为当您创建数据源时,Amazon ML 会提示您访问存储桶。

格式化数据

Amazon ML 在逗号分隔值文件(.csv)上工作,这是一种非常简单的格式,其中每一行是一个观察值,每一列是一个变量或属性。然而,还有一些条件需要满足:

  • 数据必须使用字符集(如 ASCII、Unicode 或 EBCDIC)以纯文本格式编码

  • 所有值必须用逗号分隔;如果值中包含逗号,则应将其用双引号括起来

  • 每个观察值(行)的大小必须小于 100k

关于分隔行的换行符也存在一些条件。在使用 Excel 在 OS X (Mac) 上时必须特别注意,如本页所述:docs.aws.amazon.com/machine-learning/latest/dg/understanding-the-data-format-for-amazon-ml.html

关于其他数据文件格式呢?

很遗憾,Amazon ML 数据源仅兼容 CSV 文件和 Redshift 或 RDS 数据库,并且不接受 JSON、TSV 或 XML 等格式。然而,其他服务如无服务器数据库服务 Athena,则接受更广泛的格式。我们将在本章后面看到如何使用 Athena 来绕过 Amazon ML 文件格式限制。

现在数据已位于 S3 上,下一步是通过创建数据源来告诉 Amazon ML 其位置。

创建数据源

当使用 Amazon ML 时,数据始终位于 S3 中,并且不会在 Amazon ML 中重复。数据源是元数据,指示输入数据的位置,允许 Amazon ML 访问它。创建数据源还会生成与数据相关的描述性统计信息和包含变量性质信息的模式。基本上,数据源为 Amazon ML 提供了所有必要的信息,以便能够训练模型。以下是你需要遵循的创建数据源的步骤:

  1. 前往 Amazon 机器学习:console.aws.amazon.com/machinelearning/home

  2. 点击“入门”,你将可以选择访问仪表板或标准设置。这次请选择标准设置:

执行以下步骤,如以下截图所示:

  1. 选择 S3 位置。

  2. 在 s3 位置字段中开始输入存储桶的名称,列表文件夹和文件应该会显示出来。

  3. 选择 titanic_train.csv 文件。

  4. 将数据源命名为“Titanic 训练集”,然后点击“验证”。

如果你之前没有设置存储桶策略,系统会要求你授予 Amazon ML 读取 S3 中文件的权限;点击“是”以确认:

你将看到确认信息,表明你的数据源已成功创建并可由 Amazon ML 访问:

点击“继续”以完成数据源创建。此时,Amazon ML 已扫描 titanic_train.csv 文件并推断出每列的数据类型。这些元信息被重新组合在模式中。

其他数据源:亚马逊机器学习允许您将 Redshift 或 RDS 数据库定义为数据源,而不是 S3。Amazon Redshift 是一个“快速、完全托管、PB 级数据仓库解决方案”。它比 S3 设置起来要复杂得多,但它可以处理更大和更复杂的数据量。Redshift 允许您使用行业标准 ODBC/JDBC 连接通过任何 SQL 客户端分析数据。我们将在第八章,从 Redshift 创建数据源中回到 Redshift。

验证数据模式

数据模式是元信息,是数据文件的字典。它通知亚马逊机器学习数据集中每个变量的类型。亚马逊机器学习将使用这些信息来正确读取、解释、分析和转换数据。一旦创建,数据模式可以保存并用于同一数据的其他子集。尽管亚马逊机器学习在猜测数据集的性质方面做得很好,但始终进行双重检查并在必要时进行一些必要的调整是一个好主意。

默认情况下,亚马逊机器学习假定您的文件的第一行包含一个观测值。在我们的例子中,titanic_train.csv文件的第一行包含变量的名称。请确保通过在该表单中选择是来确认这一点:

图片

亚马逊机器学习根据四种数据类型对您的数据进行分类:

  • 二进制:0 或 1,是或否,男性或女性,假或真(例如titanic数据集中的survived

  • 分类:可以取有限个数值的变量,可以是数字或字符(例如pclassembarkedsibspparch

  • 数值:具有连续值的数量(例如agefare

  • 文本:自由文本(例如nametickethome.dest

注意,Titanic数据集中的一些变量可能与不同的数据类型相关联。例如,ticketcabinhome.dest可以解释为文本或分类值。age属性可以通过分箱从数值值转换为分类值。sex属性也可以解释为二进制、分类或甚至是文本类型。将sex属性作为二进制将需要将其值从男性/女性转换为 0/1,因此尽管它只取两个值,我们仍将其保留为分类。

模型构建算法将根据变量的数据类型以不同的方式处理变量及其值。在某些情况下,探索某些变量的不同数据类型可能值得,以便提高预测的准确性。

以下截图显示了亚马逊机器学习从我们的titanic_train.csv数据中推断出的模式:

图片

注意,这里显示的采样值将因您的数据模式而不同,因为数据已经被随机化。

您可以选择保留亚马逊机器学习默认选择的选项,或者进行一些修改。以下是一个例子:

  • sibspparch,分别是兄弟姐妹和父母的人数,如果我们想要重新组合具有相似家庭成员数量的乘客,它们可以是分类的而不是数值的。

  • pclass应该更正为分类而不是数值,因为pcalss只有三个可能的值:1、2 和 3。

  • cabin可以解释为分类的,因为船舱的数量是有限的。然而,数据表明该字段可以进一步解析。它有如 C22 C26 之类的值,这似乎表明不是一个船舱,而是两个。文本数据类型将更合适。

Amazon ML 需要知道我们旨在预测的属性。在下一屏,你将被要求选择目标。确认你是否计划使用此数据集来创建或评估一个机器学习模型,并选择survived属性,如下面的截图所示:

图片

决定数值和分类之间的选择:数值是有序的,分类值不是。当将变量设置为数值时,你实际上是在告诉模型这些值是有序的。当构建模型时,这种信息对算法是有用的。分类变量之间不暗示任何顺序。

回到sibspparch属性的例子,这些变量具有有限数量的值(sibsp为 0 到 8,parch为 0 到 9)并且可能是分类的。然而,值的顺序包含一些重要的信息。八个兄弟姐妹多于一个,表明这是一个大家庭。因此,保留数值作为值也是有意义的。

分类值和独热编码:在线性回归的情况下,分类值会被独热编码。当有N个类别时,它们会被分解成N-1个二进制变量。例如,一个只有三个值ABC的变量会被分解成两个二进制变量is_it_A?is_it_B?,它们只接受真和假值。请注意,没有必要定义第三个is_it_C?二进制变量,因为它可以直接从is_it_A?is_it_B?的值中推断出来。在泰坦尼克号的案例中,我们有一个名为embarked的变量的三个值;Amazon ML 将创建两个二进制变量,相当于在皇后镇登船的乘客在瑟堡登船的乘客,第三个变量在南安普顿登船的乘客将从前两个变量的值中推断出来。

目标类型决定了 Amazon ML 将使用的模型类型。由于我们选择了二元目标,Amazon ML 将使用逻辑回归来训练模型。数值目标将意味着线性回归,而分类目标也将是逻辑回归,但这次是多类分类。Amazon ML 会确认它将使用的模型类型,如下面的截图所示:

图片

最后一步是告诉亚马逊机器学习我们的数据不包含行标识符,并最终审查我们的数据源。数据源创建现在处于待定状态;根据其大小,完成可能需要几分钟。

重新使用模式

模式是一个 JSON 文件,这意味着我们可以从头开始为我们自己的数据创建一个,或者修改亚马逊机器学习生成的模式。我们现在将修改亚马逊机器学习创建的模式,将其保存到 S3,并使用它创建一个新的数据源,该数据源不包含bodyboathome.dest变量。

点击查看输入模式,如图所示:

这为我们提供了原始的 JSON 格式模式。将其保存在本地机器上,文件名为titanic_train.csv.schema。我们将在 S3 的同一存储桶/文件夹中加载此文件,该文件夹包含titanic_train.csv文件。通过在数据 CSV 文件名中添加.schema,我们允许亚马逊机器学习自动将模式文件与数据文件关联,并绕过其自身模式的创建。

使用您最喜欢的编辑器打开模式文件,并按如下方式编辑:

  • excludedAttributeNames字段中添加home.destbodyboat

  • sibspparch的数据类型从CATEGORICAL更改为NUMERIC

注意,尽管我们想要删除字段boatbodyhome.dest,我们仍然需要在模式中声明它们及其数据类型。您的 JSON 文件现在应如下所示:

{
  "version" : "1.0",
  "rowId" : null,
  "rowWeight" : null,
  "targetAttributeName" : "survived",
  "dataFormat" : "CSV",
  "dataFileContainsHeader" : true,
  "attributes" : [ {
    "attributeName" : "pclass",
    "attributeType" : "CATEGORICAL"
    }, {
     "attributeName" : "survived",
     "attributeType" : "BINARY"
    }, {
     "attributeName" : "name",
     "attributeType" : "TEXT"
    }, {
     "attributeName" : "sex",
     "attributeType" : "BINARY"
    }, {
     "attributeName" : "age",
     "attributeType" : "NUMERIC"
    }, {
     "attributeName" : "sibsp",
     "attributeType" : "NUMERIC"
    }, {
     "attributeName" : "parch",
     "attributeType" : "NUMERIC"
    }, {
     "attributeName" : "ticket",
     "attributeType" : "TEXT"
    }, {
     "attributeName" : "fare",
     "attributeType" : "NUMERIC"
    }, {
     "attributeName" : "cabin",
     "attributeType" : "TEXT"
    }, {
     "attributeName" : "embarked",
     "attributeType" : "CATEGORICAL"
    }, {
     "attributeName" : "boat",
     "attributeType" : "CATEGORICAL"
    }, {
     "attributeName" : "body",
     "attributeType" : "NUMERIC"
    }, {
     "attributeName" : "home.dest",
     "attributeType" : "TEXT"
    } ],
     "excludedAttributeNames" : ["home.dest", "body", "boat"]
    }

将修改后的titanic_train.csv.schema文件上传到与titanic_train.csv数据文件相同的 S3 位置。您的存储桶/文件夹现在应如下所示:

现在我们创建一个新的数据源。由于模式文件和数据文件具有相同的名称,并且位于同一位置,亚马逊机器学习将使用我们提供的模式:

  1. 返回亚马逊机器学习仪表板,并在主菜单中单击创建新的数据源。

  2. 指定位置并命名数据源;我们给这个新的数据源命名为 Titanic train set 11 variables。

亚马逊机器学习确认您提供的模式已被考虑:

完成剩余的数据源创建步骤,并注意数据类型与您在模式文件中指定的数据类型相对应,并且三个字段不再存在。

模式回顾:要将模式关联到文件,只需将模式文件命名为与数据文件相同的名称,并添加.schema扩展名。例如,对于名为my_data.csv的数据文件,模式文件应命名为my_data.csv.schema,并上传到与数据文件相同的 S3 位置。

我们的数据源现在已经创建完成,我们可以探索亚马逊机器学习为我们提供的数据洞察。

检查数据统计

当亚马逊机器学习创建数据源时,它对不同的变量进行了基本的统计分析。对于每个变量,它估计以下信息:

  • 每个属性与目标的相关性

  • 缺失值的数量

  • 无效值的数量

  • 带有直方图和箱线图的数值变量分布

  • 数值变量的范围、平均值和中位数

  • 分类别变量最频繁和最不频繁的类别

  • 文本变量的词频

  • 二元变量的真实值百分比

前往数据源仪表板,然后点击您刚刚创建的新数据源,以便访问数据摘要页面。左侧菜单允许您访问针对目标和不同属性的数据统计信息,按数据类型分组。以下截图显示了数值属性的洞察。年龄票价变量值得更仔细地查看:

截图

两件事引人注目:

  • 年龄20%的缺失值。我们应该用年龄现有值的平均值或中位数来替换这些缺失值。

  • 票价的平均值是 33.28,但范围是0-512.32,表明分布高度偏斜。查看票价分布可以证实这一点。点击预览。

以下截图显示了票价属性的直方图和相关箱线图。我们可以看到,大多数值都低于 155,而高于 195 的值非常少。这表明 521.32 这个值可能是一个异常值,是由人为错误造成的无效值。回顾原始数据集,我们看到有四名来自同一家庭、持有相同票号(PC 17755)的乘客为四个头等舱(B51、B53、B55B101)支付了此价格。尽管 512.32 的票价远高于其他票价,但它看起来很合法,并不像某种错误。我们可能不应该丢弃或替换它。以下直方图显示了票价的分布:

截图

文本属性已经由 Amazon ML 自动分词,并且每个单词都从原始属性中提取出来。标点符号也已删除。Amazon ML 随后计算了以下截图所示的姓名属性的词频:

截图

使用 Athena 进行特征工程

到目前为止,我们有一组相当不错的变量,可以帮助预测乘客是否在泰坦尼克号灾难中幸存。然而,这些数据可能需要一些清理,以处理异常值和缺失值。我们还可以尝试从现有属性中提取其他有意义的特征,以提高我们的预测能力。换句话说,我们想要进行一些特征工程。特征工程是提高预测准确率的关键。

特征工程是使用数据领域知识创建特征的过程,这些特征可以使机器学习算法工作。

  • 维基百科

机器学习提供了它所谓的“数据食谱”来转换数据并适应其线性回归和逻辑回归算法。在 Amazon ML 中,数据食谱是构建预测模型的一部分,而不是创建数据源。我们在第五章 Model Creation 中广泛研究了 Amazon ML 的数据食谱。Amazon ML 的数据食谱主要适用于将数据适应到算法中,并且对于纠正原始数据中的问题或从现有数据中创建新属性有一定的局限性。例如,使用 Amazon ML 的食谱无法去除异常值、从文本中提取关键词或替换缺失值。因此,我们将使用 SQL 查询来执行一些数据清洗和特征创建。

几个 AWS 服务基于 SQL 数据库:Redshift、RDS 以及最近新增的 Athena。SQL 不仅广泛用于查询数据,正如我们将看到的,它特别适合于数据转换和数据清洗。在网上可以找到许多关于如何从原始泰坦尼克数据集中提取信息的创意想法。例如,这些在线资源提供了关于泰坦尼克数据集特征工程主题的许多想法:

在我们的案例中,我们将关注以下转换:

  • 用现有 age 值的平均值替换缺失的 age

  • 而不是替换或丢弃票价异常值,我们将创建一个新的对数变量 (fare),其分布更少偏斜

  • name 字段中提取标题

  • 每个客舱号由三个字符组成,其中第一个字符是相对于甲板级别的字母(A、B、C、...、F)以及该甲板上的客舱号数量;我们将提取每个客舱的第一个字母作为新的 deck 变量

  • 我们还将 sibpsparch 合并来创建 family_size 变量

在下一节中,我们将使用 Athena 来进行这些数据转换。我们首先在 Athena 中创建数据库和表,并用泰坦尼克数据填充它。然后,我们将使用标准 SQL 查询在新建的表上创建新特征。最后,我们将结果导出到 S3 中的 CSV 文件,并从中创建新的数据源。

介绍 Athena

Athena 于 2016 年 12 月的AWS re:Invent大会上推出。它通过提供一种简单的无服务器服务,直接与 S3 交互,补充了其他 AWS 基于 SQL 的服务。根据 AWS 的说法,Amazon Athena 是一个交互式查询服务,它使得使用标准 SQL 直接从 Amazon S3 分析数据变得容易。*Amazon Athena 的几个特性使其特别适合与 Amazon ML 进行数据准备:

  • Athena 可以直接从 S3 中不同格式的数据(CSV、JSON、TSV、亚马逊日志等)生成表格。由于 Amazon ML 中的数据源也是基于 S3 的,因此可以轻松地操作文件,执行各种数据转换,并即时创建各种数据集来测试您的模型。

  • Athena 速度快,简单,可以处理大量数据集。

  • Athena 使用prestodb,这是 Facebook 开发的一个分布式 SQL 查询引擎,它提供了一个非常丰富的 SQL 函数库,非常适合进行数据转换和特征工程。

Amazon ML 仅接受 CSV 文件从 S3 创建数据源。然而,由于 Athena 可以收集除 CSV 之外的其他文件格式的数据(JSON、TSV、ORC 等),并将查询结果导出为 CSV 文件,因此可以使用 Athena 作为转换工具,从而扩展 Amazon ML 的数据源能力。

Presto 是一个开源的分布式 SQL 查询引擎,针对低延迟、即席数据分析进行了优化。它支持 ANSI SQL 标准,包括复杂查询、聚合、连接和窗口函数。更多信息可以在prestodb.io/找到。

在本节中,我们将执行以下操作:

  1. 创建一个 Athena 账户和一个数据库。

  2. 直接从 S3 的 Titanic CSV 文件创建并填充一个表。

  3. 对数据集进行查询并创建新的特征。

  4. 将结果下载到 S3。

  5. 使用 Amazon ML 的额外功能创建新的训练和测试数据源。

Athena 简单。让我们从简单的概述开始。

AWS Athena 简要概述

在 Athena 中,数据不会存储在数据库中;它仍然保留在 S3 中。当您在 Athena 中创建一个表时,您实际上是在创建一个信息层,告诉 Athena 数据的位置、结构以及格式。Athena 中的模式是对象的逻辑命名空间。一旦 Athena 知道了数据结构和位置,您就可以通过标准 SQL 语句查询数据。

Athena 使用Hive 数据定义语言(DDL)来创建或删除数据库和表(有关 Hive DDL 的更多信息,请参阅cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL)。Athena 可以通过使用序列化-反序列化(SerDes)库理解多种格式(CSV、TSV、JSON 等)。Athena 可以通过控制台([console.aws.amazon.com/athena/](https://console.aws.amazon.com/athena/))或通过 JDBC 连接(http://docs.aws.amazon.com/athena/latest/ug/connect-with-jdbc.html)访问。我们将使用控制台。

Athena服务提供四个部分:

图片

查询编辑器:

  1. 允许您编写查询、保存它们,并查看结果,同时还可以在您的数据库和表之间导航。

  2. 一个列出您保存的所有查询的已保存查询页面。

  3. 一个列出您运行的所有查询的历史页面。

  4. 一个允许您探索存储数据的目录管理器。

这些部分的使用说明很直观,我们将让您在空闲时探索它们。

在撰写本文时,Amazon Athena 没有命令行界面(cli)。然而,Athena 可以通过 S3 上可用的Java 数据库连接(JDBC)驱动程序访问。您可以在docs.aws.amazon.com/athena/latest/ug/connect-with-jdbc.html找到更多信息。

一些需要知道的事情:

  • 您可以通过指定 S3 中的数据位置或通过 SQL 查询明确创建一个表。

  • 所有表都必须以EXTERNAL方式创建,并且无法使用CREATE TABLE AS SELECT。使用外部关键字创建的表删除不会删除底层数据:

        CREATE EXTERNAL TABLE temp ();

Athena 定价:Athena 根据查询扫描的数据量收费(在撰写本文时,每 TB 数据扫描费用为 5 美元,每个查询最低为 10 MB。您可以通过将数据转换为列式格式或对数据进行分区来降低成本。有关更多信息,请参阅aws.amazon.com/athena/pricing/)。

创建一个巨大的数据库

我们将从零开始,回到可在github.com/alexperrier/packt-aml/blob/master/ch4/original_titanic.csv找到的原始泰坦尼克号数据集。按照以下步骤准备 CSV 文件:

  1. 打开original_titanic.csv文件。

  2. 删除标题行。

  3. 删除以下标点符号字符:,"()

文件应只包含数据,不包含列名。这是包含 1309 行的原始文件。这些行按pclass排序和按字母顺序命名。结果文件可在github.com/alexperrier/packt-aml/blob/master/ch4/titanic_for_athena.csv找到。现在让我们在我们的 S3 桶中创建一个新的athena_data文件夹,并将titanic_for_athena.csv文件上传。现在转到 Athena 控制台。我们将创建一个titanic_db数据库和一个titanic表,并使用这些数据。

使用向导

在 Athena 中创建数据库和表有两种方式,通过向导或运行查询。可以通过在查询编辑器页面点击“添加表”链接来访问向导:

向导在四个步骤中允许我们创建数据库、表和加载数据。在第 3 步中创建列涉及手动输入每个列的名称并指定每个列的类型。由于数据集中有 14 个列,这种方法很耗时。因此,我们将不使用向导,直接在 SQL 中创建数据库和表:

直接在 SQL 中创建数据库和表

要创建数据库,请在查询编辑器中运行以下查询:

CREATE DATABASE titanic_db;

然后如下所示,在左侧的数据库下拉菜单中选择titanic_db

要创建表并加载数据,请运行以下 SQL 查询:

CREATE EXTERNAL TABLE IF NOT EXISTS titanic_db.titanic (
  pclass tinyint,
  survived tinyint,
  name string,
  sex string,
  age double,
  sibsp tinyint,
  parch tinyint,
  ticket string,
  fare double,
  cabin string,
  embarked string,
  boat string,
  body string,
  home_dest string
 )
 ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' WITH SERDEPROPERTIES (
  'serialization.format' = ',',
  'field.delim' = ','
  ) LOCATION 's3://aml.packt/athena_data/'

关于该查询的几点注意事项:

  • 位置s3://aml.packt/athena_data/指向我们特别创建的文件夹,而不是指向文件本身。该文件夹中的所有文件都将被视为该表的数据。

  • SERDE对应于以逗号作为字段分隔符的 CSV 格式。支持的格式及其相应的 SERDE 列表可在docs.aws.amazon.com/athena/latest/ug/supported-formats.html找到。

  • 字段类型是标准的 SQL 类型(字符串、双精度浮点数、小整数等)。

一旦查询运行完成,titanic表名就会出现在页面左侧部分。您可以点击眼睛图标来选择表的前 10 行,如下所示:

指定结果位置:Athena 将查询结果存储在新的 S3 文件夹中。您可以通过访问设置页面并指定所需的 S3 路径来指定这些结果存储在哪个文件夹中。我们在aml.packt桶中创建了一个名为*athena_query_results*的新文件夹,并将结果位置设置为*s3://aml.packt/athena_query_results/*

SQL 中的数据处理

我们现在将定义一个 SQL 查询,该查询将纠正我们之前看到的缺失值和异常值问题,并创建一些新变量。

缺失值

年龄票价变量有缺失值,分别的中位数为2814.5。我们可以使用此语句用中位数替换所有缺失值:

select coalesce(age, 28) as age, coalesce(fare, 14.5) as fare from titanic;

我们还希望保留至少对于年龄变量存在缺失值的信息。我们可以通过创建一个名为is_age_missing的新二进制变量来实现这一点:

SELECT age, CASE 
 WHEN age is null THEN 0
 ELSE 1
 END as is_age_missing
from titanic;

处理票价中的异常值

我们已经看到,票价有四个乘客支付的价格远高于其他人。处理这些值有几种方法,其中之一可以通过定义一系列特定范围对变量进行分箱。例如,低于 20;从 20 到 50;50 到 100,...,以及超过 200。可以使用 Amazon ML 食谱进行分箱。我们还可以将票价值限制在特定阈值,例如 95%分位数。然而,我们决定这些高额票价是合法的,我们应该保留它们。我们可以通过取票价的对数来创建一个新的变量log_fare,它具有更紧凑的范围和更少偏斜的分布:

select fare, log(fare +1, 2) as log_fare from titanic;

log_fare变量范围为0 - 9.0,平均值为4.3,中位数为3.95,而原始票价变量范围为[0, 512.3],平均值为32.94,中位数为14.5log_fare的分布比原始票价变量的分布更接近高斯分布。

Box-Cox 转换:由于线性回归的假设,最好有高斯分布的变量。Box-Cox 转换,也称为幂转换(见en.wikipedia.org/wiki/Power_transform),是将变量重塑为正态分布的常用方法。Box-Cox 转换是对我们刚刚应用于票价变量的对数转换的推广。

从名称中提取标题

如果你仔细观察乘客姓名,你会注意到它们都遵循{姓氏}{头衔}{名字}格式。提取头衔作为一个新变量将很有趣。以下查询使用 split 函数,它返回一个数组。我们需要该数组的第二个元素:

SELECT name, split(name, ' ')[2] as title from titanic;

从客舱推断舱位

舱位变量有三个字符值,其中第一个字符对应于甲板号(A,B,C,D,E)。这无疑是重要的信息,我们希望提取。我们可以使用以下查询来完成:

SELECT cabin, substr(cabin, 1, 1) as deck  FROM titanic;

计算家庭规模

最后,假设乘客所属的整体家庭规模可能是生存的决定性因素是有意义的。我们可以聚合兄弟姐妹的数量和父母数量,并为乘客加 1。以下简单的查询将创建家庭规模变量:

select sibps + parch + 1 as family_size from titanic; 

总结

我们可以在选择原始属性的同时合并所有这些查询。由于数据仍然按pclass和乘客name的字母顺序排序,我们还应该随机化结果。我们最终得到以下查询:

SELECT pclass, 
survived, 
name, 
sex, 
COALESCE(age, 28) as age, 
sibsp, 
parch, 
ticket, 
COALESCE(fare, 14.5) as fare, 
cabin, 
embarked, 
boat, 
body, 
home_dest, 
CASE 
 WHEN age is null THEN 0
 ELSE 1
 END as is_age_missing, 
log(fare + 1, 2) as log_fare,
split(name, ' ')[2] as title,
substr(cabin, 1, 1) as deck,
sibsp + parch + 1 as family_size 
FROM titanic
ORDER BY RAND();

让我们运行这个查询。结果将在结果面板中显示,并也写入 S3 查询结果位置的 CSV 文件中。你也可以通过点击结果面板右上角的图标将其保存到你的本地机器上:

图片

在这一点上,我们想要将数据分成训练集和测试集,就像我们之前做的那样,并创建一个新的 Amazon ML 数据源,具有扩展的架构。

创建改进的数据源

在我们将数据上传到 S3 并创建新的 Amazon ML 数据源之前,我们需要对新的Titanic数据集进行一些操作:

  1. 在你最喜欢的编辑器中打开这个新的 Titanic 数据集。

  2. 选择前1047行,并将它们保存到一个新的 CSV 文件中:ext_titanic_training.csv

  3. 选择下一263行和标题行,并将它们保存到文件ext_titanic_heldout.csv中。

我们需要更新我们的架构。打开架构文件titanic_training.csv.schema,并在 JSON 中添加以下行:

{
  "attributeName" : "is_age_missing",
  "attributeType" : "BINARY"
  }, {
  "attributeName" : "log_fare",
  "attributeType" : "NUMERIC"
  }, {
  "attributeName" : "title",
  "attributeType" : "CATEGORICAL"
  }, {
  "attributeName" : "deck",
  "attributeType" : "CATEGORICAL"
  }, {
  "attributeName" : "family_size",
  "attributeType" : "NUMERIC"
 }

新的架构文件以及训练集和保留集可以在github.com/alexperrier/packt-aml/tree/master/ch4找到。

我们接下来需要将训练集和架构文件上传到 S3。这些文件应该在同一个 S3 位置,{bucket}/{folders}

现在我们已经准备好根据扩展的 Titanic 训练集创建新的数据源,按照之前完全相同的步骤进行:

  1. 指定输入数据的位置。

  2. 检查架构。

  3. 将目标设置为survived

  4. 跳过rowID

  5. 检查并创建。

现在我们有 19 个属性在架构中,以及一个全新的数据源。

摘要

在本章中,我们专注于通常所说的数据科学流程中 Amazon ML 服务的提取、加载、转换(ETL)部分。我们了解到 Amazon ML 数据源是一组信息,包括位置、数据结构和数据分析,提供给服务以便它可以使用这些数据来开始训练模型。你现在应该能够从通过 S3 可访问的原始 CSV 数据文件中创建 Amazon ML 数据源感到舒适。

我们还探讨了通过使用简单的 SQL 查询通过 AWS Athena 服务转换数据和创建新特征的方法。利用 AWS 生态系统来补充 Amazon ML 的功能是使用 Amazon ML 的主要好处之一。

现在我们有几个 Titanic 数据集,原始的一个和扩展的一个,它们被分割成训练集和保留集,并且我们已经创建了相关的数据源。

在第五章“模型创建”中,我们将使用这些数据集来训练模型,并查看我们新的特征和数据清洗是否会导致更好的模型。

第五章:模型创建

我们现在已经在 S3 上创建了几个基于原始“泰坦尼克号”数据集的数据源。我们准备训练和评估一个亚马逊机器学习预测模型。在亚马逊机器学习中,创建一个模型包括以下步骤:

  • 选择训练数据源

  • 定义数据转换的配方

  • 设置学习算法的参数

  • 评估模型的品质

在本章中,我们将首先探索亚马逊机器学习中可用的数据转换,并将比较“泰坦尼克号”数据集的不同配方。亚马逊机器学习默认根据数据的性质定义配方。我们将调查并挑战这些默认转换。

模型构建步骤足够简单,我们将花一些时间检查可用的参数。模型评估是所有事情汇聚的地方。评估指标取决于预测的类型,回归、二分类或多分类分类。我们将探讨这些不同的评估是如何进行的。我们还将下载模型训练日志,以便更好地理解在亚马逊机器学习中训练模型时幕后发生了什么。我们将通过比较几个数据配方和正则化策略的模型评估来结束本章。

本章组织如下:

  • 配方

  • 模型参数

  • 评估

  • 日志分析

  • 特征工程、配方和正则化

在第四章“加载和准备数据集”的结尾,我们修改了模式,排除了原始数据集中的三个变量:“船”、“尸体”和home.dest,并基于此模式创建了一个新的数据源。我们将使用此数据源来训练模型。

前往您的亚马逊机器学习数据源仪表板;您应该看到三个数据源:

图片

  • 泰坦尼克号列车集:这是包含 14 个变量的原始原始数据集

  • 泰坦尼克号列车集 11 个变量:有 11 个变量;“船”、“尸体”和home.dest已从模式中删除

  • 泰坦尼克号列车集扩展版:这是我们通过基于 SQL 的特征工程获得的清洗和扩展后的数据集。

我们将使用“泰坦尼克号列车集 11 个变量”数据源。在开始模型创建之前,让我们首先回顾一下在亚马逊机器学习中可用的数据转换类型。

使用配方转换数据

数据科学工作流程的一个关键要素是特征工程。亚马逊机器学习通过其数据配方提供某些数据转换。请注意,尽管转换在概念上是预测分析工作流程的 ETL 或数据准备阶段的一部分,但在亚马逊机器学习中,数据配方是模型构建步骤的一部分,而不是初始数据源创建步骤的一部分。在本节中,我们首先回顾亚马逊机器学习中可用的数据转换,然后我们将使用“泰坦尼克号列车集 11 个变量”数据源将其中一些应用于“泰坦尼克号”数据集。

管理变量

食谱是包含以下三个部分的 JSON 结构化脚本:按照给定顺序:

  • 分配

  • 输出

一个空食谱指示亚马逊机器学习考虑所有数据集变量进行模型训练,如下所示:

{
    "groups" : {},
    "assignments" : { },
    "outputs":["ALL_INPUTS"]
}

该食谱不会以任何方式转换数据。

完整的亚马逊机器学习食谱文档可在docs.aws.amazon.com/machine-learning/latest/dg/recipe-format-reference.html找到。

变量分组

组允许将多个变量分组,以便于对多个变量应用相同的转换。食谱的组部分具有命名功能。组定义遵循以下语法:

"group_name": "group('first_variable','second_variable' )"

亚马逊机器学习(Amazon ML)根据变量的类型定义了一组默认组:ALL_TEXTALL_NUMERICALL_CATEGORICALALL_BINARY,以及用于所有变量的ALL_INPUTS组。让我们看看几个例子。

考虑以下示例,我们想要对agefare变量应用相同的转换(归一化)。我们可以定义一个组并命名为TO_BE_NORMALIZED

"groups" : {
    "TO_BE_NORMALIZED" : "group('age','fare')",
},

类似地,考虑一个电子邮件垃圾邮件检测的上下文,其中对于每封电子邮件,我们有一个标题、一个主题和一个正文。我们想要创建电子邮件标题和正文的 N-gram,但不包括标题;我们可以定义一个由所有文本变量组成的组,但排除特定的变量。在这里,我们创建一个名为N_GRAM_TEXT的组,它结合了除了标题之外的所有文本变量:

"groups" : {
    "N_GRAM_TEXT" : "group_remove(ALL_TEXT, 'header')",
},

使用分配命名变量

分配的主要目的是命名便利。您可以选择在分配部分或直接在输出部分命名转换后的变量或变量组。分配只是为了方便和可读性。

分配遵循以下语法:

"assignment_name": "transformation('group_name' )"

例如,您可以按如下方式重命名并归一化数值变量:

"assignments": {
    "normalized_numeric": "normalize(TO_BE_NORMALIZED)",
}

或者重命名并处理您的电子邮件的主题和正文:

"assignments": {
    "bigrams": "ngram(N_GRAM_TEXT,2)",
}

您也可以留空分配部分,并将转换应用于输出部分的变量组。最后,这更多是一个关于风格和可读性的问题,而不是其他任何事情。

指定输出

输出部分是您明确列出所有用于模型训练的变量的地方。如果您已经定义了一个包含一些变量的组,但仍然希望原始变量被考虑在内,您需要明确列出它们。分配部分声明了一个由以下内容组成的列表:

  • 分配

  • 变量

  • 变量转换

例如,如果您想要原始的电子邮件正文和主题,以及您在分配中定义的bigrams,您需要如下声明输出:

"outputs": [
    "header",
    "subject",
    "body",
    "bigrams"
]

以下输出声明声明了所有文本变量,并添加了之前定义的大词组分配:

"outputs": [
    "ALL_TEXT",
    "bigrams"
    ]

菜单格式参考页面提供了其他示例,展示了如何结合分组、分配和输出以创建菜单:docs.aws.amazon.com/machine-learning/latest/dg/recipe-format-reference.html。现在我们将查看可用的转换。

通过七个转换进行数据处理

Amazon ML 提供了以下七个转换。以下是对文本变量的四个转换:

  • 小写转换

  • 删除标点符号转换

  • N-gram 转换

  • 正交稀疏二元组(OSB)转换

对于数值变量,以下有两种转换方式:

  • 正态化转换

  • 分位数装箱转换

以及一种用于将文本与分类变量耦合的转换

  • 卡尔丹产品转换

这些转换在 Amazon ML 文档中有很好的解释(docs.aws.amazon.com/machine-learning/latest/dg/data-transformations-reference.html)。

使用简单的转换

小写转换将文本变量作为输入,并返回小写文本:Amazon ML is great for Predictive Analytics返回为amazon ml is great for predictive analytics。小写转换的语法是lowercase(text_variable)lowercase(group_of_text_variables)

删除标点符号转换也接受文本变量作为输入,并删除所有标点符号,但单词内的连字符除外(seat-belts将保持为seat-belts)。无法定义自己的标点符号集合。删除标点符号转换的语法是no_punct(text_variable)no_punct(group_of_text_variables)

正态化转换将数值变量标准化,使其均值为零,方差为 1。当数值变量的范围变化很大时,这种转换非常有用。这种转换对应于z 分数标准化,也称为标准化,而不是最小-最大标准化(参见第二章,机器学习定义和概念)。正态化转换的语法是normalize(numeric_variable)normalize(group_of_numeric_variables)

文本挖掘

N-gram 和正交稀疏二元组(OSB)转换是 Amazon ML 中可用的主要文本挖掘转换。

在文本挖掘中,经典的方法被称为词袋模型方法。这种方法简化为丢弃给定文本中单词的顺序,只考虑文档中单词的相对频率。尽管这种方法可能看起来过于简单,因为单词的顺序对于理解信息至关重要,但这种方法在所有类型的自然语言处理问题中都给出了令人满意的结果。词袋模型的关键部分是由从给定文本中提取单词的需求驱动的。然而,我们不仅可以将单个单词视为唯一的信息载体,还可以提取单词序列。这些序列被称为 N-gram。两个单词的序列称为 bigram,三个单词的序列称为 trigram,依此类推。单个单词也称为 unigram。N-gram 也称为 tokens,从文本中提取单词和 N-gram 的过程称为分词。

例如,考虑以下句子:“The brown fox jumps over the dog”

  • 单语元是:{The, brown, fox, jumps, over, the, dog}

  • 二元组是:{The brown, brown fox, fox jumps, jumps over, over the, the dog}

  • 三元组是:{The brown fox, brown fox jumps, fox jumps over, jumps over the, over the dog}

没有规则或启发式方法可以让你知道在你的模型中是否需要 N-gram,或者 N-gram 的顺序对模型最有益。这取决于你处理文本的类型。只有实验才能告诉你。

Amazon ML 提供两种分词转换:N-gram 和 OSB。

N-gram转换:接受一个文本变量和一个整数(2 到 10 之间),并返回预期的 N-gram。请注意,在 Amazon ML 中,所有文本变量默认按 unigram 进行分词。在配方中不需要显式指定 unigram。ngram(text_variable, n)将为 n=2 产生 bigram,为 n=3 产生 trigram,依此类推。

OSB或正交稀疏二元组转换是 bigram 转换(N-gram,其中 n=2)的扩展。给定文本中的一个单词,通过将初始单词后隔 1、2、…、N 个单词的其他单词关联起来,组成单词对。N 是 OSB 窗口的大小。

例如,在句子“这是一个限时优惠”中,首先考虑单词“优惠”。对于四个单词的窗口,OSB(One-Skip Bigram)为:time_offerlimited__offera___offeris__offerthis___offer。我们通过每次跳过 1、2、...、N 个单词来构建单词对。

OSB 转换使我们能够提取关于每个单词周围上下文的信息。例如,OSB is___offer 可以用来检测如 is a special offer 以及 is our best offer 这样的字符串。研究发现,OSB 提取通常可以提高垃圾邮件过滤算法的性能。OSB 转换的语法是 osb(text_variable, N),其中 N 是窗口大小,范围从 2 到 10。

值得注意的是,一些非常标准的文本转换在 Amazon ML 食谱中缺失。词干提取和词形还原用于将不同结尾的单词重新组合到共同的基本形式(例如,walkingwalkerwalked都会被计算为walk)并且 Amazon ML 不提供这些功能。

同样,从文本中移除非常常见的单词,称为停用词,如冠词或介词(the, a, but, in, is, are 等)也是一种非常标准的文本挖掘转换,但在 Amazon ML 食谱中不是可选项。然而,Amazon ML 可能在后台执行类似的转换,但没有明确说明。然而,现有的文档中没有任何内容表明这是事实。

耦合变量

笛卡尔积转换将两个分类或文本变量合并为一个。例如,考虑一个包含书籍的数据集,对于每本书,它们的标题和类型。我们可以想象,一本书的标题与其类型之间可能存在某种相关性,创建一个新的title_genre变量可以揭示这种关系。

考虑以下四本书,它们的标题和类型。将标题中的单词与书的类型耦合,为标题中的单词添加了额外的信息。这是模型可以有效地使用的。这在下表中的title_genre列中得到了说明:

标题 类型 title_genre
All the Birds in the Sky 科幻 {all_scifi, birds_scifi, sky_scifi}
Robots and Empire 科幻 {robots_scifi, empire_scifi}
The Real Cool Killers 犯罪 {real_crime, cool_crime, killers_crime}
Bullet in the Sky 犯罪 {bullet_crime, sky_crime}

如果它出现在犯罪小说的标题中,单词 sky 现在有不同的含义:sky_crime或出现在科幻小说的标题中:sky_scifi

Titanic数据集的情况下,我们可以通过取它们的笛卡尔积来耦合sibsp*parch*变量(兄弟姐妹数量和父母数量):sibsp*parch,从而得到一个新的变量,该变量可以区分有(无)父母和兄弟姐妹较少或没有的乘客与有(无)父母和兄弟姐妹较多的乘客。语法是cartesian(var1, var2)

分箱数值

最后也是最重要的转换是分位数分箱。分位数分箱的目标是将数值变量转换为分类变量,以便更好地提取变量与预测目标之间的关系。这在变量与目标之间存在非线性关系时特别有用。通过将原始数值变量的值分成大小相等的n个箱,可以将每个值替换为相应的箱。由于箱的数量是有限的(从 2 到 1,000),变量现在是分类的。语法是quantile_bin(var, N),其中N是箱的数量。

有两种无监督分箱类型,等频分箱和等宽分箱。在等频分箱中,每个分箱具有相同数量的样本,而在等宽分箱中,变量范围被分成 N 个宽度相等的更小范围。分位数分箱通常指的是等频分箱。

将连续数据分类并不总是好的方法,因为你按照定义,正在丢弃可能对模型有用的信息。此页面列出了与分箱相关的一些其他问题:biostat.mc.vanderbilt.edu/wiki/Main/CatContinuous。然而,亚马逊机器学习似乎非常喜欢分位数分箱技术。事实上,在我们考虑的所有数据集中,亚马逊机器学习总是对所有数值变量应用分位数分箱,在建议的食谱中,通常使用大量,有时是非常大量的分箱。例如,Titanic数据集中fare变量的默认转换是使用 500 个分箱的分位数分箱,尽管变量的范围仅从 0 到 512。我们在本章的末尾比较了保留原始数值与在保持变量为数值或应用分位数分箱?部分应用分位数分箱所获得的评估结果。

创建模型

当你创建模型时,亚马逊机器学习会根据你的数据源建议一个食谱。你可以选择使用该食谱或对其进行修改。我们现在将创建我们的第一个模型,并在创建模型的过程中分析亚马逊机器学习为我们生成的食谱。

前往模型仪表板,然后单击创建新... | 机器学习模型按钮。

你将经历三个屏幕:

  1. 选择数据源,选择Titanic train set with 11 variables

  2. 亚马逊机器学习将验证数据源并展示摘要。

  3. 选择默认或自定义模型创建;选择自定义路径:

图片

下一个屏幕在属性、它们的类型和左侧的值样本之间分割,以及右侧的建议食谱,如下面的截图所示:

图片

编辑建议的食谱

这是你可以编辑食谱并替换为你自己创建的食谱的地方。

你可以在本书的 GitHub 仓库中找到本章中所有的 JSON 文件,格式正确,缩进良好:github.com/alexperrier/packt-aml/blob/master/ch5/recipes.json

食谱在您输入时进行验证。任何不遵守 JSON 格式的都会导致以下错误消息:“食谱必须是有效的 JSON”。一些常见错误包括以下内容:

  • 缩进不被尊重

  • 大括号中的最后一个元素后面不应该跟有逗号

  • 所有字符串都必须使用双引号括起来

手动格式化 JSON 文本并不有趣。这个在线 JSON 编辑器非常有帮助:www.cleancss.com/json-editor/

将食谱应用于泰坦尼克号数据集

亚马逊机器学习为我们生成的数据集的食谱如下:

{
    "groups": {
        "NUMERIC_VARS_QB_50": "group('sibsp','parch')",
        "NUMERIC_VARS_QB_100": "group('age')",
        "NUMERIC_VARS_QB_10": "group('fare')"
    },
    "assignments": {},
    "outputs": [
        "ALL_BINARY",
        "ALL_CATEGORICAL",
        "quantile_bin(NUMERIC_VARS_QB_50,50)",
        "quantile_bin(NUMERIC_VARS_QB_100,100)",
        "quantile_bin(NUMERIC_VARS_QB_10,10)",
        "ALL_TEXT"
    ]
}

所有数值都是分位数分箱。对文本、二进制或分类变量不进行进一步处理。食谱的输出部分显示,数值变量被替换为分箱等效值。

可以对这道食谱提出进一步的意见:

  • sibspparch变量分组在一起。首先,sibspparch都有相似的取值范围,分别是 0 到 9 和 0 到 8。对这两个变量使用相同数量的分箱是有意义的。

  • 为什么亚马逊机器学习为sibspparch选择了 50 个分箱,为age选择了 100 个分箱,为fare选择了 10 个分箱,这一点不太清楚。

我们发现分箱的数量对训练集中的数据非常敏感。几个版本的初始数据集产生了非常不同的分箱数量。我们所有试验中的一个常数是,所有的数值都通过具有相当高数量的分箱进行分位数分箱。在一种情况下,亚马逊机器学习建议fare变量使用 500 个分箱,age变量使用 200 个分箱。在这两种情况下,由于我们的总训练样本数只有 1,047 名乘客,我们每个分箱的样本数将非常少。亚马逊机器学习如何计算最优分箱数量并不清楚。

亚马逊机器学习还可以决定对我们的泰坦尼克号数据集应用以下其他转换:

  • 从乘客的标题中提取二元组或 OSB

  • sibspparch与笛卡尔积转换结合

在食谱和数据预处理之间进行选择。

到目前为止,我们已经通过脚本和亚马逊机器学习食谱将我们的初始数据集进行了转换。这两种技术是互补的。一些转换和数据操作只能通过预处理数据来完成。我们在第四章, 加载数据集和准备数据 中使用 Athena 和 SQL 这样做。我们也可以使用其他脚本语言(如 Python 或 R)来实现类似的数据处理,这些语言在创意特征工程方面最有成效。SQL 和脚本还可以更好地处理异常值和缺失值——这些修正在亚马逊机器学习食谱中是不可用的。

亚马逊机器学习转换的目标是 为亚马逊机器学习算法准备数据,而脚本特征工程则是关于清理数据并从原始数据集中创建新变量。

虽然亚马逊机器学习食谱相当有限,但它们提供了一个简单的方法来调整数据集,并快速比较基于不同食谱的模型。从给定的数据源和模式创建新模型及其评估只需几点击。通过为每个模型选择不同的食谱,可以尝试广泛的数据库。食谱允许我们创建快速的尝试-失败循环。相关的流程如下:

  1. 指定数据源。

  2. 尝试不同的食谱。

  3. 创建或删除变量。

  4. 训练并选择与该食谱关联的模型。

  5. 一旦找到最佳食谱,就开始优化模型参数、正则化、遍历次数和内存。

我们可以比较使用脚本(Athena 和 SQL)和使用食谱如何转换数据:

食谱:

  • 移除特征(boatbodyhome.dest)。这也可以通过模式或直接从数据集 CSV 文件中删除列来完成。

  • 通过聚合parchsibsp来表示家庭的笛卡尔积。

  • 数值归一化(一种可能性)。

  • 对所有文本变量、名称、目的地等进行分词。

  • 对所有数值进行分位数分箱;尽管分箱数量很大,但这种转换产生了良好的结果。

脚本(SQL):

  • 处理年龄的缺失值:我们将所有缺失值替换为年龄的平均值

  • 文本处理:我们从name变量中提取了titles

  • 创建了一个新的特征,即family_size,它是parchsibsp的总和

  • 从船舱号中提取deck

这两种方法都非常互补。

参数化模型

现在数据已经为 SGD 算法准备好了,我们准备设置实验参数。在某种程度上类似于科学实验,我们将尝试几组参数来测试几个模型,并选择最好的一个。下一张截图显示了实际上指定模型参数的位置:

  • 模型内存

  • 数据遍历

  • 打乱顺序

  • 正则化

图片

设置模型内存

模型内存与亚马逊机器学习必须预留的内存有关,以构建和评估您的模型。默认设置为 100Mb。在泰坦尼克号数据集的情况下,模型内存始终低于 1Mb,如日志所示。模型内存还用于处理流数据时预留内存。

设置数据遍历次数

亚马逊机器学习将多次使用样本的训练集,每次都打乱它并使用新的序列来提高预测。这类似于挤压一块湿布——每次挤压,都会有更多的水出来。默认设置为 10 次遍历,将其设置为 100 的最大值不会伤害到模型,但会增加模型的训练时间和操作成本。

选择正则化

如第二章中所述,机器学习定义和概念,正则化可以使你的模型更加鲁棒,并允许它通过减少过拟合来更好地处理之前未见过的数据。经验法则是,如果你的评估分数不佳(欠拟合),则降低正则化;如果你的模型在训练集上表现出色,但在评估集上表现不佳(过拟合),则增加正则化。

创建评估

在 Amazon ML 中,评估和模型是独立的。你可以通过指定不同的评估数据集来训练一个模型并执行多个评估。以下截图所示的评估页面允许你命名并指定如何评估模型:

图片

如你所知,为了评估一个模型,你需要将你的数据集分成两部分,即训练集和评估集,比例为 70/30。训练部分用于训练你的模型,而评估部分用于评估模型。在这个阶段,你可以让 Amazon ML 自动将数据集分成训练集和评估集,或者指定一个不同的数据源用于评估。

请记住,最初的Titanic文件是按阶级和乘客的字母顺序排序的。使用这个有序数据集并且不进行洗牌,即按顺序取前 70%的样本,会给模型带来非常不同的训练集和评估集数据。这样的评估将不相关。然而,如果你的数据尚未洗牌,你可以告诉 Amazon ML 对其进行洗牌。让你的数据默认由 Amazon ML 重新洗牌是一个好习惯,以防你自己的随机化在数据集中留下了某些顺序模式。

Amazon ML 将对你的训练集和验证集进行一些验证,检查验证集是否有足够的数据,两个集合是否遵循相似的分布,以及评估集是否有有效的样本。有关评估警报的更多信息,请参阅docs.aws.amazon.com/machine-learning/latest/dg/evaluation-alerts.html

注意,如果你选择让 Amazon ML 分割数据,它将创建两个新的数据源,并以一种方式命名,让你可以看到分割是如何进行的。如果你决定使用不同的配方或模型参数(如正则化)测试另一个模型,你可以重用这些新数据源。

例如:

  • Titanic.csv_[percentBegin=0, percentEnd=70, strategy=sequential]

  • Titanic.csv_[percentBegin=70, percentEnd=100, strategy=random]

点击“审查”,确保你的模型符合预期,然后点击最后的“创建 ML 模型”按钮。创建模型通常需要几分钟。

评估模型

在这个阶段,Amazon ML 将使用训练集来训练多个模型,并使用评估集来选择最佳模型。

Amazon ML 并行运行多个模型训练,每次尝试新的参数并在每次新遍历时对训练集进行洗牌。一旦达到最初设置的遍历次数或算法收敛,以先到者为准,模型就被认为是训练好的。对于它训练的每个模型,Amazon ML 都会用它对验证子集进行预测以获得每个模型的评估分数。一旦所有模型都以这种方式训练和评估,Amazon ML 就会简单地选择评估分数最好的模型。

评估指标取决于手头的预测类型。对于分类(二分类和多分类),是 AUC 和F1分数,对于回归是 RMSE。Amazon ML 如何显示评估结果也取决于手头的预测类型。

我们将首先评估我们的泰坦尼克号预测的二分类性能,接着是关于空中交通延误的新数据集的回归案例,最后使用经典的Iris数据集进行多分类分类。

评估二分类

一旦你的模型准备就绪,点击服务仪表板上的模型标题,即可访问模型的结果页面,其中包含模型的摘要、设置和评估结果。

下面的截图显示,我们获得了0.880AUC分数,这在大多数机器学习应用中被认为是非常好的。AUC代表曲线下面积,在第二章,“机器学习定义和概念”中介绍。它是分类问题的实际度量标准。

二分类的基线是 0.5 的 AUC,这是随机预测 0 或 1 的模型的分数:

Amazon ML 通过检查以下条件来验证模型,并在条件不满足时发出警报:

在我们的情况下,没有触发任何警报:

  • 训练和验证数据集是分开的

  • 验证数据集有足够的样本数量

  • 验证集和训练集共享相同的模式

  • 验证集的所有样本都是有效的,并用于评估,这意味着目标变量对于一个或多个样本没有缺失

  • 目标变量的分布训练集和验证集中相似

如果我们让 Amazon ML 处理训练和验证数据的拆分,大多数这些警报将不会发生,但如果我们自己提供验证集,它们可能会更频繁。

AUC 分数不是 Amazon ML 给我们评估模型质量的唯一元素。通过点击探索性能链接,我们可以进一步分析我们模型的性能。

探索模型性能

你可能还记得第二章:机器学习定义和概念,在二元分类的情境下,逻辑回归模型为每个待预测样本计算一个概率——属于某一类或另一类的概率。模型不会直接输出待预测样本的类别。样本被分配到某一类或另一类,取决于概率是否低于或高于某个特定阈值。默认情况下,这个阈值设置为 0.5。尽管评估给出的 AUC 分数不依赖于决策阈值的值,但其他分类指标则不然。我们可以改变阈值的值,看看这对我们的预测有何影响。

探索性能页面展示了评估中的几个其他分类指标以及模型的混淆矩阵。下面图表中的垂直条是一个可以左右滑动的光标。通过滑动光标,我们可以增加或减少用于将预测样本分类为某一类或另一类的决策阈值。随着光标的移动,以下指标相应变化。

  • 假正率

  • 精确率:预测为正例的真正正例比例

  • 回忆率(正确识别的正例比例)

  • 准确率

对于0.5的阈值,我们有以下截图:

图片

如果我们将阈值降低到0.4,准确率降低,而回忆率提高,如以下截图所示:

图片

如果我们将阈值提高到0.7,准确率略有提高,而回忆率降低:

图片

在我们的情境中,预测值在“存活”和“未存活”之间非常明显地区分开来。稍微改变阈值对指标的影响并不大。

评估线性回归

Amazon ML 使用标准的 RMSE 指标进行线性回归。RMSE 定义为真实值与预测值之间差的平方和:

图片

其中ŷ是预测值,y是真实值。预测值越接近真实值,RMSE 越低;因此,较低的 RMSE 被视为更好的预测模型。

为了展示回归背景下的评估,我们将考虑 Kaggle 上可用的航空公司延误数据集的简化版本,网址为www.kaggle.com/giovamata/airlinedelaycauses。完整的数据集相当大(~250Mb)。我们从 2008 年提取了大约19,000行数据,过滤掉了取消的航班。我们还删除了与我们的目标变量Airdelay高度相关的几个变量。结果数据集和模式可在 GitHub 上找到,网址为github.com/alexperrier/packt-aml/tree/master/ch5

我们将数据集上传到 S3,创建数据源,训练和评估模型,最终获得 RMSE 为 7.0557,基线为 31.312。回归的基线由一个总是预测目标平均值的模型给出:

进一步探索,我们获得了以下残差直方图。如图所示,误差大致呈钟形,且围绕0中心,这意味着我们的误差有一半的时间高估/低估了真实值。数据集中所有可用的信息都被模型消耗了:

评估多类分类

多类分类的经典数据集是包含三种鸢尾花类型的Iris数据集。这个数据集相当简单,非常受欢迎,使用它来展示像 Amazon ML 这样强大的平台似乎有些过度。幸运的是,还有另外三个由种子组成的三类数据集。种子数据集可在archive.ics.uci.edu/ml/datasets/seeds找到,当然,也可以在本书的 GitHub 仓库中找到(以及模式)。

种子数据集有 210 个样本,均匀分布在三种不同的seedTypes和七个属性中。数据集有一个 ID,必须设置为分类,所有属性都是数值型,目标是seedType。我们将数据集上传到 S3,并创建数据源和模型。

多类分类的指标是定义为精确率和召回率的调和平均值的F1分数:

多类分类问题的基线是总是预测最常见类别的模型的宏平均F1分数。在种子数据集的情况下,我们获得了基线为 0.143 的F1分数为 0.870:

性能探索不如二分类案例那样发达。Amazon ML 提供了混淆矩阵,它显示了每个类别中正确预测样本数与该类别实际样本数的比率:

分析日志

对于它执行的每个操作,Amazon ML 都为我们提供了相关的日志访问权限。我们可以下载并分析模型训练日志,并推断出 Amazon ML 如何训练和选择最佳模型。

返回到上一个泰坦尼克号模型,在摘要部分点击下载日志链接。日志文件太长无法在此处展示,但可在github.com/alexperrier/packt-aml/blob/master/ch5/titanic_training.log找到:

Amazon ML 并行启动了五个版本的 SGD 算法。每个版本被称为学习者,对应于不同的学习率值:0.01、0.1、1、10 和 100。在算法的每次新迭代中,计算以下五个指标:

  • 准确率

  • 回忆

  • 精确度

  • F1 分数

  • AUC

还计算了负对数似然来评估最后几次迭代是否显著降低了残差误差。

优化学习率

如果您还记得第二章中的内容,即机器学习定义和概念部分下的线性模型正则化随机梯度下降(SGD)算法有一个名为学习率的参数。

SGD 基于这样的想法:每次迭代都使用新的(块)数据样本对线性回归模型的系数进行微调。在每次迭代中,输入数据样本要么逐个使用,要么分块使用,以估计对线性回归系数的最佳修正(所谓的梯度),以进一步减少估计误差。已经证明,SGD 算法收敛到线性回归权重的最优解。这些修正乘以一个称为学习率的参数,该参数驱动每次迭代对系数进行的修正量。

SGD 的计算成本较低。这是一个既迷人又简单的算法,被广泛应用于许多应用中。

想象一个在碗中的弹珠。将弹珠放在碗的边缘,然后让它以圆形运动的方式落入碗中。它在下落的过程中会围绕碗旋转。在其下降的末端,它倾向于围绕碗底旋转,并最终在碗的最低点停下来。当你将每个迭代的预测误差视为弹珠,将碗底视为可以估计的最终和最优化系数时,SGD 的行为与此类似。在每次迭代中,预测误差的平均值会变小。误差不会像弹珠那样直接沿着碗底的最直接路径下降,也不会达到最低的最优化解,但平均而言,预测会更好,误差在迭代后会逐渐减少。经过一定次数的迭代后,误差将接近其潜在的最优最小值。它达到最小误差和最佳系数的速度有多快,以及它有多接近最小误差,这直接取决于学习率的值。

学习率控制每次迭代中权重被校正的程度。学习率驱动算法的收敛。学习率越大,收敛越快,一旦收敛,潜在的残差误差可能也越大。

因此,选择一个最优的学习率将在以下方面取得平衡:

  • 更快的收敛和较差的估计

  • 更慢的收敛和更准确的估计

然而,如果学习率太小,收敛可能会太慢,并且需要太长时间才能达到最优解。一个标准的策略是随着算法的收敛而减小学习率,从而确保开始时快速收敛,随着预测误差的减小而减慢。随着学习率的减小,系数估计变得更加准确。小的学习率意味着算法收敛得慢,而高的值意味着每个新的样本对校正因子的影响更大。Amazon ML 不使用那种策略,并保持学习率恒定。在 Amazon ML 中,学习率由系统设置。您不能选择一个值。

可视化收敛

通过解析日志,我们可以提取出我们泰坦尼克号模型的以下收敛图:

图片

之前不同图的截图显示了四个指标:准确率、AUC、F1 分数和精确度,对应于五个不同学习率的值。模型被设置为在泰坦尼克号训练数据集上进行 50 次遍历,轻微的(10^-6)L2 正则化。我们可以看到,对于所有指标,最佳的学习率值要么是 10,要么是 100,其中学习率=100 略胜一筹。这些值收敛得更快,达到更好的分数。最小学习率(0.01)收敛得非常慢。在我们的情况下,更快的收敛和较大的学习率值优于较小的学习率值。

创建模型时的默认迭代次数是10。我们可以看到,10 次迭代不足以使分数稳定和收敛。在第 10 次迭代时,曲线几乎还没有走出混沌初始化阶段。

从日志中提取的负对数似然图显示,最佳学习器对应于这里用带菱形形状的曲线表示的 100 学习率:

从这些图表中可以得出一个结论,那就是你不应该将你的模型限制在默认的 10 次迭代。

这两个收敛图完全取决于当前的问题。对于不同的数据集,我们可能会得到在收敛速度、学习率和达到的分数方面完全不同的图表。

正则化的影响

以下图表比较了三个不同模型的 AUC:

  • 无正则化

  • 轻微正则化(10^-6)

  • 激进正则化(10^-2)

我们注意到,在没有正则化和轻微正则化之间没有显著的差异。然而,激进的正则化对模型性能有直接影响。算法收敛到一个较低的 AUC,最佳学习率不再是 100,而是 1。

比较亚马逊 ML 给出的轻微和激进正则化的性能图,我们看到尽管两种情况下的分数(AUC、准确率等)非常相似,但差异在于预测的确定性。在轻微正则化情况下(左图),预测值相差很远。样本为零或一的几率或预测非常明显。在激进正则化情况下(右图),这种分离远不那么明显。样本属于一个类别而不是另一个类别的概率要接近得多。决策边界不太清晰:

轻微正则化 激进正则化

正则化的目标是使模型的性能与训练数据解耦,以减少过拟合。因此,在之前未见过的数据集上,重正则化可能会给出更好的结果,而没有正则化可能比轻微正则化表现更差。在训练-验证阶段不太理想的表现有时在真实预测阶段可能更稳健。重要的是要记住,验证阶段的表现并不总是转化为预测阶段的表现。

在泰坦尼克号数据集上比较不同的方法

在本节的最后,我们想比较几个配方,看看我们的基于 SQL 的特征工程是否可以驱动更好的模型性能。在我们所有的实验中,与 Amazon ML 建议的配方相关的一个突出特点是所有数值型变量最终都通过分位数分组进行了分类。分箱的数量也值得怀疑。我们在 Titanic 数据集上比较以下场景:

  • 建议的 Amazon ML 脚本

  • 数值型值保持为数值型。在脚本中不涉及分位数分组。

  • 我们在第四章,“加载数据集和准备数据”中创建的扩展泰坦尼克号数据源,与建议的 Amazon ML 脚本一起使用

我们对第四章,“加载数据集和准备数据”中使用的扩展泰坦尼克号数据集进行了轻微修改:

  • 没有必要同时拥有 farelog_fare。我们移除了 fare

  • 我们手动纠正了一些标题,这些标题没有从名称中正确提取出来。

  • 新的扩展数据集可在 GitHub 仓库的该章节中找到,作为 ch5_extended_titanic_training.csv

在所有三种情况下,我们都应用了 L2 轻度正则化。

保持变量为数值型或应用分位数分组?

我们发现,将所有数值型变量保持为数值型并避免任何分位数分组对模型性能有非常直接且负面的影响。在数值型情况下,整体得分远低于分位数分组情况:所有数值型的 AUC 为 0.81,而 QB 的 AUC 为 0.88。

查看 All Numeric 模型的收敛图,似乎算法的收敛速度比分位数分组模型慢得多。显然,在 50 次迭代后并未收敛,因此我们将迭代次数增加到 100 次。我们还注意到,在 All Numeric 情况下,最佳学习率等于 0.01,而在分位数分组模型中,最佳学习率要大得多(10 或 100)。较小的学习率会导致收敛速度变慢:

我们还在以下性能图表中看到,分位数分组模型比所有数值型模型更好地分离了类别:

分位数分组 50 次 所有数值型 100 次

因此,分位数分组肯定比不分位数分组更可取。那么,我们用新特征扩展初始数据集的努力如何呢?好吧,不知何故,我们的扩展模型并没有比初始数据集产生更好的结果。从 name 中提取 title,为 age 替换缺失值,从 cabin 中提取 deck 并没有生成一个明显更好的模型:

  • 原始泰坦尼克号数据集:AUC 0.88

  • 扩展的泰坦尼克号数据集与特征工程:AUC 0.82

两个模型的收敛和性能图表相似,此处不再重复。可能有几个因素在起作用,可以解释为什么我们的改进数据集没有产生更好的模型,需要进一步分析来了解哪些特征工程对模型有积极影响,哪些没有。然而,我们将在下一章中看到,这也可能取决于评估集中实际的样本。平均而言,扩展的数据集生成更好的性能,但在这个特定的试验中,相关的模型的表现与在原始数据集上训练的模型大致相同。结论是,运行多次试验以评估模型的质量和性能是值得的,而不是依赖于唯一的一次试验,因为评估集的特殊性可能会影响模型之间的比较。

解析模型日志

收敛图是通过解析 Amazon ML 模型日志,将数据提取到 CSV 文件中得到的,该文件可以用于后续创建图表。这个过程很简单,主要基于命令行脚本,使用 grepsed 命令。我们想要从日志文件中提取和解析以下行:

16/12/25 13:54:03 INFO: learner-id=4202 model-performance:         accuracy=0.6562 recall=0.5000 precision=0.5909 f1-score=0.5417 auc=0.7095

并将它们转换为 CSV 格式,如下所示:

迭代 alpha 学习者 准确率 召回率 精确率 F1 AUC
1 0.01 1050 0.5937 0.56 0.4828 0.5185 0.6015

第一步是从日志文件中提取正确的行。我们注意到它们都包含字符串 model-performance:。我们使用 grep 命令提取包含此字符串的所有行到一个临时文件中,我们将其命名为 model_performance.tmp

将 Amazon ML 模型页面上的日志数据复制粘贴到日志文件(model.log)中,然后在终端运行以下命令:

grep "model-performance:" model.log >> model_performance.tmp

然后使用 sed 命令将正确的子字符串替换为逗号。sed 命令遵循以下语法:

sed -i.bak 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

-i.bak 选项使得可以在文件内部替换字符串,而无需创建临时文件。

例如,将 model_performance.tmp 文件中的字符串 INFO: learner-id= 替换为逗号,可以通过在终端运行以下行来实现:

sed -i.bak 's/ INFO: learner-id=/,/g' model_performance.tmp

通过以下命令,大部分原始日志文件已经被转换成 CSV 格式的文件,你可以将其用作可视化 Amazon ML 模型收敛的基础。其余的文件清理可以在电子表格编辑器中完成:

sed -i.bak 's/ INFO: learner-id=/,,/g' model_performance.tmp
sed -i.bak 's/ model-performance:         accuracy=/,/g' model_performance.tmp
sed -i.bak 's/ recall=/,/g' model_performance.tmp
sed -i.bak 's/ precision=/,/g' model_performance.tmp
sed -i.bak 's/ f1-score=/,/g' model_performance.tmp
sed -i.bak 's/ auc=/,/g' model_performance.tmp

可以使用类似的模式从 Amazon ML 模型日志中提取负对数似然数据:

sed -i.bak 's/ INFO: learner-id=/,,/g' filename
sed -i.bak 's/ model-convergence:         negative-log-likelihood=/,/g' filename
sed -i.bak 's/ (delta=1.000000e+00) is-converged=no//g' filename

我们最终得到一个 CSV 文件,其中每一行代表一个迭代,一列是学习率,以及每个指标。

摘要

在本章中,我们在 Amazon ML 中创建了预测模型——从选择数据源,使用配方对初始数据进行转换,到分析训练模型的性能。模型性能的探索取决于手头的预测问题类型:二元分类、多分类或回归。我们还查看了泰坦尼克号数据集的模型日志,了解了 SGD 算法如何训练并从几个不同学习率的模型中选择最佳模型。

最后,我们在泰坦尼克号数据集的背景下比较了几种数据转换策略及其对模型性能和算法收敛性的影响。我们发现,数值的分位数分箱是提高算法收敛速度的关键策略,总体上产生了更好的模型。

到目前为止,这些模型和性能评估都是在训练数据上获得的。这意味着模型从一开始就完全能够访问这些数据。这些模型的初衷不是在训练数据的子集上运行,而是在之前未见过的数据上做出稳健的预测。

在下一章中,我们将应用这些模型到我们在第四章“加载数据集和准备数据”中创建的保留数据集上,以进行实际预测。

第六章:预测和性能

是时候做出一些预测了!在第四章,“加载数据集和准备数据”,我们将Titanic数据集分为两个子集,分别是训练集和保留集,分别占原始数据集的 70%和 30%,并且随机打乱。我们在第五章“模型创建”中广泛使用了训练子集,以训练和选择最佳的分类模型。但到目前为止,我们还没有使用保留集。在本章中,我们将我们的模型应用于这个保留集,对未见数据做出预测,并对我们模型的性能和鲁棒性进行最终评估。

Amazon ML 提供两种类型的预测:批量预测和流式预测。批量预测需要一个数据源。你想要预测的样本将以批量模式一次性提供给模型。流式预测,也称为实时或在线预测,需要创建一个 API 端点,并包括通过 HTTP 请求逐个提交样本序列。实时预测不涉及创建数据源。

我们将从对Titanic保留集的批量预测开始。我们将确认我们的不同模型在保留数据集上的表现与在验证子集上的表现相似,假设所有子集具有相似的变量分布。在第五章“模型创建”中,我们得出结论,在我们的三个数据源——建议的量分箱(QB)食谱、无 QB 的食谱和扩展数据集——中,包含额外变量(如decktitlelog_fare等)的那个数据源在验证子集上得到了最佳的分数。我们将验证这一点也适用于保留集。

本章分为两部分。在第一部分,我们查看Titanic数据集的批量预测。在第二部分,我们查看基于 UCI 存储库的新文本量分箱的实时、流式预测。Spam数据集足够大,可以模拟流数据。我们将创建一个 Amazon ML 端点,并使用 Python SDK 发送和检索分类预测。

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

  • 制作批量预测

  • 制作实时预测

在现实世界的分类问题或回归问题中,你想要对其做出预测的先前未见数据将不包括目标值。在我们的案例中,保留数据集确实包含解决方案,这使我们能够评估模型在先前未见数据上的性能。但在现实世界的问题中,你不会有这样的奢侈,你将不得不信任你的模型。

制作批量预测

在 Amazon ML 上制作批量预测的过程简单明了,遵循以下步骤:

  1. 从仪表板创建一个新的批量预测。

  2. 选择模型。

  3. 选择应用模型的数据源。

  4. 设置预测输出文件夹并授予权限。

  5. 审查并启动。

我们称预测数据集或数据源为,我们想要进行预测的数据。在本章中,我们处于测试环境中,预测数据集是我们从整个原始数据集中提取的保留数据集。在现实世界的情况下,预测数据集指的是全新的数据,并且不包含目标变量。

预测只有在预测数据集的分布与模型训练所用的训练数据集的分布相似时才能工作。预测数据源和训练数据源还必须共享相同的模式,唯一的区别是预测数据集不需要包含目标变量。Amazon ML 将验证为您的训练数据定义的模式是否与您的预测数据相关,如果数据集不相似,将发出警告。

为了方便起见,我们已重新创建了本章的数据集、数据源和模型。所有数据集和脚本均可在 GitHub 仓库中找到,网址为 github.com/alexperrier/packt-aml/tree/master/ch6。由于我们对原始的泰坦尼克号数据进行了重新排序,因此评估分数将与之前相同数据集获得的分数不同。

创建批量预测作业

要创建批量预测,请转到 Amazon ML 仪表板并点击创建新的批量预测:

图片

然后选择模型。我们选择与泰坦尼克号数据集相关的原始模型,使用 Amazon ML 建议的食谱进行分位数分箱:

  • 所有数值变量的分位数分箱

  • L2 轻度正则化

图片

在模型选择之后,进行数据源选择。如果您尚未为保留集创建数据源,现在可以创建。首先,将您的预测数据集上传到 S3,并指定数据的 S3 路径:

图片

当您点击验证时,Amazon ML 将检查预测数据集是否遵循与模型训练所用的训练数据集相同的模式:

图片

解释预测输出

亚马逊机器学习预测作业的输出将包括两个文件:清单文件和以压缩 CSV 文件格式提供的实际预测结果。亚马逊机器学习将在 S3 上的指定位置s3://bucket/folder创建这些文件,您必须指定该路径。我们使用与数据路径相同的路径:s3://aml.packt/data/。亚马逊机器学习将在/batch_prediction文件夹中创建文件,其中它将写入清单文件以及一个额外的子文件夹/results,实际预测的 CSV 文件将写入该子文件夹。总结一下,在我们的场景中,清单文件将位于 s3://aml.packt/data/batch_prediction文件夹中,压缩的 CSV 结果文件将位于s3://aml.packt/data/batch_prediction/results/文件夹中。分配给批量预测的名称将决定清单和结果文件的命名:

预测定价预测定价:如果您刚刚创建了批量预测的数据源,亚马逊机器学习尚未获得计算预测成本所需的数据统计信息。在这种情况下,它将简单地通知您价格,每 1,000 次预测 0.10 美元。如果预测数据源已经过验证,并且亚马逊机器学习知道记录数,则估计价格将是行数乘以每次预测的价格,四舍五入到最接近的美分。亚马逊机器学习无法预测的无效样本不会产生费用。更多详细信息请参阅docs.aws.amazon.com/machine-learning/latest/dg/pricing.html

查看并点击创建批量预测按钮。批量预测作业将需要几分钟才能完成。完成后,它将在 S3 中创建清单和结果文件,并在亚马逊机器学习仪表板的批量预测部分显示为已完成。

读取清单文件

清单文件包含 JSON 格式的数据,将输入文件映射到预测结果文件,如下所示:

{S3 location of the batch prediction input file.csv : S3 location of the prediction results file}

在我们的场景中,清单文件包含以下行:

{"s3://aml.packt/data/ch6_titanic_heldout.csv":"s3://aml.packt/batch-prediction/result/bp-yTDNSArMqa6-ch6_titanic_heldout.csv.gz"}

多个输入文件:如果您的输入数据被分割成几个文件,并且所有文件都存储在同一个 S3 位置s3://examplebucket/input/,所有输入文件都将被批量预测作业考虑。然后清单文件将包含不同输入文件到相关结果文件的映射。例如,如果您有三个名为data1.csvdata2.csvdata3.csv的输入文件,并且它们都存储在 S3 位置s3://examplebucket/input/,您将看到一个如下所示的映射字符串:

{"s3://examplebucket/input/data1.csv":"s3://examplebucket/output/batch-prediction/result/bp-example-data1.csv.gz", "s3://examplebucket/input/data2.csv":"
 s3://examplebucket/output/batch-prediction/result/bp-example-data2.csv.gz", "s3://examplebucket/input/data3.csv":"
 s3://examplebucket/output/batch-prediction/result/bp-example-data3.csv.gz"}

预测的最大大小:Amazon ML 允许预测文件的最大数据量为 1TB。如果你想要进行预测的数据量更大,可以将数据分割成几个文件,上传到特定的 S3 位置,Amazon ML 将处理不同的文件,并通过并行运行多个批次生成与输入文件数量相等的预测结果文件。清单文件将包含所有不同的输入/输出对,{input_file.csv : prediction_results.csv.gz},针对你的不同批量预测文件。

读取结果文件

输出结果文件使用 gzip 压缩,源自 UNIX 世界,提供的压缩效果优于更常见的 zip 压缩。简单点击即可打开和解压缩 gzip 压缩的结果文件,将其转换为可读的 CSV 文件。或者,可以从命令行调用 gunzip 命令。查看www.gzip.org/获取不同系统的安装信息。

对于二元分类,解压缩的结果文件包含两列或三列,具体取决于初始输入文件是否包含目标值。在我们的二元分类案例中,结果文件包含以下列:trueLabelbestAnswerscore,其中trueLabel是初始的survived列。如果你的初始批量预测数据集没有包含目标值,结果文件将只包含bestAnswerscore列:

  • trueLabel是输入文件中包含的原始目标值

  • bestAnswer是分类结果:0 或 1

  • Score是以科学记数法表示的那个分类的概率

分数的概率分类截止阈值默认为 0.5,或者在评估模型时设置的阈值值。

对于具有N个潜在目标类别的多类分类,结果文件将包含N+1N+2列。trueLabelbestAnswerN列分别包含 N 个类别中每个类别的概率分数。所选的类别将是具有最高概率分数的类别。

对于回归模型,结果文件将只包含一个/两个分数列,包含预测值,可能还有trueLabel列。

评估我们的预测

由于我们知道保留样本的真实类别,我们可以计算ROC-AUC分数和其他指标,以查看我们的预测和验证分数有多接近。假设我们的数据子集具有非常相似的分布,这两个分数最终应该非常接近。差异仅来自验证和保留集样本中的随机性。

以下 Python 脚本使用了scikit-learn库(scikit-learn.org/)以及 pandas 库。只需几行 Python 代码即可计算模型在该预测数据集上的 AUC 得分。首先,从 S3 下载压缩文件,然后在 Python 笔记本或控制台中运行以下代码:

import pandas as pd  
from sklearn import metrics

# open file the csv file on your local
df = pd.read_csv(path/location_of_the_unzipped_results_csv_file)

# calculate the true and false positive rate
fpr, tpr, threshold = metrics.roc_curve(df.trueLabel, df.score)
roc_auc = metrics.auc(fpr, tpr)

Python 环境:本书中的所有 Python 代码都是针对 Python 3.5 或更高版本。有关 Anaconda 库的更多信息,请参阅www.continuum.io/downloads。Anaconda 是一个惊人的强大开源数据科学平台,在 Python 中。它包含最重要的库(numpypandasscikit-learnmatplotlib等)以及 Jupyter 笔记本环境。我们使用 IPython 控制台,因为它使用简单,并且有许多魔法命令(ipython.readthedocs.io/en/stable/interactive/magics.html)。

在预测结果上运行之前的 Python 脚本,我们在保留集数据集上获得 AUC 为 0.84,这非常接近我们在第五章“模型创建”中验证集上获得的 AUC(0.85)。我们可以得出结论,我们的模型在面对新的、之前未预见的数据时相当稳定和健壮。

下面的图表显示了所选模型的验证集(虚线)和保留集(实线)的 ROC 曲线。对于较高的阈值,验证集略好。这种差异反映了两个数据集中数据分布的不同:

图片

评估保留集

在第五章“模型创建”中,我们评估了我们的不同模型在训练数据源的一个切片上的性能。我们对每个模型都获得了 AUC 得分,并选择了具有最佳 AUC 得分的 AUC。我们依赖 Amazon ML 创建验证集,通过将训练数据集分成两部分,其中 70%用于训练,30%的数据用于验证。我们本可以自己进行分割,创建验证数据源,并指定用于模型评估的数据源。

事实上,我们没有任何阻止我们在保留集数据集上运行模型评估。如果您转到模型摘要页面,您会在评估部分注意到一个“执行另一个评估”按钮:

图片

点击它。您将被要求选择用于评估的数据源。选择保留集;Amazon ML 将验证数据是否遵循相同的模式,并且与训练数据相似。您最终会在模型上获得两个评估:

图片

如预期的那样,保留集的评估 AUC 与我们通过下载结果并在 Python 中计算 AUC 获得的 AUC 相等:

图片

发现谁将生存

然而,预测的真正价值并不在于验证我们模型的鲁棒性;它在于在我们的预测数据集、我们的环境中进行预测,对保留数据集中的这个“新”乘客列表进行生存预测。

结果文件中的行与预测文件中的行顺序完全相同。我们可以将保留文件的前几行和结果文件的前几行并排放置,并看到survivedtrueLabel列是相同的:

乘法试验

在各种模型和数据集版本上的评估分数在一定程度上取决于评估集中的样本。如果我们在这三个数据集上多次运行以下实验,我们会看到分数的某些变化:

  • 打乱并分割数据集为三个部分——训练、验证和保留,并创建相应的数据源

  • 在训练数据集上训练模型,保持默认的亚马逊机器学习设置(轻微的 L2 正则化)

  • 在评估和保留数据集上评估模型

下面的图表显示了三个模型在多次试验中的相应性能。图表上写有平均 AUC。我们看到平均而言,扩展数据集的性能优于默认配方下的原始数据集(AUC = 0.84)和没有分位数分箱的原始数据集(AUC = 0.83)。我们还注意到,在某些试验中,扩展数据集的性能不如原始数据集。对于试验 3,默认配方甚至略逊于没有分位数分箱的配方:

这显示了内部数据分布变异性的重要性。当尝试使用不同特征和处理的多个数据集变体时,基于您模型的多次运行得出结论是很重要的。单次评估可能会导致错过最佳模型。

进行实时预测

使用批量预测时,您可以通过创建数据源一次将所有希望模型预测的样本提交给亚马逊机器学习。使用实时预测(也称为流式或在线预测),想法是每次发送一个样本到一个 API 端点、一个 URL,通过 HTTP 查询,并为每个样本接收预测和信息。

在模型上设置实时预测包括了解预测 API 端点 URL 和编写一个脚本,该脚本可以读取您的数据,将每个新样本发送到该 API URL,并检索预测的类别或值。我们将在下一节中提供一个基于 Python 的示例。

亚马逊机器学习(Amazon ML)还提供了一种方法,可以在预测页面上对您实时创建的数据进行预测。我们可以输入一个潜在的乘客在“泰坦尼克号”上的档案,并查看该档案是否能够幸存。这是一种探索数据集变量对结果影响的绝佳方式。

在设置流式 API 之前,让我们看看通过提交几个单个乘客档案我们能收集到什么信息。我们甚至可以尝试回答这个问题——你会在大西洋号上幸存下来吗?

手动探索变量影响

前往模型摘要页面,然后点击页面左侧的“尝试实时预测”链接。接下来的页面显示一个表单,您可以在其中填写我们数据集中变量的值,除了目标变量。

让我们看看亚历克斯·佩里埃先生,一位头等舱乘客,他在南安普顿和他的家人(3 个兄弟姐妹和 2 个孩子)登船,并支付了 100 英镑的船费,他是否会幸存。好吧,在这种情况下,模型给出了非常低的生存概率(0.001),这意味着模型有信心预测这位乘客不会幸存。他的 12 岁女儿有更大的生存机会(概率为 0.56),尽管模型对此不太确定。然而,如果那个女孩独自旅行(兄弟姐妹=孩子=0),在头等舱的条件下,她的生存机会会激增到 0.98。在三等舱,她将不太幸运(概率:0.28):

图片

因此,通过逐个改变数据中的变量,我们可以更好地理解每个变量对结果的影响。

设置实时预测

为了演示实时预测,我们将使用来自 UCI 仓库的Spam数据集。这个数据集由 5,574 条标注为垃圾邮件或非垃圾邮件(非垃圾邮件)的短信组成。没有缺失值,只有两个变量:短信的性质(垃圾邮件或非垃圾邮件)和短信文本,没有其他内容。《Spam》数据集以原始形式可在archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection找到,并在本书的 GitHub 仓库github.com/alexperrier/packt-aml/tree/master/ch6中提供。我们简单地将目标从分类值:垃圾邮件非垃圾邮件转换为二进制值:1(代表垃圾邮件)和 0(代表非垃圾邮件),这样 Amazon ML 就能理解预测为二分类类型。

AWS SDK

AWS 为其众多服务提供了几个 API 和 软件开发工具包 (SDKs)。您可以使用编程方式管理 S3 上的文件,设置 EC2 实例,并在 Amazon ML 上创建数据源、模型和评估,而无需使用基于网页的用户界面。AWS API 是低级端点。通常,使用 SDKs 更简单、更高效,SDKs 是 API 的包装器,并支持多种语言(Python、Ruby、Java、C++ 等)。在这本书中,我们将使用基于 Boto3 库的 Python SDK。我们将在第七章 Command Line and SDK 中详细探讨 Python SDK 的使用。现在,我们只将使用 predict() 方法,这是实时预测所必需的。但首先,我们需要通过在我们的本地机器上设置 AWS 凭据来启用对 AWS 的访问。

设置 AWS 凭据

为了以编程方式访问 AWS,我们首先需要通过命令行访问 AWS。这需要以下内容:

  • 在 AWS IAM 为您的用户创建访问密钥

  • 在本地安装 AWS-CLI 命令行界面

  • 配置 AWS-CLI 使用 AWS 访问密钥

AWS 访问密钥

如果您还记得第三章 Overview of an Amazon Machine Learning Workflow,我们为 AML@Packt 用户创建了访问密钥。访问密钥是基于用户的,由两部分组成:访问密钥 ID,它始终在 IAM 的用户安全凭证选项卡中可用,以及秘密访问密钥,仅在创建时显示。在创建这些访问密钥时,您有下载它们的机会。如果您当时没有这样做,现在可以为您的用户重新创建访问密钥。转到 IAM 控制台 console.aws.amazon.com/iam,点击您的用户配置文件,选择安全凭证选项卡,然后点击创建访问密钥按钮。这次请确保在您的本地机器上下载密钥或将它们复制到其他地方。请注意,每个用户最多只能有两套访问密钥。如果您已经有两个与您的用户关联的密钥,您必须删除现有的密钥才能创建新的密钥:

图片

设置 AWS CLI

到目前为止,我们只使用过 AWS 网页界面,在 AWS 网站上逐页点击。另一种与 AWS 服务交互的方式是通过终端窗口中的命令行,使用 aws cli 库。CLI 代表命令行界面。

要安装 aws cli 库,打开一个终端窗口。对于基于 Python 的环境(Python 2 版本 2.6.5+ 或 Python 3 版本 3.3+),安装 aws cli 包括在终端中运行以下命令:

pip install awscli

其他环境下的安装完整说明可在docs.aws.amazon.com/cli/latest/userguide/installing.html找到。一旦 AWS-CLI 安装完成,运行以下命令来配置它:

aws configure

您将被要求输入您的访问密钥、默认区域和格式。有关更深入的说明,请参阅docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-quick-configuration

简而言之,AWS-CLI 命令遵循以下语法:

aws {service name} {command} {parameters}

通过运行以下命令来测试您的设置:

aws s3 ls aml.packt

您应该能看到您 s3 账户中的所有存储桶、文件夹和文件列表。以下是我列出aml.packt存储桶中的文件和文件夹时的输出:

我们将在第七章[efe6f699-a4eb-4c88-8e81-1408d6c3c5c4.xhtml],Command Line and SDK中详细探讨如何使用 CLI 运行您的 Amazon ML 项目。

Python SDK

在本章中,我们将不再使用 AWS-CLI,而是切换到 Python SDK。我们需要为 AWS CLI 设置凭证,以便我们的 SDK 脚本能够访问我们的 AWS 账户。要使用 Python SDK,我们需要安装Boto3库,该库包含在 Anaconda 发行版中。如果您使用 Anaconda 作为 Python 环境,您应该已经安装了boto3包。如果没有,您可以使用以下命令使用pip安装它:

pip install boto3

Boto3将使用我们为 AWS CLI 配置的凭证。无需进行特定设置。Boto3 适用于大多数 AWS 服务。完整文档可在boto3.readthedocs.io/找到。我们最小化使用Boto3,只需指定我们想要使用的服务,即机器学习,然后使用predict()方法将适当的数据发送到模型。作为回报,我们获得所需的预测。以下 Python 代码初始化一个客户端以访问机器学习服务。

import boto3
client = boto3.client('machinelearning')

predict()方法需要以下参数:

  • MLModelId: 您想要用于预测的模型的 ID

  • PredictEndpoint: 您模型对应的 Amazon ML 端点 URL

  • Record: 样本的 JSON 格式版本

MLModelIdPredictEndpoint URL 可以从模型摘要页面获取。Record是一个 JSON 格式的字符串。我们将通过打开保留的样本集,循环遍历每个样本,并通过predict()方法发送它来模拟一个流式应用程序。

我们将初始数据集分为一个包含 4,400 个样本的训练集和一个包含 1,174 个样本的保留集。这些子集可在 GitHub 仓库中找到。我们为训练子集创建一个数据源,并使用默认设置(轻微的 L2 正则化)创建一个模型及其评估。我们保留推断的架构(二进制和文本),建议的配方(除了对文本变量的标记化之外没有转换),并使用默认模型参数(10 次遍历和轻微的 L2 正则化)。训练数据集进一步由 Amazon ML 分割为较小的训练数据集和验证数据集,分别为初始4,400样本的 70%和 30%。在验证集上获得的 AUC 分数非常高,为0.98

图片

要获取ModelIDendpoint URL,请访问模型的摘要页面。从页面顶部复制ModelID。然后向下滚动到预测部分,点击创建endpoint按钮:

图片

在这一点上,您将获得模型实时预测定价的估计,并要求确认创建endpoint

您的模型大小为 502.1 KB。每当您的endpoint处于活动状态时,您将产生 0.001 美元的预留容量费用。实时预测的预测费用为每次预测 0.0001 美元,向上取整到最接近的美分

几分钟后,endpoint将被创建,您将获得endpoint URL:

图片

现在我们知道了endpoint URL,我们可以编写一个简单的 Python 代码,将短信消息、简单的文本发送到我们的预测模型,看看这条消息是否被预测为垃圾邮件或正常邮件。我们将文本Hello world, my name is Alex发送以分类为正常邮件,而文本Call now to get dating contacts for free, no cash no credit card可能因为包含freecashdating等词语而被检测为垃圾邮件。

代码的初始化/声明部分如下:

import boto3
import json  # for parsing the returned predictions

# Initialize the client
client = boto3.client('machinelearning')

# The endpoint url is obtained from the model summary
endpoint_url = "https://realtime.machinelearning.us-east-1.amazonaws.com/"

# replace with your own model ID
model_id = "ml-kJmiRxxxxxx" 

# The actual sample to be predicted. JSON formatted
record = { "nature": "Hello world, my name is Alex" }

我们现在使用机器学习服务 SDK 的predict()函数:

response = client.predict(    
   MLModelId         = model_id,
   Record               = record,
   PredictEndpoint = endpoint_url
)

最后,格式化打印响应:

print(json.dumps(response, indent=4))

这返回以下 JSON 格式的字符串:

{
   "ResponseMetadata": {
       "RetryAttempts": 0,
       "HTTPHeaders": {
           "content-type": "application/x-amz-json-1.1",
           "content-length": "143",
           "date": "Tue, 10 Jan 2017 16:20:49 GMT",
           "x-amzn-requestid": "bfab2af0-d750-11e6-b8c2-45ac3ab2f186"
       },
       "HTTPStatusCode": 200,
       "RequestId": "bfab2af0-d750-11e6-b8c2-45ac3ab2f186"
   },
   "Prediction": {
       "predictedScores": {
           "0": 0.001197131467051804
       },
       "predictedLabel": "0",
       "details": {
           "PredictiveModelType": "BINARY",
           "Algorithm": "SGD"
       }
   }
}

JSON 响应由两部分组成:第一部分与请求本身相关,ResponseMetadata,第二部分与Prediction相关。ResponseMetadata部分的HTTPStatusCode告诉我们我们的查询是成功的("HTTPStatusCode": 200)

预测部分的解释很简单。短信被预测为垃圾邮件,概率极低,为 0.12%,因此被分类为正常邮件,这正是我们对文本Hello world, my name is Alex的预期。

我们预计文本Call now to get dating contacts for free, no cash no credit card将被分类为垃圾邮件,因为freecalldating等词语通常是垃圾邮件的强烈指示。我们得到的以下结果:

{    
    "predictedScores": { "1": 0.810875654220581 },
    "predictedLabel": "1",
}

文本被分类为垃圾邮件,这正是我们预期的。从这两个简单的例子来看,我们的模型似乎运行良好。一旦我们可以通过 API 调用逐个样本地获取预测,就可以将数据流连接到端点并获取实时预测。

为了模拟这个流程,我们可以使用 Python 读取整个新样本文件,将每个样本发送到模型,并捕获结果。让我们用保留的 Spam 样本集来做这件事。

以下 Python 代码读取文件,将其加载到 pandas 数据框中,并遍历数据框中的每一行。我们使用iterrows()遍历数据框中的每一行。这种方法比itertuples()慢,但代码可读性更好。以下代码没有经过优化:

import boto3
import json
import pandas as pd

# Initialize the Service, the Model ID and the endpoint url
client = boto3.client('machinelearning')
# replace with your own endpoint url and model ID
endpoint_url = "https://realtime.machinelearning.us-east-1.amazonaws.com"
model_id = "ml-kJmiRHyn1UM"

# Memorize which class is spam and which is ham
spam_label = {'0': 'ham', '1':'spam'}

# Load the held out dataset into a panda DataFrame
df = pd.read_csv('held-out.csv')

# Loop over each DataFrame rows    
for index, row in df.iterrows():
   # The record
   record = { "body": row['sms'] }
   response = client.predict(    
       MLModelId       = model_id,
       Record          = record,
       PredictEndpoint = endpoint_url
   )

   # get the label and score from the response
   predicted_label = response['Prediction']['predictedLabel']
   predicted_score = response['Prediction']['predictedScores'][predicted_label]
   print("[%s] %s (%0.2f):t %s "% (spam_label[str(row['nature'])],
                               spam_label[predicted_label],
                               predicted_score,
                               row['sms'] )
   )

Amazon ML 的响应速度极快。数千个样本在几秒钟内被处理。以下是我们的结果摘要:

图片

在这里,每一行格式如下:

[Predicted class] trueLabel (spam probability):      SMS message  

注意,在检测到的三个垃圾短信中,只有两个实际上是垃圾短信。文本“Money i have won wining number 946 wot do i do next”可能因为包含“Money”或“wining”等词语而被检测为垃圾短信,但实际上是一条正常信息。

总体而言,在整个预测过程中,概率非常接近 0 或 1,这表明模型在分类上非常果断,没有犹豫。保留数据集的 ROC 曲线显示了高水平的确切性:

图片

摘要

在本章中,我们探讨了 Amazon ML 工作流程的最后一步,即预测。Amazon ML 提供了几种方法将您的模型应用于新的数据集以进行预测。批处理模式涉及一次性将所有新数据提交给模型,并在 S3 上返回实际的预测 csv 文件。另一方面,实时预测是基于逐个发送样本到 API 并获取预测结果。我们探讨了如何在 Amazon ML 平台上创建 API。我们还开始使用命令行和 Python SDK 与 Amazon ML 服务交互——我们将在第七章“命令行和 SDK”中更深入地探讨这一点。

如前几章所述,Amazon ML 服务是围绕随机梯度下降(SGD)算法构建的。这个算法已经存在很多年了,被用于许多不同的领域和应用,从信号处理和自适应滤波到预测分析或深度学习。

在下一章中,我们将介绍算法及其一些版本,并揭示它在处理不同类型的数据和预测问题时的行为。我们将解释为什么在我们的情况下,通常被轻视的数值分位数装箱是性能和稳定性的提升者。

第七章:命令行和 SDK

使用 AWS 网页界面来管理和运行项目是耗时的。在本章中,我们将远离网页界面,开始通过命令行使用 AWS 命令行界面AWS CLI)和 Python SDK 的 Boto3 库来运行我们的项目。

第一步将是通过 AWS CLI 驱动整个项目,包括上传文件到 S3、创建数据源、模型、评估和预测。正如您将看到的,脚本将极大地简化使用 Amazon ML。我们将利用这些新能力通过执行交叉验证和特征选择来扩展我们的数据科学能力。

到目前为止,我们已经将原始数据集分为三个数据块:训练、验证和测试。然而,我们已经看到模型选择可能强烈依赖于数据分割。打乱数据——可能不同的模型会被认为是最好的。交叉验证是一种通过平均模型在多个数据分割上的性能来减少这种依赖性的技术。交叉验证涉及创建许多用于训练、验证和测试的数据源,使用网页界面将会耗时。AWS CLI 将使我们能够快速创建新的数据源和模型,并有效地执行交叉验证。

数据科学中的另一个重要技术是特征消除。在数据集中拥有大量特征,无论是由于密集的特征工程结果还是因为它们存在于原始数据集中,都可能影响模型的表现。通过选择和保留最佳且最有意义的特征,同时拒绝不那么重要的特征,可以显著提高模型的预测能力。有许多特征选择方法。我们将实现一个简单而高效的方法,称为递归特征选择。通过 Boto3 库可访问的 AWS Python SDK 将使我们能够构建围绕 Amazon ML 的代码,以实现递归特征选择。

在本章中,您将学习以下内容:

  • 如何通过 AWS 命令行和 AWS Python SDK 处理整个项目工作流程:

    • 管理数据上传到 S3

    • 创建和评估模型

    • 制作和导出预测

  • 如何使用 AWS CLI 实现交叉验证

  • 如何使用 AWS Python SDK 实现递归特征选择

开始使用和设置

从原始数据创建一个表现良好的预测模型需要许多尝试和错误,很多来回调整。创建新特征、清理数据以及尝试为模型设定新参数都是确保模型稳健性的必要步骤。在数据、模型和评估之间需要不断的来回调整。通过 AWS CLI 或 Boto3 Python 库脚本化这个工作流程,将使我们能够加快创建、测试、选择的循环。

使用 CLI 与 SDK 的比较

AWS 提供了多种方式,除了 UI 之外,还可以与它的服务进行交互,包括 CLI、API 和多种语言的 SDK。尽管 AWS CLI 和 SDKs 并不包含所有 AWS 服务。例如,Athena SQL 是一项新服务,在撰写本文时,它尚未包含在 AWS CLI 模块或任何 AWS SDK 中。

AWS 命令行界面(CLI)是一个命令行外壳程序,允许您从您的 shell 终端管理您的 AWS 服务。一旦安装并设置了适当的权限,您就可以编写命令来管理您的 S3 文件、AWS EC2 实例、Amazon ML 模型以及大多数 AWS 服务。

一般而言,软件开发工具包(SDK),简称 SDK,是一组可用于开发针对特定平台的应用软件的工具。简而言之,SDK 是 API 的包装器。API 包含核心交互方法,而 SDK 包括调试支持、文档以及高级函数和方法。API 可以被视为 AWS 支持的最低公共基数,而 SDK 则是 API 的高级实现。

AWS SDKs 可用 12 种不同的语言,包括 PHP、Java、Ruby 和 .NET。在本章中,我们将使用 Python SDK。

使用 AWS CLI 或 SDK 需要设置我们的凭证,我们将在下一节中完成此操作。

安装 AWS CLI

为了设置您的 CLI 凭证,您需要您的访问密钥 ID 和您的秘密访问密钥。您很可能在之前的章节中下载并保存了它们。如果不是这样,您应该简单地从 IAM 控制台(console.aws.amazon.com/iam)创建新的凭证。

导航到“用户”,选择您的 IAM 用户名,然后点击“安全凭证”选项卡。选择创建访问密钥并下载 CSV 文件。将密钥存储在安全位置。我们将在几分钟内需要该密钥来设置 AWS CLI。但首先,我们需要安装 AWS CLI。

Docker 环境 – 本教程将帮助您在 Docker 容器中使用 AWS CLI:blog.flowlog-stats.com/2016/05/03/aws-cli-in-a-docker-container/。运行 AWS CLI 的 Docker 镜像可在 hub.docker.com/r/fstab/aws-cli/ 找到。

没有必要重写 AWS CLI 的安装文档。它是完整且最新的,可在 docs.aws.amazon.com/cli/latest/userguide/installing.html 找到。简而言之,安装 CLI 需要您已经安装了 Python 和 pip

然后,运行以下命令:

$ pip install --upgrade --user awscli

将 AWS 添加到您的 $PATH

$ export PATH=~/.local/bin:$PATH

重新加载 bash 配置文件(这是针对 OSX 的):

$ source ~/.bash_profile

请使用以下命令检查一切是否正常工作:

$ aws --version

您应该会看到以下类似的输出:

$ aws-cli/1.11.47 Python/3.5.2 Darwin/15.6.0 botocore/1.5.10

安装完成后,我们需要配置 AWS CLI 类型:

$ aws configure

现在输入您刚刚创建的访问密钥:

$ aws configure
 AWS Access Key ID [None]: ABCDEF_THISISANEXAMPLE
AWS Secret Access Key [None]: abcdefghijk_THISISANEXAMPLE
Default region name [None]: us-west-2
Default output format [None]: json

选择离您最近且您喜欢的格式(JSON、文本或表格)。默认格式是 JSON。

AWS configure 命令创建两个文件:一个config文件和一个凭证文件。在 OSX 上,这些文件是~/.aws/config~/.aws/credentials。您可以直接编辑这些文件来更改您的访问或配置。如果您需要访问多个 AWS 账户,您需要创建不同的配置文件。您可以通过 AWS configure 命令这样做:

$ aws configure --profile user2

您也可以直接在configcredential文件中这样做:

~/.aws/config

[default]
output = json
region = us-east-1

[profile user2]
output = text
region = us-west-2

您可以按照以下方式编辑Credential文件:

~/.aws/credentials

[default]
aws_secret_access_key = ABCDEF_THISISANEXAMPLE
aws_access_key_id = abcdefghijk_THISISANEXAMPLE

[user2]
aws_access_key_id = ABCDEF_ANOTHERKEY
aws_secret_access_key = abcdefghijk_ANOTHERKEY

请参阅 AWS CLI 设置页面以获取更深入的信息:

docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html

学习 CLI 语法

任何 AWS CLI 命令的整体格式如下:

$ aws <service> [options] <command> <subcommand> [parameters]

这里术语如下所述:

  • <service>:您正在管理的服务的名称:S3、机器学习和 EC2

  • [options]:允许您设置区域、配置文件和命令的输出

  • <command> <subcommand>:这是您想要执行的真正命令

  • [parameters]:是这些命令的参数

一个简单的例子将帮助您更好地理解语法。要列出名为aml.packt*的 S3 存储桶的内容,命令如下:

$ aws s3 ls aml.packt

在这里,s3是服务,ls是命令,aml.packt是参数。aws help命令将输出所有可用服务的列表。

要获取特定服务及其命令的帮助,请编写以下内容:

$ aws <service> help

例如,aws s3 help将通知您,在单个对象上可用的s3命令有 ls、mv 和 rm,用于列出、移动和删除,并且基本的aws s3命令遵循以下格式:

$ aws s3 <command> sourceURI destinationURI  [parameters]

在这里,sourceURIdestinationURI可以是您本地机器上的文件(或多个文件)和 S3 上的文件,也可以是 S3 上的两个文件。以下是一个例子:

$ aws s3 cp /tmp/foo/ s3://The_Bucket/ --recursive --exclude "*" --include "*.jpg"

这将复制您本地机器上/tmp/foo文件夹中的所有(多亏了参数——递归)JPG 文件(仅*.jpg文件)到名为The_Bucket的 S3 存储桶。

AWS 文档中有更多示例和解释,可在以下链接找到:

docs.aws.amazon.com/cli/latest/userguide/cli-chap-using.html

使用 JSON 文件传递参数

对于某些服务和命令,参数列表可能会变得很长,难以检查和维护。

例如,要通过命令行界面(CLI)创建一个 Amazon ML 模型,您至少需要指定七个不同的元素:模型 ID、名称、类型、模型的参数、训练数据源的 ID 以及食谱名称和 URI(aws machinelearning create-ml-model help)。

在可能的情况下,我们将使用 CLI 功能从 JSON 文件中读取参数,而不是在命令行中指定它们。AWS CLI 还提供了一种生成 JSON 模板的方法,您可以使用正确的参数使用该模板。要生成该 JSON 参数文件模型(JSON 框架),只需在命令名称后添加 --generate-cli-skeleton。例如,要生成机器学习服务创建模型命令的 JSON 框架,请编写以下内容:

$ aws machinelearning create-ml-model --generate-cli-skeleton

这将产生以下输出:

{
   "MLModelId": "",
   "MLModelName": "",
   "MLModelType": "",
   "Parameters": {
       "KeyName": ""
   },
   "TrainingDataSourceId": "",
   "Recipe": "",
   "RecipeUri": ""
}

您可以根据自己的喜好进行配置。

要使骨架命令生成 JSON 文件而不是在终端中简单地输出骨架,请添加 > filename.json

$ aws machinelearning create-ml-model --generate-cli-skeleton > filename.json

这将创建一个包含 JSON 模板的 filename.json 文件。一旦指定了所有必需的参数,您就可以使用以下命令创建模型(假设 filename.json 在当前文件夹中):

$ aws machinelearning create-ml-model file://filename.json

在我们进一步通过 CLI 深入探讨机器学习工作流程之前,我们需要介绍本章将使用的数据集。

介绍 Ames Housing 数据集

在本章中,我们将使用由 迪安·德·科克 编制的 Ames Housing 数据集,用于数据科学教育。它是流行的但较旧的 Boston Housing 数据集的一个很好的替代品。Ames Housing 数据集在 Kaggle 网站的“高级回归技术”挑战赛中被使用:www.kaggle.com/c/house-prices-advanced-regression-techniques/。数据集的原始版本可在:www.amstat.org/publications/jse/v19n3/decock/AmesHousing.xls 和本章的 GitHub 仓库中找到。

Ames Housing 数据集包含 79 个解释变量,描述了爱荷华州艾姆斯市住宅的各个方面(几乎涵盖所有方面),目的是预测每套住宅的售价。该数据集有 2930 行。变量数量众多,使该数据集成为特征选择的良好候选。

关于该数据集的起源以及不同变量的深入解释,请阅读由 迪安·德·科克 撰写的论文,该论文可在 PDF 格式下在 ww2.amstat.org/publications/jse/v19n3/decock.pdf 获取。

如同往常,我们将首先将数据集分割成训练集和验证集,并在训练集上构建模型。训练集和验证集都可在 GitHub 仓库中找到,分别命名为 ames_housing_training.csvames_housing_validate.csv。整个数据集位于 ames_housing.csv 文件中。

使用 shell 命令分割数据集

命令行是数据科学家经常忘记但非常强大的盟友。许多非常强大的数据处理操作可以通过正确的 shell 命令实现,并且执行速度极快。为了说明这一点,我们将使用 shell 命令来打乱、分割,并创建Ames Housing数据集的训练和验证子集:

  1. 首先,将第一行提取到单独的文件ames_housing_header.csv中,并从原始文件中删除:
 $ head -n 1 ames_housing.csv > ames_housing_header.csv

  1. 我们只需将第一行之后的全部行尾接到同一个文件中:
 $ tail -n +2 ames_housing.csv > ames_housing_nohead.csv

  1. 然后将行随机排序到一个临时文件中。(gshuf是 OSX 中 Linux shuf shell命令的等价物。可以通过brew install coreutils安装。)
 $ gshuf ames_housing_nohead.csv -o ames_housing_nohead.csv

  1. 提取前 2,050 行作为训练文件,最后 880 行作为验证文件:
 $ head -n 2050 ames_housing_nohead.csv > ames_housing_training.csv
 $ tail -n 880 ames_housing_nohead.csv > ames_housing_validate.csv

  1. 最后,将标题添加回训练和验证文件中:
 $ cat ames_housing_header.csv ames_housing_training.csv > tmp.csv 
        $ mv tmp.csv ames_housing_training.csv

 $ cat ames_housing_header.csv ames_housing_validate.csv > tmp.csv
        $ mv tmp.csv ames_housing_validate.csv

使用 CLI 的一个简单项目

我们现在准备好使用 CLI 执行一个简单的 Amazon ML 工作流程。这包括以下内容:

  • 在 S3 上上传文件

  • 创建数据源和配方

  • 创建模型

  • 创建评估

  • 预测批量和实时

让我们从上传训练和验证文件到 S3 开始。在以下行中,将存储桶名称aml.packt替换为您自己的存储桶名称。

要将文件上传到 S3 位置s3://aml.packt/data/ch8/,运行以下命令行:

$ aws s3 cp ./ames_housing_training.csv s3://aml.packt/data/ch8/
upload: ./ames_housing_training.csv to s3://aml.packt/data/ch8/ames_housing_training.csv

$ aws s3 cp ./ames_housing_validate.csv s3://aml.packt/data/ch8/
upload: ./ames_housing_validate.csv to s3://aml.packt/data/ch8/ames_housing_validate.csv

Amazon ML CLI 命令概述

S3 部分到此结束。现在让我们探索 Amazon 机器学习服务的 CLI。

所有 Amazon ML CLI 命令均可在docs.aws.amazon.com/cli/latest/reference/machinelearning/找到。共有 30 个命令,可以根据对象和动作进行分组。

您可以执行以下操作:

  • create : 创建对象

  • describe: 根据某些参数(位置、日期、名称等)搜索对象

  • get: 给定一个对象 ID,返回信息

  • update: 给定一个对象 ID,更新对象

  • delete: 删除一个对象

这些可以在以下元素上执行:

  • datasource

    • create-data-source-from-rds

    • create-data-source-from-redshift

    • create-data-source-from-s3

    • describe-data-sources

    • delete-data-source

    • get-data-source

    • update-data-source

  • ml-model

    • create-ml-model

    • describe-ml-models

    • get-ml-model

    • delete-ml-model

    • update-ml-model

  • 评估

    • create-evaluation

    • describe-evaluations

    • get-evaluation

    • delete-evaluation

    • update-evaluation

  • 批量预测

    • create-batch-prediction

    • describe-batch-predictions

    • get-batch-prediction

    • delete-batch-prediction

    • update-batch-prediction

  • 实时端点

    • create-realtime-endpoint

    • delete-realtime-endpoint

    • predict

您还可以处理标签并设置等待时间。

注意,AWS CLI 允许您从 S3、Redshift 和 RDS 创建数据源,而 Web 界面只允许从 S3 和 Redshift 创建数据源。

创建数据源

我们将首先创建数据源。让我们首先通过生成以下骨架来查看需要哪些参数:

$ aws machinelearning create-data-source-from-s3 --generate-cli-skeleton

这生成了以下 JSON 对象:

{
   "DataSourceId": "",
   "DataSourceName": "",
   "DataSpec": {
       "DataLocationS3": "",
       "DataRearrangement": "",
       "DataSchema": "",
       "DataSchemaLocationS3": ""
   },
   "ComputeStatistics": true
}

不同的参数大多一目了然,更多详细信息可以在 AWS 文档中找到,链接为docs.aws.amazon.com/cli/latest/reference/machinelearning/create-data-source-from-s3.html

关于模式的话:当从 Web 界面创建数据源时,您有使用向导的可能性,以指导您创建模式。如您所回忆的,您将通过几个屏幕进行指导,在这些屏幕上您可以指定所有列的类型,以及目标变量和索引列的存在。向导通过猜测变量的类型来简化过程,从而提供一个默认的模式,您可以对其进行修改。

通过 AWS CLI 没有默认的模式可用。您必须自己定义整个模式,无论是在DataSchema字段中的 JSON 格式,还是在DataSchemaLocationS3字段中上传一个模式文件并指定其位置。

由于我们的数据集包含许多变量(79 个),我们采取了捷径,使用向导创建了一个默认的模式,并将其上传到 S3。在整个章节的其余部分,我们将指定模式位置而不是其 JSON 定义。

在本例中,我们将创建以下数据源参数文件,dsrc_ames_housing_001.json

{
   "DataSourceId": "ch8_ames_housing_001",
   "DataSourceName": "[DS] Ames Housing 001",
   "DataSpec": {
       "DataLocationS3": 
         "s3://aml.packt/data/ch8/ames_housing_training.csv",
       "DataSchemaLocationS3": 
         "s3://aml.packt/data/ch8/ames_housing.csv.schema"        
   },
   "ComputeStatistics": true
}

对于验证子集(保存为dsrc_ames_housing_002.json):

{
   "DataSourceId": "ch8_ames_housing_002",
   "DataSourceName": "[DS] Ames Housing 002",
   "DataSpec": {
       "DataLocationS3": 
         "s3://aml.packt/data/ch8/ames_housing_validate.csv",
       "DataSchemaLocationS3": 
         "s3://aml.packt/data/ch8/ames_housing.csv.schema"        
   },
   "ComputeStatistics": true
}

由于我们已经将数据分为训练集和验证集,因此无需指定数据DataRearrangement字段。

或者,我们也可以避免分割数据集,并在原始数据集上指定以下DataRearrangement,假设它已经被洗牌了:(保存为dsrc_ames_housing_003.json):

{
   "DataSourceId": "ch8_ames_housing_003",
   "DataSourceName": "[DS] Ames Housing training 003",
   "DataSpec": {
       "DataLocationS3": 
         "s3://aml.packt/data/ch8/ames_housing_shuffled.csv",
       "DataRearrangement": 
         "{"splitting":{"percentBegin":0,"percentEnd":70}}",
       "DataSchemaLocationS3":
         "s3://aml.packt/data/ch8/ames_housing.csv.schema"        
   },
   "ComputeStatistics": true
}

对于验证集(保存为dsrc_ames_housing_004.json):

{
   "DataSourceId": "ch8_ames_housing_004",
   "DataSourceName": "[DS] Ames Housing validation 004",
   "DataSpec": {
       "DataLocationS3":
         "s3://aml.packt/data/ch8/ames_housing_shuffled.csv",
       "DataRearrangement": 
         "{"splitting":{"percentBegin":70,"percentEnd":100}}",
   },
   "ComputeStatistics": true
}

在这里,ames_housing.csv文件之前已经使用gshuf命令行进行了洗牌,并上传到了 S3:

$ gshuf ames_housing_nohead.csv -o ames_housing_nohead.csv
$ cat ames_housing_header.csv ames_housing_nohead.csv > tmp.csv
$ mv tmp.csv ames_housing_shuffled.csv
$ aws s3 cp ./ames_housing_shuffled.csv s3://aml.packt/data/ch8/

注意,我们不需要创建这四个数据源;这些只是创建数据源的替代方法示例。

我们然后通过运行以下命令创建这些数据源:

$ aws machinelearning create-data-source-from-s3 --cli-input-json file://dsrc_ames_housing_001.json

我们可以检查数据源创建是否挂起:

作为回报,我们得到了之前指定的数据源 ID:

{
   "DataSourceId": "ch8_ames_housing_001"
}

我们可以使用以下方式获取该数据源的信息:

$ aws machinelearning  get-data-source --data-source-id ch8_ames_housing_001

这返回以下内容:

{
   "Status": "COMPLETED",
   "NumberOfFiles": 1,
   "CreatedByIamUser": "arn:aws:iam::178277xxxxxxx:user/alexperrier",
   "LastUpdatedAt": 1486834110.483,
   "DataLocationS3": "s3://aml.packt/data/ch8/ames_housing_training.csv",
   "ComputeStatistics": true,
   "StartedAt": 1486833867.707,
   "LogUri": "https://eml-prod-emr.s3.amazonaws.com/178277513911-ds-ch8_ames_housing_001/.....",
   "DataSourceId": "ch8_ames_housing_001",
   "CreatedAt": 1486030865.965,
   "ComputeTime": 880000,
   "DataSizeInBytes": 648150,
   "FinishedAt": 1486834110.483,
   "Name": "[DS] Ames Housing 001"
}

注意,我们有访问操作日志 URI 的权限,这可能在稍后分析模型训练时很有用。

创建模型

使用create-ml-model命令创建模型遵循相同的步骤:

  1. 使用以下步骤生成骨架:
        $ aws machinelearning create-ml-model --generate-cli-skeleton > 
        mdl_ames_housing_001.json

  1. 编写配置文件:
        {
            "MLModelId": "ch8_ames_housing_001",
            "MLModelName": "[MDL] Ames Housing 001",
            "MLModelType": "REGRESSION",
            "Parameters": {
                "sgd.shuffleType": "auto",
                "sgd.l2RegularizationAmount": "1.0E-06",
                "sgd.maxPasses": "100"
            },
            "TrainingDataSourceId": "ch8_ames_housing_001",
            "RecipeUri": "s3://aml.packt/data/ch8
              /recipe_ames_housing_001.json"
        }

注意算法的参数。在这里,我们使用了温和的 L2 正则化和 100 次迭代。

  1. 使用以下步骤启动模型创建:
        $ aws machinelearning create-ml-model --cli-input-json 
        file://mdl_ames_housing_001.json

  1. 返回模型 ID:
        {
            "MLModelId": "ch8_ames_housing_001"
        }

  1. 这个 get-ml-model 命令会给你操作的状态更新以及日志的 URL。
        $ aws machinelearning get-ml-model --ml-model-id 
        ch8_ames_housing_001

  1. watch 命令允许你每 n 秒重复一次 shell 命令。要每 10s 获取模型创建的状态,只需写下以下命令:
        $ watch -n 10 aws machinelearning get-ml-model --ml-model-id 
        ch8_ames_housing_001

get-ml-model 的输出将每 10 秒刷新一次,直到你将其终止。

通过 AWS CLI 命令无法创建默认的食谱。你总是可以定义一个空白食谱,该食谱不会对数据进行任何转换。然而,默认食谱已被证明对模型性能有积极影响。为了获得此默认食谱,我们通过 Web 界面创建它,将其复制到一个我们上传到 S3 的文件中。生成的文件 recipe_ames_housing_001.json 可在我们的 GitHub 仓库中找到。由于数据集有 79 个变量,其内容相当长,这里为了简洁起见没有展示。

使用 create-evaluation 评估我们的模型

我们现在的模型已经训练好了,我们想在评估子集上评估它。为此,我们将使用 create-evaluation CLI 命令:

  1. 生成骨架:
        $ aws machinelearning create-evaluation --generate-cli-skeleton >  
        eval_ames_housing_001.json

  1. 配置参数文件:
        {
            "EvaluationId": "ch8_ames_housing_001",
            "EvaluationName": "[EVL] Ames Housing 001",
            "MLModelId": "ch8_ames_housing_001",
            "EvaluationDataSourceId": "ch8_ames_housing_002"
        }

  1. 启动评估创建:
        $ aws machinelearning create-evaluation --cli-input-json 
        file://eval_ames_housing_001.json

  1. 获取评估信息:
        $ aws machinelearning get-evaluation --evaluation-id 
        ch8_ames_housing_001

  1. 从那个输出中,我们以 RMSE 的形式获得了模型的性能:
        "PerformanceMetrics": {
            "Properties": {
                 "RegressionRMSE": "29853.250469108018"
            }
        }

这个值可能看起来很大,但它相对于房屋的 salePrice 变量的范围是相对的,该变量的均值为 181300.0,标准差为 79886.7。因此,RMSE 为 29853.2 是一个不错的分数。

你不必等待数据源创建完成就可以启动模型训练。Amazon ML 将简单地等待父操作完成后再启动依赖的操作。这使得操作链式执行成为可能。

下一步将是进行批量预测或创建实时端点。这些将遵循与模型创建和评估完全相同的步骤,这里没有展示。

到目前为止,我们有一个训练和评估好的模型。我们选择了一组特定的参数,并通过默认的食谱对数据进行了一定的预处理。我们现在想知道是否可以通过尝试新的算法参数和进行一些创造性的特征工程来改进该模型和特征集。然后我们将训练新的模型并在验证子集上评估它们。正如我们之前看到的,这种方法的问题在于我们的评估分数可能高度依赖于评估子集。通过打乱数据来生成新的训练和验证集可能会导致不同的模型性能,并使我们选择错误的模型。尽管我们已经打乱了数据以避免顺序模式,但我们无法确保我们的分割是真正中立的,并且两个子集显示出相似的数据分布。其中一个子集可能呈现异常,如异常值或缺失数据,而另一个子集没有。为了解决这个问题,我们转向交叉验证。

什么是交叉验证?

为了降低对每个分割中数据分布的依赖,想法是并行运行许多试验,每个试验都有不同的数据分割,并平均结果。这被称为交叉验证。

简单来说,就是将模型性能在 K 次试验中平均,其中每次试验都是基于原始数据集的不同分割。有许多分割数据集的策略。最常见的一种称为 k 折交叉验证,它包括将数据集分割成 K 个块,并在每个试验中使用 K-1 个块来聚合训练模型,剩余的块来评估它。另一种策略,称为 留一法LOO),是将这个想法推向极端,其中 K 是样本数量。你将在所有样本中除了一个之外训练你的模型,并在剩余的样本上估计误差。LOO 显然更耗费资源。

我们将实施的策略称为 蒙特卡洛交叉验证,其中在每次试验中,初始数据集被随机分割成训练集和验证集。这种方法相对于 k 折交叉验证的优势在于,训练/验证分割的比例不依赖于迭代次数(K)。它的缺点是,一些样本可能永远不会被选入验证子集,而另一些样本可能被选中多次。验证子集可能重叠。

让我们来看一个 k =5 次试验的例子。我们将重复这些步骤五次来评估一个模型(例如,L2 轻度正则化):

  1. 打乱 Ames 住房数据集。

  2. 将数据集分为训练集和验证集。

  3. 在训练集上训练模型。

  4. 在验证集上评估模型。

到目前为止,我们有五个衡量模型性能的指标;我们将它们平均以获得整体模型性能的衡量标准。我们重复上述五个步骤来评估另一个模型(例如,L1 中等正则化)。一旦我们测试了所有模型,我们将选择在试验中给出最佳平均性能的模型。

这就是为什么脚本变得必要。为了测试一个模型设置,进行带有 K trials(K 折或蒙特卡洛)的交叉验证需要 2*K 数据源,K 个模型和 K 个评估。如果仅通过网络界面完成,这肯定会非常耗时。这就是为什么整个过程的脚本化变得极其有用且效率更高。

实际上创建不同子集文件进行交叉验证的方法有很多。最简单的方法可能是使用带有随机排序的电子表格编辑器,并进行一些剪切和粘贴操作。R 和 Python 库,如流行的 scikit-learn 库或 Caret 包,提供了丰富的开箱即用的方法。然而,由于本章是关于 AWS 命令行界面,我们将使用 shell 命令来生成文件。我们还将编写 shell 脚本来生成 AWS CLI 命令的序列,以避免手动编辑不同数据文件和模型的相同命令。

实施蒙特卡洛交叉验证

我们现在将使用 shell 命令和 AWS CLI 实施一个包含五个试验的蒙特卡洛交叉验证策略。我们将使用这种评估方法来比较两个模型,一个在 Ames Housing 数据集上具有 L2 轻度正则化,另一个具有 L1 重度正则化。交叉验证将使我们能够以一定程度的信心得出哪个模型表现更好的结论。

生成打乱的数据集

我们将使用数据源创建的 DataRearrangement 字段来将数据分成训练集和验证集。因此,最初我们只需要创建五个打乱顺序的数据文件。

以下 shell 脚本将创建五个 Ames Housing 数据集的打乱版本,并将文件上传到 S3。您可以将该代码保存为具有 .sh 扩展名的文件(datasets_creation.sh),或者通过 sh ./datasets_creation.sh 运行它:

#!/bin/bash
for k in 1 2 3 4 5 
do
    filename="data/ames_housing_shuffled_$k.csv"
    gshuf data/ames_housing_nohead.csv -o data/ames_housing_nohead.csv
    cat data/ames_housing_header.csv data/ames_housing_nohead.csv > tmp.csv;
    mv tmp.csv $filename
    aws s3 cp ./$filename s3://aml.packt/data/ch8/
done

注意,在本章中,代码是围绕以下文件夹结构组织的。所有命令行都是从根目录运行的,例如,运行一个 Python 脚本:python py/the_script.py,列出数据文件 ls data/,以及运行 shell 脚本:sh ./shell/the_script.sh

.

├── data

├── images

├── py

└── shell 所有 shell 脚本和命令都是基于 bash shell 的,可能需要适应其他 shell,如 zsh。

我们的数据集已经创建并上传到 S3。现在的总体策略是为 Amazon ML CLI 命令所需的每个参数 JSON 文件创建模板:创建数据源、模型和评估。我们将为以下内容创建模板文件:

  • 训练数据源

  • 评估数据源

  • L2 模型

  • L1 模型

  • L2 评估

  • L1 评估

在所有这些模板文件中,我们将使用 {k} 来索引文件名,并使用 sed 命令行工具将 {k} 替换为适当的索引(1 到 5)。一旦我们有了模板文件,我们就可以使用一个简单的 shell 脚本来生成数据源、模型和评估的实际 JSON 参数文件。最终我们将得到以下内容:

  • 10 个数据源配置文件(五个用于训练,五个用于评估)

  • 10 个模型配置文件(五个用于 L2,五个用于 L1)

  • 10 个评估配置文件(每个模型一个)

最后,我们将获得 L2 模型的五个 RMSE 结果和 L1 模型的五个 RMSE 结果,它们的平均值将告诉我们哪个模型是最好的,应该选择哪种正则化类型来在 Ames 住房数据集上预测销售价格。

让我们从编写配置文件开始。

生成数据源模板

训练文件模板如下:

{
   "DataSourceId": "CH8_AH_training_00{k}",
   "DataSourceName": "[DS AH] training 00{k}",
   "DataSpec": {
       "DataLocationS3": "s3://aml.packt/data/ch8/shuffled_{k}.csv",
       "DataSchemaLocationS3":"s3://aml.packt/data/ch8
        /ames_housing.csv.schema",
       "DataRearrangement": "{"splitting":
       {"percentBegin":0,"percentEnd":70}}"
   },
   "ComputeStatistics": true
}

验证数据源模板如下:

{
   "DataSourceId": "CH8_AH_evaluate_00{k}",
   "DataSourceName": "[DS AH] evaluate 00{k}",
   "DataSpec": {
       "DataLocationS3": "s3://aml.packt/data/ch8/shuffled_{k}.csv",
       "DataSchemaLocationS3":"s3://aml.packt/data/ch8
       /ames_housing.csv.schema",
       "DataRearrangement": "{"splitting":
       {"percentBegin":70,"percentEnd":100}}"
   },
   "ComputeStatistics": true
}

训练和验证模板之间的唯一区别是DataRearrangement字段中的名称/ID 和分割比率。我们将这些文件分别保存到dsrc_training_template.jsondsrc_validate_template.json

生成模型模板

对于具有 L2 正则化的模型,模型模板如下:

{
   "MLModelId": "CH8_AH_L2_00{k}",
   "MLModelName": "[MDL AH L2] 00{k}",
   "MLModelType": "REGRESSION",
   "Parameters": {
       "sgd.shuffleType": "auto",
       "sgd.l1RegularizationAmount": "0.0",
       "sgd.l2RegularizationAmount": "1.0E-06",
       "sgd.maxPasses": "100"
   },
   "TrainingDataSourceId": "CH8_AH_training_00{k}",
   "RecipeUri": "s3://aml.packt/data/ch8/recipe_ames_housing_001.json"
}

对于具有 L1 正则化的模型,模型模板如下:

{
   "MLModelId": "CH8_AH_L1_00{k}",
   "MLModelName": "[MDL AH L1] 00{k}",
   "MLModelType": "REGRESSION",
   "Parameters": {
       "sgd.shuffleType": "auto",
       "sgd.l1RegularizationAmount": "1.0E-04",
       "sgd.l2RegularizationAmount": "0.0",
       "sgd.maxPasses": "100"
   },
   "TrainingDataSourceId": "CH8_AH_training_00{k}",
   "RecipeUri": "s3://aml.packt/data/ch8/recipe_ames_housing_001.json"
}

注意,相同的配方用于两个模型。如果我们想比较数据预处理策略的性能,我们可以修改两个模型中使用的配方。模板文件非常相似。唯一的不同在于模型名称和 ID 以及l1RegularizationAmountl2RegularizationAmount的值。我们将这些文件分别保存到mdl_l2_template.jsonmdl_l1_template.json****。**

生成评估模板

对于具有 L2 正则化的模型,评估模板如下:

{
   "EvaluationId": "CH8_AH_L2_00{k}",
   "EvaluationName": "[EVL AH L2] 00{k}",
   "MLModelId": "CH8_AH_L2_00{k}",
   "EvaluationDataSourceId": "CH8_AH_evaluate_00{k}"
}

对于具有 L1 正则化的模型,评估模板如下:

{
   "EvaluationId": "CH8_AH_L1_00{k}",
   "EvaluationName": "[EVL AH L1] 00{k}",
   "MLModelId": "CH8_AH_L1_00{k}",
   "EvaluationDataSourceId": "CH8_AH_evaluate_00{k}"
}

将这些文件保存到eval_l2_template.jsoneval_l1_template.json分别。

我们现在将使用这些模板文件来生成数据源、模型和评估的不同配置文件。为了保持独立,所有生成的文件都在子文件夹cfg/中。

下面的 shell 脚本生成了我们将要提供给 AWS CLI 机器学习命令的实际配置文件。它使用sed命令查找并替换{k}实例为 1 到 5 的数字。输出被写入配置文件。由于将生成许多配置文件,这些文件被写入/data下的/cfg子文件夹中。现在的文件夹结构如下:

.
├── data
│   └── cfg
│   └── templates
├── images
├── py
└── shell

#!/bin/bash

for k in 1 2 3 4 5 
do
    # training datasource
    sed 's/{k}/1/g' data/templates/dsrc_training_template.json > data/cfg
    /dsrc_training_00$k.json

    # evaluation datasource
    sed 's/{k}/1/g' data/templates/dsrc_validate_template.json > data/cfg
    /dsrc_validate_00$k.json

    # L2 model
    sed 's/{k}/1/g' data/templates/mdl_l2_template.json > data/cfg
    /mdl_l2_00$k.json

    # L2 evaluation
    sed 's/{k}/1/g' data/templates/eval_l2_template.json > data/cfg
    /eval_l2_00$k.json

    # L1 model
    sed 's/{k}/1/g' data/templates/mdl_l1_template.json > data/cfg
    /mdl_l1_00$k.json

    # L1 evaluation
    sed 's/{k}/1/g' data/templates/eval_l1_template.json > data/cfg
    /eval_l1_00$k.json

done

最后一个剩余步骤是执行 AWS 命令,这些命令将在 Amazon ML 中创建对象。我们同样使用 shell 循环来执行 AWS CLI 命令。

创建用于训练和评估的数据源:

#!/bin/bash
for k in 1 2 3 4 5 
do
    aws machinelearning create-data-source-from-s3 --cli-input-json 
    file://data/cfg/dsrc_kfold_training_00$k.json
    aws machinelearning create-data-source-from-s3 --cli-input-json 
    file://data/cfg/dsrc_kfold_validate_00$k.json
done

使用 L2 和 L1 正则化训练模型:

#!/bin/bash
for k in 1 2 3 4 5 
    aws machinelearning create-ml-model --cli-input-json file://data
    /cfg/mdl_l2_00$k.json
    aws machinelearning create-ml-model --cli-input-json file://data
    /cfg/mdl_l1_00$k.json
done

评估训练模型:

#!/bin/bash
for k in 1 2 3 4 5 
    aws machinelearning create-evaluation --cli-input-json file://cfg
    /eval_l2_00$k.json
    aws machinelearning create-evaluation --cli-input-json file://cfg
    /eval_l1_00$k.json
done

您可以使用get-data-sourceget-ml-modelget-evaluationCLI 命令或在美国机器学习仪表板上检查不同作业的状态。一旦所有评估完成,您首先创建几个文件来接收 RMSE 分数,然后运行以下最终 shell 循环:

#!/bin/bash
for k in 1 2 3 4 5 
    aws machinelearning get-evaluation --evaluation-id CH8_AH_L2_00$k | 
    grep RegressionRMSE >> l2_model_rmse.log
    aws machinelearning get-evaluation --evaluation-id CH8_AH_L1_00$k |
    grep RegressionRMSE >> l1_model_rmse.log
done

给定评估的 ID,get-evaluation 命令返回一个 JSON 格式的字符串,该字符串被传递给 grep 命令并添加到 l1/l2_model_rmse.log 文件中。

结果

我们得到了两个模型以下的结果:

l1 | 26570.0 | 28880.4 | 27287.8 | 29815.7 | 27822.0]

L2 | 36670.9 | 25804.3 | 28127.2 | 30539.0 | 24740.4

平均而言,L1 给出 RMSE 为 28075.2(标准差:1151),而 L2 给出 RMSE 为 29176.4(标准差:4246.7)。L1 模型不仅性能更好,而且在处理数据变化时也更为稳健,因为其标准差较低。

结论

仅通过 shell 实现交叉验证可能过于耗时。需要创建和协调许多文件。使用 scikit-learn(Python 的库)或 Caret(R 的库)等库实现交叉验证有更简单的方法,其中整个模型训练和评估循环只需几行代码即可覆盖多个训练和验证集。然而,我们展示了使用 Amazon ML 实现交叉验证是可能的。交叉验证是数据科学工作流程的关键组成部分。如果不能使用 Amazon ML 进行交叉验证,这将是在服务中的一个重大缺陷。最终,AWS CLI 对于机器学习是一个非常强大且有用的工具,可以执行一系列试验并比较不同模型、数据集、食谱和特征的结果。

Boto3,Python SDK

另一个在网页界面之外与 Amazon ML 服务交互的工具是 SDK。简单来说,SDK 是 API 的包装器,它使得与服务交互变得更加简单和高效,因为许多交互的细节都得到了处理。AWS 为最广泛使用的语言提供了 SDK,如 PHP、Java、Ruby、.Net,当然还有 Python。在本章中,我们将专注于通过 Python SDK 与 Amazon ML 服务交互。Python SDK 需要 Boto3 模块。

Boto3 模块的安装是通过 pip 完成的。如果您需要更多信息或故障排除,请参阅可用的快速入门指南boto3.readthedocs.io/en/latest/guide/quickstart.html

pip install boto3

Boto3 可用于大多数 AWS 服务。完整的列表可以在boto3.readthedocs.io/en/latest/reference/services/index.html找到。我们将专注于 Boto3 的 S3 和 Amazon ML。

设置 SDK 访问权限可以通过我们在本章开头提到的 aws configure 命令来完成,或者直接将您的访问密钥添加到 ~/.aws/credentials 文件中。

总体来说,Boto3 逻辑与 AWS CLI 逻辑非常相似,遵循类似的步骤:声明要使用的服务并运行带有适当参数集的命令。让我们从一个简单的例子开始,围绕 S3 使用以下 Python 脚本,该脚本将列出您账户中的所有存储桶:

import boto3
# Initialize the S3 client
s3 = boto3.resource('s3')
# List all the buckets in out account
for bucket in s3.buckets.all():
    print(bucket.name)

将本地文件上传到存储桶可以通过以下方式实现:

# load the file    
data = open('data/ames_housing_nohead.csv', 'rb')
s3.Object('aml.packt', 'data/ames_housing_nohead.csv').put(Body=data)

put 命令返回一个 JSON 字符串,其中包含一个 HTTPStatusCode 字段,其值为 200,表示上传成功。

使用亚马逊机器学习的 Python SDK

可用方法的列表可以在 boto3.readthedocs.io/en/latest/reference/services/machinelearning.html 找到,并且紧密遵循围绕主要对象(数据源、模型、评估、批量预测和实时端点)组织的 AWS CLI 机器学习服务的可用命令列表。对于每个对象,方法有:创建、更新、描述、获取和删除。

我们现在将实现标准的亚马逊机器学习工作流程。但首先,让我们为我们将要创建的对象定义一个命名方法。工作流程的一个重要部分是围绕对象名称和 ID 的命名约定。当使用 CLI 时,我们即时创建了名称和 ID。这次我们将使用以下函数来命名我们的对象:

def name_id_generation(prefix, mode, trial):
    Id = '_'.join([prefix, mode, "%02d"%int(trial)])
    name = "[%s] %s %02d"% (prefix, mode, int(trial) )
    return {'Name':name, 'Id':Id}

此函数接受两个字符串和一个整数作为参数,一个用于对象类型(数据源、模型等)的前缀,一个用于指定训练或验证数据源的模式,以及一个用于轻松增加实验的试验值。该函数返回一个字典。

让我们现在定义一些将在脚本中稍后使用的变量:

# The iteration number of our experiements
trial = 5
# The S3 location of schemas and files
data_s3   = 's3://aml.packt/data/ch8/ames_housing_shuffled.csv'
schema_s3 = 's3://aml.packt/data/ch8/ames_housing.csv.schema'
recipe_s3 = 's3://aml.packt/data/ch8/recipe_ames_housing_001.json'

# And the parameters for the SGD algrithm
sgd_params = {
  "sgd.shuffleType": "auto",
  "sgd.l1RegularizationAmount": "1.0E-04",
  "sgd.maxPasses": "100"
} 

我们需要导入以下库:

import boto3
import time
import json

声明我们想要与机器学习服务交互:

client = boto3.client('machinelearning')

我们现在已准备好使用以下方式创建我们的训练和验证数据源:

# Create datasource for training
resource = name_id_generation('DS', 'training', trial)
print("Creating datasources for training (%s)"% resource['Name'] )
response = client.create_data_source_from_s3(
  DataSourceId = resource['Id'] ,
  DataSourceName = resource['Name'],
  DataSpec = {
    'DataLocationS3' : data_s3,
    'DataSchemaLocationS3' : schema_s3,
   'DataRearrangement':'{"splitting":{"percentBegin":0,"percentEnd":70}}'
  },
   ComputeStatistics = True
)

# Create datasource for validation
resource = name_id_generation('DS', 'validation', trial)
print("Creating datasources for validation (%s)"% resource['Name'] )
response = client.create_data_source_from_s3(
  DataSourceId = resource['Id'] ,
  DataSourceName = resource['Name'],
  DataSpec = {
    'DataLocationS3': data_s3,
    'DataSchemaLocationS3': schema_s3,
    'DataRearrangement':'{"splitting":{"percentBegin":0,"percentEnd":70}}'
  },
  ComputeStatistics = True
)

在这两种情况下,我们调用之前定义的命名函数来生成数据源的名称和 ID,并在调用 create_data_source_from_s3 Boto3 方法时使用该字典。

我们使用以下方式启动模型的训练:

# Train model with existing recipe
resource = name_id_generation('MDL', '', trial) 
print("Training model (%s) with params:n%s"% 
               (resource['Name'], json.dumps(sgd_params, indent=4)) )
response = client.create_ml_model(
  MLModelId = resource['Id'],
  MLModelName = resource['Name'],
  MLModelType = 'REGRESSION',
  Parameters = sgd_params,
  TrainingDataSourceId= name_id_generation('DS', 'training', trial)['Id'],
  RecipeUri = recipe_s3
)

然后创建评估:

resource = name_id_generation('EVAL', '', trial) 
print("Launching evaluation (%s) "% resource['Name'] )
response = client.create_evaluation(
  EvaluationId = resource['Id'],
  EvaluationName = resource['Name'],
  MLModelId = name_id_generation('MDL', '', trial)['Id'],
  EvaluationDataSourceId = name_id_generation('DS', 'validation', trial)
  ['Id']
)

现在,您可以访问亚马逊机器学习仪表板并验证您有两个数据源、一个模型和一个评估处于“进行中”或“待处理”状态:

图片

等待操作完成

所有这些对象创建操作默认情况下都是由亚马逊机器学习链式执行的。这意味着亚马逊机器学习将在启动模型训练之前等待数据源准备就绪,并在尝试运行评估之前等待模型训练完成。然而,在这个阶段,我们仍然需要等待评估完成才能访问其结果。同样,我们需要等待不同对象被下一个操作使用后才能删除它们。

这就是等待方法变得有用的地方。等待器是简单地等待 AWS 操作完成、状态为 完成 的方法。所有 AWS 操作和服务都有等待器。亚马逊机器学习为模型、数据源、评估和批量预测提供了四个等待器:

  • MachineLearning.Waiter.BatchPredictionAvailable

  • MachineLearning.Waiter.DataSourceAvailable

  • MachineLearning.Waiter.EvaluationAvailable

  • MachineLearning.Waiter.MLModelAvailable

机器学习服务员遵循以下语法——首先,声明服务员需要监控的对象,例如一个评估:

waiter = client.get_waiter('evaluation_available')

然后在您刚刚声明的服务员上调用wait方法:

waiter.wait(FilterVariable='Name', EQ='the name of the evaluation')

一旦调用等待方法,Python 脚本就会挂起,直到操作达到Completed状态。等待函数需要以下内容:

  • 一个过滤值:FilterVariable = CreatedAt, LastUpdatedAt, Status, Name, IAMUser, MLModelId, DataSourceId, DataURI

  • 操作符:EQ, GT, LT, GE, LE, NE

  • 其他依赖于对象性质的参数

使用这种参数结构,您可以让脚本等待特定对象的完成,或者根据数据源、模型或甚至用户名等待所有对象。如果我们要对基于相同验证数据源的多个模型进行评估,我们只需为每个模型调用一个服务员即可:

waiter.wait(FilterVariable='DataSourceId', EQ='the DatasourceId')

总结基于 Python 的工作流程

现在我们知道了如何等待所有评估完成,我们仍然需要获取评估结果并删除我们创建的模型和数据源。正如get-evaluation AWS CLI 命令的情况一样,Boto3 的get_evaluation方法返回一个包含模型性能度量、回归情况下的 RMSE 的 JSON 字符串。以下脚本总结了我们的试验:

t0 = time.time()
# declare the waiter and call the wait method on the evaluation
waiter = client.get_waiter('evaluation_available')
print("Waiting on evaluation to finish ")
waiter.wait(FilterVariable='Name', EQ=name_id_generation('EVAL', '', trial)['Name'])
t = time.time() - t0
print("Evaluation has finished after %sm %ss"% (int(t/60), t%60) )
# get the evaluation results
response = client.get_evaluation(
  EvaluationId=name_id_generation('EVAL', '', trial)['Id']
)
RMSE =float(response['PerformanceMetrics']['Properties']['RegressionRMSE'])
print("[trial %0.2f] RMSE %0.2f"% (trial, RMSE) )
# and delete the resources
print("Deleting datasources and model")
response = client.delete_data_source(
  DataSourceId=name_id_generation('DS', 'training', trial)['Id']
)
response = client.delete_data_source(
  DataSourceId=name_id_generation('DS', 'validation', trial)['Id']
)
response = client.delete_ml_model(
  MLModelId=name_id_generation('MDL', '', trial)['Id']
)

将所有代码块放在一起返回以下输出:

Creating datasources for training ([DS] training 04)
Creating datasources for validation ([DS] validation 04)
Training model ([MDL] 04) with params:
{
  "sgd.shuffleType": "auto",
  "sgd.l1RegularizationAmount": "1.0E-04",
  "sgd.maxPasses": "100"
}
Launching evaluation ([EVAL] 04)
Waiting on evaluation to finish
Evaluation has finished after 11m 43.78s
[trial 4] RMSE 22437.33
Deleting datasources and model

使用 Boto3 实现递归特征选择

在许多实际案例中,原始数据集可能包含大量变量。随着维度的增加,对更大样本集的需求也增加。这被称为维度诅咒,是一个经典的预测分析问题。简单来说,如果没有足够的多样性来推断某些变量的代表性分布,算法将无法从这些变量中提取相关信息。这些低信号变量会拖累算法的性能,而不会通过向模型添加无用的复杂性来增加任何数据燃料。一种策略是减少模型训练的变量数量。然而,这暗示了需要确定哪些特征可以被删除而不会导致信息损失。

有许多技术可以减少特征数量:

  • 包装器技术:这些技术使用规则和标准来选择最佳和最具影响力的特征。

  • 过滤技术:这些技术使用统计测试来衡量每个特征的重要性。与目标的相关性测量可能是一种简单的方法来删除非显著变量。

  • 嵌入式方法:对于某些模型,例如随机森林,可以在特征子集上迭代训练模型,因此可以评估每次迭代中省略的特征的影响,从而推断每个特征的重要性。

适用于 Amazon 机器学习环境的方法是递归评估每个特征的重要性,通过测量在丢弃某个特征时性能显著下降来过滤掉最不重要的特征。这是递归特征消除的暴力版本。

它遵循以下步骤:

  1. 使用所有N个特征构建一个初始模型。

  2. 然后通过以下方式识别并移除最不重要的特征:

    • 构建 N 个子集,每个子集中删除不同的特征

    • 为每个子集构建模型并评估其性能

    • 识别对模型性能影响最小的特征

  3. 现在您有N-1个特征。重复步骤 1 到 3 以识别并移除下一个最不重要的特征。

  4. 当您注意到与初始 N 特征模型相比模型性能显著下降时停止。

该算法的逆版本从N个模型开始,每个模型仅使用一个特征构建,并在每次迭代中添加一个新特征。选择新特征为能带来最佳性能提升的新特征。当添加新特征不再导致模型性能显著提升时停止。

在本章的其余部分,我们将展示如何在 Python 中实现这种特征选择策略。

管理模式和配方

直接删除或添加数据集的特征会直接影响模式和配方。模式用于创建数据源时使用,而配方需要用于训练模型,因为它指定了在模型训练之前将执行哪些数据转换。

通过简单地将变量的名称添加到excludedAttributeNames字段中,可以修改模式以从数据集中删除特征。我们可以从初始模式开始,每次从初始特征列表中删除一个特征时,就将其添加到excludedAttributeNames列表中。步骤如下:

  1. 将 JSON 格式的模式打开到模式字典中

  2. 将特征名称追加到模式['excludedAttributeNames']

  3. 将模式保存到正确缩进的 JSON 文件中

  4. 将文件上传到 S3

在创建数据源时,我们将指向我们刚刚更新的模式的 S3 位置。

亚姆住房数据集默认由 Amazon ML 生成的初始配方对某些数值特征应用不同的分位数分箱转换。配方的分组部分如下:

"groups": {
  "NUMERIC_VARS_QB_50":   "group('LotFrontage','KitchenAbvGr','BsmtFinSF1','GarageCars','1stFlrSF','ScreenPorch','LowQualFinSF','LotArea','OverallCond','2ndFlrSF','GarageArea','EnclosedPorch','HalfBath')",
 "NUMERIC_VARS_QB_100": "group('BsmtFinSF2','WoodDeckSF','BsmtHalfBath','MiscVal','GrLivArea','Fireplaces')",
 "NUMERIC_VARS_QB_500": "group('OverallQual')",
 "NUMERIC_VARS_QB_20": "group('TotalBsmtSF')",
 "NUMERIC_VARS_QB_200": "group('MSSubClass','OpenPorchSF','YearRemod/Add','BsmtFullBath','MasVnrArea')",
 "NUMERIC_VARS_QB_10": "group('PoolArea','BedroomAbvGr','TotRmsAbvGrd','YearBuilt','MoSold','YrSold','GarageYrBlt','FullBath','BsmtUnfSF','3SsnPorch')"
 }

在该结构中添加或删除变量名称需要比在列表中添加元素更复杂的脚本。由于这样的脚本不会给本书带来很多教育价值,我们决定使用一个默认的简单配方,不对数据集进行任何转换。只要我们有包含所有特征的基线 RMSE,递归特征消除策略仍然有效。唯一的区别是,由于没有对数值数据应用任何分位数分箱,整体 RMSE 分数可能会更高。我们使用的配方如下:

{
 "groups" : {},
 "assignments" : { },
 "outputs" : [ "ALL_INPUTS" ]
}

这可以在我们的示例中找到,位于 S3 位置s3://aml.packt/data/ch8/recipe_ames_housing_default.json。使用该配方评估我们的基线模型给出基线 RMSE 为61507.35。我们将使用该基线 RMSE 来查看移除特征是否提高了(降低)或降低了模型性能。

以下脚本分为三个部分:

  • 初始化和函数

  • 启动 Amazon ML 工作流程

  • 获取评估结果和删除资源

脚本可以在我们的 GitHub 仓库中找到,其完整内容如下。我们使用相同的策略来创建一个生成名称和 ID 的函数。我们以下面的脚本初始化变量并声明函数:

import pandas as pd
import boto3
import json

# Local schema with all the features
original_schema_filename = 'data/ames_housing.csv.schema'
# Initialize boto3 objects
s3 = boto3.resource('s3')
client = boto3.client('machinelearning')

# load dataset and feature_ names
df = pd.read_csv('data/ames_housing.csv')
original_features = df.columns.difference(['SalePrice', 'Order'])

# load original schema with all the features
schema = json.load( open(original_schema_filename) )

# SGD parameters: L1 heavy regularization
sgd_parameters = {
  "sgd.shuffleType": "auto",
  "sgd.l1RegularizationAmount": "1.0E-04",
  "sgd.maxPasses": "100"
}

# memorize all object Ids for future deletion
baseline_rmse = 61507.35
datasource_ids = []
model_ids = []
evaluation_ids = []
features_rmse = {}

def generate_trial(n):
  n = "X" + str(n).zfill(3)
  return {
   'schema_filename': "rfs_ames_housing_%s.schema"% n, 
   'recipe_s3': 's3://aml.packt/RFS/recipe_ames_housing_default.json',
   'data_s3': 's3://aml.packt/RFS/ames_housing_shuffled.csv',
   'datasource_training_id': "rfs_training_%s"% n,
   'datasource_training_name': "[DS RFS] training %s"% n,
   'datasource_validation_id': "rfs_validation_%s"% n,
   'datasource_validation_name': "[DS RFS] validation %s"% n,
   'model_id': "rfs_%s"% n,
   'model_name': "[MDL RFS] %s"% n,
   'evaluation_id': "rfs_%s"% n,
   'evaluation_name': "[EVAL RFS] %s"% n,
  }

我们现在启动数据源、模型和评估的创建。脚本只查看前 10 个特征,而不是整个 79 个特征的集合,以节省资源。

你会注意到我们在 Amazon ML 对象的编号中添加了前缀"X"。我们发现有时,如果 ID 和名称已被用于现在已删除的先前对象,Amazon ML 可能无法创建对象。这个问题可能在一段时间后消失。无论如何,确保所有新的数据源、模型、评估和批量预测都有从未使用过的名称和 ID,可以消除任何命名问题。

第二部分启动数据源、模型和评估的创建:

for k in range(10):
 print("="* 10 + " feature: %s"% original_features[k])
 trial = generate_trial(k)

 # remove feature[k] from schema and upload to S3
 schema['excludedAttributeNames'] = [original_features[k]]
 with open("data/%s"%trial['schema_filename'], 'w') as fp:
 json.dump(schema, fp, indent=4)
 s3.Object('aml.packt', "RFS/%s"% trial['schema_filename']).put(Body=open("data/%s"%trial['schema_filename'], 'rb'))

 # create datasource
 print("Datasource %s"% trial['datasource_training_name'])
 datasource_ids.append( trial['datasource_training_id'] )
 response = client.create_data_source_from_s3(
   DataSourceId = trial['datasource_training_id'] ,
   DataSourceName= trial['datasource_training_name'] ,
   DataSpec={
     'DataLocationS3': trial['data_s3'],
     'DataRearrangement': '{"splitting":
      {"percentBegin":0,"percentEnd":70}}',
     'DataSchemaLocationS3': "s3://aml.packt/RFS/%s"% 
     trial['schema_filename']
   },
   ComputeStatistics=True
 )

 # Create datasource for validation
 print("Datasource %s"% trial['datasource_validation_name'])
 datasource_ids.append( trial['datasource_validation_id'] )
 response = client.create_data_source_from_s3(
 DataSourceId = trial['datasource_validation_id'] ,
 DataSourceName= trial['datasource_validation_name'] ,
 DataSpec={
 'DataLocationS3': trial['data_s3'],
 'DataRearrangement': '{"splitting":{"percentBegin":70,"percentEnd":100}}',
 'DataSchemaLocationS3': "s3://aml.packt/RFS/%s"% trial['schema_filename']
 },
 ComputeStatistics=True
 )

 # Train model with existing recipe
 print("Model %s"% trial['model_name'])
 model_ids.append(trial['model_id'] )
 response = client.create_ml_model(
 MLModelId = trial['model_id'],
 MLModelName = trial['model_name'],
 MLModelType = 'REGRESSION',
 Parameters = sgd_parameters,
 TrainingDataSourceId = trial['datasource_training_id'] ,
 RecipeUri = trial['recipe_s3']
 )

 print("Evaluation %s"% trial['evaluation_name'])
 evaluation_ids.append(trial['evaluation_id'])
 response = client.create_evaluation(
 EvaluationId = trial['evaluation_id'],
 EvaluationName = trial['evaluation_name'],
 MLModelId = trial['model_id'],
 EvaluationDataSourceId= trial['datasource_validation_id'] 
 )

最后,第三部分等待评估完成,记录每个移除特征的 RMSE,并删除数据源和模型(我们保留了评估结果,以避免需要重新运行整个脚本来获取结果):

for k in range(10):
 trial = generate_trial(k)
 waiter = client.get_waiter('evaluation_available')
 print("Waiting on evaluation %s to finish "% trial['evaluation_name'])
 waiter.wait(FilterVariable='Name', EQ=trial['evaluation_name'])
 print("Evaluation has finished ")

 response = client.get_evaluation( EvaluationId=trial['evaluation_id'] )
 features_rmse[original_features[k]] = float(response['PerformanceMetrics']['Properties']['RegressionRMSE'])
 print("[%s] RMSE %0.2f"% (original_features[k], float(response['PerformanceMetrics']['Properties']['RegressionRMSE'])) )
 # Now delete the resources
 print("Deleting datasources and model")
 response = client.delete_data_source(
 DataSourceId = trial['datasource_training_id']
 )
 response = client.delete_data_source(
 DataSourceId = trial['datasource_validation_id']
 )
 response = client.delete_ml_model(
 MLModelId = trial['model_id']
 )

print("removing the feature increased the RMSE by ")
for k,v in features_rmse.items():
 print("%s t%0.2f %% "% (k, (baseline_rmse - v)/ baseline_rmse *100.0 ) )

最后,我们得到前 10 个特征的以下 RMSE 变化:

  • 1stFlrSF 0.07%

  • 2ndFlrSF -18.28%

  • BedroomAbvGr -0.02 %

  • BsmtExposure -0.56 %

  • BsmtFinSF2 -0.50 %

  • BsmtCond 0.00 %

  • BsmtFinSF1 -2.56 %

  • Alley 0.00 %

  • 3SsnPorch -4.60 %

  • BldgType -0.00 %

例如,移除2ndFlrSF特征将 RMSE 提高了近 20%。这个特征对于预测销售价格无疑非常重要。同样,特征3SsnPorchBsmtFinSF1对于模型也很重要,因为移除它们会增加 RMSE。另一方面,移除1stFlrSFAlleyBedroomAbvGrBldgType仅使 RMSE 变化小于 0.10%。我们可能可以移除这些特征,而对模型性能的影响不大。

摘要

在本章中,我们已经从 Amazon ML 的 Web 界面转向,学习了如何通过 AWS CLI 和 Python SDK 与该服务交互。这两种交互类型的命令和方法非常相似。函数和命令执行从创建到删除 Amazon ML 对象的标准操作集:数据源、模型、评估和批量预测。Amazon ML 将依赖对象创建的序列链式化,这使得您可以在创建下游对象(模型或评估)之前,无需等待上游对象(数据源或模型)完成,一次性创建所有对象。等待方法使得在检索结果和进行必要的对象删除之前,能够等待所有评估完成。

我们展示了如何通过脚本化 Amazon ML 来实现机器学习方法,如交叉验证和递归特征选择,这两种方法在预测分析中都非常有用。尽管我们最终需要创建许多数据源和对象来进行交叉验证和特征选择,但总体成本仍然保持在可控范围内。

在下一章中,我们将开始使用其他 AWS 服务来扩展 Amazon ML 的功能。我们将探讨 S3 之外的其它数据源,例如 Redshift 和 RDS,以及如何使用 Amazon Lambda 进行机器学习。

第八章:从 Redshift 创建数据源

在本章中,我们将利用 SQL 查询的力量来处理非线性数据集。在创建数据源之前,在 Redshift 或 RDS 中创建数据源为我们提供了上游基于 SQL 的特征工程潜力。我们在第四章,加载数据集和准备数据中实施了一种类似的方法,通过利用新的 AWS Athena 服务在创建数据源之前对数据进行初步转换。

这使我们能够通过创建新的特征,如“甲板”号,用其对数替换“票价”,或替换“年龄”变量的缺失值来扩展“泰坦尼克”数据集。SQL 转换很简单,但使我们能够非常灵活地扩展原始数据集。AWS Athena服务是基于 S3 的。它允许我们在 S3 上托管的数据集中运行 SQL 查询,并将结果存储在 S3 桶中。我们仍然从 S3 创建亚马逊 ML 数据源,但只是添加了一个额外的数据预处理层来整理数据集。

AWS 还提供了另外两种 SQL 服务,可以从这些服务中创建数据源:RDS 和 Redshift。RDS 和 Redshift 的数据源创建非常相似,我们将重点介绍通过 Python SDK Boto3在 Redshift 中创建数据源。对我们来说,关键点是基于 RDS/Redshift 的数据源是通过 SQL 查询直接创建的,这为我们提供了进一步的特征工程的机会。Redshift 还与 AWS Kinesis 无缝集成,我们将在第九章构建流数据分析管道中探讨该服务。

亚马逊机器学习建立在本质上线性的模型之上,利用良好的结果量级分箱数据转换作为处理数据集中非线性的一种方法。多项式回归是另一种重要的机器学习方法,用于处理非线性数据集。我们将利用我们新的 SQL 功能,通过亚马逊 ML 实现多项式回归。

在本章中,你将学习以下内容:

  • 在 RDS 和 Redshift 之间进行选择

  • 如何使用 PostgreSQL 创建 Redshift 数据库

  • 如何将您的 S3 数据加载到 Redshift

  • 如何从 Redshift 创建数据源

  • 什么是多项式回归

  • 如何使用亚马逊 ML 进行多项式回归

在 RDS 和 Redshift 之间进行选择

AWS 提供了不少于六种不同的云数据库和 SQL/NoSQL 服务:RDS、Aurora、DynamoDB、Redshift、Athena 和 AWS 数据库迁移服务!在所有这些服务中,只有两个与亚马逊机器学习兼容:RDS 和 Redshift。您可以在任一服务中存储数据,并从这些来源创建数据源。这两个服务的源数据创建方法具有类似的参数,但在底层 AWS 服务通信方面差异很大。

RDS 和 Redshift 是两种非常不同的服务。Redshift 是一个数据仓库,用于在大数据集上回答一些复杂且运行时间较长的查询,而 RDS 是为频繁、小规模和快速查询而设计的。Redshift 更适合进行大规模并行处理,以最小延迟执行数百万行数据的操作,而 RDS 提供了一个运行特定数据库的服务器实例。RDS 提供多种不同的数据库类型——MySQL、PostgreSQL、MariaDB、Oracle、SQL Server 和 Amazon Aurora,而 Redshift 是基于ParAccel技术的亚马逊自己的分析数据库,运行的是 PostgreSQL 的一个分支。您可以使用标准的 ODBC 和 JDBC 连接连接到 Redshift。

在您在 Redshift 中构建数据库时,亚马逊 Redshift 和 PostgreSQL 之间存在许多非常重要的差异,您必须注意。许多函数、数据类型和 PostgreSQL 功能在亚马逊 Redshift 中不受支持。更多信息可在docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html找到。

关于 RDS 和 Redshift 之间差异的更深入解释,可以在本线程中找到:www.quora.com/What-is-the-difference-between-redshift-and-RDS

在亚马逊机器学习(Amazon ML)的背景下,这两种服务之间的重要区别在于,亚马逊机器学习 Web 控制台仅允许从 S3 和 Redshift 创建数据源,但不能从 RDS 创建,如本截图所示:

图片

然而,Python SDK 和 AWS CLI 都允许从 RDS 和 Redshift 创建数据源。

SDK

CLI

现在我们比较 Python SDK 连接到任一服务所需的参数:

  • Redshift 参数:
        {
            "DatabaseInformation": {
              "DatabaseName": "string",
              "ClusterIdentifier": "string"
            },
            "SelectSqlQuery": "string",
            "DatabaseCredentials": {
              "Username": "string",
              "Password": "string"
            },
            "S3StagingLocation": "string",
            "DataRearrangement": "string",
            "DataSchema": "string",
            "DataSchemaUri": "string"
        }

  • RDS 参数:
        {
            "DatabaseInformation": {
              "DatabaseName": "string"
              "InstanceIdentifier": "string",
            },
            "SelectSqlQuery": "string",
            "DatabaseCredentials": {
              "Username": "string",
              "Password": "string"
            },
            "S3StagingLocation": "string",
            "DataRearrangement": "string",
            "DataSchema": "string",
            "DataSchemaUri": "string",
            "ResourceRole": "string",
            "ServiceRole": "string",
            "SubnetId": "string",
            "SecurityGroupIds": ["string", ...]
        }

这两组参数之间的区别在于我们允许访问数据存储的方式。两组都包括 DatabaseInformationDatabaseCredentialsSelectSqlQueryDataSchemaDataRearrangement。RDS 还需要手动设置两个具有正确策略的角色:ResourceRole: DataPipelineDefaultRoleServiceRole:DataPipelineDefaultResourceRole

RDS 更适合我们的数据量,我们应该在机器学习项目中使用 RDS 而不是 Redshift。然而,我们之前发现手动创建 RDS 的角色和策略需要深入了解 AWS 内部权限的工作方式,这对于本书来说过于复杂。尽管从 RDS 和 Redshift 创建数据源时的参数和概念非常相似,但在后台,它们有很大的不同。RDS 数据源创建涉及创建 AWS 数据管道,这是另一个 AWS 服务,允许您在不同 AWS 计算和存储服务之间处理和移动数据。必须设置数据管道为整个项目增加了非平凡的复杂性层。

另一方面,Redshift 不需要构建数据管道和设置权限、角色和策略来创建数据源。最终,这种额外的简单性使 Redshift 更适合本书,因为我们希望保持对机器学习方面的关注,而不是深入研究 AWS 访问角色和策略的复杂性,尽管 RDS 对于我们低数据量来说可能更合适。

Redshift:深入介绍 Redshift 超出了本书的范围。我们推荐阅读由 Stefan Bauer, Packt 编写的 《Amazon Redshift 入门》 书籍 (www.packtpub.com/big-data-and-business-intelligence/getting-started-amazon-redshift)),AWS 文档 (aws.amazon.com/redshift/getting-started/) 以及这篇关于集群配置的博客文章 (www.periscopedata.com/amazon-redshift-guide/cluster-configuration),以获得对集群配置的良好介绍。

让我们从 Redshift 开始,使用 AWS Redshift 控制台创建一个基于 PostgreSQL 的实例,并加载我们已经在 S3 存储桶中可用的 Titanic 数据集。

创建 Redshift 实例

登录您的 AWS 账户,并转到 Redshift 仪表板,链接为 console.aws.amazon.com/redshift/

在 Redshift 中创建数据库非常简单,并且由 AWS Redshift 向导很好地处理。首先,点击“启动集群”按钮。在第一个屏幕中,我们定义集群标识符*为 amlpackt,数据库名称为 amlpacktdb,以及主用户名,如以下截图所示:

在下一屏幕中,我们选择默认参数来配置节点,如下面的截图所示:

图片

在下一个配置屏幕中选择默认设置,但请确保集群是公开可访问的。您不需要公共 IP:

图片

选择机器学习/RDS VPC 安全组:

图片

在启动集群之前的最终验证屏幕将显示相关的成本,如下所示:

图片

点击最终的启动集群按钮后,集群准备就绪可能需要几分钟。我们将使用 Psql 连接到新创建的数据库。其他外部连接类型可通过 JDBC 和 ODBC 获得。集群信息页面显示了您需要连接到新创建数据库的端点 URL:

图片

通过命令行连接

Psql 是一个命令行程序,充当 PostgreSQL 数据库的主要前端。它提供了一系列的 shell 命令(例如,pg_dump 用于转储数据库内容,createdb 用于创建数据库,以及其他许多命令)。它还具有许多元命令来列出元素和显示信息(请参阅 Psql 快速参考 信息框)。

Psql 快速参考

q: 退出/退出

d __table__: 显示表定义,包括触发器

dt : 列出表

df: 列出函数

dv: 列出视图

x: 以更美观的格式显示查询结果,而不是不那么有用的 ASCII 表

connect __database__: 连接到数据库

l: 列出数据库

d 命令上添加一个 + 将显示更多结果:例如,比较 d titanicd+ titanic

有关 Psql 的更多信息,请参阅postgresguide.com/utilities/psql.html

您现在可以使用以下命令从终端连接到您的数据库使用 Psql,使用端点 URL (amlpackt.cenllwot8v9r.us-east-1.redshift.amazonaws.com) 作为主机:

$ psql --host=amlpackt.cenllwot8v9r.us-east-1.redshift.amazonaws.com --port=5439 --username=alexperrier --password --dbname=amlpacktdb

当然,amlpacktdb 数据库是空的:

alexperrier@amlpacktdb=> dt
 No relations found.

我们有多种方式可以将数据导入 Redshift 数据库。为了简化,我们将使用复制命令将 S3 上可用的 CSV 文件上传到 Redshift 表,如下所示:

alexperrier@amlpacktdb=> copy <table name> from '<s3 path to csv file>' CREDENTIALS 'aws_access_key_id=<aws access key id>;aws_secret_access_key=<aws secret access key>' CSV;

当使用复制命令时,Redshift 需要与 S3 服务进行认证。AWS 服务之间的认证可以通过两种方式实现:

  • 基于用户的:通过传递用户访问密钥进行认证。这是一种在 AWS 服务之间授予访问权限的更简单方式,但它并不总是可用。

  • 基于角色的:认证需要创建具有正确策略和权限的角色。与基于用户的认证相比,这是一种更受欢迎且更安全的认证方式。然而,它需要额外的角色和策略创建步骤,并且设置起来不太直接。

关于 AWS 服务的用户与基于角色的身份验证的更多信息,请参阅docs.aws.amazon.com/redshift/latest/mgmt/redshift-iam-authentication-access-control.html。在我们的复制示例中,我们计划使用我们主要 AWS 用户的 aws 访问密钥。但在我们可以将数据复制到表中之前,我们首先需要使用 SQL 查询创建它。对于我们一直在工作的Titanic CSV 文件,创建表的查询如下:

CREATE TABLE IF NOT EXISTS titanic (
  id integer primary key,
  pclass integer,
  survived boolean,
  name varchar(255),
  sex varchar(255),
  age real,
  sibsp integer,
  parch integer,
  ticket varchar(255),
  fare real,
  cabin varchar(255),
  embarked char(1),
  boat varchar(8),
   body varchar(8),
  home_dest varchar(255)
);

dt命令所示,该表现在存在于我们的 Redshift 数据库中:

alexperrier@amlpacktdb=> dt
 List of relations
 Schema | Name | Type | Owner
 --------+---------+-------+-------------
 public | titanic | table | alexperrier
 (1 row)

表结构如预期所示,如d+命令所示:

alexperrier@amlpacktdb=> d+ titanic
 Table "public.titanic"
 Column | Type | Modifiers | Storage | Stats target | Description
 -----------+------------------------+------------------------------------------------------+----------+--------------+-------------
 id | integer | not null default nextval('titanic_id_seq'::regclass) |
 plain | | pclass | integer | | plain | |
 survived | boolean | | plain | |
 name | character varying(255) | | extended | |
 sex | character varying(255) | | extended | |
 age | real | | plain | |
 sibsp | integer | | plain | |
 parch | integer | | plain | |
 ticket | character varying(255) | | extended | |
 fare | real | | plain | |
 cabin | character varying(255) | | extended | |
 embarked | character(1) | | extended | |
 boat | character varying(8) | | extended | |
 body | character varying(8) | | extended | |
 home_dest | character varying(255) | | extended | |
 Indexes:
 "titanic_pkey" PRIMARY KEY, btree (id)

我们现在可以将 CSV 文件上传到 S3,并从终端运行以下命令来填充我们的表:

# Load file on S3
$ aws s3 cp data/titanic.csv s3://aml.packt/data/ch9/
# connect to database via psql
$ psql --host=amlpackt.cenllwot8v9r.us-east-1.redshift.amazonaws.com --port=5439 --username=alexperrier --password --dbname=amlpacktdb
# upload data from your S3 location into the titanic table
$ copy titanic from 's3://aml.packt/data/ch9/titanic.csv' CREDENTIALS 'aws_access_key_id=<access key id>;aws_secret_access_key=<access secret key>' CSV;

注意,CSV 文件不应包含 CSV 标题。为了验证复制命令是否成功,我们可以通过运行以下查询来计算titanic表中的记录数:

alexperrier@amlpacktdb=> select count(*) from titanic;
 -[ RECORD 1 ]
 count | 1309

结果显示,我们现在在titanic表中有了 1309 条记录。

使用 Psql 执行 Redshift 查询

我们已经看到我们可以使用以下Psql命令连接到我们的数据库:

$ psql -h amlpackt.cenllwot8v9r.us-east-1.redshift.amazonaws.com -p 5439 -U alexperrier --password -d amlpacktdb

然后,我们需要输入我们的密码。为了缩短行并避免每次都输入密码,我们可以将连接字符串和密码都设置为 shell 环境变量。在你的终端中,执行以下命令以创建全局REDSHIFT_CONNECT shell 变量:

$ export REDSHIFT_CONNECT='-h amlpackt.cenllwot8v9r.us-east-1.redshift.amazonaws.com -p 5439 -U alexperrier -d amlpacktdb'

对于密码,执行以下命令:

$ export PGPASSWORD=your_password

从现在开始,你可以使用以下简单命令连接到数据库:

$ psql $REDSHIFT_CONNECT

注意,REDSHIFT_CONNECT是我们选择的变量名,而PGPASSWORD是一个由 Psql 识别的预定义 shell 变量名。

我们现在可以选择在 Redshift 数据库上运行查询的方式。我们可以执行以下步骤中的任何一个:

  • 使用Psql进入数据库 shell 并输入一些 SQL 查询:
 $ psql $REDSHIFT_CONNECT
 alexperrier@amlpacktdb=> select count(*) from titanic;

  • 将 SQL 查询写入文件(例如,my_file.sql),然后从终端运行以下命令:
 $ psql $REDSHIFT_CONNECT -f my_file.sql

  • 使用Psql命令直接运行查询:
 $ psql $REDSHIFT_CONNECT -c 'SELECT count(*) FROM my_table'

我们现在可以开始处理我们的数据集了。由于我们已经对泰坦尼克号数据集进行了广泛的研究,我们将使用另一个数据集来完成本章的剩余部分。让我们创建一个表现出强烈非线性模式的合成数据集。

创建我们自己的非线性数据集

创建非线性数据集的一个好方法是混合不同相位的正弦波。我们将在本章中使用以下 Python 脚本创建数据集,并将其导出为 CSV 文件:

import numpy as np
n_samples = 1000
de_linearize = lambda X: np.cos(1.5 * np.pi * X) + np.cos( 5 * np.pi * X )
X = np.sort(np.random.rand(n_samples)) * 2
y = de_linearize(X) + np.random.randn(n_samples) * 0.1

如常,X是预测变量,y是结果。你可以通过修改该脚本轻松生成其他非线性数据集。注意,我们使用了lambda函数,这是一种在需要时即时声明函数的 Python 方式。然后我们通过随机排序(np.random.rand(n_samples))对数据集进行洗牌。然后我们使用Pandas数据框将数据保存到 CSV 文件(nonlinear.csv)中:

import pandas as pd
df = pd.DataFrame( {'X':X, 'y': y} )
df = df.sample(frac=1) # shuffles the entire dataframe
df.to_csv('data/nonlinear.csv', index = False)

绘制数据给出以下图表:

图片

显然不是线性的。一条线根本无法近似,更不用说从预测变量X预测结果y了。现在我们有一个高度非线性的数据集可用,我们需要将其上传到 Redshift。

将非线性数据上传到 Redshift

我们首先需要创建一个将托管数据的表。我们将把这个表称为nonlinear。它只有三个列:一个索引,预测变量X和结果y

CREATE TABLE IF NOT EXISTS nonlinear (
 id integer primary key,
 x1 real,
 y real
);

一旦创建了表,我们就可以将 CSV 文件上传到 S3,连接到数据库,并使用以下命令将数据导入非线性表:

copy nonlinear from 's3://aml.packt/data/ch9/nonlinear.csv' CREDENTIALS 'aws_access_key_id=<access key id>;aws_secret_access_key=<access secret key>' CSV;

我们可以通过查询来验证nonlinear表现在有一千行:

$ psql $REDSHIFT_CONNECT -c "select count(*) from nonlinear"
 > count
> 1000
 >(1 row)

我们的数据已经上传到 Redshift。我们准备创建数据源并训练和评估模型!但在我们深入到这个数据集上的模型构建之前,让我们介绍多项式回归方法,这将使我们能够处理这个高度非线性的数据集。

介绍多项式回归

在二维空间中,我们有一个预测变量和一个结果,线性建模就是找到最佳直线来近似你的数据。在三维空间(两个预测变量和一个结果)中,想法是找到最佳平面,或者最佳平坦表面,来近似你的数据。在N维空间中,表面变成了超平面,但目标始终相同——找到维度为N-1的超平面,以给出回归的最佳近似或最好地分离类。那个超平面总是平坦的。

回到我们创建的非常非线性二维数据集,很明显,没有任何一条线能够恰当地近似预测变量和结果之间的关系。有许多不同的方法可以用来建模非线性数据,包括多项式回归、阶梯函数、样条曲线和广义加性模型GAM)。参见由 James, Witten, Hastie 和 Tibshirani 编写的《统计学习引论》的第七章,那里对这些方法有很好的介绍。这本书的 PDF 版本可在www-bcf.usc.edu/~gareth/ISL/找到。我们将应用多项式回归方法。

多项式回归包括用标准线性模型替换:

图片

在这里,ŷ是预测结果,x是预测变量,(w[0], w[1])是线性模型的系数。通过一个阶数为k的多项式函数:

图片

多项式回归方法的优势在于我们可以使用与线性模型相同的线性建模方法,因此我们仍然可以使用 Amazon ML SGD 来找到多项式回归方程的系数{w[k]}。在下一节中,我们将通过增加多项式的次数来训练连续的模型。

建立基线

我们首先需要建立一个基准。Amazon ML 的量级分箱转换是处理数据集中非线性问题的 Amazon 机器学习首选方法。让我们看看一个简单的线性模型使用 Amazon 的默认配方表现如何。我们将使用常规的 AWS 控制台工具创建一个基准分数。这次,我们选择从 Redshift 而不是 S3 创建数据源。填写下一张截图所示的信息,然后点击“测试访问”以检查您的访问权限,同时让 Amazon ML 创建必要的 IAM 角色。完成后,点击“验证”:

Amazon ML 通过创建以下资源在后台处理所有角色和策略的创建:

  • 一个新角色:AmazonMLRedshift_us-east-1_amlpackt。我们将使用与此角色相关的 arn 在 Python SDK 创建数据源时使用。

  • 附属于此角色的两个新策略:

    • AmazonMLS3LocationAccess_aml.packt

    • AmazonMLRedshiftAccess_us-east-1_amlpackt

  • AWS 还设置了信任关系,使得roleAmazonMLRedshift_us-east-1_amlpackt能够承担machinelearning服务角色。

手动创建这些角色和策略需要深入了解 AWS 中服务之间的访问权限。使用控制台创建它们可以节省大量时间。接下来的步骤是标准模式和目标定义,以及数据源创建。Amazon ML 生成的默认模式如下:

{
  "version" : "1.0",
  "rowId" : null,
  "rowWeight" : null,
  "targetAttributeName" : "y",
  "dataFormat" : "CSV",
  "dataFileContainsHeader" : false,
  "attributes" : [ {
    "attributeName" : "x",
    "attributeType" : "NUMERIC"
  }, {
    "attributeName" : "y",
    "attributeType" : "NUMERIC"
  } ],
  "excludedAttributeNames" : [ ]
}

我们稍后通过将 JSON 字符串保存到data/nonlinear.schema文件并将它上传到 S3(使用aws s3 cp data/nonlinear.schema s3://aml.packt/data/ch9/)来重用该模式。

一旦数据源可用,我们就可以通过控制台创建和评估一个模型。Amazon ML 在模型创建期间生成的配方对预测变量使用 500 个分箱的量级分箱转换,这可能看起来像是一个很大的值,因为我们只有 700 个训练数据集样本。自动生成的 Amazon ML 配方如下:

{
  "groups": {
    "NUMERIC_VARS_QB_500": "group('x')"
  },
  "assignments": {},
  "outputs": [
    "ALL_CATEGORICAL",
    "quantile_bin(NUMERIC_VARS_QB_500,500)"
  ]
}

我们使用 L2 轻微正则化和 100 次迭代训练一个模型,并在我们数据集的 30%上评估该模型。我们得到了以下结果:

  • 使用量级分箱

    • RMSE:0.1540

    • 基准 RMSE:1.034

  • 不使用量级分箱

    • RMSE:1.0207

    • 基准 RMSE:1.025

量级分箱正确处理了非线性,并得到了相当不错的分数,而原始线性模型的表现并没有比基准好多少。在线性回归的情况下,Amazon ML 的基准仅仅是训练数据集中结果的平均值。

让我们看看我们是否能通过多项式回归击败这些结果。

Amazon ML 中的多项式回归

我们将使用Boto3和 Python SDK,并遵循我们在第七章,命令行和 SDK中使用的方法来生成数据源的参数,进行蒙特卡洛验证:我们将生成对应于x的 2 次幂到xP次幂的特征,并运行N次蒙特卡洛交叉验证。伪代码如下:

for each power from 2 to P:
    write sql that extracts power 1 to P from the nonlinear table
    do N times
        Create training and evaluation datasource
        Create model
        Evaluate model
        Get evaluation result
        Delete datasource and model
    Average results

在这个练习中,我们将从x的 2 次幂到 5 次幂进行,并为每个模型进行 5 次试验。使用create_data_source_from_rds()从 Redshift 创建数据源的 Python 代码如下:

response = client.create_data_source_from_redshift(
    DataSourceId='string',
    DataSourceName='string',
    DataSpec={
        'DatabaseInformation': {
            'InstanceIdentifier': 'amlpackt',
            'DatabaseName': 'amlpacktdb'
        },
        'SelectSqlQuery': 'select x, y from nonlinear order by random()',
        'DatabaseCredentials': {
            'Username': 'alexperrier',
            'Password': 'my_password'
        },
    'S3StagingLocation': 's3://aml.packt/data/ch9/',
    'DataRearrangement': '{"splitting":{"percentBegin":0,"percentEnd":70 }
  }',
    'DataSchemaUri': 's3://aml.packt/data/ch9/nonlinear.csv.schema'
 },
 RoleARN='arn:aws:iam::178277513911:role/service-role/AmazonMLRedshift_us-east-1_amlpackt',
 ComputeStatistics=True
)

除了明显的参数(数据库信息数据模式 URI数据源 ID数据源名称)之外,你还需要找到 Role ARN 标识符的值。转到 IAM 控制台,点击角色,然后点击 AmazonMLRedshift_us-east-1_amlpackt 角色,以找到 Role ARN 字符串:

DataRearrangement字符串将取决于数据源的性质,训练数据源为 0%到 70%的分割,评估数据源为 70%到 100%。SelectSqlQuery是我们将要进行特征工程并创建新的变量作为x的幂的地方。

例如,以下查询生成一个x的 2 次幂变量:

select x, power(x,2) as x2, y from nonlinear order by random()

这个查询还生成一个x的三次幂变量:

select x, power(x,2) as x2, power(x,3) as x3, y from nonlinear order by random()

除了为每个新的集合或变量生成新的查询之外,我们还需要生成一个新的模式。原始的非线性数据集模式如下:

{
  "version" : "1.0",
  "rowId" : null,
  "rowWeight" : null,
  "targetAttributeName" : "y",
  "dataFormat" : "CSV",
  "dataFileContainsHeader" : false,
  "attributes" : [ {
    "attributeName" : "x",
    "attributeType" : "NUMERIC"
  }, {
    "attributeName" : "y",
    "attributeType" : "NUMERIC"
  } ],
  "excludedAttributeNames" : [ ]
}

我们通过向每个新的x变量幂的模式属性列表中添加以下元素来修改这个原始模式:

{ 
    "attributeName" : "x{N}", 
    "attributeType" : "NUMERIC"
}

为了运行我们的试验,比较不同的特征集,并进行交叉验证以选择最佳模型,我们需要编写一组 Python 函数。

在 Python 中驱动试验

到目前为止,我们已经在 Python 中编写了顺序代码。最终编写简单的面向对象代码总是节省时间。代码更组织化,易于维护,并且不太可能在一段时间后变得不可用。花时间编写具有清晰初始化、实例和类方法的简单类,最终会使你的代码更加简单和健壮。考虑到这一点,我们现在将为我们的实验编写一个NonLinear类。

让我们先写下那个类中不同的函数,这些函数生成一些依赖于多项式回归幂的域:

  • 这个函数接受一个幂p并返回一个 SQL 查询:
      def generate_sql(self, p):
        powers = [ 'power(x,{0}) as x{0}'.format(i) for i in range(1,p+1) ]
        return 'select ' + ','.join(powers) + ', y from nonlinear order by
        random()'

  • 这个函数接受数据分割的名称(训练与评估),并返回一个格式为 JSON 的字符串,这在数据源创建过程中是必需的:
      def generate_data_rearrangement(self,split):
           if split == 'training':
              pct_begin = 0
              pct_end = 70
           else:
              pct_begin = 70
              pct_end = 100
       return json.dumps( { "splitting": 
       {"percentBegin":pct_begin,"percentEnd":pct_end } } )

  • 最后,以下函数接受幂p并返回模式 JSON 字符串:
      def generate_schema(self, p):
      attributes = [ { "attributeName" : "x{0}".format(i), "attributeType"
      : "NUMERIC" } for i in range(1,p+1) ]
     attributes.append({ "attributeName" : "y", "attributeType" : "NUMERIC"  
     })
     return json.dumps({ "version" : "1.0",
         "rowId" : None,
         "rowWeight" : None,
         "targetAttributeName" : "y",
         "dataFormat" : "CSV",
         "dataFileContainsHeader" : False,
         "attributes" : attributes,
         "excludedAttributeNames" : [ ]
     })

下面的三个函数使用机器学习客户端来创建数据源、模型和评估。它们与我们写的Chapter 7, 命令行和 SDK中的脚本非常相似。

  • 数据源创建接受一个幂p和一个交叉验证的索引k,并分割创建的数据源的性质。脚本调用generate_sqlgenerate_data_rearrangement方法:
      def create_datasource(self, p, k, split ):
        print("Create datasource {0} {1} {2} {3}".format(p,k,split, 
        self.prefix))
        return self.client.create_data_source_from_redshift(
        DataSourceId = "ds_{2}_{3}_p{0}_{1}".format(p,k,split, self.prefix),
        DataSourceName = "DS {2} {3} p{0} {1}".format(p,k,split, 
         self.prefix),
        DataSpec = {
          'DatabaseInformation': {
            'DatabaseName': 'amlpacktdb',
            'ClusterIdentifier': 'amlpackt'
           },
           'SelectSqlQuery': self.generate_sql(p),
           'DatabaseCredentials': {
             'Username': 'alexperrier',
             'Password': 'password'
            },
            'S3StagingLocation': 's3://aml.packt/data/ch9/',
            'DataRearrangement': self.generate_data_rearrangement(split),
            'DataSchema': self.generate_schema(p)
          },
          RoleARN='arn:aws:iam::178277513911:role/service-role
          /AmazonMLRedshift_us-east-1_amlpackt',
          ComputeStatistics=True
        )

  • 创建模型方法也接受幂p和索引k
      def create_model(self, p, k):
        print("Create model {0} {1} {2}".format(p, k, self.prefix))
        return self.client.create_ml_model(
        MLModelId = "mdl_{2}_p{0}_{1}".format(p,k, self.prefix),
        MLModelName = "MDL {2} p{0} {1}".format(p,k, self.prefix),
        MLModelType = 'REGRESSION',
        Parameters = self.sgd_parameters,
        TrainingDataSourceId = self.ds_training['DataSourceId'] ,
        Recipe = json.dumps(self.recipe)
      )

  • 最后,创建评估方法如下:
      def create_evaluation(self, p, k):
      print("Create evaluation {0} {1} {2}".format(p, k, self.prefix))

      return self.client.create_evaluation(
      EvaluationId = "eval_{2}_p{0}_{1}".format(p,k, self.prefix),
      EvaluationName = "EVAL {2} p{0} {1}".format(p,k, self.prefix),
      MLModelId = self.model['MLModelId'],
      EvaluationDataSourceId= self.ds_evaluation['DataSourceId']
      )

我们使用create_sql(p)create_schema(p)在创建数据源时渲染SelectSqlQueryData.Schema字段。模型创建函数使用两个尚未初始化的类项:sgd_parametersrecipe。数据源创建函数返回 Amazon ML 的create_data_source_from_redshift函数的响应。我们将响应保存在ds_trainingds_evaluation中,并使用这些项在模型和评估创建函数中传递适当的DataSourceId

运行所有不同评估的全局代码如下:

# Initialize the object 
nl = NonLinear(max_p, n_crossval, prefix)
# Run all the datasources, models and evaluations creation  
nl.run_all_trials()
# Wait until the evaluations are finished and get the results
nl.get_results()
# Export the results to a csv file
nl.to_csv(filename)
# Free the resources
nl.delete_resources()

这些函数由以下定义:

import pandas as pd
import boto3
import json
import csv

class NonLinear():

 def __init__(self, max_p, n_crossval, prefix):
 self.trials = []
 self.max_p = max_p
 self.n_crossval = n_crossval
 self.prefix = prefix
 self.client = boto3.client('machinelearning')
 self.sgd_parameters = {
 "sgd.shuffleType": "auto",
 "sgd.l2RegularizationAmount": "1.0E-06",
 "sgd.maxPasses": "100"
 }

 self.recipe = {
 "groups" : {},
 "assignments" : { },
 "outputs": ["ALL_INPUTS"]
 # "outputs": ["quantile_bin(ALL_NUMERIC,200)"]
 }

 def run_all_trials(self):
 for p in range(1,self.max_p+1):
 for k in range(self.n_crossval):
 self.trials.append( self.run_trial(p,k) )

 def run_trial(self, p, k ):
 self.ds_training = self.create_datasource(p, k, 'training')
 self.ds_evaluation = self.create_datasource(p, k, 'evaluation')
 self.model = self.create_model(p,k)
 self.evaluation = self.create_evaluation(p,k)
 return {
 "p": p,
 "k": k,
 "ds_training_id": self.ds_training['DataSourceId'],
 "ds_evaluation_id": self.ds_evaluation['DataSourceId'],
 "model_id": self.model['MLModelId'],
 "evaluation_id": self.evaluation['EvaluationId'],
 "rmse": None
 }

 def get_results(self):
 results = []
 for trial in self.trials:

 waiter = self.client.get_waiter('evaluation_available')
 print("Waiting on evaluation {0} to finish ".format( trial['evaluation_id'] ) )
 waiter.wait(FilterVariable='DataSourceId', EQ=trial['ds_evaluation_id'] )

 response = self.client.get_evaluation( EvaluationId=trial['evaluation_id'] )
 rmse = float(response['PerformanceMetrics']['Properties']['RegressionRMSE'])
 trial["rmse"] = rmse
 results.append(trial)
 print("Evaluation score {0}".format(rmse))
 self.trials = results

 def delete_resources(self):
 # Now delete the resources
 print("Deleting datasources and model")
 for trial in self.trials:
 response = self.client.delete_data_source(
 DataSourceId = trial['ds_training_id']
 )
 response = self.client.delete_data_source(
 DataSourceId = trial['ds_evaluation_id']
 )
 response = self.client.delete_ml_model(
 MLModelId = trial['model_id']
 )

 def to_csv(self, filename):
 print("exporting to csv {0}".format(filename))
 keys = self.trials[0].keys()
 with open(filename, 'w') as output_file:
 dict_writer = csv.DictWriter(output_file, keys)
 dict_writer.writeheader()
 dict_writer.writerows(self.trials)

整个代码在 GitHub 上可用,地址为github.com/alexperrier/packt-aml

解释结果

下面的图表显示了五次交叉验证和不同多项式度(1 到 5)获得的不同 RMSE:

我们看到,对于三次和四次多项式,最佳拟合效果最好。最后,我们基于多项式回归模型的模型整体 RMSE 与使用分位数分箱获得的 RMSE 相比并不好。多项式回归的最佳 RMSE 值约为 0.85,而分位数分箱的 RMSE 发现约为 0.15。分位数分箱,如 Amazon ML 所做的那样,比多项式回归好得多。

摘要

在本章中,我们看到了如何将 Redshift 用作 Amazon ML 的数据源。尽管 RDS 也可以用来创建数据源,但与 Amazon ML 相比,Redshift 更容易使用,因为所有访问配置都由 AWS 向导处理。

我们展示了如何使用 Redshift 上的简单 SQL 查询进行特征工程,并在高度非线性数据集上实现多项式回归方法。我们还展示了如何生成所需的 SQL 查询、模式和配方以执行蒙特卡洛交叉验证。

在下一章中,我们将基于我们的 Redshift 集成,并开始使用 AWS Kinesis 服务进行数据流。

第九章:构建流式数据分析管道

在本书的最后一章,我们将构建一个端到端的流式数据处理管道,该管道将 Amazon ML 集成到 Kinesis Firehose、AWS Lambda 和 Redshift 管道中。我们通过将其与其他 AWS 数据服务集成来扩展 Amazon ML 的功能,以实现实时推文分类。

在本章的第二部分,我们将展示如何解决超出简单回归和分类的问题,并使用 Amazon ML 进行命名实体识别和基于内容的推荐系统。

本章涵盖的主题如下:

  • 训练推特分类模型

  • 使用 Kinesis 进行流式数据处理

  • 使用 Redshift 进行存储

  • 使用 AWS Lambda 进行处理

  • 命名实体识别和推荐系统

在本章的结论中,我们将总结 Amazon ML 的优势和劣势。

流式推特情感分析

在本章中,我们的主要项目是实时推文情感分类。这将使我们能够展示如何使用我们训练的 Amazon ML 模型来处理实时数据流,通过利用 AWS 数据生态系统。

我们将构建一个包括以下内容的 AWS 服务基础设施:

  • Amazon ML:提供实时分类端点

  • Kinesis firehose:收集推文

  • AWS Lambda:调用 Amazon ML 的流式端点

  • Redshift:存储推文及其情感

  • S3:作为 Kinesis Firehose 收集的推文的临时存储

  • AWS Cloudwatch:调试和监控

我们还将编写必要的 Python 脚本,将推文输入到 Kinesis Firehose。

推特上的流行竞赛

所有优秀的数据科学项目都始于一个问题。我们想要一个与社会网络相关的问题,而不是与当前的政治或社会背景相关。我们将研究推特上蔬菜的流行度。我们想知道推特上最受欢迎的蔬菜是什么。这是一个经得起时间考验的问题,可以适应其他事物列表,如水果、饮料、天气条件、动物,甚至品牌和政治人物。结果可能会让你感到惊讶……或者不会。

我们将首先使用一个大型公开的推特情感分析数据集来训练一个二元分类的 Amazon ML 模型。

有许多可用的情感分析库,只需几行代码,就可以根据一段文本返回情感评分或极性(正面、中性、负面)。TextBlob是 Python 中的一个这样的库。它可在textblob.readthedocs.io找到。建立在NLTK之上,TextBlob是一个强大的库,可以从文档中提取信息。除了情感分析外,它还可以执行一些词性标注、分类、分词、命名实体识别等功能。我们将TextBlob的结果与我们的 Amazon ML 分类模型进行比较。我们的首要目标是学习如何使这些 AWS 服务无缝协作。我们的社交媒体蔬菜流行比赛将遵循以下步骤:

  1. 我们首先在 Amazon ML 上训练一个 Twitter 情感分类模型,并创建一个实时预测端点。

  2. 我们设置了一个 Kinesis Firehose,它将内容存储在 S3 上。

  3. 我们编写了一个简单的 Python 脚本,称为producer,它从 Twitter API 收集推文并将它们发送到 Kinesis。在此阶段,Kinesis Firehose 将推文存储在 S3 中。

  4. 我们从将流数据存储在 S3 转移到将流数据存储在 Redshift。为此,我们必须启动一个 Redshift 集群并创建所需的表。

  5. 最后,我们将一个 AWS Lambda 函数添加到我们的管道中,以便查询我们的 Amazon ML 分类端点。

  6. 在整个项目中,我们使用 AWS CloudWatch 来检查状态和调试我们的数据管道。

我们最终得到一个包含推文的集合,其中包含两种类型的情感评分,我们可以进行比较。我们将查看根据两种方法对蔬菜的排名、它们的可变性以及两种方法之间的评分一致性,并尝试评估哪一种更好。值得注意的是,我们避免对推文进行任何复杂的文本处理。推文是一种非常具体的文本内容,其中包含 URL、表情符号、缩写、俚语和标签。关于 Twitter 情感分析的出版物有很多,它们探讨了不同的预处理和信息提取技术。我们不会使用任何特定的特征提取技术,而将自身限制在简单的词袋方法上。我们通过 Amazon ML 和 TextBlog 进行的情感分析比较是一个概念验证,而不是基准。

训练数据集和模型

我们项目的第一步是为推文训练一个情感分析和分类的 Amazon ML 模型。幸运的是,我们可以访问一个相当大的 Twitter 情感数据集,该数据集由超过 150 万条标记为 0/1 的推文组成,分别代表负面/正面情感。该数据集可在thinknook.com/twitter-sentiment-analysis-training-corpus-dataset-2012-09-22/找到。该数据集是两个 Twitter 情感分析数据集的汇总:

这个 Twitter 情感分析 数据集包含 1,578,627 个分类推文。每一行都标记为 0/1,表示负面/正面情感。我们使用该数据集的样本(大约 10%,158K 条推文)在 Amazon ML 中训练一个分类模型。我们在 S3 上加载数据集,并使用 Amazon ML 服务训练和评估一个二元分类模型。我们对文本没有进行特定的文本转换。配方如下:

{
 "groups": {},
 "assignments": {},
 "outputs": [
     "ALL_BINARY",
     "ALL_TEXT"
 ]
}

我们将模型设置为具有轻微的 L2 正则化和 100 次迭代。训练模型需要一些时间(超过 10 分钟)才能完成,这可能是由于样本数量较多。模型评估显示整体性能相当不错,AUC 为 0.83。大约三分之二的推文被 Amazon ML 模型正确分类。预测在类别概率范围内均匀分布,如模型评估图所示:

现在,我们通过点击模型页面底部的“创建端点”按钮从模型页面创建一个实时预测端点:

端点 URL 的形式为 https://realtime.machinelearning.us-east-1.amazonaws.com。我们将使用该端点通过 Lambda 服务的请求来对新推文进行分类。让我们通过以下 Python 脚本来测试我们的 Amazon ML 分类器是否按预期工作:

import boto3
client = boto3.client('machinelearning')
client.predict(
   MLModelId = "ml-ZHqxUjPNQTq",
   Record = { "SentimentText": "Hello world, this is a great day" },
   PredictEndpoint = "https://realtime.machinelearning.us-east-1.amazonaws.com"
)

句子 Hello world, this is a great day 返回句子为积极的概率评分为 0.91,并将其分类为 1,而句子 Hello world, this is a sad day 返回的概率为 0.08,并分类为 0。单词 greatsad 是驱动情感分类的单词。分类器按预期工作。

现在,让我们将注意力转向 Kinesis 服务。

Kinesis

Kinesis 是一个围绕三个子服务组织的多形式服务:StreamsFirehoseAnalytics。Kinesis 作为高度可用的管道,在数据生产者和数据消费者之间传输消息。

  • 数据生产者是来自流式 API、物联网设备、系统日志和其他高容量数据流的来源

  • 数据消费者最常用于存储、处理数据或触发警报

Kinesis 能够每小时处理高达 500 太字节的数据。我们使用 Kinesis 的最低级别和最简单的配置。对 Kinesis 的更复杂用法感兴趣的用户应阅读 AWS 文档,网址为 aws.amazon.com/documentation/kinesis/。在博客文章 www.sumologic.com/blog/devops/kinesis-streams-vs-firehose/ 中可以找到 AWS Kinesis 的不同概念和元素的良好概述。

Kinesis Stream

Kinesis Stream 用于收集由生产者提供的流数据并由消费者处理。它是三个 Kinesis 服务中最简单的一个,因为它以任何方式都不存储数据。Kinesis Stream 主要充当缓冲区,数据在 24 小时到 168 小时内保持可用。物联网设备或日志服务通常会作为生产者并将数据发送到 Kinesis 流。同时,一个消费者正在运行以处理这些数据。消费者服务基于事件检测算法触发警报(短信、电子邮件)、实时仪表板实时更新、数据聚合器或任何同步检索数据的任何东西。生产者和消费者必须同时运行,Kinesis 流才能正常工作。如果您的生产者和消费者是在您的本地机器上运行的脚本,它们必须并行运行。

可以通过向流中添加 AWS Lambda 函数来对传入的数据进行处理。整个数据处理管道现在将遵循以下步骤:

  1. 自定义应用程序或脚本将记录发送到流中(生产者)。

  2. AWS Lambda 会轮询流,并在检测到新记录时调用您的 Lambda 函数。

  3. AWS Lambda 执行 Lambda 函数并将记录(原始或修改后的)发送回 Kinesis 流。

我们将不会使用 Kinesis Streams,因为我们想存储我们收集的数据。Kinesis 流是开始使用 Kinesis 服务的良好方式。

Kinesis Analytics

Kinesis Analytics 是 AWS 对 Kinesis 混合的最新补充。Kinesis Analytics 允许您使用标准 SQL 查询分析实时流数据。想法是从查询数据的静态表示形式转变为动态表示形式,该表示形式随着数据流的到来而演变。而不是使用 AWS Lambda 通过脚本语言处理数据,目标是使用 SQL 处理数据并将结果馈送到仪表板或警报系统。我们将不会使用 Kinesis Analytics,而是专注于 Kinesis Firehose。

设置 Kinesis Firehose

我们将专注于 Kinesis Firehose 服务,该服务提供两个重要功能:接收到的数据可以馈送到 AWS Lambda 函数进行额外处理,并且结果数据可以存储在 S3 或 Redshift 数据库上。

我们将首先设置一个 Kinesis Firehose 交付流,该流将数据存储在 S3 而不使用任何 Lambda 功能:

  1. 前往 Kinesis Firehose 仪表板console.aws.amazon.com/firehose/并点击“创建传输流”。

  2. 填写以下字段:

    • 目标: Amazon S3

    • 传输流名称: veggieTweets(或选择你自己的名称)

    • S3 存储桶: aml.packt

    • S3 前缀: veggies

  3. 点击“下一步”:

图片

注意,S3 前缀字段对应于你的 S3 存储桶中的一个文件夹。你现在应该前往 S3 并在你的存储桶中创建一个veggies文件夹。模仿下一个截图的设置:

图片

  1. 目前,我们将保持使用 AWS Lambda 进行数据转换的禁用状态。

  2. 启用错误日志记录,因为我们需要使用 CloudWatch Logs 调试我们的数据交付错误。

  3. 对于 IAM 角色,选择 Firehose Delivery IAM 角色。你将被带到 IAM 服务,并指导创建所需的角色。你可以选择现有的策略并创建一个新的策略。角色/策略向导将处理详细信息。点击“允许”以跳转到初始 Firehose 配置屏幕。

  4. 点击“下一步”,查看 Firehose 传输流的详细信息,然后点击“创建传输流”。

现在我们有一个 Kinesis Firehose 传输流,一旦我们向其发送数据,数据将存储在 S3 packt.aml/veggies 位置。我们现在需要创建一个生产脚本,将数据发送到 Kinesis Firehose 服务。

发布推文

Kinesis 生产者可以有多种形式,只要它向 Kinesis 流发送数据。我们将使用 Python Firehose SDK 并编写一个简单的脚本,从 Twitter API 收集推文,过滤其中的一些,并将它们发送到 Kinesis Firehose。我们使用 Python-Twitter 库。

在 GitHub 上有几个 Twitter API Python 包可用。其中两个更受欢迎的,TwitterPython-Twitter(pypi.python.org/pypi/python-twitter/)有相同的导入调用import twitter,但方法调用不同,这可能导致混淆并浪费时间。

Python-Twitter 包提供了一个GetSearch方法,该方法接受查询字符串termraw_query作为 Twitter 搜索的参数:

  • 查询字符串(term)对应于你会在 Twitter 网站搜索栏中写下的关键词;例如,term = brocolli OR potato OR tomato

  • 当你点击搜索按钮后,raw_query参数会接收 URL 的参数部分:URL 中?后面的字符串。你可以从 Twitter 高级搜索页面twitter.com/search-advanced构建高级查询。例如,我们搜索"西兰花 OR 土豆 OR 番茄"转换为raw_query = q=brocolli%20OR%20potato%20OR%20tomato&src=typd。我们在调用搜索 API 时使用raw_query参数。

  • 要获取自己的 Twitter 开发 API 密钥,请访问apps.twitter.com/并创建一个应用程序。

我们首先定义一个类,该类初始化对 Twitter API 的访问。这个类有一个capture方法,它根据一个原始查询执行搜索。将以下代码保存到tweets.py文件中:

import twitter
class ATweets():
    def __init__(self, raw_query):
         self.twitter_api = twitter.Api(consumer_key='your own key',
              consumer_secret= 'your own key',
              access_token_key='your own key',
              access_token_secret='your own key')
         self.raw_query = raw_query

    # capture the tweets: see http://python-twitter.readthedocs.io/en/latest/twitter.html 
    def capture(self):
        statuses = self.twitter_api.GetSearch(
             raw_query = self.raw_query,
             lang = 'en',
             count=100, result_type='recent', include_entities=True
        )
     return statuses

给定这个类和一个raw_query字符串,收集 Tweets 的过程包括用raw_query初始化ATweets类,并在实例化的对象上应用捕获方法,如下所示:

tw = ATweets(raw_query) 
statuses = tw.capture()

在这里,statuses是一个包含许多元素的 Tweets 列表。我们只使用其中的一些元素。现在我们能够从Twitter API 收集 Tweets,我们需要一个生产脚本,将 Tweets 发送到 Kinesis。以下是一个生产 Python 脚本:

from tweets import ATweets
import json
import boto3

# The kinesis firehose delivery stream we send data to
stream_name = "veggieTweets" 

# Initialize the firehose client
firehose = boto3.client('firehose')

# Our own homemade list of vegetables, feel free to add seasoning
vegetables = ['artichoke','asparagus', 'avocado', 'brocolli','cabbage', 'carrot', 'cauliflower','celery', 'chickpea', 'corn','cucumber', 'eggplant','endive', 'garlic', 'green beans', 'kale', 'leek', 'lentils', 'lettuce','mushroom','okra', 'onion','parsnip', 'potato','pumpkin', 'radish','turnip', 'quinoa', 'rice', 'spinach', 'squash' , 'tomato', 'yams', 'zuchinni']

# Loop over all vegetables
for veggie in vegetables:
 # for a given veggie define the query and capture the tweets
 raw_query = 'f=tweets&vertical=default&l=en&q={0}&src=typd'.format(veggie)
 # capture the tweets
 tw = ATweets(raw_query)
 statuses = tw.capture()
 # and for each tweet, cleanup, add other data and send to firehose
 for status in statuses:
 # remove commas and line returns from tweets
 clean_tweet = ''.join([s for s in st.text if s not in [',', 'n']])
 # and build the record to be sent as a comma separated string followed by a line return
 record = ','.join([str(st.id), st.user.screen_name,veggie, clean_tweet]) + 'n'
 # send the record to firehose
 response=firehose.put_record(DeliveryStreamName = stream_name, Record={'Data': record} )

将此代码保存到与tweets.py文件相同的文件夹中的producer.py文件。

如你所注意到的,我们通过在GetSearch调用中指定lang = 'en'将 Twitter 搜索限制为英文 Tweets。然而,这并没有产生预期的结果,并且返回了许多非英文 Tweets。在后续版本的生产脚本中,我们在将 Tweets 发送到 Firehose 之前,添加了以下条件到 Tweets 本身,实际上过滤掉了长度小于 10 个字符或作为转发发送的非英文 Tweets:

(st.lang=='en') & (st.retweeted_status is None) & (len(st.text) > 10):

我们现在可以运行我们的生产脚本了。最后,需要注意的一个重要细节是,对 Twitter API 的调用是有限制的。如果你调用 API 过于频繁,你将不得不在请求之间等待越来越长时间,直到它们被允许通过。有可靠的方法来处理这些限制,并且很容易在网上找到显示如何延迟 API 调用的代码。我们将简单地使用带有 10 分钟(600 秒)延迟的watch命令行。watch命令简单地执行你之后写的任何命令,每 n 秒执行一次。要运行你的生产代码,打开一个终端,并运行以下命令:

$ watch -n 600 python producer.py

每 10 分钟,将捕获 Tweets 并发送到 Kinesis Firehose。为了验证你的脚本和交付流是否正常工作,请访问你的 S3 存储桶和aml.packt/veggies文件夹。你应该会看到文件堆积。这些文件是由 Kinesis 按日期/年/月/日和小时结构化的子文件夹保存的。在最后一个子文件夹中的文件名格式类似于veggieTweets-2-2017-04-02-19-21-51-1b78a44f-12b2-40ce-b324-dbf4bb950458。在这些文件中,你会找到在生产代码中定义的记录。我们的生产代码发送以逗号分隔的数据,格式为 tweet ID/twitter 用户名/蔬菜/tweet 内容。以下是一个这样的记录示例:

848616357398753280,laurenredhead,artichoke,Artichoke gelatin dogs. Just one of many delicious computer-algorithm generated recipe titles:https://t.co/mgI8HtTGfs

我们现在将设置 Redshift,以便这些 Tweets 和相关元素最终存储在 SQL 数据库中。

Redshift 数据库

我们在第八章“从 Redshift 创建数据源”中看到了如何创建 Redshift 集群,我们不会再次介绍这些步骤。对于我们的蔬菜竞赛项目,我们创建了一个蔬菜集群和一个vegetablesdb数据库。等待集群端点就绪并定义后,通过以下Psql命令连接到您的 Redshift 数据库:

$ psql --host=vegetables.cenllwot8v9r.us-east-1.redshift.amazonaws.com --port=5439 --username=alexperrier --password --dbname=vegetablesdb

连接到数据库后,使用以下 SQL 查询创建以下表:

CREATE TABLE IF NOT EXISTS tweets (
 id BIGINT primary key,
 screen_name varchar(255),
 veggie varchar(255),
 text varchar(65535)
);

注意,在 Redshift SQL 中没有blobtext数据类型。我们将推文定义为varchar(65535),这可能是过于大了,但由于我们使用了varchar而不是char,数据占用的体积缩小到文本的实际长度,而不是整个 65KB。在该表中,我们只捕获推文的 ID、推文本身、与推文关联的蔬菜以及撰写推文的人的屏幕名。我们忽略任何其他推文元素。

将 Redshift 添加到 Kinesis Firehose

这部分比较复杂,因为来自不同服务的多个部分必须相互配合:

  • 数据结构、表声明和 Kinesis Redshift 配置

  • 数据字段聚合和后续解析

  • 角色及其相关策略

存储数据的 Redshift 表字段需要在三个不同的地方进行同步:

  1. 具有适当字段定义的 Redshift 表。

  2. 将数据发送到 Kinesis 的脚本。根据发送到 Kinesis 的记录是如何聚合在一起以及随后由 Redshift 解析的方式,脚本必须以与 Redshift 表中定义的相同顺序连接相同数量的字段。例如,当我们在脚本中写入record = ','.join([str(st.id), st.user.screen_name,veggie, clean_tweet]) + 'n'时,这意味着表有四个字段,且类型正确:intvarcharvarchar,和varchar

  3. 如 Kinesis Firehose 定义中定义的列。

对于最后一个问题,我们需要回到 Firehose 仪表板,创建一个新的流,并将其定义为基于 Redshift 的交付流。点击创建交付流,选择 Redshift 作为目标。按照不同的屏幕提示,填写以下值:

  • S3 存储桶:您的自己的存储桶

  • S3 前缀:我们保留前缀 veggies

  • 数据转换:目前禁用

  • Redshift 集群:Vegetables

  • Redshift 数据库:Vegetablesdb

  • Redshift 表列:ID,screen_name,veggie,text(这个非常重要)

  • Redshift 用户名:您使用该用户名访问 Redshift

  • Redshift COPY 选项:分隔符,(同样非常重要)

创建后,您的 Kinesis Firehose 流应类似于以下截图:

图片

注意屏幕右下角的COPY命令,此处重现:

COPY tweets (id,screen_name, veggie,tb_polarity, text) FROM 's3://aml.packt/<manifest>' CREDENTIALS 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' MANIFEST delimiter ',';

此命令指示 Redshift 将如何摄取 Kinesis 发送到 S3 的数据,它期望哪些字段,以及它将如何解析不同的字段(例如,由逗号分隔)。还有其他潜在的COPY格式,包括 JSON 或 CSV。我们发现这个格式简单且有效。重要的是,记录在生成脚本中的定义和格式(由逗号分隔的四个变量)与table name (name of the four fields)命令的 COPY 部分相对应,并具有正确的分隔符定义','

当数据没有记录到数据库中时,这个 COPY 命令也是调试管道的好方法。进入数据库的 Psql,然后运行相同的查询,以便获取有关查询失败原因的有用错误信息。

现在是时候谈谈基于角色的访问控制了。

设置角色和策略

AWS 中有两种类型的访问控制:基于密钥和基于角色。基于密钥的设置更容易,但不能用来使 Kinesis、Redshift 和 S3 相互通信,如 AWS 在docs.aws.amazon.com/redshift/latest/dg/copy-usage_notes-access-permissions.html中指出的:

基于角色的访问控制允许您的集群代表您临时假定一个 IAM 角色。然后,根据授予该角色的授权,您的集群可以访问所需的 AWS 资源。IAM 角色与 IAM 用户类似,因为它是一个 AWS 身份,具有权限策略,该策略确定身份可以在 AWS 中做什么和不能做什么。然而,与唯一关联于一个用户不同,任何需要它的实体都可以假定一个角色。此外,角色没有与之关联的任何凭证(密码或访问密钥)。相反,如果角色与集群关联,则会动态创建访问密钥并将其提供给集群。我们建议使用基于角色的访问控制,因为它提供了对 AWS 资源和敏感用户数据的更安全、更细粒度的访问控制。

我们必须为您的用户创建正确的角色,以便他们能够访问 Redshift,然后我们必须给它必要的策略。

有三个步骤:

  1. 第一步,授权 Amazon Redshift 代表你访问其他 AWS 服务。请按照以下说明操作:docs.aws.amazon.com/redshift/latest/mgmt/authorizing-redshift-service.html

  2. 第二步,将角色附加到聚类中。查看docs.aws.amazon.com/redshift/latest/mgmt/copy-unload-iam-role.html

  3. 最后,使用控制台管理 IAM 角色关联,执行以下步骤:

    1. 前往 Redshift,然后点击管理 IAM 角色。

    2. 从可用角色中选择。

    3. 等待状态从修改中变为可用。

在 AWS 中,当尝试让不同的服务相互连接时,角色和策略可能会具有挑战性和耗时。对于生产级应用程序和服务,显然需要严格的访问控制,但 AWS 平台缺乏一个更宽松或松散的通用访问级别,以允许进行概念验证和宠物项目。面对与角色相关的访问问题时的一般黑客思路是,前往 IAM 角色页面,并将你认为必要的策略附加到给你带来麻烦的角色上。通过试错,你最终会找到解决方案。

如果一切顺利,当你运行生产者脚本时,你应该看到以下情况发生:

  • 文件和基于日期的子文件夹将创建在{bucket}/veggies文件夹中

  • 图表和查询应在 Redshift 集群页面上显示或更新

  • 在“Firehose 传输流页面”上,检查 S3 日志和 Redshift 日志标签以查找错误信息

  • 你的vegetablesdb.tweets应该开始填充内容行。

如果情况并非如此,并且你未在数据库中看到推文,那么是时候开始调试了。

依赖关系和调试

如果你不是经验丰富的 AWS 用户,连接不同的服务——Firehose、Redshift、S3 并不是一项简单任务。许多细节需要解决,而且文档并不总是清晰,有时甚至过于复杂。许多错误也可能以隐蔽的方式发生,而且错误发生的位置和如何检测它并不总是明显,更不用说理解它了。在我们必须解决的所有的错误和问题中,这些是最耗时的。

数据格式同步

如果你向 Redshift 发送一些数据,它需要被 Redshift 解析。你发送的字符串格式为id, usernamesentiment, tweet或 JSON 字符串{id: 'id', username:'twetterin_chief', sentiment: '0.12', tweet:'Hello world it's a beautiful day'}。你需要确保 Kinesis 中的 Redshift 配置遵循你的数据格式。你可以在 Kinesis-Redshift 配置屏幕上的以下两个字段中这样做:

  • Redshift 表列

  • Redshift 的 COPY 选项

调试

当你运行你的生产者,数据最终没有出现在 Redshift 表中时,你应该记住有一个延迟。这个延迟是在你创建 Kinesis 传输流时设置的,默认设置为 3,600 秒。如果你想避免长时间的等待,请将其设置为至少 60 秒。以下是在你的数据没有在数据库中流式传输时需要检查的地方:

  1. 检查 S3:S3 前缀对应于您定义的存储桶中的一个文件夹。如果有错误,您将看到一个名为errorsprocessing errors的新子文件夹。点击子文件夹直到到达实际的错误文件,将其公开(有一个按钮),下载文件,并检查它。它有时会包含有用的信息。错误子文件夹还包含一个清单文件。清单文件对于重新处理失败的文件很有用。

  2. 连接到您的 Redshift 数据库,并使用select * from STL_LOAD_ERRORS检查STL_LOAD_ERRORS表。如果问题是由基于 SQL 的错误(可能是解析相关)引起的,那么有用的信息将显示在那里。不过,错误消息并不总是解释性的。在我们的情况下,那个表显示了 Redshift 未能摄取的第一条推文,这极大地帮助了我们找出问题所在。最后,我们面临的问题是某些字符被 Redshift 当作列分隔符。我们在生产者直接从推文中移除了这些字符。

  3. 检查Redshift 查询页面,您将看到最新的查询。如果您看到查询被终止而不是完成,那么您有一个与 SQL 查询相关的问题。

  4. 最后,一个好的调试方法是连接到您的数据库,并运行在 Kinesis 传输流摘要页面中显示的 COPY 查询,同时别忘了用正确的值替换账户 ID 和角色名称。这将模拟 Redshift 实际从 S3 存储桶中摄取数据的方式。如果失败,相关的错误会为您提供更多信息。

使用 Lambda 进行预处理

我们现在希望将推文用于情感分类发送到我们的 Amazon ML 模型。为了做到这一点,我们将启用 Kinesis Firehose 传输流页面中可用的数据转换,并使用一个 Lambda 函数:

图片

AWS Lambda 是一种数据处理服务,允许您运行脚本(包括 Python 2.7 等多种语言,但不包括 Python 3)。它与其他服务(如 Kinesis)一起用作数据处理附加组件。您可以将数据流重定向到 Lambda 进行处理,如果需要,可以将结果发送回初始流。您还可以使用 Lambda 调用其他服务,例如发送警报或使用其他存储服务。

AWS Lambda 的主要默认设置是,你可以导入 Python 脚本中的包的选择是有限的。尝试导入包,如scikit-learn、NLTK,或者任何尚未提供的包,相当复杂。有关如何在 AWS Lambda 上使用scikit-learn的指南,请访问serverlesscode.com/post/deploy-scikitlearn-on-lamba/serverlesscode.com/post/deploy-scikitlearn-on-lamba/。这显著限制了 Lambda 的即插即用功能。我们使用 AWS Lambda 的方式要简单得多。我们将使用 AWS Lambda 来完成以下工作:

  1. 从 Kinesis Firehose 捕获数据。

  2. 解析数据并提取推文。

  3. 将数据发送到 Amazon ML 实时端点。

  4. 从响应中提取分类分数。

  5. 将数据连同分类分数一起发送回 Kinesis Firehose 传输流。

前往 AWS Lambda,点击创建 Lambda 函数。然后执行以下步骤:

  1. 选择 Blank Function 蓝图和 Python 2.7 运行时。

  2. 不要配置触发器。

  3. 填写名称为vegetablesLambda,并选择 Python 2.7 运行时。

最后,将以下代码粘贴到内联编辑器中:

from __future__ import print_function

import base64
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ml_client = boto3.client('machinelearning')

print('Loading function')

def lambda_handler(event, context):
  output = []
  for record in event['records']:
     payload = base64.b64decode(record['data'])
     payload = payload.split(',')
     tweet = payload.pop(4)

     predicted_label, predicted_score = get_sentiment(tweet)

     payload.append(str(predicted_label) )
     payload.append(str(predicted_score) )
     payload.append(tweet)
     payload = ','.join(payload)

     output_record = {
       'recordId': record['recordId'],
       'result': 'Ok',
       'data': base64.b64encode(payload)
     }
     output.append(output_record)
     return {'records': output}

lambda_handler函数由 Kinesis Firehose 自动触发。它捕获并解析消息(即负载)event['records'],提取推文,并调用返回情感分数和情感标签的get_sentiment()函数。最后,它将情感数字添加回记录,重建负载,并将其发送回 Kinesis。get_sentiment()函数将推文发送到我们的 Amazon 分类端点,并返回predicted_labelpredicted_score。该函数定义在以下脚本中:

def get_sentiment(tweet):

   response = ml_client.predict(
       MLModelId = "ml-ZHqxUjPNQTq",
       Record = { "SentimentText": tweet },
       PredictEndpoint = "https://realtime.machinelearning.us-east-1.amazonaws.com"
   )
   predicted_label = response['Prediction']['predictedLabel']
   predicted_score = response['Prediction']['predictedScores'][predicted_label]

   return predicted_label, predicted_score

由于我们在负载中添加了两个新元素,我们还需要将它们添加到 Redshift 表和 Kinesis-Redshift 配置中。为了在 Redshift 中重新创建tweets表,请运行以下查询:

drop table if exists tweets;
CREATE TABLE IF NOT EXISTS tweets (
 id BIGINT primary key,
 screen_name varchar(255),
 veggie varchar(255),
 ml_label int,
 ml_score float,
 text varchar(65535)
);

在 Kinesis 级别,将 Redshift 表列字段改为id,screen_name, veggie,ml_label, ml_score, text

分析结果

我们现在有一个完整的流数据管道,它能够捕获、转换和存储数据。一旦收集了几千条推文,你就可以开始分析你的数据了。在我们能够找到本章开头设定的答案之前,还有一些事情需要完成:

  • 推特上最受欢迎的蔬菜是什么?

  • TextBlob与我们的 Amazon ML 分类模型相比如何?

我们简单的生产者不尝试处理重复的推文。然而,最终,我们的数据集有许多重复的推文。西兰花和胡萝卜作为推文主题的频率比预期的要低。因此,当我们每 10 分钟收集大约一百条推文时,许多推文最终被收集多次。我们还需要从 TextBlob 获取情感得分和相关类别。

现在,我们将下载我们收集的推文数据集,删除重复项,并使用 TextBlob 分类。

从 RedShift 下载数据集

从 Redshift 下载数据的正确方式是使用 Psql 连接到数据库,并使用 Unload 命令将 SQL 查询的结果导出到 S3。以下命令使用适当的角色将所有推文导出到 s3://aml.packt/data/veggies/results/ 位置:

unload ('select * from tweets') to 's3://aml.packt/data/veggies/results/' iam_role 'arn:aws:iam::0123456789012:role/MyRedshiftRole';

然后,我们可以下载文件并将它们汇总:

# Download
$ aws s3 cp s3://aml.packt/data/veggies/results/0000_part_00 data/
$ aws s3 cp s3://aml.packt/data/veggies/results/0001_part_00 data/
# Combine
$ cp data/0000_part_00 data/veggie_tweets.tmp
$ cat data/0001_part_00 >> data/veggie_tweets.tmp

veggie_tweets.csv 文件不是以逗号分隔的。值是通过 | 字符分隔的。我们可以使用以下命令行将文件中的所有管道符号替换为逗号:

$ sed 's/|/,/g' data/veggie_tweets.tmp > data/veggie_tweets.csv

现在,我们已准备好将数据加载到 pandas 数据框中:

import pandas as pd
df = pd.read_csv('data/veggie_tweets.csv')

注意,我们也可以在加载 pandas 数据框时使用 | 作为分隔符,即 df = pd.read_csv('data/veggie_tweets.tmp', delimiter = '|', header=None, names = ['id', 'username', 'vegetable', 'ml_label', 'ml_score', 'text'])。

使用 TextBlob 进行情感分析

TextBlob 在几行 Python 代码中提供情感分析、评分和分类。对于给定的文本,初始化一个 TextBlob 实例,并使用以下两行代码检索其极性:

from textblob import TextBlob
print(TextBlob(text).sentiment)

TextBlob 情感对象有一个极性和主观性得分。文本的极性范围从 -1 到 +1,从负到正,主观性从 0 到 1,从非常客观到非常主观。例如,句子 I love brocoli 返回 Sentiment(polarity=0.5, subjectivity=0.6),而句子 I hate brocoli 返回情感 Sentiment(polarity=-0.8, subjectivity=0.9)。我们可以在处理推文时添加 TextBlob 情感,无论是在生产者内部还是在下载了这些数据集后,都可以用以下简单行代码实现:

from textblob import TextBlob
df['tb_polarity'] = 0
for i, row in df.iterrows():
    df.loc[i, 'tb_polarity'] = TextBlob(row['text']).sentiment.polarity

我们数据框的每一行现在也都有一个情感得分。

删除重复推文

在所有基于 Twitter 的 NLP 分析中,你最终都会处理机器人,即使是在收集关于蔬菜的推文时也是如此!在我们的数据集中,我们有许多版本的宣传推文,其中推文中的文本相同,但链接和用户不同。我们通过首先从推文中删除 URL,然后使用 drop_duplicates Pandas 方法来删除重复推文。

注意到推文中所有的 URL 都以 https://t.co/ 开头,因此很容易从推文中移除所有 URL。我们将在我们的数据框中创建一个不带 URL 的新推文列。我们输入以下行,它将返回不带 URL 的推文:

' '.join([token for token tk in tweet.split(' ') if 'https://t.co/' not in tk])

当使用 pandas 数据框时,创建基于数据框其他列操作的新列的一个非常实用的方法是将 apply 方法与 Lambda 函数结合使用。创建从 existing_columnnew_columnnew_column 的整体模式是:

df['new_column'] = df[existing_column].apply( 
                      lambda existing_column : {
                         some operation or function on existing_column 
                      } 
                   )

我们应用这个模式来创建包含没有网址的推文的 no_urls 列:

df['no_urls'] = df['text'].apply(
                   lambda tweet : ' '.join(
                       [tk for  tk in tweet.split(' ') if 'https://t.co/' not in tk]
                   )
                )

no_urls 列不再包含任何网址。现在我们可以使用以下行基于此列删除重复项:

df.drop_duplicates(subset= ['no_urls'], inplace = True)

这大约移除了我们推文的 30%。

那么最受欢迎的蔬菜是什么呢?

比较我们的 Amazon ML 模型和 TextBlob 的情感分数分布很有趣。我们可以在下面的图中看到,我们的 Amazon ML 模型擅长区分正面和负面的推文,而 TextBlob 的分布则更加集中。事实上,相当一部分推文被 TextBlob 评分为零(中性)。我们从直方图中移除了它们:

根据我们的 Amazon ML 模型,Twitter 上最受欢迎的蔬菜是绿豆,其次是芦笋和大蒜。根据 TextBlob,花椰菜排名第四受欢迎,其次是韭菜和黄瓜。以下图显示了推文量较多的前 10 种蔬菜以及使用 TextBlob 和我们自己的 Amazon ML 二元分类器获得的相应情感分数:

令人惊讶的结果是,根据 TextBlob,绿豆是最不受欢迎的蔬菜。碰巧的是,TextBlob 给单词 green 分配了一个负的 -0.2 情感分数。因此,短语 green beans 直接得分 -0.2

我们的 Amazon ML 模型似乎比 TextBlob 更可靠。毕竟,绿豆肯定比花椰菜更受欢迎!

超越分类和回归

虽然 Amazon ML 被设置为解决分类和回归问题,但该服务也可以用于其他监督数据科学问题。在本节的最后,我们探讨了两个经典问题:推荐系统和命名实体识别。

  • 制作推荐:推荐系统试图预测用户会对一个项目给出多少评分或偏好。构建推荐系统有几种策略:

  • 协同过滤:这涉及到使用类似用户的的行为模式来预测给定用户的偏好。这是“其他人也买了这个”的方法。

  • 基于内容的过滤:这是一种策略,其中使用特定内容的特征来分组相似的产品或内容。

要使用 Amazon ML 进行推荐,你可以将你的解决方案构造成一个基于内容的推荐问题。一种方法是提取产品和用户的特征,并构建一个训练数据集,其中结果为二元:用户要么喜欢该产品,要么不喜欢。推荐系统被转换为一个二元推荐问题。

命名实体识别:命名实体识别旨在在文本中定位和分类实体到预定义的类别中,例如人名、组织、地点等。亚马逊机器学习也可以用于命名实体识别问题。想法是使用单个单词,并提取特征作为训练数据。潜在的特征可能包括以下内容:

  • 单词本身

  • ngram()osb()的上下文,例如前后的三个单词。

  • 前缀和后缀

  • 前三个单词的预测类别

  • 单词的长度

  • 单词是否首字母大写?

  • 单词是否有连字符?

  • 句子中的第一个单词

  • 单词在数据集中的频率

  • 数字特征--单词是否是数字?

  • 单词或周围单词的词性

其中一些特征提取方法在亚马逊机器学习中可用;其他则需要外部处理。

摘要

在本章中,我们利用了 AWS 生态系统的力量来构建一个实时流数据分类管道。我们的管道能够使用亚马逊机器学习分类端点对实时推文进行分类。AWS 数据生态系统多样且复杂,对于给定的问题,通常有几种可能的解决方案和架构。我们构建的Kinesis-Lambda-Redshift-机器学习架构简单,但非常强大。

亚马逊机器学习服务的真正优势在于其易用性和简单性。从头开始训练和评估一个模型只需几分钟和几个点击,并且可以产生非常好的性能。使用AWS CLI和 SDK,可以轻松实现更复杂的数据流和模型探索。该服务足够敏捷,可以通过提供实时分类和回归成为更广泛数据流的一部分。

在简单的界面之下,亚马逊的机器学习专业知识在许多层面上都闪耀着光芒。从自动数据转换到随机梯度算法的调整,有许多元素推动了模型的总体性能。在用户对模型的控制与自动优化之间达到了良好的平衡。用户可以尝试几种类型的正则化和数据转换来优化模型和特征集,但整体感觉是使用默认参数通常也能很好地工作。

服务的简单性有一些缺点,主要是在数据转换有限以及缺乏任何嵌入的交叉验证机制,这对于数据科学工作流程至关重要。

最后,亚马逊机器学习是一个有用的回归和分类服务,将机器学习自动化更近一步。在数据科学项目中,就像在其他任何软件项目中一样,重要的是真正的拥有成本。与自建解决方案相比,亚马逊机器学习在易用性、可维护性、资源成本和通常性能方面占优势。

posted @ 2025-09-04 14:14  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报