AutoML-实用手册-全-
AutoML 实用手册(全)
零、前言
亲爱的读者,欢迎来到自动化机器学习 ( ML )的世界。自动化 ML ( AutoML )旨在自动化 ML 的零件。现成的自动工具使数据科学从业者的任务变得更容易,并在高级分析社区中受到好评。这本书涵盖了创建 AutoML 模块所需的基础,并展示了如何以最实用的方式跟上它们的速度。
您将学习在 ML 流水线中自动化不同的任务,例如数据预处理、特征选择、模型训练、模型优化等等。这本书还演示了如何使用已经可用的自动化库,如 auto-sklearn 和 MLBox,以及如何为 ML 创建和扩展您自己的自定义 AutoML 组件。
到本书结束时,您将对 AutoML 的不同方面有更清晰的了解,并且能够使用实际数据集合并自动化任务。你从这本书获得的知识可以用来在你的项目中实现 ML,或者离赢得 ML 竞赛更近一步。我们希望每一个购买这本书的人都觉得它有价值,内容丰富。
这本书是给谁的
这本书是初露头角的数据科学家、数据分析师和 ML 爱好者的理想之选,他们对 AutoML 的概念是陌生的。对为他们的项目开发快速机器学习流水线感兴趣的机器学习工程师和数据专业人员也会发现这本书很有用。
充分利用这本书
在你开始阅读之前,你唯一需要的是你的好奇心,去了解更多关于 ML 的知识。除此之外,需要先接触 Python 编程和 ML 基础知识,才能从本书中获得最佳效果,但它们不是强制性的。你应该安装了 Python 3.5 和 Jupyter 笔记本。
如果对任何一章有具体要求,在开篇都会提到。
下载示例代码文件
你可以从你在www.packtpub.com的账户下载这本书的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册将文件直接通过电子邮件发送给您。
您可以按照以下步骤下载代码文件:
- 登录或注册www.packtpub.com。
 - 选择“支持”选项卡。
 - 点击代码下载和勘误表。
 - 在搜索框中输入图书的名称,并按照屏幕指示进行操作。
 
下载文件后,请确保使用最新版本的解压缩文件夹:
- 视窗系统的 WinRAR/7-Zip
 - zipeg/izp/un ARX for MAC
 - 适用于 Linux 的 7-Zip/PeaZip
 
这本书的代码包也托管在 GitHub 上,网址为。如果代码有更新,它将在现有的 GitHub 存储库中更新。
我们还有来自丰富的图书和视频目录的其他代码包,可在【https://github.com/PacktPublishing/】获得。看看他们!
下载彩色图像
我们还提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。你可以在这里下载:https://www . packtpub . com/sites/default/files/downloads/handsonautomatemachinerlearning _ color images . pdf。
使用的约定
本书通篇使用了许多文本约定。
CodeInText:表示文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟网址、用户输入和推特句柄。这里有一个例子:“例如,让我们使用sklearn.preprocessing模块中的StandardScaler来标准化satisfaction_level列的值。”
代码块设置如下:
{'algorithm': 'auto',
 'copy_x': True,
 'init': 'k-means++',
 'max_iter': 300,
 'n_clusters': 2,
 'n_init': 10,
 'n_jobs': 1,
 'precompute_distances': 'auto',
 'random_state': None,
 'tol': 0.0001,
 'verbose': 0}
任何命令行输入或输出都编写如下:
pip install nltk
粗体:表示一个新的术语、一个重要的单词或者你在屏幕上看到的单词。例如,菜单或对话框中的单词像这样出现在文本中。这里有一个例子:“你会得到一个 NLTK 下载器弹出窗口。从标识符部分选择全部,等待安装完成。”
Warnings or important notes appear like this. Tips and tricks appear like this.
取得联系
我们随时欢迎读者的反馈。
综合反馈:发邮件feedback@packtpub.com并在邮件主题中提及书名。如果您对本书的任何方面有疑问,请发电子邮件至questions@packtpub.com。
勘误表:虽然我们已经尽了最大的努力来保证内容的准确性,但是错误还是会发生。如果你在这本书里发现了一个错误,如果你能向我们报告,我们将不胜感激。请访问www.packtpub.com/submit-errata,选择您的图书,点击勘误表提交链接,并输入详细信息。
盗版:如果您在互联网上遇到任何形式的我们作品的非法拷贝,如果您能提供我们的位置地址或网站名称,我们将不胜感激。请通过copyright@packtpub.com联系我们,并提供材料链接。
如果你有兴趣成为一名作者:如果有一个你有专长的话题,你有兴趣写或者投稿一本书,请访问authors.packtpub.com。
复习
请留下评论。一旦你阅读并使用了这本书,为什么不在你购买它的网站上留下评论呢?然后,潜在的读者可以看到并使用您不带偏见的意见来做出购买决定,我们在 Packt 可以了解您对我们产品的看法,我们的作者可以看到您对他们的书的反馈。谢谢大家!
更多关于 Packt 的信息,请访问packtpub.com。
一、AutoML 介绍
如果没有别的,过去十年是一次惊心动魄的科技冒险。第一部 iPhone 于 2007 年发布,当时它的所有竞争对手都有一个物理集成键盘。触摸屏的想法并不新鲜,因为苹果以前也有类似的原型,IBM 在 1994 年推出了西蒙个人通信器。苹果的想法是拥有一个充满多媒体娱乐的设备,比如听音乐和流式视频,同时拥有所有有用的功能,比如网络和全球定位系统导航。当然,在苹果发布第一代 iPhone 时,所有这一切都有可能实现,因为有了负担得起的计算能力。如果你真的想一想这些伟大的公司在过去 20 年中所经历的挣扎,你会发现技术发展到今天的地步是多么的迅速。客观地说,在第一代 iPhone 发布 10 年后的今天,你的 iPhone 和其他手机一样,可以追踪人脸,识别动物、车辆和食物等物体。它能听懂自然语言并和你交谈。
可以打印器官的 3D 打印机、自动驾驶汽车、成群和谐飞行的无人机、基因编辑、可重复使用的火箭,以及可以做后空翻的机器人呢?这些不再是你在科幻小说中读到的故事,而是发生在你读到这些台词的时候。你只能想象这在过去,但今天,科幻小说正在成为现实。人们已经开始谈论人工智能 ( AI )的威胁。许多领先的科学家,如斯蒂芬·霍金,警告官员人类可能会灭亡,这可能是由基于人工智能的生命形式引起的。
AI 和机器学习 ( ML )在最近几年达到了巅峰,完全是在抢风头。很有可能你已经听说了 ML 算法的成功和过去十年该领域的巨大进步。谷歌 AlphaGo 最近的成功表明,当它击败地球上最好的人类围棋手柯洁时,这项技术可以走多远。这不是 ML 算法第一次在图像识别等特定任务中击败人类。当涉及到细粒度的细节时,例如识别不同种类的动物,这些算法的表现往往比人类竞争对手更好。
这些进步引起了商界的极大兴趣。虽然这听起来像是一个学术研究领域,但这些技术具有巨大的商业意义,可以直接影响您组织的财务状况。
来自不同行业的企业希望利用这些算法的力量,并尝试适应不断变化的技术场景。每个人都意识到,那些想出如何将这些技术集成到他们业务中的人将在这个领域处于领先地位,其他人将很难赶上。
我们将在书中探讨更多这样的例子。在本书中,我们将涵盖以下主题:
- 机器学习的范围
 - 汽车是什么
 - 为什么使用 AutoML 以及它有什么帮助
 - 何时使用 AutoML
 - 自动图书馆概述
 
机器学习的范围
机器学习和预测分析现在帮助公司专注于重要的领域,在问题发生之前预测问题,降低成本,增加收入。这是在与商业智能 ( BI )解决方案合作后的自然进化。商业智能应用程序通过有组织地监控业务流程来帮助公司做出更好的决策,通常使用具有各种关键绩效指标 ( 关键绩效指标)和绩效指标的仪表板。
商业智能工具允许您更深入地挖掘组织的历史数据,揭示趋势,了解季节性,发现不规则事件,等等。它们还可以提供实时分析,您可以在其中设置一些警告和警报,以更好地管理特定事件。所有这些东西都很有用,但今天企业需要的不仅仅是这些。这是什么意思?商业智能工具允许您处理历史和接近实时的数据,但它们不能为您提供关于未来的答案,也不能回答以下问题:
- 你生产线上的哪台机器可能会出故障?
 - 您的哪些客户可能会转向您的竞争对手?
 - 哪家公司的股价明天会上涨?
 
如今,企业希望回答这类问题,这促使他们寻找合适的工具和技术,这将他们带到了 ML 和预测分析领域。
但是你需要小心!当您使用 BI 工具时,您对将要获得的结果更有信心,但是当您使用 ML 模型时,没有这样的保证,而且地面很滑。现在关于人工智能和人工智能肯定有一个巨大的嗡嗡声,人们对即将到来的人工智能产品的能力提出了令人愤慨的要求。毕竟,计算机科学家长期以来一直在寻求创造智能机器,在这一过程中偶尔会因为不真实的期望而遭受痛苦。你可以在谷歌上快速搜索一下 AI winter ,了解更多那个时期的情况。尽管这些进步超出了想象,并且该领域进展迅速,但您应该在噪音中导航,看看 ML 真正闪耀的实际用例是什么,它们可以帮助您以可衡量的方式为您的研究或业务创造价值。
为此,您需要从小型试点项目开始,其中:
- 你有一个相对容易的决策过程
 - 你很清楚你的假设
 - 你很了解你的数据
 
这里的关键是要有一个明确的项目范围和您将要执行的步骤。不同团队之间的协作在这个过程中非常有用,这就是为什么您应该打破组织内部的孤岛。还有,从小做起不代表你的眼界也要小。您应该始终考虑未来的可扩展性,并慢慢准备好利用大数据源。
有各种各样的 ML 算法可供您试验,每种算法都旨在解决特定的问题,各有利弊。在这个领域有越来越多的研究,从业者每天都在想出新的方法,推动这个领域的极限。因此,一个人可能很容易丢失所有可用的信息,尤其是在开发 ML 应用程序时,因为模型构建过程的每个阶段都有许多可用的工具和技术。为了简化 ML 模型的构建,您需要将整个过程分解成小块。Automated ML(AutoML)流水线有很多运动部件,例如特征预处理、特征选择、模型选择和超参数优化。为了交付成功的项目,需要特别小心地处理这些部分。
你会在整本书里听到很多关于 ML 的概念,但是让我们退一步来理解为什么你需要特别关注 AutoML。
当你的武器库中有更多的工具和技术来解决你的问题时,有太多的选择通常会成为一个问题本身,并且需要相当多的时间来研究和理解针对给定问题的正确方法。当你处理 ML 问题时,也是类似的情况。构建高性能的 ML 模型包含几个精心制作的小步骤。每一步都将引导您进入另一个步骤,如果您没有中途放弃,那么当您在生产环境中部署您的流水线时,您将使您的 ML 流水线正常运行并很好地推广。
流水线中涉及的步骤数量可能会很大,而且过程可能会非常漫长。在每一步,都有许多可用的方法,一旦您考虑了不同组合的可能数量,您就会很快意识到,您需要一种系统的方式在您的 ML 流水线中尝试所有这些组件。
这就引出了 AutoML 的话题!
什么是 AutoML?
AutoML 旨在通过自动化常用步骤(如特征预处理、模型选择和超参数调整)来简化 ML 模型的构建过程。在接下来的章节中,您将详细了解这些步骤,并且您将实际构建一个 AutoML 系统来更深入地了解 AutoML 可用的工具和库。
在不涉及细节的情况下,回顾什么是 ML 模型以及如何训练 ML 模型是很有用的。
ML 算法会对你的数据进行运算,找到一定的模式,这个学习过程叫做模型训练。作为模型训练的结果,你将拥有一个 ML 模型,它可以给你关于数据的见解/答案,而不需要你写明确的规则。
当你在实践中使用 ML 模型时,你会抛出一堆数值数据作为训练算法的输入。训练过程的输出是一个 ML 模型,您可以使用它进行预测。预测可以帮助您根据服务器的当前状态来决定是否应该在未来四小时内对其进行维护,或者您的客户是否会转向您的竞争对手。
有时候你正在解决的问题不会被很好地定义,你甚至不知道你在寻找什么样的答案。在这种情况下,ML 模型将帮助您探索您的数据集,例如确定一组在行为方面彼此相似的客户,或者根据他们的相关性找到股票的层次结构。
当你的模型出现客户群时,你会怎么做?嗯,你至少知道这一点:属于同一个集群的客户在特征上是相似的,比如年龄、职业、婚姻状况、性别、产品偏好、日/周/月消费习惯、消费总额等等。属于不同集群的客户互不相同。有了这样的洞察力,您可以利用这些信息为每个集群创建不同的广告活动。
从更技术性的角度来看,让我们用简单的数学术语来理解这个过程。有一个数据集 X ,其中包含 n 个例子。这些例子可以代表顾客或不同种类的动物。每个例子通常是一组实数,称为特征,例如,如果我们有一个 35 岁的女性客户在您的商店花了 12000 美元,您可以用以下向量(0.0,35.0,12000.0)表示该客户。请注意,性别用 0.0 表示,这意味着男性客户将拥有该功能的 1.0 。向量的大小代表维度。因为这是一个大小为三的向量,我们通常用 m 来表示,所以这是一个三维数据集。
根据问题类型,您可能需要为每个示例设置一个标签。例如,如果这是一个监督学习问题,如二进制分类,你可以用 1.0 或 0.0 标记你的例子,这个新变量被称为标记或目标变量。目标变量通常被称为 y 。
ML 模型有 x 和 y ,简单来说就是一个函数, f ,带权重, w (模型参数):

模型参数是在训练过程中学习的,但是在训练开始之前,您可能还需要设置其他参数,这些参数被称为超参数,稍后将对此进行解释。
数据集中的要素通常应在用于模型训练之前进行预处理。例如,一些 ML 模型隐含地假设特征是正态分布的。在许多现实场景中,情况并非如此,您可以通过应用特征转换(如日志转换)来使它们正常分布。
一旦完成特征处理并设置了模型超参数,模型训练就开始了。在模型训练结束时,模型参数将被学习,我们可以为模型以前没有见过的新数据预测目标变量。模型做出的预测通常称为
:

训练中到底发生了什么?因为我们知道用于训练的数据集的标签,所以我们可以基于当前模型预测的和原始标签的比较来迭代更新模型参数。
这个比较是基于一个叫做损失函数(或成本函数)
的函数。损失函数代表预测的不准确性。您可能听说过的一些常见损失函数有平方损失、铰链损失、逻辑损失和交叉熵损失。
一旦模型训练完成,您将在test数据上测试您的 ML 模型的性能,该数据是在训练过程中没有使用过的数据集,以查看您的模型概括得有多好。您可以使用不同的性能指标来评估性能;根据结果,您应该回到前面的步骤,进行多次调整以获得更好的性能。
在这一点上,你应该对训练一个 ML 模型在引擎盖下是什么样子有一个总体的概念。
那么什么是自动驾驶?当我们谈论 AutoML 时,我们主要指的是自动化数据准备(即特征预处理、生成和选择)和模型训练(模型选择和超参数优化)。根据问题类型的不同,此过程中每个步骤的可能选项数量可能会有很大差异。
AutoML 允许研究人员和实践者为每一步从这些可能的选项中自动构建 ML 流水线,以便为给定的问题找到高性能的 ML 模型。
下图显示了典型的 ML 模型生命周期,每一步都有几个例子:

数据可以从各种来源获取,如平面文件、数据库和 API。一旦您能够摄取数据,您应该对其进行处理,使其为 ML 做好准备,并且有典型的操作,例如清理和格式化、特征转换和特征选择。在数据处理之后,您的最终数据集应该为 ML 做好准备,您将列出候选算法。入围的算法应该通过交叉验证和超参数优化等技术进行验证和调整。您的最终模型将准备好使用合适的工作负载类型(如在线、批处理和流式部署)进行操作。一旦模型投入生产,您需要监控其性能,并在需要时采取必要的措施,如重新培训、重新评估和重新部署。
一旦你面临构建 ML 模型,你将首先对你正在研究的领域进行研究,并确定你的目标。在这个过程中涉及到许多步骤,在你真正开始工作之前,应该提前计划和记录下来。想要了解更多关于项目管理的全过程,可以参考 CRISP-DM 模型(https://en . Wikipedia . org/wiki/Cross-industry _ standard _ process _ for _ data _ mining),项目管理对于交付一个成功的应用程序至关重要,然而,这已经超出了本书的范围。
就构建 ML 流水线而言,您通常会有多个数据源,例如关系数据库或平面文件,您可以在其中获取历史数据。您还可以让流数据从各种资源流入您的系统。
您将对这些数据源进行操作,以了解哪些数据源对您的特定任务有用,然后您将进入数据处理步骤,在该步骤中,您将进行大量的清理、格式化和数据质量检查,随后是特征转换和选择。
当您决定您的数据集准备好被输入到 ML 模型中时,您将需要考虑使用一个或多个合适的 ML 模型。您将训练多个模型,评估它们,并搜索最佳超参数设置。在这一点上,版本控制将帮助您跟踪变更。作为实验的结果,您将拥有一个性能 ML 流水线,每一步都针对性能进行了优化。性能最好的 ML 流水线是您希望在生产环境中进行测试的流水线,这也是您希望在部署步骤中对其进行操作的地方。
操作 ML 流水线意味着您需要选择一种部署类型。一些工作负载将用于批量处理数据库中的数据,在这种情况下,您需要批量部署。其他可能用于处理各种数据提供者提供的实时数据,您将需要在这些地方进行流式部署。
如果你仔细检查每一个步骤,尤其是数据处理和训练步骤的选项是巨大的。首先,您需要选择合适的方法和算法,然后您还应该微调所选方法和算法的超参数,使它们对您的给定问题表现最佳。
仅举一个简单的例子,让我们假设您已经完成了模型训练步骤,您需要选择一组 ML 模型进行实验。为了让事情变得更简单,假设你唯一想尝试的算法是 k-means,它只是调整它的参数。
k-means 算法有助于将相似的数据点聚集在一起。下面的代码片段使用了 scikit-learn 库,您可以使用 pip(http://scikit-learn.org/stable/install.html)安装它,如果您不理解每一行,请不要担心:
# Sklearn has convenient modules to create sample data.
# make_blobs will help us to create a sample data set suitable for clustering
from sklearn.datasets.samples_generator import make_blobs
X, y = make_blobs(n_samples=100, centers=2, cluster_std=0.30, random_state=0)
# Let's visualize what we have first
import matplotlib.pyplot as plt
import seaborn as sns
plt.scatter(X[:, 0], X[:, 1], s=50)
前面代码片段的输出如下:

你可以很容易地看到我们在图上有两个集群:
# We will import KMeans model from clustering model family of Sklearn
from sklearn.cluster import KMeans
k_means = KMeans(n_clusters=2)
k_means.fit(X)
predictions = k_means.predict(X)
# Let's plot the predictions
plt.scatter(X[:, 0], X[:, 1], c=predictions, cmap='brg')
前面代码片段的输出如下:

很好!我们的算法如我们预期的那样工作。机敏的读者可能已经注意到,k-means 模型有一个名为n_clusters的论点。当您将此值提供给 k-means 算法时,它会尝试将此数据集分成两个聚类。正如你所猜测的,k-means 在这种情况下的超参数是簇的数量。k-means 模型在训练之前需要知道这个参数。
不同的算法有不同的超参数,如决策树的树深、隐藏层数、神经网络的学习率、Lasso 或 C 的α参数、支持向量机 ( 支持向量机)的核和γ。
让我们通过使用get_params方法来看看 k-means 模型有多少个参数:
k_means.get_params()
输出将是您可以优化的所有参数列表:
{'algorithm': 'auto',
 'copy_x': True,
 'init': 'k-means++',
 'max_iter': 300,
 'n_clusters': 2,
 'n_init': 10,
 'n_jobs': 1,
 'precompute_distances': 'auto',
 'random_state': None,
 'tol': 0.0001,
 'verbose': 0}
在大多数实际使用案例中,您既没有资源也没有时间尝试每种可能的组合以及所有考虑的步骤选项。
在这一点上,AutoML 库通过为各种 ML 流水线仔细设置实验来帮助您,这些实验涵盖了从数据摄取、数据处理、建模和评分的所有步骤。
为什么使用 AutoML,它有什么帮助?
互联网上有很多 ML 教程,通常样本数据集是干净的、格式化的,并准备好与算法一起使用,因为许多教程的目的是展示某些工具、库或软件即服务(【SaaS】)产品的功能。
实际上,数据集有不同的类型和大小。Kaggle 最近在 2017 年进行的一项名为数据科学和机器学习的现状的行业调查获得了超过 16,000 份回复,调查显示,最常用的三种数据类型是关系数据、文本数据和图像数据。
此外,同样基于卡格尔调查,杂乱的数据是人们必须处理的首要问题。当数据集很乱,需要大量特殊处理才能被 ML 模型使用时,您会花费大量时间在数据清理、操作和手工制作特征上,以使其处于正确的形状。这是任何数据科学项目中最耗时的部分。
在训练、验证和测试阶段,性能最大似然模型的选择、模型的超参数优化如何?这些也是非常重要的步骤,可以通过多种方式执行。
当合适的组件组合在一起处理和建模数据集时,该组合就代表了一个 ML 流水线,并且要实验的流水线数量可能会快速增长。
为了成功构建高性能的 ML 流水线,您应该系统地检查每一步的所有可用选项,考虑您在时间和硬件/软件资源方面的限制。
AutoML systems help you to define robust approaches to automatically constructing ML pipelines for a given problem and effectively execute them in order to find performant models.
什么时候自动化 ML?
一旦你对构建 ML 流水线有了信心,你就会意识到你必须执行许多平凡的例程来准备特性和调整超参数。您还会对某些方法更有信心,并且您会非常清楚哪些技术可以与不同的参数设置一起很好地工作。
在不同项目之间,您可以通过执行多个实验来评估您的处理和建模流水线,以迭代的方式优化整个工作流,从而获得更多经验。如果你从一开始就没有条理,管理整个过程会很快变得非常糟糕。
Necessity of AutoML arises out of these difficult situations, when you are dealing with many moving parts and a great number of parameters. These are the situations where AutoML can help you focus on the design and implementation details in a structured manner.
你会学到什么?
在这本书里,你将学习自动驾驶系统的理论和实践方面。更重要的是,你将通过从头开始开发一个 AutoML 系统来练习你的技能。
汽车系统的核心部件
在本节中,您将回顾 AutoML 系统的以下核心组件:
- 
自动特征预处理
 - 
自动算法选择
 - 
超参数优化
 
更好地理解核心组件将有助于您创建 AutoML 系统的思维导图。
自动特征预处理
当您处理 ML 问题时,您通常有一个包含各种类型数据的关系数据集,在训练 ML 算法之前,您应该正确处理每一个数据。
例如,如果您正在处理数字数据,您可以通过应用最小-最大缩放或方差缩放等方法来缩放它。
对于文本数据,您可能希望删除停止词,如 a 、a和a,并执行词干、解析和标记化等操作。
对于分类数据,您可能需要使用热编码、虚拟编码和特征散列等方法对其进行编码。
拥有非常多的功能怎么样?例如,当你有成千上万个功能时,其中有多少是真正有用的?使用主成分分析 ( 主成分分析)等方法降维会更好吗?
如果您有不同格式的数据,如视频、音频和图像,该怎么办?你如何处理它们?
例如,对于图像数据,您可以应用一些变换,例如将图像重新缩放到公共形状,并分割以分离某些区域。
There is an abundance of feature preprocessing methods, and ML algorithms will perform better with some set of transformations. Having a flexible AutoML system in your arsenal will allow you to experiment with different combinations in a smart way, which will save you much needed time and money in your projects.
自动算法选择
一旦你完成了特征处理,你需要找到一套合适的算法进行训练和评估。
每个 ML 算法都有解决某些问题的能力。让我们考虑聚类算法,如 k-means、层次聚类、谱聚类和 DBSCAN。我们熟悉 k-means,但是其他的呢?这些算法中的每一个都有应用领域,并且基于数据集的分布特性,每一个都可能比其他算法表现得更好。
AutoML 流水线可以帮助您从一组适合给定问题的算法中选择正确的算法。
超参数优化
每个 ML 算法都有一个或多个超参数,你已经熟悉了 k-means。但不仅仅是 ML 算法有超参数,特征处理方法也有超参数,这些也需要微调。
调整超参数对于模型的成功至关重要,AutoML 流水线将帮助您定义一系列您想要尝试的超参数,从而产生性能最佳的 ML 流水线。
为每个组件构建原型子系统
在整本书中,您将从头开始构建 AutoML 系统的每个核心组件,并了解每个部分是如何相互作用的。
拥有从头开始构建这样的系统的技能将会让你对这个过程以及流行的 AutoML 库的内部工作有更深的理解。
将所有这些整合为一个端到端的自动化系统
完成所有章节后,您将很好地理解组件以及它们如何协同工作来创建 ML 流水线。然后,您将使用您的知识从头开始编写 AutoML 流水线,并以任何方式调整它们,以解决您想要解决的一系列问题。
自动图书馆概述
有许多流行的 AutoML 库,在本节中,您将对数据科学社区中常用的库有一个概述。
功能工具
featuretools(https://www.featuretools.com/)是一个很好的库,用于从关系和事务数据中自动设计特征。该库引入了名为深度特征合成 ( DFS )的概念。如果您有多个数据集,其中定义了关系,例如基于用作唯一标识符的列的父子关系,例如,DFS 将基于某些计算创建新的特征,例如求和、计数、平均值、模式、标准差等。让我们看一个小例子,其中有两个表,一个显示数据库信息,另一个显示每个数据库的数据库事务:
import pandas as pd
# First dataset contains the basic information for databases.
databases_df = pd.DataFrame({"database_id": [2234, 1765, 8796, 2237, 3398], 
"creation_date": ["2018-02-01", "2017-03-02", "2017-05-03", "2013-05-12", "2012-05-09"]})
databases_df.head()
您将获得以下输出:

以下是数据库事务的代码:
# Second dataset contains the information of transaction for each database id
db_transactions_df = pd.DataFrame({"transaction_id": [26482746, 19384752, 48571125, 78546789, 19998765, 26482646, 12484752, 42471125, 75346789, 16498765, 65487547, 23453847, 56756771, 45645667, 23423498, 12335268, 76435357, 34534711, 45656746, 12312987], 
                "database_id": [2234, 1765, 2234, 2237, 1765, 8796, 2237, 8796, 3398, 2237, 3398, 2237, 2234, 8796, 1765, 2234, 2237, 1765, 8796, 2237], 
                "transaction_size": [10, 20, 30, 50, 100, 40, 60, 60, 10, 20, 60, 50, 40, 40, 30, 90, 130, 40, 50, 30],
                "transaction_date": ["2018-02-02", "2018-03-02", "2018-03-02", "2018-04-02", "2018-04-02", "2018-05-02", "2018-06-02", "2018-06-02", "2018-07-02", "2018-07-02", "2018-01-03", "2018-02-03", "2018-03-03", "2018-04-03", "2018-04-03", "2018-07-03", "2018-07-03", "2018-07-03", "2018-08-03", "2018-08-03"]})
db_transactions_df.head()
您将获得以下输出:

实体的代码如下:
# Entities for each of datasets should be defined
entities = {
"databases" : (databases_df, "database_id"),
"transactions" : (db_transactions_df, "transaction_id")
}
# Relationships between tables should also be defined as below
relationships = [("databases", "database_id", "transactions", "database_id")]
print(entities)
对于前面的代码,您会得到以下输出:

以下代码片段将创建特征矩阵和特征定义:
# There are 2 entities called ‘databases’ and ‘transactions’
# All the pieces that are necessary to engineer features are in place, you can create your feature matrix as below
import featuretools as ft
feature_matrix_db_transactions, feature_defs = ft.dfs(entities=entities,
relationships=relationships,
target_entity="databases")
以下输出显示了生成的一些功能:

Generated features using databases and transaction entities
您可以通过查看以下features_defs来查看所有特征定义:
feature_defs
输出如下:

这是您可以基于关系和事务数据集轻松生成要素的方式。
汽车硬化
Scikit-learn 有一个很好的开发 ML 模型和流水线的 API。Scikit-learn 的 API 非常一致和成熟;如果你习惯了使用它,那么a**uto-sklearn(http://automl.github.io/auto-sklearn/stable/)也同样容易使用,因为它确实是 scikit-learn 估算器的替代产品。
让我们看一个小例子:
# Necessary imports
import autosklearn.classification
import sklearn.model_selection
import sklearn.datasets
import sklearn.metrics
from sklearn.model_selection import train_test_split
# Digits dataset is one of the most popular datasets in machine learning community.
# Every example in this datasets represents a 8x8 image of a digit.
X, y = sklearn.datasets.load_digits(return_X_y=True)
# Let's see the first image. Image is reshaped to 8x8, otherwise it's a vector of size 64.
X[0].reshape(8,8)
输出如下:

您可以绘制一些图像来看看它们的外观:
import matplotlib.pyplot as plt
number_of_images = 10
images_and_labels = list(zip(X, y))
for i, (image, label) in enumerate(images_and_labels[:number_of_images]):
    plt.subplot(2, number_of_images, i + 1)
    plt.axis('off')
    plt.imshow(image.reshape(8,8), cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('%i' % label)
plt.show()
运行前面的代码片段会得到下面的图:

分割数据集以训练和测试数据:
# We split our dataset to train and test data
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)
# Similarly to creating an estimator in Scikit-learn, we create AutoSklearnClassifier
automl = autosklearn.classification.AutoSklearnClassifier()
# All you need to do is to invoke fit method to start experiment with different feature engineering methods and machine learning models
automl.fit(X_train, y_train)
# Generating predictions is same as Scikit-learn, you need to invoke predict method.
y_hat = automl.predict(X_test)
print("Accuracy score", sklearn.metrics.accuracy_score(y_test, y_hat))
# Accuracy score 0.98
那很容易,不是吗?
mvbox
MLBox(http://mlbox.readthedocs.io/en/latest/)是另一个 AutoML 库,它支持分布式数据处理、清理、格式化和最先进的算法,如 LightGBM 和 XGBoost。它还支持模型堆叠,这允许您组合模型的信息集合来生成新模型,旨在比单个模型具有更好的性能。
下面是它的用法示例:
# Necessary Imports
from mlbox.preprocessing import *
from mlbox.optimisation import *
from mlbox.prediction import *
import wget
file_link = 'https://apsportal.ibm.com/exchange-api/v1/entries/8044492073eb964f46597b4be06ff5ea/data?accessKey=9561295fa407698694b1e254d0099600'
file_name = wget.download(file_link)
print(file_name)
# GoSales_Tx_NaiveBayes.csv
GoSales数据集包含客户及其产品偏好的信息:
import pandas as pd
df = pd.read_csv('GoSales_Tx_NaiveBayes.csv')
df.head()
从前面的代码中可以得到以下输出:

让我们通过删除一个target列,从同一数据集创建一个test集:
test_df = df.drop(['PRODUCT_LINE'], axis = 1)
# First 300 records saved as test dataset
test_df[:300].to_csv('test_data.csv')
paths = ["GoSales_Tx_NaiveBayes.csv", "test_data.csv"]
target_name = "PRODUCT_LINE"
rd = Reader(sep = ',')
df = rd.train_test_split(paths, target_name)
输出将类似于以下内容:

Drift_thresholder将帮助您在train和test数据集之间删除标识和漂移变量:
dft = Drift_thresholder()
df = dft.fit_transform(df)
您将获得以下输出:

Optimiser将优化超参数:
opt = Optimiser(scoring = 'accuracy', n_folds = 3)
opt.evaluate(None, df)
通过运行前面的代码,您可以获得以下输出:

下面的代码定义了 ML 流水线的参数:
space = {  
        'ne__numerical_strategy':{"search":"choice", "space":[0]},
        'ce__strategy':{"search":"choice",
               "space":["label_encoding","random_projection", "entity_embedding"]}, 
        'fs__threshold':{"search":"uniform", "space":[0.01,0.3]}, 
        'est__max_depth':{"search":"choice", "space":[3,4,5,6,7]}
        }
best = opt.optimise(space, df,15)
下面的输出向您展示了通过给定 ML 算法正在测试的选定方法,在这个输出中是 LightGBM:

您还可以看到各种度量,如准确性、方差和 CPU 时间:

使用Predictor,可以使用最佳模型进行预测:
predictor = Predictor()
predictor.fit_predict(best, df)
您将获得以下输出:

TPOT 的
基于树的流水线优化工具 ( TPOT )正在使用遗传编程来寻找性能最好的 ML 流水线,它是建立在 scikit-learn 之上的。
一旦数据集被清理并准备好使用,TPOT 将帮助您完成 ML 流水线的以下步骤:
- 特征预处理
 - 特征构造和选择
 - 型号选择
 - 超参数优化
 
一旦 TPOT 完成了它的实验,它将为你提供最好的性能流水线。
TPOT 非常方便用户,因为它类似于使用 scikit-learn 的 API:
from tpot import TPOTClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
# Digits dataset that you have used in Auto-sklearn example
digits = load_digits()
X_train, X_test, y_train, y_test = train_test_split(digits.data, digits.target,
                                                    train_size=0.75, test_size=0.25)
# You will create your TPOT classifier with commonly used arguments
tpot = TPOTClassifier(generations=10, population_size=30, verbosity=2)
# When you invoke fit method, TPOT will create generations of populations, seeking best set of parameters. Arguments you have used to create TPOTClassifier such as generations and population_size will affect the search space and resulting pipeline.
tpot.fit(X_train, y_train)
print(tpot.score(X_test, y_test))
# 0.9834
tpot.export('my_pipeline.py')
在 Python my_pipeline.py文件中导出流水线后,您将看到选定的流水线组件:
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# NOTE: Make sure that the class is labeled 'target' in the data file
tpot_data = pd.read_csv('PATH/TO/DATA/FILE', sep='COLUMN_SEPARATOR', dtype=np.float64)
features = tpot_data.drop('target', axis=1).values
training_features, testing_features, training_target, testing_target =\
            train_test_split(features, tpot_data['target'].values, random_state=42)
exported_pipeline = KNeighborsClassifier(n_neighbors=6, 
   weights="distance")
exported_pipeline.fit(training_features, training_target)
results = exported_pipeline.predict(testing_features)
就是这里!
摘要
到目前为止,您应该对什么是自动化的 ML 以及为什么需要熟悉 ML 流水线有了一个总体的了解。
您已经回顾了 AutoML 系统的核心组件,并使用流行的 AutoML 库练习了您的技能。
这绝对不是全部,AutoML 是一个活跃的研究领域。您应该查看其他库,如 Auto-WEKA 和 Xcessive,Auto-WEKA 也使用了贝叶斯优化的最新创新,Xcessive 是创建堆叠系综的用户友好工具。
就目前而言,这已经足够神奇了!你应该开始着手你自己的杰作,在接下来的几章中,你将构建一个你自己的 AutoML 系统!
二、Python 机器学习简介
最后一章介绍了机器学习 ( ML )的世界。在本章中,我们将开发构建和使用自动化 ML ( AutoML )平台所需的 ML 基础。不总是清楚如何最好地应用 ML,或者实现它需要什么。然而,ML 工具的使用变得越来越简单,并且 AutoML 平台使它更容易被更广泛的受众访问。未来,人与机器之间无疑会有更高层次的协作。
ML 的未来可能需要人们为其消费准备数据,并确定用于实现的用例。更重要的是,人们需要解释结果并审核 ML 系统——他们是否遵循正确和最佳的方法来解决问题。未来看起来相当惊人,但我们需要建设那个未来;这就是我们在这本书里要做的。在本章中,我们将引导您完成以下主题:
- 机器学习过程及其不同类型
 - 监督学习—回归和分类
 - 无监督学习—聚类
 - 套装——装袋、助推和堆叠
 - 基于数据推断任务
 - 特定任务评估指标
 
我们明白单个章节不足以学习和练习 ML。ML 上已经有很多优秀的书籍和资料,你可以在其中找到关于每个提到的主题的详细讨论。你可以在我们的《后事》的其他你可能喜欢的书部分看到一些推荐。本章的目的是为您提供不同 ML 技术的概述,并讨论它的一些基本方面,这些方面对于后续章节的工作是必要的。
所以,机器对学习很兴奋。你准备好帮助他们了吗?抓紧了。我们先来看看什么是机器学习!
技术要求
所有代码示例都可以在 GitHub 的Chapter 02文件夹中找到。
机器学习
机器学习可以追溯到几个世纪前。它诞生于这样一种理论,即计算机可以在没有被编程来执行特定任务的情况下学习。ML 的迭代方面是必不可少的,因为机器总是需要适应新的数据。他们需要从历史数据中学习,为更好的计算进行优化,并且还需要概括自己以提供适当的结果。
我们都知道基于规则的系统,其中我们有一组预定义的条件供机器执行并提供结果。当机器自己学习这些模式,交付结果,并解释它发现的规则时,它会有多棒;这是 ML。它是一个更广泛的术语,用于描述机器从数据中学习的各种方法和算法。作为人工智能 ( 人工智能)的一个分支,最大似然算法经常被用来发现隐藏的模式,建立关系,以及预测一些事情。
机器学习依赖于一些格式化的输入,并根据任务提供结果。输入格式特定于所使用的 ML 技术的类型,也特定于所考虑的算法。输入数据的这种特定表示被称为特征或预测器。
机器学习过程
我们如何学习?当我们在学校或大学学习时,我们是由老师教的。我们从他们的教导(训练)中学到了东西。学期结束时,我们需要进行一次测试,基本上是为了验证我们的知识。我们获得的分数决定了我们的命运(评价)。通常,评估是通过考虑一个阈值(基线)来进行的。分数决定了我们是需要重考科目,还是准备进入下一个级别(部署)。
这也正是机器学习的方式。括号中的单词是 ML 专业人员使用的术语。然而,这只是我们和机器学习的方式之一。这是一种典型的监督学习方法。人们有时也从经验中学习,这是无监督的学习。让我们研究一下这些学习方法的更多细节。
Broadly we have two categories of ML algorithms as described earlier—supervised and unsupervised learning. There are a few other types, such as reinforcement learning, transfer learning, and semi-supervised learning, which are less often used and so are not in the scope of this book.
监督学习
顾名思义,学习过程是基于特定的目标/结果进行监督的。
The objective of supervised ML models is to learn and discover the patterns that can correctly predict the outcome. In case of supervised learning, there is always a labeled historical dataset with a target attribute. All attributes other than the target are termed as predictors/features.
目标可以是连续的数字属性、指示是/否决策的二进制属性或具有两个以上结果的多类属性。基于目标,模型识别模式,建立预测器之间的关系,然后使用导出的配方来预测新的独立数据集中的未知目标。
许多 ML 算法都属于这类学习方法,例如线性和物流回归、决策树、随机森林和支持向量机 ( 支持向量机)等等。
To identify and select the most suited algorithm for a job is the most critical task in an ML project. This is also an essential section that requires significant attention while creating an AutoML system. There are various factors that govern this selection process and they will be covered at length in this book.
无监督学习
同样,在无监督学习的情况下,没有目标属性。无监督学习的目标是通过推断输入数据集中特征的结构和关系来识别模式。它可以用来发现共同定义一个组的规则,如主题生成、划分——如客户细分或确定数据的内部结构,如基因聚类。无监督学习算法的例子包括关联规则挖掘和聚类算法。
在创建自动学习系统之前,了解不同的学习算法是非常必要的。在使用算法之前,了解它的三重W——它是什么、它在哪里以及通过什么方法可以实现它是至关重要的。
在接下来的部分中,我们将对不同的算法的三重 W 提出质疑,这将有助于创建一个健壮的 AutoML 系统。
线性回归
让我们先从线性回归开始我们的三重 W 会话。
什么是线性回归?
这是传统和最常用的回归分析。它被严格研究并广泛用于实际目的。线性回归是一种确定因变量( y )和一个或多个自变量( x )之间关系的方法。这个导出的关系可以用来根据观察到的 x 预测一个无法解释的 y 从数学上来说,如果 x 是一个自变量(通常称为预测值) y 是一个因变量(也称为目标值),这个关系表示如下:

其中 m 为直线斜率, b 为最佳拟合回归线截距,ε为实际值与预测值偏差的误差项。
这是简单线性回归的方程,因为它只涉及一个预测因子( x )和一个目标( y )。当预测一个目标涉及多个预测因子时,称为多元线性回归。术语线性表明有一个基本假设,即基础数据呈现线性关系。
让我们在两个变量之间创建一个散点图:产品的销售数量和收入。从图中我们可以推断,这两个变量之间存在某种正相关关系,即当产品销量激增时,收入就上升了。然而,我们无法在它们之间建立关系来预测销售数量的收入:

如果我们扩展之前的散点图,并在其中添加趋势线,我们会看到最佳拟合线。位于这条线上的任何数据点都是完美的预测值。当我们离开这条线时,预测的可靠性会降低:

那么,我们如何找到最合适的线路呢?最常见和最广泛使用的技术是普通最小二乘 ( OLS )估计。
OLS 回归工作
OLS LinearRegression方法是将函数拟合到数据的最直接的方法。它通过最小化数据的平方误差 ( SSE )的总和来找到最佳拟合线。上证综指是实际值与平均值的偏差之和。然而,一如既往,简单是有代价的。一个优秀的 OLS 方法的代价是坚持它的几个基本假设。
OLS 的假设
为了获得 OLS 回归技术的好处,所有这些关于数据的假设都应该成立:
- 线性:真正的 X 和 Y 之间的潜在关系是线性的。
 - 同态:残差的方差必须是常数。残差是目标的观察值和预测值之间的差值。
 - 正态性:残差/误差应为正态分布。
 - 没有或很少多重共线性:残差/误差必须是独立的。
 
OLS 也受到数据中异常值的影响。在使用 OLS 线性回归进行线性回归建模之前,异常值处理是必要的。
线性回归在哪里使用?
线性回归有许多实际的使用案例,其中大多数属于以下两大类之一:
- 如果目标是预测或预测,则可以使用它来构建一个预测模型,该模型是由相关值和独立值组成的公认数据集
 - 如果目标是确定目标变量和预测变量之间关系的强度,则可以将其应用于量化给定值 X 的 Y 的变化
 
用什么方法可以实现线性回归?
我们可以使用 scikit-learn 的LinearRegression方法在 Python 中创建一个线性回归模型。由于这是我们讨论使用 Python 实现模型的第一个例子,我们将绕过算法的讨论,学习一些在 Python 中创建模型所需的基本包:
- 
numpy:是用于数学函数的数值 Python 模块。它为多维数组和矩阵的有效计算提供了健壮的数据结构。 - 
pandas:为数据操作提供 DataFrame 对象。数据框可以保存不同类型的值和数组。它用于在 Python 中读取、写入和操作数据。 - 
scikit-learn:是 Python 中的 ML 库。它包括各种 ML 算法,是一个广泛使用的库,用于在 Python 中创建 ML 模型。除了 ML 算法之外,它还提供了开发模型所需的各种其他功能,如train_test_split、模型评估指标和优化指标。 
在创建模型之前,我们需要首先将这些必需的库导入 Python 环境。如果是在 Jupyter 笔记本上运行代码,需要声明%matplotlib inline在界面内内联查看图形。我们需要导入numpy和pandas包,以便于数据操作和数值计算。本练习的计划是创建一个线性回归模型,因此我们还需要从 scikit-learn 包中导入LinearRegression方法。我们将使用 scikit-learn 的示例Boston数据集来完成任务:
%matplotlib inline
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
接下来,我们需要使用以下命令加载Boston数据集。它是一本字典,我们可以检查它的键来查看它的内容:
boston_data = load_boston()
boston_data.keys()
前面代码的输出如下:

boston_data有四个键,它们所指向的值的种类是不言自明的。我们可以从键data和target中检索数据和目标值。feature_names键保存属性的名称,DESCR有每个属性的描述。
在处理数据之前,最好先看看数据大小。这有助于决定是使用完整的数据还是使用它的样本,也有助于推断执行可能需要多长时间。
Python 中的data.shape函数是查看数据维度(行和列)的绝佳方式:
print(" Number of rows and columns in the data set ", boston_data.data.shape)
print(boston_data.feature_names)
前面代码的输出如下:

接下来,我们需要将字典转换为数据帧。这可以通过调用pandas库的DataFrame函数来实现。我们使用head()显示记录的子集来验证数据:
boston_df =pd.DataFrame(boston_data.data)
boston_df.head()
A DataFrame is a collection of vectors and can be treated as a two-dimensional table. We can consider DataFrame as having each row correspond to some observation and each column to some attribute of the observation. This makes them extremely useful for fitting to a ML modeling task.
前面代码的输出如下:

列名只是数字索引,并没有给出数据框的含义。因此,让我们将feature_names作为列名分配给boston_df数据帧,以获得有意义的名称:
boston_df.columns = boston_data.feature_names
我们再次查看boston房屋租金数据的样本,现在它对栏目的描述比以前更好了:
boston_df.head()
前面代码的输出如下:

在线性回归中,必须有一个数据帧作为目标变量,另一个数据帧具有其他特征作为预测因子。本练习的目的是预测房价,因此我们将PRICE指定为目标属性(Y),其余的都指定为预测因子(X)。使用drop功能将PRICE从预测值列表中删除。
接下来,我们打印每个变量的截距和系数。这些系数决定了每个预测者对预测房价的权重和贡献(目标Y)。截距提供了一个恒定值,当所有的预测因子都不存在时,我们可以认为它是房价:
boston_df['PRICE'] = boston_data.target
X = boston_df.drop('PRICE', axis=1)
lm = LinearRegression()
lm.fit(X, boston_df.PRICE)
print("Intercept: ", lm.intercept_)
print("Coefficient: ", lm.coef_)
前面代码的输出如下:

从前面的截图中不清楚哪个系数属于什么预测因子。因此,我们使用以下代码将特征和系数联系在一起:
pd.DataFrame(list(zip(X.columns, lm.coef_)),columns= ['features','estimatedCoefficients'])
前面代码的输出如下:

接下来,我们计算并查看均方误差度量。现在,让我们把它看作是模型在预测房价时的平均误差。评估指标对于理解模型的动态性及其在生产环境中的表现非常重要:
lm.predict(X)[0:5
mseFull = np.mean((boston_df.PRICE - lm.predict(X)) ** 2)
print(mseFull)
前面代码的输出如下:

我们在整个数据集上创建了模型,但是当在实际生产环境中使用时,确保我们开发的模型在不同的数据集上正常工作是非常重要的。因此,用于建模的数据分为两组,通常比例为 70:30。最重要的分割用于训练模型,另一个用于测试开发的模型。这个独立的测试数据集被认为是一个虚拟生产环境,因为它在模型的训练阶段是隐藏的。测试数据集用于生成预测和评估模型的准确性。Scikit-learn 提供了一种train_test_split方法,可用于将数据集分成两部分。该功能中的test_size参数表示待测试数据的百分比。在下面的代码中,我们将数据集分成train和test集,并重新训练模型:
#Train and Test set
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, boston_df.PRICE, test_size=0.3, random_state=42)
print(X_train)
由于我们使用了test_size=0.3,70%的数据集将用于创建train集,30%将保留给test数据集。我们遵循与前面相同的步骤来创建线性回归模型,但是现在我们将只使用训练数据集(X_train和Y_train)来创建模型:
lm_tts = LinearRegression()
lm_tts.fit(X_train, Y_train)
print("Intercept: ", lm_tts.intercept_)
print("Coefficient: ", lm_tts.coef_)
前面代码的输出如下:

我们预测train和test数据集的目标值,并计算它们的均方误差 ( 均方误差):
pred_train = lm.predict(X_train)
pred_test = lm.predict(X_test)
print("MSE for Y_train:", np.mean((Y_train - lm.predict(X_train)) ** 2))
print("MSE with Y_test:", np.mean((Y_test - lm.predict(X_test)) ** 2))
前面代码的输出如下:

我们看到train和test数据集的均方误差分别是22.86和19.65。这意味着该模型的性能在训练和测试阶段几乎是相似的,并且可以用于在新的独立的相同数据集上预测房价。
接下来,让我们绘制一个残差图,看看残差是否遵循线性模式:
plt.scatter(pred_train,pred_train - Y_train, c = 'b',s=40,alpha=0.5)
plt.scatter(pred_test,pred_test - Y_test, c = 'r',s=40,alpha=0.7)
plt.hlines(y = 0, xmin=0, xmax = 50)
plt.title('Residual Plot - training data (blue) and test data(green)')
plt.ylabel('Residuals')
前面代码的输出如下:

由于残差围绕水平虚线对称分布,因此它们呈现出完美的线性模式。
开发一个模型很容易,但是设计一个有用的模型很难。评估 ML 模型的性能是 ML 流水线中至关重要的一步。一旦一个模型准备好了,我们就必须对它进行评估,以确定它的正确性。在下一节中,我们将向您介绍用于评估回归模型的一些广泛使用的评估指标。
重要评估指标–回归算法
评估 ML 模型的价值是一个两阶段的过程。首先,必须评估模型的统计准确性,即统计假设是否正确,模型性能是否突出,以及其他独立数据集的性能是否成立。这是使用几个模型评估指标来完成的。然后,评估一个模型,看看结果是否如业务需求所期望的那样,涉众是否真正从中获得了一些见解或有用的预测。
回归模型基于以下指标进行评估:
- 平均绝对误差 ( MAE ):是预测误差绝对值之和。预测误差定义为预测值和实际值之间的差值。这个标准给出了误差大小的概念。然而,我们无法判断该模型是预测过度还是预测不足。一个人应该始终瞄准一个低的 MAE 分数:
 

其中, y i =实际值
 =预测值
n =病例数(记录)
- 均方误差:均方误差之和的平均值。这个度量描述了误差的大小和方向。但是,测量单位会随着值的平方而改变。另一个度量标准弥补了这一不足:均方根误差。分数越低,模型越好:
 

- 均方根误差 ( RMSE ):该指标由均方误差的平方根计算得出。取平方根会将测量单位转换回原始单位。RMSE 分数低的模型是好模型:
 

- R 2 评分:又称决定系数。它描述了模型解释的方差百分比。例如 R 2 为 0.9,则模型中使用的属性或特征可以代表其 90%的变异。 R 2 从 0 到 1 不等,这个值越高,模型越好。然而,需要有一个好的测试策略来验证模型不会过度:
 

其中, y i =实际值
 =预测值
n =病例数(记录)
= y 的平均值
Overfitting occurs when a machine learning model learns the training data very well. These models have low bias and high variance in their results. In such cases, the model might lead to poor predictions on new data.
在这一节中,我们学习了回归分析作为监督最大似然方法之一。它可以用于目标数据是连续数字数据的情况,例如预测员工的期望工资、预测房价或预测支出值。
如果目标有离散数据怎么办?我们如何预测客户是否会流失?我们如何预测是否应该为潜在客户批准贷款/信用卡?线性回归不适用于这些情况,因为这些问题违反了其基本假设。我们还有其他方法吗?对于这些情况,我们可以使用分类模型。
Classification modeling is another form of supervised machine learning methods that is used to predict targets with discrete input target values. The classification algorithms are known as classifiers, as they identify the set of categories that input data support and use this information to assign a class to an unidentified or unknown target label.
在接下来的部分中,我们将介绍一些广泛使用的分类器,如物流回归、决策树、支持向量机和 k 近邻。物流回归可以被认为是回归和分类方法之间的桥梁。这是一个伪装成回归特征的分类器。然而,它是最有效和最容易解释的分类模型之一。
逻辑回归
让我们从物流回归的三重 W 开始。重申一下 tripe W 方法,我们先问算法是什么,然后问在哪里可以用,最后问用什么方法可以实现模型。
什么是逻辑回归?
逻辑回归可以被认为是线性回归算法的扩展。它基本上像线性回归一样工作,但它意味着离散或分类的结果。
逻辑回归在哪里使用?
逻辑回归适用于离散目标变量的情况,如二进制响应。在这种情况下,线性回归的一些假设,如目标属性和特征,不遵循线性关系,残差可能不是正态分布,或者误差项是异方差的。在逻辑回归中,目标被重建为其优势比的对数,以拟合回归方程,如下所示:

赔率反映了特定事件发生的概率或可能性与同一事件不发生的概率之比。如果 P 是一个事件/类别出现的概率,P–1是第二个事件/类别出现的概率。
用什么方法可以实现逻辑回归?
可以通过导入 scikit-learn 的LogisticRegression方法来创建逻辑回归模型。我们像前面创建线性回归模型一样加载包:
import pandas as pd
import numpy as np
from sklearn import preprocessing
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
我们将使用一个HR部门的数据集,该数据集包含过去流失的员工以及继续工作的员工列表:
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
前面代码的输出如下:

数据集有14999行和10列。data.columns功能显示属性的名称。salary属性有三个值— high、low、medium、sales有七个值— IT、RandD、marketing、product_mng、sales、support和technical。为了在模型中使用这些离散的输入数据,我们需要将其转换为数字格式。有多种方法可以做到这一点。其中一种方法是对值进行伪编码,也称为一热编码。使用这种方法,为分类属性的每个类生成虚拟列。
对于每个伪属性,类的存在由 1 表示,它的不存在由 0 表示。
Discrete data can either be nominal or ordinal. When there is a natural ordering of values in the discrete data, it is termed as ordinal. For example, categorical values such as high, medium, and low are ordered values. For these cases, label encoding is mostly used. When we cannot derive any relationship or order from the categorical or discrete values, it is termed as nominal. For example, colors such as red, yellow, and green have no order. For these cases, dummy encoding is a popular method.
pandas的get_dummies方法提供了一个在 Python 中创建虚拟变量的简单界面。函数的输入是数据集和要虚拟编码的属性的名称。在这种情况下,我们将对HR数据集的salary和sales属性进行虚拟编码:
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
前面代码的输出如下:

现在,数据集已经可以建模了。sales和salary属性成功一键编码。接下来,当我们要预测流失时,我们将使用left属性作为目标,因为它包含员工是否流失的信息。我们可以从代码中称为X的输入预测数据集中删除left数据。左侧属性用Y(目标)表示:
X = data_trnsf.drop('left', axis=1)
X.columns
前面代码的输出如下:

我们将数据集拆分为train和test集,比例为 70:30。70%的数据将用于训练逻辑回归模型,其余 30%用于评估模型的准确性:
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
print(X_train)
当我们执行代码片段时,创建了四个数据集。X_train和X_test是train和test输入预测值数据。Y_train和Y_test是train和test输入的目标数据。现在,我们将在列车数据上拟合模型,并在测试数据上评估模型的准确性。首先,我们创建一个LogisticsRegression()分类器类的实例。接下来,我们在训练数据上拟合分类器:
attrition_classifier = LogisticRegression()
attrition_classifier.fit(X_train, Y_train)
一旦模型成功创建,我们使用测试输入预测数据集上的predict方法来预测相应的目标值(Y_pred):
Y_pred = attrition_classifier.predict(X_test)
我们需要创建一个confusion_matrix来评估一个分类器。大多数模型评估指标都基于混淆矩阵本身。在这一节之后,将详细讨论混淆矩阵和其他评估指标。现在,让我们将混淆矩阵视为由四个值组成的矩阵,它为我们提供了正确和错误预测的值的计数。基于混淆矩阵中的值,计算分类器的精度。我们的分类器的准确率为 0.79 或 79%,这意味着 79%的案例被正确预测:
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(Y_test, Y_pred)
print(confusion_matrix)
print('Accuracy of logistic regression model on test dataset: {:.2f}'.format(attrition_classifier.score(X_test, Y_test)))
前面代码的输出如下:

有时,准确性可能不是判断模型性能的好方法。例如,在不平衡数据集的情况下,预测可能偏向多数类。因此,我们需要查看其他指标,如 f1-score、曲线下面积 ( AUC )、精度和召回率,这些指标给出了关于模型的公正判断。我们可以通过从 scikit-learn 的metric方法中导入classification_report来检索所有这些指标的分数:
from sklearn.metrics import classification_report
print(classification_report(Y_test, Y_pred))
前面代码的输出如下:

接收器操作特性 ( ROC )最常用于可视化二进制分类器的性能。AUC 度量是 ROC 曲线下的面积,它提供了一个单一的数字来总结基于 ROC 曲线的分类器的性能。以下代码片段可用于使用 Python 绘制 ROC 曲线:
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
# Compute false positive rate(fpr), true positive rate(tpr), thresholds and roc auc(Area under Curve)
fpr, tpr, thresholds = roc_curve(Y_test, Y_pred)
auc = auc(fpr,tpr)
# Plot ROC curve
plt.plot(fpr, tpr, label='AUC = %0.2f' % auc)
#random prediction curve
plt.plot([0, 1], [0, 1], 'k--') 
#Set the x limits
plt.xlim([0.0, 1.0])
#Set the Y limits
plt.ylim([0.0, 1.0])
#Set the X label
plt.xlabel('False Positive Rate(FPR) ')
#Set the Y label
plt.ylabel('True Positive Rate(TPR)')
#Set the plot title
plt.title('Receiver Operating Characteristic(ROC) Cure')
# Location of the AUC legend
plt.legend(loc="right")
前面代码的输出如下:

我们车型的 AUC 为 0.63 。我们已经看到了一些用于评估分类模型的指标,其中一些看起来很奇怪。因此,在继续讨论分类算法之前,让我们先了解一下这些指标。
重要评估指标–分类算法
用于评估分类模型的大多数指标都是基于我们在混淆矩阵的四个象限中获得的值。让我们从了解它是什么开始这一部分:
- 混淆矩阵:是评价一个分类模型(即分类器)的基石。顾名思义,矩阵有时令人困惑。让我们尝试将混淆矩阵可视化为图形中的两个轴。 x 轴标签为预测,有两个值— 正值和负值。同样的, y 轴标签实际上是相同的两个值——正和负,如下图所示。该矩阵是一个包含分类器实际值和预测值计数信息的表格:
 

- 如果我们试图推断矩阵中每个象限的信息:
- 象限一是被准确识别的正类预测的数量。所以称之为真阳性 ( TP )。
 - 象限二,也称为假阳性 ( FP )是对实际阳性病例的不准确预测数。
 - 象限三,即假阴性 ( FN )是阴性病例的不准确预测数。
 - 象限四为真负 ( TN ),是准确分类的负类预测数。
 
 - 准确度:准确度衡量分类器做出准确预测的频率。它是正确预测数与预测总数的比率:
 

- 精度:精度估计被准确识别的真实阳性的比例。它是真实阳性与所有预测阳性的比率:
 

- 召回:召回也称为灵敏度或真阳性率 ( TPR )。它估计目标所有观察到的正值中的真阳性比例:
 

- 误分类率:估计分类器预测不准确的频率。它是错误预测与所有预测的比率:
 

- 特异性:特异性又称真阴性率 ( TNR )。它估计目标所有观察到的负值中真实负值的比例:
 

- ROC 曲线:ROC 曲线总结了分类器在所有可能阈值上的性能。对于所有可能的阈值,ROC 曲线的图形在 y 轴上用真阳性率 ( TPR )绘制,在 x 轴上用假阳性率 ( FPR )绘制。
 - AUC : AUC 是 ROC 曲线下的面积。如果分类器突出,真阳性率会增加,曲线下面积接近 1。如果分类器类似于随机猜测,则真阳性率将随着假阳性率(1–灵敏度)线性增加。在这种情况下,AUC 将在 0.5 左右。AUC 度量越好,模型越好。
 - 提升:提升有助于估计模型预测能力相对于平均或基线模型的提高。例如,人力资源流失数据集的基线模型精度为 40%,但同一数据集上新模型的精度为 80%。然后,该模型的升力为 2 (80/40)。
 - 平衡精度:有时候,精度本身并不是评价一个模型的好尺度。对于数据集不平衡的情况,它可能不是一个有用的评估指标。在这种情况下,平衡精度可以用作评估指标之一。平衡精度是根据在以下任一类别中获得的平均精度计算的度量:
 

Unbalanced dataset—Where one class dominates the other class. In such cases, there is an inherent bias in prediction towards the major class. However, this is a problem with base learners such as decision trees and logistic regression. For ensemble models such as random forest it can handle unbalanced classes well.
- F1 分数:F1 分数也是估计不平衡分类器的一个合理的度量。F1 分数是精确度和召回率的调和平均值。它的值介于 0 和 1 之间:
 

- 海明损失:这标识了标签被错误预测的分数。
 - 马修斯相关系数 ( MCC ): MCC 是目标和预测之间的相关系数。它在-1 和+1 之间变化。-1 当实际值和预测值完全不一致时,1 当实际值和预测值完全一致时,0 当关于实际值的预测值可能是随机的时。因为它涉及混淆矩阵的所有四个象限的值,所以它被认为是一个平衡的度量。
 
有时候,创建一个预测模型不仅仅是一个要求。我们需要了解模型是如何构建的,以及描述模型的关键特性。在这种情况下,决策树可以用来建模。
决策树
决策树是 ML 世界中广泛使用的分类器,因为它们在表示驱动分类/预测的规则时是透明的。让我们向这个算法提出三重问题,以了解更多信息。
什么是决策树?
决策树以层次树状结构排列,易于解释和解释。他们对异常值不敏感。创建决策树的过程是一种递归划分方法,它将训练数据分成不同的组,目标是找到同质的纯子组,即只有一个类的数据。
Outliers are values that lie far away from other data points and distort the data distribution.
决策树用在哪里?
决策树非常适合需要解释特定决策原因的情况。例如,金融机构可能需要在发放贷款或信用卡之前对影响客户信用评分的规则进行完整的描述。
决策树可以通过哪种方法实现?
决策树模型可以通过导入 scikit-learn 的DecisionTreeClassifier来创建:
import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn import tree
接下来,我们读取HR损耗数据集,并执行前面物流回归示例中完成的所有数据预处理:
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(" Data Set Shape ", hr_data.shape)
print(list(hr_data.columns))
print(" Sample Data ", hr_data.head())
前面代码的输出如下:

以下代码为分类数据创建虚拟变量,并将数据拆分为train和test集:
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
X = data_trnsf.drop('left', axis=1)
X.columns
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
print(X_train)
接下来,为了创建一个决策树分类器,我们需要实例化一个DecisionTreeClassifier至少有所需的参数。以下是用于生成决策树模型的一些参数:
criterion:形成决策树的杂质度量;可以是entropy也可以是ginimax_depth:树的最大深度min_samples_leaf:构建叶节点所需的最小样本数max_depth和min_sample_leafs是树木预修剪的两个标准
让我们使用以下参数创建一个决策树模型:
attrition_tree = DecisionTreeClassifier(criterion = "gini", random_state = 100,
max_depth=3, min_samples_leaf=5)
attrition_tree.fit(X_train, Y_train)
前面代码的输出如下:

接下来,我们生成一个混淆矩阵来评估模型:
Y_pred = attrition_tree.predict(X_test)
from sklearn.metrics import confusion_matrix
confusionmatrix = confusion_matrix(Y_test, Y_pred)
print(confusionmatrix)
前面代码的输出如下:

如果我们查看混淆矩阵,我们可以假设分类器在对真阳性和真阴性进行分类方面做了可靠的工作。但是,让我们根据总结的评估指标来验证我们的假设:
print('Accuracy of Decision Tree classifier on test set: {:.2f}'.format(attrition_tree.score(X_test, Y_test)))
from sklearn.metrics import classification_report
 print(classification_report(Y_test, Y_pred))
前面代码的输出如下:

准确度和其他指标都是0.95,这是一个相当不错的分数。
基于树的模型比逻辑回归模型具有更好的结果。现在,让我们了解另一种流行的基于支持向量的分类建模技术。
支持向量机
SVM 是一个监督的最大似然算法,主要用于分类任务,然而,它也可以用于回归问题。
什么是 SVM?
SVM 是一个基于分离超平面原理的分类器。给定一个训练数据集,算法会找到一个最大化类分离的超平面,并将这些分区用于新数据集的预测。超平面是比其环境平面小一维的子空间。这意味着线是二维数据集的超平面。
SVM 用在哪里?
SVM 拥有与其他分类器相似的用例,但是 SVM 非常适合特征/属性的数量比数据点/记录的数量多的情况。
用什么方法可以实现 SVM?
创建 SVM 模型的过程类似于我们之前研究的其他分类方法。唯一不同的是从 scikit-learn 的库中导入svm方法。我们使用pandas库导入HR损耗数据集,并将数据集拆分为train和test集:
import numpy as np
import pandas as pd
from sklearn import svm
from sklearn.metrics import accuracy_score
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(" Data Set Shape ", hr_data.shape)
print(list(hr_data.columns))
print(" Sample Data ", hr_data.head())
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
X = data_trnsf.drop('left', axis=1)
X.columns
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
print(X_train)
接下来,我们创建一个 SVM 模型实例。我们将内核设置为线性的,因为我们希望用一条线来分隔两个类。为线性可分数据寻找最优超平面很容易。然而,当数据不可线性分离时,数据被映射到一个新的空间以使其可线性分离。这种方法被称为核心技巧:
attrition_svm = svm.SVC(kernel='linear') 
attrition_svm.fit(X_train, Y_train)
前面代码的输出如下:

将 SVM 模型实例拟合到列车数据后,我们预测test集合的Y值,并创建一个混淆矩阵来评估模型性能:
Y_pred = attrition_svm.predict(X_test)
from sklearn.metrics import confusion_matrix
confusionmatrix = confusion_matrix(Y_test, Y_pred)
print(confusionmatrix)
前面代码的输出如下:

然后,计算模型精度和其他指标的值:
print('Accuracy of SVM classifier on test set: {:.2f}'.format(attrition_svm.score(X_test, Y_test)))
from sklearn.metrics import classification_report
print(classification_report(Y_test, Y_pred))
前面代码的输出如下:

我们看到,带有默认参数的 SVM 模型的表现并不比决策树模型好。因此,到目前为止,决策树在HR流失预测排行榜中占据了榜首的位置。让我们尝试另一种分类算法,k-近邻 ( KNN ),它更容易理解和使用,但资源密集得多。
k-最近邻
在我们为HR损耗数据集构建 KNN 模型之前,让我们了解一下 KNN 的三重 w
什么是 k 近邻?
KNN 算法是最直接的算法之一,它存储所有可用的数据点,并基于距离相似性度量(如欧氏距离)预测新数据。它是一种可以直接使用训练数据集进行预测的算法。然而,它需要更多的资源,因为它没有任何训练阶段,并且需要内存中的所有数据来预测新的实例。
Euclidean distance is calculated as the square root of the sum of the squared differences between two points. 
KNN 用在哪里?
KNN 可以用于建立分类和回归模型。它适用于二元和多元分类任务。KNN 甚至可以用于创建推荐系统或输入缺失值。它易于使用,易于训练,易于解释结果。
用什么方法可以实现 KNN?
同样,我们对 KNN 采用了类似的流程,就像我们创建之前的模型一样。我们从 scikit-learn 的库中导入KNeighborsClassifier方法,使用 KNN 算法进行建模。接下来,我们使用pandas库导入HR损耗数据集,并将数据集拆分为train和test集:
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(" Data Set Shape ", hr_data.shape)
print(list(hr_data.columns))
print(" Sample Data ", hr_data.head())
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
X = data_trnsf.drop('left', axis=1)
X.columns
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
print(X_train)
为了创建一个 KNN 模型,我们需要指定距离计算要考虑的最近邻居的数量。
In real life, when we create models, we create different models for a range of n_neighbors values with various distance measures and choose the model that returns the highest accuracy. This process is also known as tuning the hyperparameters.
对于下面的HR损耗模型,我们将n_neighbors定义为6,距离度量为欧几里得:
n_neighbors = 6
attrition_knn = KNeighborsClassifier(n_neighbors=n_neighbors, metric='euclidean')
attrition_knn.fit(X_train, Y_train)
前面代码的输出如下:

然后对test数据集进行预测,我们回顾混淆矩阵和其他评估指标:
Y_pred = attrition_knn.predict(X_test)
from sklearn.metrics import confusion_matrix
confusionmatrix = confusion_matrix(Y_test, Y_pred)
print(confusionmatrix)
前面代码的输出如下:

以下代码报告了其他指标的准确性分数和值:
print('Accuracy of KNN classifier on test set: {:.2f}'.format(attrition_knn.score(X_test, Y_test)))
from sklearn.metrics import classification_report
print(classification_report(Y_test, Y_pred))
前面代码的输出如下:

KNN 模型的结果比 SVM 模型好,但是,它仍然低于决策树的得分。KNN 是一个资源密集型算法。明智的做法是使用某种不同算法的模型,如果使用 KNN 只是略有改进的话。然而,用户可以根据他们的环境和他们试图解决的问题来决定什么是最好的。
集成方法
集合模型是一种增强预测模型效率的稳健方法。这是一个经过深思熟虑的策略,非常类似于一个充满力量的词——团队!!团队完成的任何任务都会带来重大成就。
什么是合奏模型?
同样,在 ML 世界中,一个合奏模型是一个由模型组成的团队,他们一起工作来提高他们工作的结果。从技术上讲,集成模型由几个单独训练的监督学习模型组成,结果以各种方式合并,以实现最终的预测。该结果比其独立构成的任何学习算法的结果具有更高的预测能力。
通常,使用的集成学习方法有三种:
- 制袋材料
 - 助推
 - 堆叠/混合
 
制袋材料
打包也称为引导聚合。这是减小模型结果方差误差的一种方法。有时弱学习算法非常敏感——稍微不同的输入会导致非常不寻常的输出。随机森林通过运行多个实例减少了这种可变性,从而降低了方差。在这种方法中,使用带有替换模型的随机样本从训练数据集准备随机样本(自举过程)。
使用监督学习方法在每个样本上开发模型。最后,通过平均预测或利用多数投票技术选择最佳预测来合并结果。多数投票是一个过程,其中集成的预测是所有分类器中预测数量最高的类。还有各种其他方法,如加权和秩平均,用于产生最终结果。
scikit-learn 中提供了各种打包算法,如打包决策树、随机森林和额外树。我们将演示最流行的随机森林模型,您可以尝试其他模型。我们可以通过从 scikit-learn 的ensemble包导入RandomForestClassifier来实现随机森林。由于我们仍在处理HR流失数据,本演示的部分代码段也保持不变:
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn import tree
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(" Data Set Shape ", hr_data.shape)
print(list(hr_data.columns))
print(" Sample Data ", hr_data.head())
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
X = data_trnsf.drop('left', axis=1)
X.columns
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
print(X_train)
没有强制参数来实例化随机林模型。但是,对于创建良好的随机森林模型,有几个重要的参数需要了解,如下所述:
n_estimators:我们可以指定模型中要创建的树的数量。默认值为 10。max_features:指定每次分割时随机选择作为候选的变量/特征的数量。默认为
。
我们创建了一个随机森林模型,使用n_estimators作为100,使用max_features作为3,如下面的代码片段所示:
num_trees = 100
max_features = 3
attrition_forest = RandomForestClassifier(n_estimators=num_trees, max_features=max_features)
attrition_forest.fit(X_train, Y_train)
前面代码的输出如下:

一旦模型拟合成功,我们根据test预测Y_pred或保持数据集:
Y_pred = attrition_forest.predict(X_test)
from sklearn.metrics import confusion_matrix
confusionmatrix = confusion_matrix(Y_test, Y_pred)
print(confusionmatrix)
混淆矩阵中的结果看起来非常好,错误分类更少,预测更准确。让我们看看评估指标是如何得出的:

接下来,我们检查分类报告中Random Forest classifier和print的准确性:
print('Accuracy of Random Forest classifier on test set: {:.2f}'.format(attrition_forest.score(X_test, Y_test)))
from sklearn.metrics import classification_report
print(classification_report(Y_test, Y_pred))
前面代码的输出如下:

这是一个优秀的模型,所有的评估指标都接近完美的预测。这太令人难以置信了,可能是过度拟合的一个例子。然而,让我们认为随机森林是目前我们的HR损耗数据集上的最佳算法,然后继续另一种广泛使用的集成建模技术——boosting。
助推
Boosting 是一个迭代的过程,在这个过程中,基于前人的缺陷一个接一个地建立连续的模型。这有助于减少模型中的偏差,也导致方差的减少。Boosting 试图生成新的分类器,以更好地预测先前模型性能较低的值。与打包不同,训练数据的重采样取决于早期分类器的性能。Boosting 使用所有数据来训练单个分类器,但是被前一个分类器错误分类的实例被赋予了更多的重要性,以便后续的分类器增强结果。
梯度增压机 ( GBMs )也称为随机梯度增压 ( SGB )就是增压方法的一个例子。我们再次导入所需的包并加载HR损耗数据集。此外,我们执行相同的过程,将分类数据集转换为单一热编码值,并将数据集拆分为比例为 70:30 的train和test集:
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score
from sklearn import tree
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(" Data Set Shape ", hr_data.shape)
print(list(hr_data.columns))
print(" Sample Data ", hr_data.head())
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
X = data_trnsf.drop('left', axis=1)
X.columns
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
print(X_train)
有几个最佳参数对GradientBoostedClassifier很重要。然而,并非所有都是强制性的:
- 
n_estimators:这类似于随机森林算法的n_estimators,但是树是按顺序创建的,在 boosting 方法中被认为是不同的阶段。使用这些参数,我们指定模型中的树或提升阶段的数量。默认为100。 - 
max_depth:这是寻找最佳分割时要考虑的特征数量。当max_features小于特征数时,会导致方差减小,但会增加模型中的偏差。 - 
max_depth:每棵树要生长的最大深度。默认值为3: 
num_trees = 100
attrition_gradientboost= GradientBoostingClassifier(n_estimators=num_trees, random_state=42)
attrition_gradientboost.fit(X_train, Y_train)
前面代码的输出如下:

一旦模型成功拟合到数据集,我们就使用训练好的模型来预测test数据的Y值:
Y_pred = attrition_gradientboost.predict(X_test)
from sklearn.metrics import confusion_matrix
confusionmatrix = confusion_matrix(Y_test, Y_pred)
print(confusionmatrix)
下面的混淆矩阵看起来不错,错误分类错误很少:

我们打印准确性和其他指标来评估分类器:
print('Accuracy of Gradient Boosting Classifier classifier on test set: {:.2f}'.format(attrition_gradientboost.score(X_test, Y_test)))
from sklearn.metrics import classification_report
print(classification_report(Y_test, Y_pred))
前面代码的输出如下:

准确率 97%,很优秀,但不如随机森林模型。还有一种集合模型,我们将在下一节讨论。
堆积/混合
在该方法中,多层分类器一层堆叠在另一层上。应用第一层分类器的预测概率来训练第二层分类器,以此类推。最终的结果是通过使用诸如逻辑回归之类的基本分类器获得的。我们还可以使用不同的算法,如决策树、随机森林或 GBM,作为最终的层分类器。
scikit-learn 中没有现成的堆叠系综实现。然而,我们将在第 4 章、自动算法选择中演示使用 scikit-learn 的基础算法创建堆叠集成的自动功能。
比较分类器的结果
我们已经在HR磨损数据集上创建了大约六个分类模型。下表总结了每个模型的评估分数:

随机森林模型似乎是所有六个模型中的赢家,准确率达到创纪录的 99%。现在,我们不需要进一步改进随机森林模型,而是检查它是否能够很好地推广到新的数据集,并且结果没有过度拟合train数据集。方法之一是进行交叉验证。
交叉验证
交叉验证是一种在未用于训练的数据集(即训练模型未知的数据样本)上评估模型准确性的方法。这确保了在生产环境中部署时模型在独立数据集上的泛化。其中一种方法是将数据集分成两组——训练集和测试集。我们在前面的例子中演示了这种方法。
另一种流行且更健壮的方法是 k 倍交叉验证方法,其中数据集被划分为大小相等的 k 个子样本。其中 k 为非零正整数。在训练阶段, k-1 样本用于训练模型,剩余的一个样本用于测试模型。这个过程重复 k 次,其中一个 k 个样本只使用一次来测试模型。然后以某种方式对评估结果进行平均或合并,例如多数投票以提供单一估计。
我们将在之前创建的随机森林模型上生成5和10折叠交叉验证,以评估其性能。只需在随机森林代码的末尾添加以下代码片段:
crossval_5_scores = cross_val_score(attrition_forest, X_train, Y_train, cv=5)
print(crossval_5_scores)
print(np.mean(crossval_5_scores))
crossval_10_scores = cross_val_score(attrition_forest, X_train, Y_train, cv=10)
print(crossval_10_scores)
print(np.mean(crossval_10_scores))
5和10折叠交叉验证的准确率分别为0.9871和0.9875。这是一个很好的分数,非常接近我们实际的随机森林模型分数 0.99,如下图截图所示。这确保了模型可以很好地推广到其他独立的数据集:

现在我们已经对有监督机器学习的含义有了一些了解,是时候切换到无监督机器学习了。
我们在本章前面介绍了无监督学习。重申目标:
无监督学习的目标是通过推断输入数据集中属性的结构和关系来识别模式。
那么,我们可以用什么算法和方法来识别模式呢?有很多,比如聚类和自动编码器。我们将在下一节中讨论集群和第 7 章、深入学习中的自动编码器。
使聚集
我们将以一个问题开始这一部分。我们如何开始学习一种新的算法或机器学习方法?我们从三重 w 开始。那么,让我们从聚类方法开始。
什么是集群?
聚类是一种将相似的数据分组在一起的技术,一个组有一些不同于其他组的独特特征。可以使用各种方法将数据聚集在一起。其中一种是基于规则的,即根据某些预定义的条件形成组,例如根据客户的年龄或行业对客户进行分组。另一种方法是使用 ML 算法将数据聚集在一起。
聚类在哪里使用?
作为一个无监督的学习过程,它最常用于从数据中推导出逻辑关系和模式。集群在各个部门和业务功能中得到应用。它用于信息检索、客户细分、图像分割、对网页、新闻文章等非结构化文本进行聚类等。
通过哪种方法可以实现集群?
有各种机器学习方法来创建集群。聚类算法分为以下几类:
- 层次聚类:也称为凝聚聚类,试图通过距离度量将每个数据点链接到最近的邻居。这是一个递归过程,从一条记录开始,迭代地将它们配对在一起,直到所有记录都合并成一个集群。如果我们想象一下,它的结构类似于一棵倒树,可以通过一个树形图来可视化。使用这种方法的问题之一是确定聚类的过程。这是资源密集型的,但人们可以可视化树图,并选择集群的数量。
 - 基于分区的聚类:在这种方法中,数据被分割成分区。划分基于数据点之间的距离。k-means 算法是一种常用的划分聚类方法。在这种方法中,选择合适的距离函数会影响簇的形状。欧几里德距离、曼哈顿距离和余弦距离是三种广泛用于创建 k 均值聚类的距离函数。欧氏距离对输入向量的尺度最敏感。在这种情况下,必须对输入向量的比例进行归一化或标准化,或者选择对比例不敏感的距离度量,如余弦距离。
 - 基于密度的技术:这里的聚类是利用数据点的特定概率分布形成的。这个想法是,只要邻域内的密度超过一个定义的阈值,就继续扩展集群。高密度区域被标记为与低密度区域分离的簇,这可能是噪声。噪声是数据中的随机变化或误差,在统计上是不确定的,无法解释。
 - 基于网格的方法:该方法首先通过划分数据集的属性来创建超矩形网格单元。然后,它会丢弃低于定义的阈值参数的低密度细胞。然后将相邻的高密度细胞融合在一起,直到达到目标函数或保持不变。得到的细胞被解释为簇。
 
我们将介绍层次聚类和 k-means 聚类,这是两种在行业中广泛使用的方法。
分级聚类
我们可以使用 scikit-learn 在 Python 中执行分层聚类。我们需要从sklearn.cluster导入AgglomerativeClustering方法来创建集群。分层聚类对距离度量有效,因此我们需要在构建模型之前将分类数据转换为合适的数字格式。我们已经使用了 one-hot 编码将分类属性转换为数字格式,并且存在各种其他方法来完成这个任务。该主题将在下一章中详细介绍:
import pandas as pd
import numpy as np
from sklearn import preprocessing
from sklearn.cluster import AgglomerativeClustering
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
接下来,我们需要用以下参数实例化AgglomerativeClustering,并将数据拟合到模型中:
n_clusters:要查找的簇数。默认值为 2。affinity:是用来计算联动的距离度量。默认为euclidean;manhattan、cosine、l1、l2和precomputed是可以使用的其他距离度量。linkage:该参数决定了用于合并集群对的度量。不同的链接指标有:- 沃德:最小化正在合并的聚类的方差。这是默认参数值。
 - 平均值:采用两组每次观测距离的平均值。
 - 完成:使用两组所有观测值之间的最大距离。
 
现在,让我们使用一些描述的参数建立一个AgglomerativeClustering模型:
n_clusters = 3
clustering = AgglomerativeClustering(n_clusters=n_clusters,
affinity='euclidean', linkage='complete')
clustering.fit(data_trnsf)
cluster_labels = clustering.fit_predict(data_trnsf)
一旦模型准备好了,我们就需要评估模型。评估聚类结果的最佳方法是对形成的聚类进行人工检查,并确定每个聚类代表什么以及每个聚类中的数据有什么共同的值。
结合人体检查,还可以使用轮廓分数来确定最佳模型。轮廓值在-1 和+1 的范围内:
- +1 表示集群中的数据靠近分配的集群,远离其相邻集群
 - -1 表示数据点与其相邻的簇比与其分配的簇更接近
 
当一个模型的平均剪影分数为-1 时,它是一个可怕的模型,剪影分数为+1 的模型是一个理想的模型。所以,这就是为什么平均轮廓分数越高,聚类模型越好的原因:
silhouette_avg = silhouette_score(data_trnsf, cluster_labels)
print("For n_clusters =", n_clusters,"The average silhouette_score is :", silhouette_avg)
前面代码的输出如下:

由于我们模型的平均轮廓得分为0.49,我们可以假设集群形成良好。
我们可以将这个分数与 k-means 聚类结果进行比较,并在HR磨损数据集上选择创建三个聚类的最佳模型。
分区聚类
我们需要从 scikit-learn 包中导入一个KMeans方法,剩下的代码仍然类似于层次聚类的代码:
import pandas as pd
import numpy as np
from sklearn import preprocessing
import matplotlib.pyplot as plt 
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
我们需要在 k-means 函数中指定聚类数(n_clusters)来创建模型。它是创建 k-均值聚类的一个基本参数。它的默认值是八。接下来,将数据拟合到KMeans实例,并建立模型。我们需要fit_predict值来获得集群标签,就像对AgglomerativeClustering所做的那样:
n_clusters = 3
kmeans = KMeans(n_clusters=n_clusters)
kmeans.fit(data_trnsf)
cluster_labels = kmeans.fit_predict(data_trnsf)
如果我们想查看聚类质心和标签,我们可以使用cluster_centers_和means_labels_来实现:
centroid = kmeans.cluster_centers_
labels = kmeans.labels_
print (centroid)
print(labels)
silhouette_avg = silhouette_score(data_trnsf, cluster_labels)
print("For n_clusters =", n_clusters,"The average silhouette_score is :", silhouette_avg)
前面代码的输出如下:

k-means 聚类的平均值silhouette_score为0.58,大于层次聚类得到的平均剪影得分。
这意味着这三个聚类在 k-means 模型中比在HR磨损数据集上构建的分层模型更好地形成。
摘要
ML 及其自动化之旅是漫长的。本章的目的是让我们熟悉机器学习的概念;最重要的是 scikit-learn 和其他 Python 包,这样我们就可以在接下来的章节中顺利地加速我们的学习,创建一个线性回归模型和六个分类模型,并学习聚类技术并相互比较模型。
我们使用单个HR磨损数据集来创建所有分类器。我们观察到这些代码有许多相似之处。导入的库除了用于实例化机器学习类的库之外都是相似的。数据预处理模块在所有代码中都是冗余的。机器学习技术基于目标属性的任务和数据而改变。此外,评估方法相当于类似类型的最大似然方法。
你认为这些领域有些是多余的,需要自动化吗?是的,他们可以,但没那么容易。当我们开始考虑自动化时,模型中和模型周围的一切都需要连接在一起。每个代码段都是自己的模块。
下一章是关于数据预处理模块的。这是机器学习项目中最关键、最耗时的课题。我们将讨论创建可靠的机器学习模型所需的各种数据准备任务。
三、数据预处理
任何对机器学习 ( ML )感兴趣的人肯定都会听说,一个数据科学家或者机器学习工程师 80%的时间都花在了准备数据上,剩下的 20%都花在了构建和评估模型上。准备数据花费的大量时间被认为是构建良好模型的投资。这是一个简单的模型,使用优秀的数据集构建的模型超过了使用糟糕的数据集开发的复杂模型。在现实生活中,找到一个可靠的数据集是非常困难的。我们必须创造和培育好的数据。你一定在想,如何创造好的数据?这是我们将在本章中发现的东西。我们将研究创建优秀且可行的数据集所需的一切。理论上,好与我们手头的任务以及我们如何感知和消费数据有关。在本章中,我们将引导您完成以下主题:
- 数据转换
 - 特征选择
 - 降维
 - 特征生成
 
对于每一个主题,我们将讨论在数据集中遇到的不同类型的数据上可以做的各种事情。我们还将考虑一些自动化的开源特性准备工具,这些工具在用 Python 准备数据时会派上用场。
让我们从数据转换的第一个主题开始。
技术要求
所有代码示例都可以在 GitHub 的Chapter 03文件夹中找到。
数据转换
让我们假设我们正在研究一个 ML 模型,其任务是预测员工流失。基于我们对业务的理解,我们可能会包含一些创建一个好模型所必需的相关变量。另一方面,我们可能会选择丢弃一些没有相关信息的功能,比如EmployeeID。
Identifying the ID columns is known as identifier detection. Identifier columns don't add any information to a model in pattern detection and prediction. So, identifier column detection functionality can be a part of the AutoML package and we use it based on the algorithm or a task dependency.
一旦我们决定了要使用的领域,我们就可以探索数据来转换某些有助于学习过程的特征。转换为数据增加了一些经验,这有利于 ML 模型。例如,员工起始日期为 2018 年 2 月 11 日,该日期不提供任何信息。但是,如果我们将此功能转换为四个属性—日期、日、月和年,它将为模型构建练习增加价值。
特征变换也很大程度上取决于所使用的最大似然算法的类型。大体上,我们可以将监督模型分为两类——基于树的模型和非基于树的模型。
Tree-based models handle the abnormality in most features by themselves. Non-tree-based models, such as nearest neighbors and linear regression, improve their predictive power when we do feature transformations.
理论解释已经够多了。现在,让我们直接跳到一些可以对我们经常遇到的各种数据类型执行的功能转换。我们将首先从数字特征开始。
数字数据转换
以下是一些最广泛使用的数值数据转换方法:
- 缩放比例
 - 缺少值
 - 极端值
 
这里显示的技术可以嵌入到函数中,这些函数可以直接应用于在 AutoML 流水线中转换数值数据。
缩放比例
标准化和标准化是行业内使用的缩放技术的两个术语。这两种技术都确保模型中使用的数字特征在其表示中具有同等的权重。大多数时候,人们交替使用标准化和规范化。虽然两者都是缩放技术,但两者之间只有一线之差。
Standardization assumes the data to be normally distributed. It rescales the data to mean as zero and standard deviation as one. Normalization is a scaling technique that assumes no prior distribution of the data. In this technique, the numerical data is rescaled to a fixed range either: 0 to 1, -1 to +1, and so on.
以下是一些广泛使用的标准化或规范化数据的技术:
- Z 评分标准化:这里,如果数据遵循高斯分布,则数据以均值为零、标准差为 1 的方式重新缩放。一个优先的要求是使数字数据呈正态分布。数学上表示为
,其中
为数值的平均值,σ为数值的标准差。 
Scikit-learn 提供了各种方法来标准化和规范化数据。让我们首先使用以下代码片段加载HR损耗数据集:
%matplotlib inline
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print (hr_data.head())
前面代码的输出显示了数据集的各种属性以及一些数据点:

让我们使用以下代码来分析数据集的分布:
hr_data[hr_data.dtypes[(hr_data.dtypes=="float64")|(hr_data.dtypes=="int64")].index.values].hist(figsize=[11,11])
前面代码的输出是几个不同数值属性的直方图:

例如,让我们使用sklearn.preprocessing模块中的StandardScaler来标准化satisfaction_level列的值。一旦我们导入了方法,我们首先需要创建一个StandardScaler类的实例。接下来,我们需要使用fit_transform方法来拟合和转换我们需要标准化的列。在下面的例子中,我们使用satisfaction_level属性进行标准化:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
hr_data_scaler=scaler.fit_transform(hr_data[['satisfaction_level']])
hr_data_scaler_df = pd.DataFrame(hr_data_scaler)
hr_data_scaler_df.max()
hr_data_scaler_df[hr_data_scaler_df.dtypes[(hr_data_scaler_df.dtypes=="float64")|(hr_data_scaler_df.dtypes=="int64")].index.values].hist(figsize=[11,11])
一旦我们执行了前面的代码,我们就可以再次查看satisfication_level实例直方图,并观察到值在 -2 到 1.5 之间是标准化的:

- 最小-最大标准化:在这种技术中,变量的最小值从其实际值减去其最大值和最小值之差。从数学上讲,它由以下内容表示:
 

MinMaxScaler方法可在 scikit-learn 的preprocessing模块中获得。在本例中,我们对HR损耗数据集的四个属性进行了规范化— average_monthly_hours、last_evaluation、number_project和satisfaction_level。我们遵循类似于StandardScaler的流程。我们首先需要从sklearn.preprocessing模块导入MinMaxScaler,并创建一个MinMaxScaler类的实例。
接下来,我们需要使用fit_transform方法来拟合和变换列:
from sklearn.preprocessing import MinMaxScaler
minmax=MinMaxScaler()
hr_data_minmax=minmax.fit_transform(hr_data[[ 'average_montly_hours',
 'last_evaluation', 'number_project', 'satisfaction_level']])
hr_data_minmax_df = pd.DataFrame(hr_data_minmax)
hr_data_minmax_df.min()
hr_data_minmax_df.max()
hr_data_minmax_df[hr_data_minmax_df.dtypes[(hr_data_minmax_df.dtypes=="float64")|(hr_data_minmax_df.dtypes=="int64")].index.values].hist(figsize=[11,11])
以下直方图描述了转换后的四个属性的值分布在 0 和 1 之间:

缺少值
我们经常遇到数据集,其中并非所有的值都适用于特定的变量/属性。出现这种情况有几个原因,例如调查中被忽略的问题、打字错误、设备故障等等。在数据挖掘项目中会遇到这些缺失的值,处理这些值是必不可少的。
缺失值插补占据了数据科学家的大部分时间。我们可以通过各种方法来估算缺失值。决定性的因素是当归因于这些不可用的值时使用什么。决定什么时候用什么来输入缺失值的过程是一种天赋,来自于处理数据的经验。有时直接移除这些值更好,对于某些赋值,最好使用高级挖掘技术来估算这些值。
因此,出现了两个最重要的问题:
- 你什么时候使用哪种插补方法?
 - 估算价值的最好方法是什么?
 
我们认为它来自于处理缺失值的经验;最好的开始方法是进行比较研究,对数据应用不同的插补方法,然后选择适当的技术,用偏差最小的估计值分配空值。
一般来说,当我们遇到一个丢失的值时,我们会首先尝试检查一个值为什么会丢失。是因为收集数据时的一些问题,还是罪魁祸首是数据源本身?最好是从根源上解决问题,而不是直接将价值强加于人。这是一个理想的情况,而且,大多数时候,这是不可能的。例如,如果我们正在处理一个调查数据集,而一些受访者选择不透露具体信息,在这种情况下,输入值可能是不可避免的。
因此,在我们开始输入值之前,我们可以使用以下准则:
- 调查丢失的数据
 - 分析缺失的数据
 - 决定产生最小偏差估计的最佳策略
 
我们可以将此记为缺失值插补的 IAD 规则(即调查、分析和决定)。以下是我们处理缺失值的一些可用方法:
- 删除或删除数据:当很少的数据点缺失时,我们可以忽略数据,单独分析那些案例。这种方法被称为列表明智删除。但是,当缺少太多值时,这是不可取的,因为我们可能会丢失数据中的一些有价值的信息。成对删除是另一种技术,我们可以只删除缺失的值。这表明我们分析所有只存在感兴趣数据的情况。这是一个安全的策略,但是,使用这种方法,即使数据有微小的变化,我们也可能每次从每个样本中获得不同的结果。
 
我们将再次使用HR磨损数据集来演示缺失值处理。让我们首先加载数据集,并查看数据集中的空值数量:
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print (hr_data.head())
print('Nulls in the data set' ,hr_data.isnull().sum())
从下面的输出中我们可以看到,数据集相对干净,只是promotion_last_5years有一些缺失值。因此,我们将把一些缺失的值合成到一些列中:

我们使用以下代码片段将列promotion_last_5years、average_montly_hours和number_project中的一些值替换为空值:
#As there are no null introduce some nulls by replacing 0 in promotion_last_5years with NaN
hr_data[['promotion_last_5years']] = hr_data[[ 'promotion_last_5years']].replace(0, np.NaN)
#As there are no null introduce some nulls by replacing 262 in promotion_last_5years with NaN
hr_data[['average_montly_hours']] = hr_data[[ 'average_montly_hours']].replace(262, np.NaN)
#Replace 2 in number_project with NaN
hr_data[['number_project']] = hr_data[[ 'number_project']].replace(2, np.NaN)
print('Nulls in the data set', hr_data.isnull().sum())
在本练习之后,为这三列插入了一些空值,我们可以从以下结果中看到:

在我们删除行之前,让我们首先创建一个hr_data的副本,这样我们就不会替换原始数据集,该数据集将用于演示其他缺失值插补方法。接下来,我们使用dropna方法删除缺少值的行:
#Remove rows
hr_data_1 = hr_data.copy()
print('Shape of the data set before removing nulls ', hr_data_1.shape)
# drop rows with missing values
hr_data_1.dropna(inplace=True)
# summarize the number of rows and columns in the dataset
print('Shape of the data set after removing nulls ',hr_data_1.shape)
我们可以观察到这个练习后的行数从14999减少到278。删除行必须小心使用。由于promotion_last_5years有大约 14,680 个缺失值,14,680 个记录被完全删除:

- 用全局常量填充缺失值:我们可以用一个全局常量,比如 NA 或者-999,把缺失值和数据集的其他部分分开。此外,还有没有任何值的空值,但它们构成了数据集的固有部分。这些值被故意保留为空白。当空值无法从缺失值中分离出来时,使用全局常数是一种安全的策略。
 
我们可以使用fillna方法将缺失值替换为常量值,如-999。下面的代码片段演示了该方法的使用:
#Mark global constant for missing values
hr_data_3 = hr_data.copy()
# fill missing values with -999
hr_data_3.fillna(-999, inplace=True)
# count the number of NaN values in each column
print(hr_data_3.isnull().sum())
print(hr_data_3.head())
我们可以从以下结果中看到,所有缺失的值都被-999值替换:

- 用属性均值/中值替换缺失值:这是数据科学家和机器学习工程师最喜欢的方法。我们可以用数值的平均值或中值以及分类值的模式来替换缺失的值。这种方法的缺点是它可能会降低属性的可变性,这反过来又会削弱相关性估计。如果我们处理的是监督分类模型,我们还可以用数值的组均值或中值以及分类值的分组模式来替换缺失的值。在这些分组均值/中值方法中,属性值按目标值分组,该组中缺失的值被替换为该组的均值/中值。
 
我们可以使用相同的fillna方法,以均值函数为参数,用均值替换缺失值。下面的代码演示了它的用法:
#Replace mean for missing values
hr_data_2 = hr_data.copy()
# fill missing values with mean column values
hr_data_2.fillna(hr_data_2.mean(), inplace=True)
# count the number of NaN values in each column
print(hr_data_2.isnull().sum())
print(hr_data_2.head())
我们可以从下面的输出中看到,丢失的值被替换为每个属性的平均值:

- 使用指示变量:我们还可以生成一个二进制变量,指示记录中是否有缺失值。我们可以将其扩展到多个属性,其中我们可以为每个属性创建二进制指示器变量。我们还可以估算缺失的值,并构建二元指标变量来表示它是真实变量还是估算变量。如果一个值因为真正的跳过而丢失,结果不会有偏差。
 
正如我们演示其他插补方法一样,让我们首先复制原始数据,并创建新的列来指示被插补的属性和值。下面的代码首先为那些缺少值的属性创建新的列,并将_was_missing追加到原始列名中。
接下来,丢失的值被替换为全局常数-999。虽然我们使用了全局常数插补方法,但您可以使用任何插补方法来插补缺失的值:
# make copy to avoid changing original data (when Imputing)
hr_data_4 = hr_data.copy()
# make new columns indicating what is imputed
cols_with_missing = (col for col in hr_data_4.columns 
 if hr_data_4[col].isnull().any())
for col in cols_with_missing:
 hr_data_4[col + '_was_missing'] = hr_data_4[col].isnull()
hr_data_4.fillna(-999, inplace=True)
hr_data_4.head()
我们可以从以下结果中看到,创建了新的列,表明属性中是否存在缺失值:

- 使用数据挖掘算法预测最大可能值:我们可以应用 ML 算法,如 KNN、线性回归、随机森林或决策树技术,来预测缺失属性的最大可能值。这种方法的一个缺点是,如果计划在另一项任务(如预测或分类)中对同一数据集使用相同的算法,则可能会过度填充数据。
 
在 Python 中,fancyimpute是一个提供高级数据挖掘选项来估算缺失值的库。这是我们最常使用的东西,所以我们想演示一下这个包。Python 中可能还有其他一些库也可以完成类似的任务。首先,我们需要使用以下命令安装fancyimpute库。这必须在命令提示符下执行:
pip install fancyimpute
安装完成后,我们可以返回 Jupyter 笔记本,从fancyimpute库中导入KNN方法。KNN 插补方法只适用于数值。因此,我们首先从hr_data集合中只选择数字列。接下来,创建一个 k 等于3的 KNN 模型,并为数字属性替换缺失的值:
from fancyimpute import KNN
hr_data_5 = hr_data.copy()
hr_numeric = hr_data_5.select_dtypes(include=[np.float])
hr_numeric = pd.DataFrame(KNN(3).complete(hr_numeric))
hr_numeric.columns = hr_numeric.columns
hr_numeric.index = hr_numeric.index
hr_numeric.head()
fancyimpute库使用 TensorFlow 后端,执行起来需要一些时间。一旦执行完成,我们可以向下滚动查看插补结果,如以下结果截图所示:

极端值
异常值是不符合整体数据模式的极端值。它们通常远离其他观测结果,扭曲了数据的总体分布。在模型构建过程中包含它们可能会导致错误的结果。适当地对待他们是非常重要的。异常值有两种类型——单变量和多变量。
检测和处理单变量异常值
顾名思义,单变量异常值基于数据集中的单个属性。单变量异常值是通过使用箱线图和查看属性值的分布来发现的。然而,当我们构建 AutoML 流水线时,我们没有权限可视化数据分布。相反,AutoML 系统应该能够检测异常值并自行处理它们。
因此,我们可以部署以下三种方法中的任何一种来自动进行单变量异常检测和处理:
- 四分位数范围和过滤
 - Winsorizing
 - 整理
 
让我们创建一个虚拟异常数据集来演示异常检测和处理方法:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
number_of_samples = 200
outlier_perc = 0.1
number_of_outliers = number_of_samples - int ( (1-outlier_perc) * number_of_samples )
# Normal Data
normal_data = np.random.randn(int ( (1-outlier_perc) * number_of_samples ),1)
# Inject Outlier data
outliers = np.random.uniform(low=-9,high=9,size=(number_of_outliers,1))
# Final data set
final_data = np.r_[normal_data,outliers]
让我们使用以下代码绘制新创建的数据集:
#Check data
plt.cla()
plt.figure(1)
plt.title("Dummy Data set")
plt.scatter(range(len(final_data)),final_data,c='b')
从下图中我们可以看出,在数据集的末尾有一些异常值:

我们还可以使用下面的代码生成一个方框图来观察异常值。箱线图,也称为箱线图和触须图,是一种基于五个数字汇总来表示数据分布的方法:最小值、第一个四分位数、中值、第三个四分位数和最大值。任何低于最小值和高于最大值的都被认为是异常值:
## Detect Outlier###
plt.boxplot(final_data)
从得到的方框图中,我们可以观察到存在一些超出最大和最小标记的值。因此,我们可以假设我们成功地创建了一些异常值:

去除异常值的一种方法是过滤高于最大值和低于最小值的值。要完成这个任务,首先需要计算四分位数区间 ( IQR )。
四分位数范围
四分位数之间的范围是数据集中可变性或分布的度量。它是通过将数据集分成四分位数来计算的。四分位数根据我们之前研究的五个数字的汇总将数据集分成四份,即最小值、第一个四分位数、第二个四分位数、第三个四分位数和最大值。第二个四分位数是排序数据集的中值;第一个四分位数是排序数据集前半部分的中间值,第三个四分位数是排序数据集后半部分的中间值。
四分位数范围是第三个四分位数(quartile75或Q3)与第一个四分位数(quartile25或Q1)之间的差值。
我们使用以下代码计算 Python 中的IQR:
## IQR Method Outlier Detection and Removal(filter) ##
quartile75, quartile25 = np.percentile(final_data, [75 ,25])
## Inter Quartile Range ##
IQR = quartile75 - quartile25
print("IQR",IQR)
从下面的代码中我们可以看到数据集的IQR是1.49:

过滤值
我们可以过滤高于最大值和低于最小值的值。最小值可用公式计算:quartile25 - (IQR * 1.5)最大值为quartile75 + (IQR*1.5)。
The method to calculate maximum and minimum values is based on Turkey Fences, which was developed by John Turkey. The value 1.5 indicates about 1% of measurements as outliers and is synonymous with the 3σ principle, which is practiced as a bound in many statistical tests. We can use any value other than 1.5, which is at our discretion. However, the bound may increase or decrease the number of outliers in the dataset.
我们使用下面的 Python 代码来计算数据集的Max和Min值:
## Calculate Min and Max values ##
min_value = quartile25 - (IQR*1.5)
max_value = quartile75 + (IQR*1.5)
print("Max", max_value)
print("Min", min_value)
在执行前面的代码后,我们注意到以下输出。最大值和最小值分别为2.94和-3.03:

接下来,我们使用以下代码过滤低于min_value和高于max_value的值:
filtered_values = final_data.copy()
filtered_values[ filtered_values< min_value] = np.nan
filtered_values[ filtered_values > max_value] = np.nan
#Check filtered data
plt.cla()
plt.figure(1)
plt.title("IQR Filtered Dummy Data set")
plt.scatter(range(len(filtered_values)),filtered_values,c='b')
代码执行成功完成后,我们可以看到离群值被消除了,数据集看起来远比之前的数据集好:

Winsorizing
Winsorizing 是用较小的绝对值代替极值的方法。它对数值列中的非空值进行排序,计算尾值,然后用定义的参数替换尾值。
我们可以使用 SciPy 包中的winsorize方法来处理异常值。SciPy 是一个 Python 库,是科学和技术计算领域开源 Python 贡献的集合。它拥有大量的统计计算模块、线性代数、优化、信号和图像处理模块以及更多模块。
一旦导入winsorize方法,我们需要将data和limit参数传递给函数。尾值的计算和替换是通过这种方法进行的,并生成结果无异常值数据:
##### Winsorization ####
from scipy.stats.mstats import winsorize
import statsmodels.api as sm
limit = 0.15
winsorized_data = winsorize(final_data,limits=limit)
#Check winsorized data
plt.cla()
plt.figure(1)
plt.title("Winsorized Dummy Data set")
plt.scatter(range(len(winsorized_data)),winsorized_data,c='b')
从下面的图中我们可以观察到,极值已经被消除,数据看起来没有异常值:

整理
修剪与 winsorizing 相同,只是尾部值被裁剪掉了。
stats库中的trimboth方法从数据的两端分割数据集。final_data和0.1的极限作为参数传递给函数,从两端修剪 10%的数据:
### Trimming Outliers ###
from scipy import stats
trimmed_data = stats.trimboth(final_data, 0.1)
#Check trimmed data
plt.cla()
plt.figure(1)
plt.title("Trimmed Dummy Data set")
plt.scatter(range(len(trimmed_data)),trimmed_data,c='b')
我们可以从下面的结果图中观察到,极值被截断,不再存在于数据集中:

多元异常值的检测和处理
多元异常值是至少两个变量的极端分数的混合。单变量离群点检测方法非常适合处理一维数据,但是当我们越过一维时,使用这些方法检测离群点就变得具有挑战性。多元异常检测方法也是异常检测方法的一种形式。一类 SVM、局部异常因子 ( LOF )和IsolationForest等技术是检测多元异常值的有用方法。
我们使用以下IsolationForest代码描述HR磨损数据集上的多元异常检测。我们需要从sklearn.ensemble包装中进口IsolationForest。接下来,我们加载数据,将分类变量转换为一个热编码变量,并使用估计量的数量调用IsolationForest方法:
##Isolation forest
import numpy as np
import pandas as pd
from sklearn.ensemble import IsolationForest
hr_data = pd.read_csv('data/hr.csv', header=0)
print('Total number of records ',hr_data.shape)
hr_data = hr_data.dropna()
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
clf = IsolationForest(n_estimators=100)
然后,我们将IsolationForest实例(clf)拟合到数据,并使用predict方法预测异常值。异常值由-1表示,非异常值(也称为新数据)由1表示:
clf.fit(data_trnsf)
y_pred_train = clf.predict(data_trnsf)
data_trnsf['outlier'] = y_pred_train
print('Number of outliers ',data_trnsf.loc[data_trnsf['outlier'] == -1].shape)
print('Number of non outliers ',data_trnsf.loc[data_trnsf['outlier'] == 1].shape)
从下面的输出中我们可以看到,模型能够从14999记录的数据集中识别出大约1500个异常值:

扔掉
宁滨是一个将连续数值分组到更小数量的桶或箱中的过程。这是离散化连续数据值的重要技术。许多算法如朴素贝叶斯和 Apriori 在离散数据集上运行良好,因此有必要将连续数据转换为离散值。
有各种类型的宁滨方法:
- 等宽宁滨:通过将数据划分为大小相等的 k 区间来确定等宽仓;
 

其中 w 为箱的宽度, maxval 为数据中的最大值, minval 为数据中的最小值, k 为所需的箱数
区间边界形成如下:

- 等频宁滨:通过将数据分成 k 组来确定等频仓,其中每组包括相同数量的值。
 
在这两种方法中, k 的值是根据我们的要求以及试错过程确定的。
除了这两种方法之外,我们还可以明确提到创建面元的切割点。当我们知道数据并希望它以某种格式入库时,这非常有帮助。以下代码是基于预定义切割点执行宁滨的函数:
#Bin Values:
def bins(column, cutpoints, labels=None):
 #Define min and max values:
 min_val = column.min()
 max_val = column.max()
 print('Minimum value ',min_val)
 print(' Maximum Value ',max_val)
 break_points = [min_val] + cut_points + [max_val]
 if not labels:
   labels = range(len(cut_points)+1)
 #Create bins using the cut function in pandas
 column_bin = pd.cut(column,bins=break_points,labels=labels,include_lowest=True)
 return column_bin
以下代码将员工满意度分为三类:low、medium和high。低于0.3的被认为是low满意度,高于0.6的分数被认为是高度满意的员工分数,介于这两个值之间的被认为是medium:
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
#Binning satisfaction level:
cut_points = [0.3,0.6]
labels = ["low","medium","high"]
hr_data["satisfaction_level"] = bins(hr_data["satisfaction_level"], cut_points, labels)
print('\n####The number of values in each bin are:###\n\n',pd.value_counts(hr_data["satisfaction_level"], sort=False))
一旦我们执行了前面的代码,我们可以从下面的结果中观察到为satisfaction_level属性创建了三个箱,其中low箱中有1941值,medium箱中有4788,而high箱中有8270:

对数和幂变换
对数和幂变换通常有助于非基于树的模型,使高度偏斜的分布不那么偏斜。这种预处理技术有助于满足线性回归模型的假设和推断统计的假设。这种类型转换的一些示例包括对数转换、平方根转换和对数-对数转换。
以下是使用虚拟数据集进行平方根转换的演示:
import numpy as np
values = np.array([-4, 6, 68, 46, 89, -25])
# Square root transformation #
sqrt_trnsf_values = np.sqrt(np.abs(values)) * np.sign(values)
print(sqrt_trnsf_values)
以下是前面平方根转换的输出:

接下来,让我们使用另一个虚拟数据集尝试一个日志转换:
values = np.array([10, 60, 80, 200])
#log transformation #
log_trnsf_values = np.log(1+values)
print(log_trnsf_values)
虚拟数据集上的日志转换产生以下结果:

现在,我们已经对数值数据的不同预处理方法有了一个合理的想法,让我们看看分类数据有什么储备。
分类数据转换
分类数据本质上是非参数的。这意味着它不遵循任何数据分布。然而,为了在参数模型中使用这些变量,它们需要使用各种编码方法进行转换,缺失的值将被替换,并且我们可以使用宁滨技术减少类别的数量。
编码
在许多实际的 ML 活动中,数据集将包含分类变量。它在企业环境中更为合适,在企业环境中,大多数属性都是分类的。这些变量有不同的离散值。例如,组织的规模可以是Small、Medium或Large,或者地理区域可以是Americas、Asia Pacific和Europe。许多 ML 算法,尤其是基于树的模型,可以直接处理这种类型的数据。
然而,许多算法并不直接接受数据。因此,需要将这些属性编码成数值,以便进一步处理。有各种方法来编码分类数据。以下部分描述了一些广泛使用的方法:
- 标签编码:顾名思义,标签编码将分类标签转换为数字标签。标签编码更适合于有序分类数据。标签总是在 0 和 n-1 之间,其中 n 是类的数量。
 - 一热编码:这也叫伪编码。在这种方法中,为分类属性/预测器的每个类生成虚拟列。对于每个伪预测值,值的存在由 1 表示,其不存在由 0 表示。
 - 基于频率的编码:在这种方法中,首先计算每个类的频率。然后计算总类别中每个类别的相对频率。该相对频率被指定为属性级别的编码值。
 - 目标均值编码:在这种方法中,每一类分类预测因子都被编码为目标均值的函数。这种方法只能用于有目标特征的监督学习问题。
 - 二进制编码:类首先被转换为数值。然后这些数值被改变成它们相似的二进制字符串。这将在以后分成单独的列。每个二进制数字成为一个独立的列。
 - 哈希编码:这种方法也就是俗称的特征哈希。我们大多数人都知道有一个散列函数,用于将数据映射到一个数字。此方法可能会将不同的类分配给同一个桶,但在输入要素存在数百个类别或类时非常有用。
 
这些技术中的大多数,以及许多其他技术,也在 Python 中实现,并且在包category_encoders中可用。您可以使用以下命令安装category_encoders库:
pip install category_encoders
接下来,我们将category_encoders库导入为ce(支持在代码中轻松使用的短代码)。我们加载HR损耗数据集,并对salary属性进行一次热编码:
import pandas as pd
import category_encoders as ce
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
onehot_encoder = ce.OneHotEncoder(cols=['salary'])
onehot_df = onehot_encoder.fit_transform(hr_data)
onehot_df.head()
我们可以观察到使用category_encoders库将分类属性转换为其对应的单热编码属性是多么容易:

同样,我们使用OrdinalEncoder对salary数据进行标签编码:
ordinal_encoder = ce.OrdinalEncoder(cols=['salary'])
ordinal_df = ordinal_encoder.fit_transform(hr_data)
ordinal_df.head(10)
ordinal_df['salary'].value_counts()
上述代码将低、中、高工资等级映射为三个数值:0、1和2:

同样,你可以从CategoryEncoders开始尝试其他分类编码方法,使用以下代码片段,观察结果:
binary_encoder = ce.BinaryEncoder(cols=['salary'])
df_binary = binary_encoder.fit_transform(hr_data)
df_binary.head()
poly_encoder = ce.PolynomialEncoder(cols=['salary'])
df_poly = poly_encoder.fit_transform(hr_data)
df_poly.head()
helmert_encoder = ce.HelmertEncoder(cols=['salary'])
helmert_df = helmert_encoder.fit_transform(hr_data)
helmert_df.head()
下一个讨论的主题是处理分类属性缺失值的方法。
分类数据转换缺少值
对于分类变量,评估缺失值的技术也保持不变。然而,一些插补技术是不同的,一些方法类似于所讨论的数值缺失值处理方法。我们将演示 Python 代码中专门针对分类缺失值处理的技术:
- 移除或删除数据:决定是否移除分类变量缺失的数据点的过程与我们讨论的数值缺失值处理的过程相同。
 - 用模式替换缺失值:由于分类数据是非参数数据,与数字数据不同,它们没有平均值或中值。因此,替换缺失分类值的最简单方法是使用模式。模式是分类变量中出现频率最高的一类。例如,让我们假设我们有三个类别的预测值:红色、绿色和蓝色。红色在数据集中出现频率最高,为 30,其次是绿色,为 20,蓝色为 10。然后,丢失的值可以用红色替换,因为这是预测值出现最多的地方。
 
我们将再次利用HR损耗数据集来解释分类属性的缺失值处理。让我们首先加载数据集,并观察数据集中的空值数量:
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print('Nulls in the data set' ,hr_data.isnull().sum())
我们从以下输出中了解到,数据集没有缺失分类属性sales和salary的数据。因此,我们将综合考虑这些特征的一些缺失值:

我们使用下面的代码片段来替换sales属性中的sales值为空和salary属性为低的空值:
#As there are no null introduce some nulls by replacing sales in sales column with NaN
hr_data[['sales']] = hr_data[[ 'sales']].replace('sales', np.NaN)
#As there are no null introduce some nulls by replacing low in salary column with NaN
hr_data[['salary']] = hr_data[[ 'salary']].replace('low', np.NaN)
print('New nulls in the data set' ,hr_data.isnull().sum())
执行完代码后,我们可以在salary和sales属性中找到一些空值,如下图所示:

现在,我们可以用每个列的模式来替换这些空值。正如我们对数值缺失值插补所做的那样,即使在这里,我们也首先创建hr_data的副本,这样我们就不会替换原始数据集。接下来,我们使用fillna方法用模式值填充行,如以下代码片段所述:
#Replace mode for missing values
hr_data_1 = hr_data.copy()
# fill missing values with mode column values
for column in hr_data_1.columns:
 hr_data_1[column].fillna(hr_data_1[column].mode()[0], inplace=True)
# count the number of NaN values in each column
print(hr_data_1.isnull().sum())
print(hr_data_1.head())
从下面的输出中我们可以看到sales列中缺失的值被technical和salary列中的medium替换:

- 使用全局常数填充缺失值:类似于数值缺失值处理,我们可以使用全局常数如
AAAA或NA来区分缺失值与数据集的其余部分: 
#Mark global constant for missing values
hr_data_2 = hr_data.copy()
# fill missing values with global constant values
hr_data_2.fillna('AAA', inplace=True)
# count the number of NaN values in each column
print(hr_data_2.isnull().sum())
print(hr_data_2.head())
前面代码的输出产生以下结果,缺失值用AAA代替:

- 使用指标变量:类似于我们讨论的数值变量,我们可以有一个指标变量来识别缺失分类数据的估算值。
 - 使用数据挖掘算法预测最可能值:就像我们对数值属性所做的一样,我们也可以使用数据挖掘算法,例如决策树、随机森林或 KNN 方法,来预测缺失值的最可能值。同样的
fancyimpute库也可以用于这个任务。 
我们已经讨论了如何处理结构化数据的数据预处理。在这个数字时代,我们从各种来源捕获了大量非结构化数据。在下一节中,让我们了解预处理文本数据的方法,以便为模型使用做好准备。
文本预处理
有必要通过去除在分析期间给文本增加噪声的不必要的文本来减小文本数据的特征空间的大小。通常会执行一系列步骤来预处理文本数据。然而,并不是每个任务都需要所有步骤,只要有必要,就会使用这些步骤。例如,如果文本数据项中的每个单词都已经是小写的,就不需要修改文本的大小写来使其统一。
文本预处理任务有三个主要元素:
- 标记化
 - 正常化
 - 代替
 
我们将使用nltk库演示不同的文本预处理方法。通过在命令提示符下发出以下命令来安装nltk库:
pip install nltk
安装完成后,在 Python 环境中运行以下代码片段:
##Run this cell only once##
import nltk
nltk.download()
你会得到一个 NLTK 下载器弹出窗口。从标识符部分选择全部,等待安装完成。
在本节中,我们将研究一些预处理步骤,这些步骤用于预处理文本以生成规范化的表单:
- 标记化—这是一种将文本分成更小块的方法,例如句子或单词。此外,一些文本挖掘任务,如准备 Word2Vec 模型,更喜欢文本是段落或句子风格。所以,我们可以用 NLTK 的
sent_tokenizer把文字转换成句子。首先,我们使用以下代码片段从data文件夹中读取文本文件: 
import pandas as pd
import category_encoders as ce
text_file = open('data/example_text.txt', 'rt')
text = text_file.read()
text_file.close()
- 接下来,为了将文本标记为句子,我们从
nltk库中导入sent_tokenize方法,并将文本作为参数传递: 
## Sentence tokenization ##
from nltk import sent_tokenize
sentence = sent_tokenize(text)
print(sentence[0])
上述代码产生以下输出:

- 类似地,一些建模方法,如单词包模型,需要文本采用单独的单词格式。对于这种情况,我们可以使用 NLTK 的
word_tokenizer方法将文本转换为单词,如下面的代码片段所示: 
## Word tokenization ##
from nltk import word_tokenize
words = word_tokenize(text)
print(words[:50])
- 以下输出显示文本中的
50标记单词。我们可以看到一些非字母字符,如标点符号被标记化。这对分析练习没有任何价值,因此,我们需要移除这些变量: 

- 标点符号等非字母字符在准备单词包模型时不会增加任何价值,因此可以删除
.、"、+、~等各种标点符号。 
有各种方法可以去掉非字母字符。现在我们将说明 Python 中的一种方法:
# Remove punctuations and keep only alphabets
words_cleaned = [word for word in words if word.isalpha()]
print(words_cleaned[:50])
- 我们可以从下面的标记中看到,不需要的符号如
(从标记列表中被移除。但是,有些常用词如at和of对分析没有任何价值,可以使用stop word removal方法删除: 

- 停止词是写文本文档时常用的短虚词。它们可能是填充词或介词。NLTK 提供了一个标准的英语停止词集合,可以用来过滤我们文本中的停止词。此外,有时,特定领域的终止词可以用来消除非正式词。我们总是可以从文本中创建一个我们认为与我们的分析无关的单词列表。
 
为了删除停止词,我们首先从ntlk.corpus库中导入stopwords方法。然后我们从stopwords.words方法中调用english停止单词字典,并移除在标记列表中找到的任何常见单词:
# remove the stop words
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
words_1 = [word for word in words_cleaned if not word in stop_words]
print(words_1[:50])
- 从下面的输出中我们可以看到,之前出现的单词如
at、the都从令牌列表中删除了。然而,一些类似的词,如Data和data作为单独的词出现在标记列表中。我们现在需要将这些词转换成类似的情况: 

- 大小写折叠是一种将所有单词转换为相似大小写的方法,以使单词不区分大小写。它通常包括将所有大写字母转换成小写字母。
 
我们可以使用lower函数将所有大写字母转换为小写,如下面的代码片段所示:
# Case folding
words_lower = [words_1.lower() for words_1 in words_1]
print(words_lower[:50])
从下面的输出中我们可以看到Data等单词不再出现在列表中,被转换为全小写字母:

词干是将单词简化为基本或词根形式的过程。比如工作、工作则源于工作。这种转换是有用的,因为它将所有相似的词带到一个基础形式,有助于更好的情感分析、文档分类等等。
我们从nltk.stem.porter库中导入PorterStemmer,并实例化PorterStemmer类。接下来,words_lower标记列表被传递到porter.stem类,以将每个单词简化为其词根形式:
#Stemming
from nltk.stem.porter import PorterStemmer
porter = PorterStemmer()
stemmed_words = [porter.stem(word) for word in words_lower]
print(stemmed_words[:50])
前面的代码生成了以下词干标记列表:

并非所有的特征或属性对 ML 模型都很重要。在接下来的几节中,我们将学习一些在使用 ML 流水线时减少特征数量的方法。
特征选择
ML 模型使用一些关键特征来学习数据中的模式。所有其他特征都会给模型增加噪声,这可能会导致模型精度下降,并使模型过度拟合数据。因此,选择合适的功能是至关重要的。此外,使用一组简化的重要特性可以减少模型训练时间。
以下是创建模型前选择正确特征的一些方法:
- 我们可以识别相关变量并移除任何一个高度相关的值
 - 移除方差较小的特征
 - 测量可用特征集的信息增益,并相应地选择顶部 N 特征
 
此外,在创建基线模型后,我们可以使用以下一些方法来选择正确的特征:
- 使用线性回归并基于 p 值选择变量
 - 使用逐步选择进行线性回归,并选择重要变量
 - 使用随机森林,选择顶部 N 个重要变量
 
在接下来的部分中,我们将看到 scikit 中可用的一些方法-学习减少数据集中可用的要素数量。
排除低方差特征
数据中没有太多差异或可变性的特征不会为学习模式的 ML 模型提供任何信息。例如,数据集中每条记录的值只有 5 的要素是一个常数,并且是一个不重要的要使用的要素。删除此功能至关重要。
我们可以使用 scikit-learn 的featureselection包中的VarianceThreshold方法来移除方差不满足特定标准或阈值的所有特征。sklearn.feature_selection 模块实现了特征选择算法。它目前包括单变量滤波器选择方法和递归特征消除算法。下面是一个例子来说明这种方法:
%matplotlib inline
import pandas as pd
import numpy as np
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
前面代码的输出如下:

接下来,我们将left指定为目标变量,将其他属性指定为独立属性,如下代码所示:
X = data_trnsf.drop('left', axis=1)
X.columns
Y = data_trnsf.left# feature extraction
现在我们已经准备好数据,我们基于VarianceThreshold方法选择特征。首先,我们从 scikit-learn 的feature_selection模块导入VarianceThreshold方法。然后我们在VarianceThreshold方法中将阈值设置为0.2。这意味着,如果某个属性的数据差异小于 20%,它将被丢弃,并且不会被选为特征。我们执行以下代码片段来观察精简后的功能集:
#Variance Threshold
from sklearn.feature_selection import VarianceThreshold
# Set threshold to 0.2
select_features = VarianceThreshold(threshold = 0.2)
select_features.fit_transform(X)
# Subset features
X_subset = select_features.transform(X)
print('Number of features:', X.shape[1])
print('Reduced number of features:',X_subset.shape[1])
从下面的输出中,我们可以确定20属性中有 5 个属性通过了方差阈值测试,并且表现出变异性,变异性大于 20%方差:

在下一节中,我们将研究单变量特征选择方法,该方法基于某些统计测试来确定重要特征。
单变量特征选择
在这种方法中,对每个特征分别进行统计检验。根据测试结果分数,我们只保留最佳特征。
以下示例说明了卡方统计测试,以从HR磨损数据集中选择最佳特征:
#Chi2 Selector
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
chi2_model = SelectKBest(score_func=chi2, k=4)
X_best_feat = chi2_model.fit_transform(X, Y)
# selected features
print('Number of features:', X.shape[1])
print('Reduced number of features:',X_best_feat.shape[1])
从下面的输出中我们可以看到4最佳特征被选中。我们可以通过改变k值来改变要考虑的最佳特征的数量:

下一节演示递归特征消除方法。
递归特征消除
递归特征消除是基于这样的思想:通过去除特征递归地构建模型,用剩余的特征构建模型,并计算模型的精度。重复此过程,直到数据集中的所有要素都用尽。这是一种贪婪的优化方法,寻找性能最好的特征子集,然后根据它们被淘汰的时间对它们进行排序。
在下面的示例代码中,HR磨损数据集用于说明递归特征消除 ( RFE )的使用。RFE方法的稳定性在很大程度上取决于所用算法的类型。为了演示,我们使用了LogisticRegression方法:
#Recursive Feature Elimination
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
# create a base classifier used to evaluate a subset of attributes
logistic_model = LogisticRegression()
# create the RFE model and select 4 attributes
rfe = RFE(logistic_model, 4)
rfe = rfe.fit(X, Y)
# Ranking of the attributes
print(sorted(zip(map(lambda x: round(x, 4), rfe.ranking_),X)))
以下输出显示了按等级排序的要素:

随机森林通常用于 ML 流水线中的特征选择。所以,我们了解这项技术是至关重要的。
使用随机森林的特征选择
随机森林使用的基于树的特征选择策略自然根据它们提高节点纯度的程度进行排序。首先,我们需要构建一个随机森林模型。我们已经在第 2 章中讨论了创建随机森林模型的过程,并介绍了 使用 Python 进行机器学习的过程:
# Feature Importance
from sklearn.ensemble import RandomForestClassifier
# fit a RandomForest model to the data
model = RandomForestClassifier()
model.fit(X, Y)
# display the relative importance of each attribute
print(model.feature_importances_)
print(sorted(zip(map(lambda x: round(x, 4), model.feature_importances_),X)))
一旦模型构建成功,模型的feature_importance_ attribute用于可视化按等级排序的导入要素,如下图所示:

在本节中,我们讨论了使用不同的特征选择方法来选择特征子集的不同方法。接下来,我们将看到使用降维方法的特征选择方法。
使用降维的特征选择
降维方法通过从原始特征的组合中产生新的合成特征来降维。它们是强有力的技术,并且保留了数据的可变性。这些技术的一个缺点是很难解释属性,因为它们是通过组合各种属性的元素来准备的。
主成分分析
主成分分析 ( 主成分分析)将高维空间中的数据转换为更少维度的空间。让我们考虑 100 维数据集的可视化。几乎不可能有效地显示这种高维数据分布的形状。主成分分析提供了一种有效的降维方法,通过形成各种主成分来解释降维空间中数据的可变性。
数学上,给定一组变量, X 1 ,X 2 ,....,X p ,这里有 p 的原始变量。在主成分分析中,我们正在寻找一组新的变量, Z 1 ,Z 2 ,....,Z p ,即原始变量的加权平均值(减去其平均值后):


其中每对 Z 的相关性=0
得到的 Z 按其方差排序,其中Z1T5】方差最大,ZpT9】方差最小。**
Always, the first component extracted in a PCA accounts for a maximum amount of total variance in the observed variables. The second component extracted will account for a maximal amount of variance in the dataset that was not accounted for by the first component and it is also uncorrelated with the first component. If we compute the correlation between the first component and second component the correlation would be zero.
我们使用HR磨损数据来演示主成分分析的使用。首先,我们将numpy和pandas库加载到环境中,并加载HR数据集:
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print (hr_data.head())
以下是前面代码的输出,其中显示了数据集中每个属性的前五行:

主成分分析非常适合数字属性,并且在属性标准化时效果很好。所以,我们从sklearn.preprocessing库中导入StandardScaler。我们只包括数据预处理的数字属性。使用StandardScaler方法,HR数据集的数字属性被标准化:
from sklearn.preprocessing import StandardScaler
hr_numeric = hr_data.select_dtypes(include=[np.float])
hr_numeric_scaled = StandardScaler().fit_transform(hr_numeric)
接下来,我们从sklearn.decomposition导入PCA方法,将n_components作为2传递。n_components参数定义了要构建的主要组件的数量。然后,确定由这两个主成分解释的方差:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
principalComponents = pca.fit_transform(hr_numeric_scaled)
principalDf = pd.DataFrame(data = principalComponents,columns = ['principal component 1', 'principal component 2'])
print(pca.explained_variance_ratio_)
我们可以看到,这两个主成分解释了人力资源数据集数字属性的可变性:

有时,我们使用的原始数据没有足够的信息来创建一个好的模型。在这种情况下,我们需要创建特征。在下一节中,我们将描述几种不同的方法来创建特征。
特征生成
在现有功能的基础上创建新功能是一门艺术,可以通过许多不同的方式来实现。
The objective of feature creation is to provide ML algorithms with such predictors that makes it easy for them to understand the patterns and derive better relationship from the data.
例如在HR减员问题中,员工在组织中的停留时间是一个重要属性。但是,有时我们在数据集中没有停留时间作为特征,但是我们有员工开始日期。在这种情况下,我们可以通过从当前日期减去员工开始日期来创建停留时间特征的数据。
在接下来的部分中,我们将看到从数据中生成新特征的一些不同方法。然而,这不是一个广泛的列表,而是一些可以用来创建新特性的不同方法。一个人需要思考问题陈述,探索数据,并创造性地发现构建功能的新方法:
- 数值特征生成:从数值数据中生成新特征比其他数据类型稍微容易一些。即使我们不理解各种数字特征的含义,我们也可以做各种运算,比如把两个或两个以上的数字相加,计算相对差,再把数字相乘和相除。在这个任务之后,我们从所有生成的特征中识别出什么是重要的特征,并丢弃其他特征。尽管这是一项资源密集型任务,但当我们不知道派生新特性的直接方法时,它有助于发现新特性。
 
添加和计算一对数字特征之间差异的过程称为成对特征创建。
还有一种称为PolynomialFeatures创建的方法,我们自动执行特征的所有多项式组合。它有助于映射可能暗示某些独特状态的要素之间的复杂关系。
我们可以使用 scikit-learn 的PolynomialFeatures方法生成多项式特征。让我们首先创建虚拟数据,如下面的代码片段所示:
#Import PolynomialFeatures
from sklearn.preprocessing import PolynomialFeatures
#Create matrix and vectors
var1 = [[0.45, 0.72], [0.12, 0.24]]
接下来,通过首先调用带有参数度的PolynomialFeatures来生成多项式特征。该函数将生成度数小于或等于指定度数的要素:
# Generate Polynomial Features 
ploy_features = PolynomialFeatures(degree=2)
var1_ = ploy_features.fit_transform(var1)
print(var1_)
代码执行完成后,它会生成新的特性,如下图所示:

- 分类特征创建:从分类数据中创建新特征的方法有限。然而,我们可以计算每个分类属性的频率,或者组合不同的变量来构建新的特征。
 - 时态特征创建:如果遇到日期/时间特征,可以衍生出各种新的特征,例如:
- 一周中的某一天
 - 每月的某一天
 - 季度日
 - 一年中的某一天
 - 一天中的一小时
 - 今天的第二天
 - 一天中的一周
 - 一年中的一周
 - 一年中的月份
 
 
从单个数据/时间特征中创建这些特征将有助于 ML 算法更好地学习数据中的时间模式。
摘要
在本章中,我们学习了与机器学习流水线非常相关的各种数据转换和预处理方法。准备属性、清理数据并确保数据没有错误,可以确保 ML 模型正确学习数据。使数据无噪声并生成良好的特征有助于 ML 模型有效地发现数据中的模式。
下一章将集中讨论自动语言算法的技术。我们将讨论各种特定于算法的特征转换,自动化有监督和无监督的学习,等等。
四、自动算法选择
本章简要介绍了机器学习 ( ML )算法的广阔前景。鸟瞰图将向您展示您可以用 ML 解决的那种学习问题,您已经学过了。让我们简单回顾一下。
如果数据集中的示例/观察具有关联的标签,则这些标签可以在模型训练期间为算法提供指导。有了这个指导或监督,你将使用监督或半监督学习算法。如果你没有标签,你将使用无监督学习算法。
还有其他需要不同方法的情况,例如强化学习,但是,在这一章中,主要重点将是有监督和无监督的算法。
ML 流水线的下一个前沿是自动化。当您第一次考虑自动化 ML 流水线时,核心元素是特征转换、模型选择和超参数优化。但是,对于您的具体问题,还需要考虑其他一些问题,您将在本章中研究以下几点:
- 计算的复杂性
 - 训练和得分时间的差异
 - 线性与非线性
 - 特定于算法的特征变换
 
了解这些将有助于您了解哪些算法可能适合您对给定问题的需求。本章结束时:
- 您将已经学习了自动监督学习和无监督学习的基础知识
 - 您将了解使用 ML 流水线时需要考虑的主要方面
 - 您将在各种用例上练习您的技能,并构建有监督和无监督的 ML 流水线
 
技术要求
查看requirements.txt文件,查找要安装的库,以便在本章的 GitHub 中运行代码示例。
所有代码示例都可以在 GitHub 的Chapter 04文件夹中找到。
计算的复杂性
计算效率和复杂性是选择 ML 算法的重要方面,因为它们将决定模型训练和评分在时间和内存需求方面所需的资源。
例如,计算密集型算法需要更长的时间来训练和优化其超参数。您通常会在可用的中央处理器或图形处理器之间分配工作负载,以将花费的时间减少到可接受的水平。
在本节中,将根据这些约束来检查一些算法,但是在深入了解 ML 算法的细节之前,您需要了解算法复杂性的基础知识。
The complexity of an algorithm will be based on its input size. For ML algorithms, this could be the number of elements and features. You will usually count the number of operations needed to complete the task in the worst-case scenario and that will be your algorithm's complexity.
大 O 符号
你可能听说过大 O 符号。它有不同的表示复杂度的类,如线性— O(n)、对数— O(log n)、二次— O(n2)、三次— O(n3),以及类似的类。您使用大 O 的原因是因为算法的运行时间高度依赖于硬件,并且您需要一种系统的方法来根据输入的大小来衡量算法的性能。大 O 查看算法的步骤,并计算出上面提到的最坏情况。
例如,如果n是您想要追加到列表中的元素的数量,那么它的复杂性就是O(n),因为追加操作的数量取决于n。下面的代码块将帮助您绘制不同复杂性如何随着输入大小的变化而增长:
# Importing necessary libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Setting the style of the plot
plt.style.use('seaborn-whitegrid')
# Creating an array of input sizes
n = 10
x = np.arange(1, n)
# Creating a pandas data frame for popular complexity classes
df = pd.DataFrame({'x': x,
                   'O(1)': 0,
                   'O(n)': x,
                   'O(log_n)': np.log(x),
                   'O(n_log_n)': n * np.log(x),
                   'O(n2)': np.power(x, 2), # Quadratic
                   'O(n3)': np.power(x, 3)}) # Cubic
# Creating labels
labels = ['$O(1) - Constant$',
          '$O(\log{}n) - Logarithmic$',
          '$O(n) - Linear$',
          '$O(n^2) - Quadratic$',
          '$O(n^3) - Cubic$',
          '$O(n\log{}n) - N log n$']
# Plotting every column in dataframe except 'x'
for i, col in enumerate(df.columns.drop('x')):
    print(labels[i], col)
    plt.plot(df[col], label=labels[i])
# Adding a legend
plt.legend()
# Limiting the y-axis
plt.ylim(0,50)
plt.show()
作为前面代码的输出,我们得到如下图:

Different complexities grow as a function of their input size
这里需要注意的一点是,不同复杂程度之间有一些交叉点。这显示了数据大小的作用。简单例子的复杂性很容易理解,但是 ML 算法的复杂性呢?如果到目前为止的介绍已经激起了你的兴趣,继续阅读下一部分。
训练和得分时间的差异
花在训练和评分上的时间可以决定一个 ML 项目的成败。如果一个算法在当前可用的硬件上训练时间太长,用新数据和超参数优化更新模型将是痛苦的,这可能会迫使你从候选列表中划掉该算法。如果一个算法花费太长时间来评分,那么这可能是生产环境中的一个问题,因为您的应用程序可能需要快速的推理时间,例如毫秒或微秒来获得预测。这就是为什么学习最大似然算法的内部工作方式很重要,至少在开始的时候是常见的,以检测算法的适用性。
例如,监督学习算法在训练过程中学习示例集及其相关标签之间的关系,其中每个示例由一组特征组成。训练作业在成功完成后将输出一个 ML 模型,该模型可用于进行新的预测。当一个模型被输入没有标签的新例子时,在训练期间在特征和标签之间映射的关系被用来预测标签。用于预测的时间通常很少,因为模型的学习权重将应用于新数据。
然而,一些有监督的算法跳过这个训练阶段,它们基于训练数据集中所有可用的例子进行评分。这种算法被称为基于实例的或懒惰学习者。对于基于实例的算法,训练仅仅意味着将所有特征向量及其相关标签存储在内存中,这是整个训练数据集。这实际上意味着随着数据集大小的增加,模型将需要更多的计算和内存资源。
训练和得分时间的简单度量
让我们看一个快速的例子 k 最近邻 ( k-NN )算法,它对分类和回归问题都有效。当一个算法对一个新的特征向量进行评分时,它会检查 k 最近的邻居并输出一个结果。如果是分类问题,使用多数票进行预测;如果是回归问题,那么取值的平均值作为预测。
让我们通过处理一个示例分类问题来更好地理解这一点。首先,您将创建一个样本数据集,并根据训练和评分所花费的时间来检查 k-NN 算法。
为了使事情变得更简单,下面的函数将用于测量在给定线路上花费的时间:
from contextlib import contextmanager
from time import time
@contextmanager
def timer():
    s = time()
    yield
    e = time() - s
    print("{0}: {1} ms".format('Elapsed time', e))
您可以通过以下方式使用此功能:
import numpy as np
with timer():
    X = np.random.rand(1000)
它输出执行该行所花费的时间:
Elapsed time: 0.0001399517059326172 ms
现在,您可以使用 scikit-learn 库的KNeighborsClassifier来测量培训和评分所花费的时间:
from sklearn.neighbors import KNeighborsClassifier
# Defining properties of dataset
nT = 100000000 # Total number of values in our dataset
nF = 10 # Number of features
nE = int(nT / nF) # Number of examples
# Creating n x m matrix where n=100 and m=10
X = np.random.rand(nT).reshape(nE, nF)
# This will be a binary classification with labels 0 and 1
y = np.random.randint(2, size=nE)
# Data that we are going to score
scoring_data = np.random.rand(nF).reshape(1,-1)
# Create KNN classifier
knn = KNeighborsClassifier(11, algorithm='brute')
# Measure training time
with timer():
    knn.fit(X, y)
# Measure scoring time
with timer():
    knn.predict(scoring_data)
让我们看看输出:
Elapsed time: 1.0800271034240723 ms
Elapsed time: 0.43231201171875 ms
为了了解这与其他算法相比如何,您可以尝试多一个分类器,例如逻辑回归:
from sklearn.linear_model import LogisticRegression
log_res = LogisticRegression(C=1e5)
with timer():
    log_res.fit(X, y)
with timer():
    prediction = log_res.predict(scoring_data)
逻辑回归的输出如下:
Elapsed time: 12.989803075790405 ms
Elapsed time: 0.00024318695068359375 ms
看起来很不一样!逻辑回归在训练中较慢,在评分中快得多。为什么会这样?
您将了解这个问题的答案,但是,在详细讨论前面的结果之前,让我们先来谈谈 Python 中的代码分析。
Python 中的代码分析
一些应用程序将要求您的机器学习模型在训练和评分时间方面表现出色。例如,推荐引擎可能要求您在不到一秒钟的时间内生成推荐,如果您有超过一秒钟的延迟,那么概要分析是理解密集操作的一种方式。代码分析将帮助你很好地理解程序的不同部分是如何执行的。分析统计信息将给出指标,如调用次数、执行函数调用(包括/不包括对其子函数的调用)所花费的总时间,以及增量和总内存使用量。
Python 中的cProfile模块将帮助您查看每个功能的时间统计。这里有一个小例子:
# cProfile
import cProfile
cProfile.run('np.std(np.random.rand(1000000))')
在前一行中,从均匀分布中抽取的 1,000,000 个随机样本计算标准偏差。输出将显示执行给定行的所有函数调用的时间统计:
23 function calls in 0.025 seconds
   Ordered by: standard name
   ncalls tottime percall cumtime percall filename:lineno(function)
        1 0.001 0.001 0.025 0.025 <string>:1(<module>)
        1 0.000 0.000 0.007 0.007 _methods.py:133(_std)
        1 0.000 0.000 0.000 0.000 _methods.py:43(_count_reduce_items)
        1 0.006 0.006 0.007 0.007 _methods.py:86(_var)
        1 0.001 0.001 0.008 0.008 fromnumeric.py:2912(std)
        2 0.000 0.000 0.000 0.000 numeric.py:534(asanyarray)
        1 0.000 0.000 0.025 0.025 {built-in method builtins.exec}
        2 0.000 0.000 0.000 0.000 {built-in method builtins.hasattr}
        4 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}
        2 0.000 0.000 0.000 0.000 {built-in method builtins.issubclass}
        1 0.000 0.000 0.000 0.000 {built-in method builtins.max}
        2 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.array}
        1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1 0.017 0.017 0.017 0.017 {method 'rand' of 'mtrand.RandomState' objects}
        2 0.001 0.001 0.001 0.001 {method 'reduce' of 'numpy.ufunc' objects}
23函数调用在0.025秒内执行,大部分时间都花在生成随机数和计算标准差上,如您所料。
有一个很棒的库叫做snakeviz,可以用来可视化cProfile的输出。创建一个名为profiler_example_1.py的文件,并添加以下代码:
import numpy as np
np.std(np.random.rand(1000000))
在您的终端中,导航到您拥有profiler_example_1.py的文件夹,并运行以下命令:
python -m cProfile -o profiler_output -s cumulative profiler_example_1.py
这将创建一个名为profiler_output的文件,现在你可以使用snakeviz来创建一个可视化
可视化性能统计
Snakeviz 是基于浏览器的,它将允许您与性能指标进行交互。snakeviz将使用由名为profiler_output的分析器生成的文件来创建可视化:
snakeviz profiler_output
这个命令将在127.0.0.1:8080上运行一个小的网络服务器,它将为你提供一个你可以找到你的可视化的地址,比如http://127.0.0.1:8080/snakeviz/…/2FAutomated_Machine_Learning%2FCh4_Automated_Algorithm_Selection%2Fprofiler_output。
在这里,您可以看到带有各种设置的阳光爆发样式图表,例如深度和截止:

您可以将鼠标悬停在上面,它会显示功能名称、累计时间、文件、行和目录。您可以深入到特定区域并查看详细信息。
如果选择 Icicle 样式,您将看到不同的可视化效果:

您可以使用“样式”、“深度”和“截止”来查看哪些设置最适合您。
如果向下滚动到底部,会出现一个类似于下面截图的表格:

如果您按照percall列对这些值进行排序,您会看到mtrand.RandomState对象的rand方法和_var方法是最耗时的调用。
您可以检查以这种方式运行的任何东西,这是更好地理解和诊断代码的良好的第一步。
从头开始实现 k-NN
你已经看到了 k-NN 算法在起作用;让我们看一个非常简单的实现。将以下代码块保存为knn_prediction.py:
import numpy as np
import operator
# distance module includes various distance functions
# You will use euclidean distance function to calculate distances between scoring input and training dataset.
from scipy.spatial import distance
# Decorating function with @profile to get run statistics
@profile
def nearest_neighbors_prediction(x, data, labels, k):
    # Euclidean distance will be calculated between example to be predicted and examples in data
    distances = np.array([distance.euclidean(x, i) for i in data])
    label_count = {}
    for i in range(k):
        # Sorting distances starting from closest to our example
        label = labels[distances.argsort()[i]]
        label_count[label] = label_count.get(label, 0) + 1
    votes = sorted(label_count.items(), key=operator.itemgetter(1), reverse=True)
    # Return the majority vote
    return votes[0][0]
# Setting seed to make results reproducible
np.random.seed(23)
# Creating dataset, 20 x 5 matrix which means 20 examples with 5 features for each
data = np.random.rand(100).reshape(20,5)
# Creating labels
labels = np.random.choice(2, 20)
# Scoring input
x = np.random.rand(5)
# Predicting class for scoring input with k=2
pred = nearest_neighbors_prediction(x, data, labels, k=2)
# Output is ‘0’ in my case
您将分析这个函数,看看每一行执行需要多长时间。
逐行剖析您的 Python 脚本
转到您的终端并运行以下命令:
$ pip install line_profiler
安装完成后,您可以将前面的片段保存到文件名knn_prediction.py中。
您已经注意到,nearest_neighbors_prediction的装饰如下:
@profile
def nearest_neighbors_prediction(x, data, labels, k):
 …
它允许line_profiler知道要剖析哪个功能。运行以下命令保存配置文件结果:
$ kernprof -l knn_prediction.py
输出如下:
Start
Wrote profile results to knn_prediction.py.lprof
您可以按如下方式查看探查器结果:
$ python -m line_profiler knn_prediction.py.lprof
Timer unit: 1e-06 s
Total time: 0.001079 s
File: knn_prediction.py
Function: nearest_neighbors_prediction at line 24
Line # Hits Time Per Hit % Time Line Contents
==============================================================
 24 @profile
 25 def nearest_neighbors_prediction(x, data, labels, k):
 26 
 27 # Euclidean distance will be calculated between example to be predicted and examples in data
 28 1 1043.0 1043.0 96.7 distances = np.array([distance.euclidean(x, i) for i in data])
 29 
 30 1 2.0 2.0 0.2 label_count = {}
 31 3 4.0 1.3 0.4 for i in range(k):
 32 # Sorting distances starting from closest to our example
 33 2 19.0 9.5 1.8 label = labels[distances.argsort()[i]]
 34 2 3.0 1.5 0.3 label_count[label] = label_count.get(label, 0) + 1
 35 1 8.0 8.0 0.7 votes = sorted(label_count.items(), key=operator.itemgetter(1), reverse=True)
 36 
 37 # Return the majority vote
 38 1 0.0 0.0 0.0 return votes[0][0]
最耗时的部分是计算距离,正如您所料。
In terms of big O notation, the complexity of the k-NN algorithm is O(nm + kn), where n is the number of examples, m is the number of features, and k is the algorithm's hyperparameter. You can think about the reason as an exercise for now.
每个算法都有相似的属性,您应该知道这些属性会影响算法的训练和评分时间。这些限制对于生产用例变得尤为重要。
线性与非线性
另一个考虑因素是决策边界。一些算法,如逻辑回归或支持向量机 ( SVM )可以学习线性决策边界,而另一些算法,如基于树的算法,可以学习非线性决策边界。虽然线性决策边界相对容易计算和解释,但您应该意识到线性算法在存在非线性关系时会产生的错误。
绘制决策边界
下面的代码片段将允许您检查不同类型算法的决策边界:
import matplotlib.cm as cm
# This function will scale training datatset and train given classifier.
# Based on predictions it will draw decision boundaries.
def draw_decision_boundary(clf, X, y, h = .01, figsize=(9,9), boundary_cmap=cm.winter, points_cmap=cm.cool):
    # After you apply StandardScaler, feature means will be removed and all features will have unit variance.
    from sklearn.preprocessing import StandardScaler
    X = StandardScaler().fit_transform(X)
    # Splitting dataset to train and test sets.
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.4, random_state=42)
    # Training given estimator on training dataset by invoking fit function.
    clf.fit(X_train, y_train)
    # Each estimator has a score function.
    # Score will show you estimator's performance by showing metric suitable to given classifier.
    # For example, for linear regression, it will output coefficient of determination R^2 of the prediction.
    # For logistic regression, it will output mean accuracy.
    score = clf.score(X_test, y_test)
    print("Score: %0.3f" % score)
    # Predict function of an estimator, will predict using trained model
    pred = clf.predict(X_test)
    # Figure is a high-level container that contains plot elements
    figure = plt.figure(figsize=figsize)
    # In current figure, subplot will create Axes based on given arguments (nrows, ncols, index)
    ax = plt.subplot(1, 1, 1)
    # Calculating min/max of axes
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    # Meshgrid is usually used to evaluate function on grid.
    # It will allow you to create points to represent the space you operate
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Generate predictions for all the point-pair created by meshgrid
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # This will draw boundary
    ax.contourf(xx, yy, Z, cmap=boundary_cmap)
    # Plotting training data
    ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=points_cmap, edgecolors='k')
    # Potting testing data
    ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=points_cmap, alpha=0.6, edgecolors='k')
    # Showing your masterpiece
    figure.show()
逻辑回归的决策边界
您可以从逻辑回归开始测试这个函数:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
# sklearn.linear_model includes regression models where target variable is a linear combination of input variables
from sklearn.linear_model import LogisticRegression
# make_moons is another useful function to generate sample data
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
X, y = make_moons(n_samples=1000, noise=0.1, random_state=0)
# Plot sample data
plt.scatter(X[:,0], X[:,1], c=y, cmap=cm.cool)
plt.show()
我们得到下面的图:

现在,您可以使用draw_decision_boundary功能来可视化LogisticRegression的决策边界:
draw_decision_boundary(LogisticRegression(), X, y)
它将输出以下图表:

逻辑回归是广义线性模型的一个成员,它产生一个线性决策边界。线性决策边界不能为这样的数据集划分类别。逻辑回归的输出是根据其输入的加权和计算的。由于输出不依赖于其参数的乘积或商,它将产生一个线性决策边界。有一些方法可以克服这个问题,例如正则化和特征映射,但是在这种情况下,您可以使用其他能够处理非线性数据的算法。
随机森林的决策边界
随机森林是一个元估计器,它将建立许多不同的模型,并汇总它们的预测,得出最终的预测。随机森林能够产生非线性决策边界,因为输入和输出之间没有线性关系。它有许多超参数可以使用,但为了简单起见,您将使用默认配置:
from sklearn.ensemble import RandomForestClassifier
draw_decision_boundary(RandomForestClassifier(), X, y)
从前面的代码中,我们得到了下面的图:

一点也不难看!每种算法都将根据它们的内部工作原理为您提供不同的决策边界,您肯定应该尝试不同的估计器来更好地理解它们的行为。
常用的机器学习算法
作为练习,以下是常用的有监督和无监督算法列表;scikit-learn 拥有大部分功能:
- 
监督算法:
- 线性回归
 - 逻辑回归
 - 神经网络
 - 随机森林
 - 增强算法(GBM、XGBoost 和 LightGBM)
 - SVM
 - 神经网络
 
 - 
无监督算法:
- k 均值
 - 分级聚类
 - 主成分分析
 - 混合模型
 - 自动编码器
 
 
必要的特征变换
正如您可能已经注意到的,在训练机器学习算法之前,在前面的部分中对特征进行了缩放。特征变换通常是 ML 算法正常工作所必需的。例如,根据经验,对于使用正则化的 ML 算法,通常将归一化应用于特征。
下面是一个用例列表,在这些用例中,您应该转换您的要素,以便为 ML 算法准备好数据集:
- SVM 预计其投入将在标准范围内。在将变量输入到算法中之前,您应该对它们进行规范化。
 - 主成分分析 ( 主成分分析)帮助你基于方差最大化将你的特征投影到另一个空间。然后,您可以选择覆盖数据集中大部分方差的组件,留下其余的组件来降低维度。当您使用主成分分析时,您可以应用归一化,因为一些特征似乎可以解释由于尺度差异而导致的几乎所有差异。您可以通过规范化功能来消除比例差异,如您将在下一节的一些示例中看到的。
 - 如果您正在使用正则化回归,这通常是高维数据集的情况,您将规范化您的变量以控制比例,因为正则化不是比例不变的。
 - 要使用朴素贝叶斯算法,其中特征和标签列应该是分类的,您应该转换您的连续变量,使它们通过应用宁滨离散化。
 - 在时间序列中,通常应用对数变换来处理指数增长的趋势,以便具有线性趋势和恒定的方差。
 - 当处理非数值变量(如分类数据)时,您将通过应用转换(如一次性编码、虚拟编码或特征散列)将它们编码为数值特征。
 
监督最大似然
除了前面提到的特征变换,每个 ML 算法都有自己的超参数空间需要优化。您可以将搜索最佳的 ML 流水线视为遍历您的配置空间,并以明智的方式尝试您的选项,以找到性能最佳的 ML 流水线。
Auto-sklearn 对实现这一目标非常有帮助,您在介绍性章节中看到的例子向您展示了库的易用性。本节将解释为使这一实施成功,幕后发生了什么。
Auto-sklearn 使用元学习根据给定数据集的属性选择有前途的数据/特征处理器和 ML 算法。有关预处理方法、分类器和回归器的列表,请参考以下链接:
- 分类器(https://github . com/automl/auto-sklearn/tree/master/auto klearn/pipeline/components/classing)
 - 回归器(https://github . com/automl/auto-sklearn/tree/master/auto klearn/pipeline/components/revolution)
 - 预处理程序(https://github . com/automl/auto-sklearn/tree/master/auto klearn/pipeline/components/feature _ premination)
 
元学习通过分析不同数据集上 ML 流水线的性能来模仿数据科学家的经验,并将这些发现与新数据集进行匹配,以对初始配置提出建议。
一旦元学习创建了初始配置,贝叶斯优化将处理调整不同流水线的超参数,顶级流水线将用于创建一个可能优于其任何成员的集成,并有助于避免过度拟合。
auto-sklearn 的默认配置
当你创建一个AutoSklearnClassifier对象时,顺便说一下,你很快就会做,有一些默认的配置你需要知道;您可以通过运行以下代码来查看它们:
from autosklearn.classification import AutoSklearnClassifier
AutoSklearnClassifier?
在 Python 中,在函数后添加?会输出非常有用的信息,比如签名、文档字符串、参数说明、属性和文件位置。
如果您查看签名,您将看到默认值:
Init signature: AutoSklearnClassifier(time_left_for_this_task=3600, per_run_time_limit=360, initial_configurations_via_metalearning=25, ensemble_size=50, ensemble_nbest=50, seed=1, ml_memory_limit=3072, include_estimators=None, exclude_estimators=None, include_preprocessors=None, exclude_preprocessors=None, resampling_strategy='holdout', resampling_strategy_arguments=None, tmp_folder=None, output_folder=None, delete_tmp_folder_after_terminate=True, delete_output_folder_after_terminate=True, shared_mode=False, disable_evaluator_output=False, get_smac_object_callback=None, smac_scenario_args=None)
例如,time_left_for_this_task设置为 60 分钟。如果您正在处理一个相当复杂的数据集,您应该将该参数设置为更高的值,以增加找到更好的 ML 流水线的机会。
另一个是per_run_time_limit,设置为六分钟。许多最大似然算法的训练时间与输入数据大小成正比,加上训练时间也会受到算法复杂度的影响。您应该相应地设置此参数。
ensemble_size和ensemble_nbest是集合相关参数,用于设置集合中包含的最佳模型的大小和数量。
ml_memory_limit是一个重要的参数,因为,如果你的算法需要更多的内存,训练将被取消。
通过提供使用以下参数的列表,您可以在 ML 流水线中包括/排除特定的数据预处理程序或估计程序:include_estimators、exclude_estimators、include_preprocessors和exclude_preprocessors
resampling_strategy会给你选项来决定如何处理过拟合。
您可以检查签名中的其余参数,看看是否需要对您的环境进行任何特定的调整。
寻找产品线预测的最佳最大似然流水线
让我们先编写一个小包装函数,通过编码分类变量来准备数据集:
# Importing necessary variables
import numpy as np
import pandas as pd
from autosklearn.classification import AutoSklearnClassifier
from autosklearn.regression import AutoSklearnRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
import wget
import pandas as pd
# Machine learning algorithms work with numerical inputs and you need to transform all non-numerical inputs to numerical ones
# Following snippet encode the categorical variables
link_to_data = 'https://apsportal.ibm.com/exchange-api/v1/entries/8044492073eb964f46597b4be06ff5ea/data?accessKey=9561295fa407698694b1e254d0099600'
filename = wget.download(link_to_data)
print(filename)
# GoSales_Tx_NaiveBayes.csv
df = pd.read_csv('GoSales_Tx_NaiveBayes.csv')
df.head()
这将输出数据帧的前五条记录:
# PRODUCT_LINE GENDER AGE MARITAL_STATUS PROFESSION
# 0 Personal Accessories M 27 Single Professional
# 1 Personal Accessories F 39 Married Other
# 2 Mountaineering Equipment F 39 Married Other
# 3 Personal Accessories F 56 Unspecified Hospitality
# 4 Golf Equipment M 45 Married Retired
该数据集中有四个要素(GENDER、AGE、MARITAL_STATUS、PROFESSION)和一个标签(PRODUCT_LINE)列。目标是预测客户感兴趣的产品线。
您需要对要素和标签的文本数据进行编码。可以应用LabelEncoder:
df = df.apply(LabelEncoder().fit_transform)
df.head()
这将对label列进行编码:
#   PRODUCT_LINE GENDER AGE MARITAL_STATUS PROFESSION
# 0 4 1 27 1 3
# 1 4 0 39 0 2
# 2 2 0 39 0 2
# 3 4 0 56 2 1
# 4 1 1 45 0 5
如您所见,所有分类列都是编码的。请记住,在 auto-sklearn 的 API 中,您有feat_type参数,该参数允许您将列指定为Categorical或Numerical:
feat_type : list, optional (default=None)
描述属性类型的len(X.shape[1])的str列表。可能的类型有Categorical和Numerical。分类属性将被自动一键编码。用于分类属性的值必须是整数,例如通过sklearn.preprocessing.LabelEncoder获得。
然而,在这个例子中,你也可以使用熊猫数据框的apply功能。
以下包装函数将使用 auto-sklearn 的自动分类或自动回归算法处理输入数据并运行实验:
# Function below will encode the target variable if needed
def encode_target_variable(df=None, target_column=None, y=None):
    # Below section will encode target variable if given data is pandas dataframe
    if df is not None:
        df_type = isinstance(df, pd.core.frame.DataFrame)
        # Splitting dataset as train and test data sets
        if df_type:
            # If column data type is not numeric then labels are encoded
            if not np.issubdtype(df[target_column].dtype, np.number):
                le = preprocessing.LabelEncoder()
                df[target_column] = le.fit_transform(df[target_column])
                return df[target_column]
            return df[target_column]
    # Below section will encode numpy array.
    else:
        # numpy array's data type is not numeric then labels are encoded
        if not np.issubdtype(y.dtype, np.number):
            le = preprocessing.LabelEncoder()
            y = le.fit_transform(y)
            return y
        return y
# Create a wrapper function where you can specify the type of learning problem
def supervised_learner(type, X_train, y_train, X_test, y_test):
    if type == 'regression':
        # You can play with time related arguments for discovering more pipelines
        automl = AutoSklearnRegressor(time_left_for_this_task=7200, per_run_time_limit=720)
    else:
        automl = AutoSklearnClassifier(time_left_for_this_task=7200, per_run_time_limit=720)
    # Training estimator based on learner type
    automl.fit(X_train, y_train)
    # Predicting labels on test data
    y_hat = automl.predict(X_test)
    # Calculating accuracy_score
    metric = accuracy_score(y_test, y_hat)
    # Return model, labels and metric
    return automl, y_hat, metric
# In function below, you need to provide numpy array or pandas dataframe together with the name of the target column as arguments
def supervised_automl(data, target_column=None, type=None, y=None):
    # First thing is to check whether data is pandas dataframe
    df_type = isinstance(data, pd.core.frame.DataFrame)
    # Based on data type, you will split dataset as train and test data sets
    if df_type:
        # This is where encode_target_variable function is used before data split
        data[target_column] = encode_target_variable(data, target_column)
        X_train, X_test, y_train, y_test = \
            train_test_split(data.loc[:, data.columns != target_column], data[target_column], random_state=1)
    else:
        y_encoded = encode_target_variable(y=y)
        X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, random_state=1)
    # If learner type is given, then you invoke supervied_learner
    if type != None:
        automl, y_hat, metric = supervised_learner(type, X_train, y_train, X_test, y_test)
    # If type of learning problem is not given, you need to infer it
    # If there are more than 10 unique numerical values, problem will be treated as regression problem,
    # Otherwise, classification problem
    elif len(df[target_column].unique()) > 10:
            print("""There are more than 10 uniques numerical values in target column. 
            Treating it as regression problem.""")
            automl, y_hat, metric = supervised_learner('regression', X_train, y_train, X_test, y_test)
    else:
        automl, y_hat, metric = supervised_learner('classification', X_train, y_train, X_test, y_test)
    # Return model, labels and metric
    return automl, y_hat, metric
您现在可以运行它来查看结果:
automl, y_hat, metric = supervised_automl(df, target_column='PRODUCT_LINE')
以下输出显示了所选模型及其参数:
automl.get_models_with_weights()
 [(1.0,
  SimpleClassificationPipeline({'balancing:strategy': 'none', 'categorical_encoding:__choice__': 'no_encoding', 'classifier:__choice__': 'gradient_boosting', 'imputation:strategy': 'most_frequent', 'preprocessor:__choice__': 'feature_agglomeration', 'rescaling:__choice__': 'robust_scaler', 'classifier:gradient_boosting:criterion': 'friedman_mse', 'classifier:gradient_boosting:learning_rate': 0.6019977814828193, 'classifier:gradient_boosting:loss': 'deviance', 'classifier:gradient_boosting:max_depth': 5, 'classifier:gradient_boosting:max_features': 0.4884281825655421, 'classifier:gradient_boosting:max_leaf_nodes': 'None', 'classifier:gradient_boosting:min_impurity_decrease': 0.0, 'classifier:gradient_boosting:min_samples_leaf': 20, 'classifier:gradient_boosting:min_samples_split': 7, 'classifier:gradient_boosting:min_weight_fraction_leaf': 0.0, 'classifier:gradient_boosting:n_estimators': 313, 'classifier:gradient_boosting:subsample': 0.3242201709371377, 'preprocessor:feature_agglomeration:affinity': 'cosine', 'preprocessor:feature_agglomeration:linkage': 'complete', 'preprocessor:feature_agglomeration:n_clusters': 383, 'preprocessor:feature_agglomeration:pooling_func': 'mean', 'rescaling:robust_scaler:q_max': 0.75, 'rescaling:robust_scaler:q_min': 0.25},
  dataset_properties={
    'task': 1,
    'sparse': False,
    'multilabel': False,
    'multiclass': False,
    'target_type': 'classification',
    'signed': False}))]
您可能会看到通常会选择梯度增强算法,这是有充分理由的。目前,在 ML 社区中,基于 boosting 的算法是最先进的,最受欢迎的是 XGBoost 、 LightGBM 和 CatBoost 。
Auto-sklearn 为 sklearn 的GradientBoostingClassifier提供支持,XGBoost 目前由于集成问题被禁用,但预计很快会被添加回来。
寻找网络异常检测的最佳机器学习流水线
让我们在 ML 社区中流行的另一个数据集上运行这个流水线。KDDCUP 99数据集是 1998 年 DARPA Intrusion Detection System Evaluation数据集的 tcpdump 部分,目标是检测网络入侵。它包括数字特征,因此更容易设置我们的自动管线:
# You can import this dataset directly from sklearn
from sklearn.datasets import fetch_kddcup99
# Downloading subset of whole dataset
dataset = fetch_kddcup99(subset='http', shuffle=True, percent10=True)
# Downloading https://ndownloader.figshare.com/files/5976042
# [INFO] [17:43:19:sklearn.datasets.kddcup99] Downloading https://ndownloader.figshare.com/files/5976042
X = dataset.data
y = dataset.target
# 58725 examples with 3 features
X.shape
# (58725, 3)
y.shape
(58725,)
# 5 different classes to represent network anomaly
from pprint import pprint
pprint(np.unique(y))
# array([b'back.', b'ipsweep.', b'normal.', b'phf.', b'satan.'], dtype=object)
automl, y_hat, metric = supervised_automl(X, y=y, type='classification')
无人监管的 AutoML
当数据集没有目标变量时,可以根据不同的特征,使用聚类算法来探索它。这些算法将示例组合在一起,这样每个组的示例将尽可能相似,但与其他组的示例不同。
因为在执行这种分析时,您通常没有标签,所以有一个性能指标可以用来检查算法所得到的分离的质量。
它被称为轮廓系数。轮廓系数将帮助您理解两件事:
- 凝聚力:集群内的相似性
 - 分离:簇之间的不同
 
它将为您提供一个介于 1 和-1 之间的值,接近 1 的值表示形成良好的集群。
如果您的培训数据中有标签,您还可以使用其他指标,如同质性和完整性,您将在本章后面看到。
聚类算法被用来处理许多不同的任务,例如寻找相似的用户、歌曲或图像,检测模式中的关键趋势和变化,理解社交网络中的社区结构。
常用的聚类算法
有两种常用的聚类算法:基于距离的和概率模型。例如,k-means 和基于密度的噪声应用空间聚类 ( DBSCAN )是基于距离的算法,而高斯混合模型是概率的。
基于距离的算法可以使用各种距离度量,其中通常使用欧几里德距离度量。
概率算法将假设存在一个概率分布与未知参数混合的生成过程,目标是从数据中计算这些参数。
由于有许多聚类算法,选择正确的算法取决于数据的特征。例如,k-means 将处理聚类的质心,这要求数据中的聚类大小均匀,形状凸起。这意味着 k-means 在拉长的簇或不规则形状的流形上不能很好地工作。当数据中的聚类大小不均匀或形状不规则时,您可能希望使用 DBSCAN 来对任何形状的区域进行聚类。
对你的数据略知一二会让你更接近找到正确的算法,但是如果你对你的数据了解不多呢?很多时候,当你进行探索性分析时,你可能很难理解正在发生的事情。如果您发现自己处于这种情况,自动化的无监督 ML 流水线可以帮助您更好地理解数据的特征。
但是,在执行这种分析时要小心;你以后将采取的行动将由你将看到的结果驱动,如果你不谨慎,这可能会很快让你走上错误的道路。
使用 sklearn 创建样本数据集
在sklearn中,有一些有用的方法来创建用于测试算法的样本数据集:
# Importing necessary libraries for visualization
import matplotlib.pyplot as plt
import seaborn as sns
# Set context helps you to adjust things like label size, lines and various elements
# Try "notebook", "talk" or "paper" instead of "poster" to see how it changes
sns.set_context('poster')
# set_color_codes will affect how colors such as 'r', 'b', 'g' will be interpreted
sns.set_color_codes()
# Plot keyword arguments will allow you to set things like size or line width to be used in charts.
plot_kwargs = {'s': 10, 'linewidths': 0.1}
import numpy as np
import pandas as pd
# Pprint will better output your variables in console for readability
from pprint import pprint
# Creating sample dataset using sklearn samples_generator
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import StandardScaler
# Make blobs will generate isotropic Gaussian blobs
# You can play with arguments like center of blobs, cluster standard deviation
centers = [[2, 1], [-1.5, -1], [1, -1], [-2, 2]]
cluster_std = [0.1, 0.1, 0.1, 0.1]
# Sample data will help you to see your algorithms behavior
X, y = make_blobs(n_samples=1000,
                  centers=centers,
                  cluster_std=cluster_std,
                  random_state=53)
# Plot generated sample data
plt.scatter(X[:, 0], X[:, 1], **plot_kwargs)
plt.show()
从前面的代码中,我们得到了下面的图:

cluster_std会影响弥散量。将其更改为[0.4, 0.5, 0.6, 0.5]并重试:
cluster_std = [0.4, 0.5, 0.6, 0.5] 
X, y = make_blobs(n_samples=1000,
                  centers=centers,
                  cluster_std=cluster_std,
                  random_state=53)
plt.scatter(X[:, 0], X[:, 1], **plot_kwargs)
plt.show()
从前面的代码中,我们得到了下面的图:

现在看起来更逼真了!
让我们写一个小课堂,用有帮助的方法来创建无监督的实验。首先,您将使用fit_predict方法对样本数据集应用一个或多个聚类算法:
class Unsupervised_AutoML:
    def __init__(self, estimators=None, transformers=None):
        self.estimators = estimators
        self.transformers = transformers
        pass
Unsupervised_AutoML类将用一组估计器和转换器初始化。第二类方法是fit_predict:
def fit_predict(self, X, y=None):
    """
    fit_predict will train given estimator(s) and predict cluster membership for each sample
    """
    # This dictionary will hold predictions for each estimator
    predictions = []
    performance_metrics = {}
    for estimator in self.estimators:
        labels = estimator['estimator'](*estimator['args'], **estimator['kwargs']).fit_predict(X)
        estimator['estimator'].n_clusters_ = len(np.unique(labels))
        metrics = self._get_cluster_metrics(estimator['estimator'].__name__, estimator['estimator'].n_clusters_, X, labels, y)
        predictions.append({estimator['estimator'].__name__: labels})
        performance_metrics[estimator['estimator'].__name__] = metrics
    self.predictions = predictions
    self.performance_metrics = performance_metrics
    return predictions, performance_metrics
fit_predict方法使用_get_cluster_metrics方法获取性能指标,在以下代码块中定义:
# Printing cluster metrics for given arguments
def _get_cluster_metrics(self, name, n_clusters_, X, pred_labels, true_labels=None):
    from sklearn.metrics import homogeneity_score, \
        completeness_score, \
        v_measure_score, \
        adjusted_rand_score, \
        adjusted_mutual_info_score, \
        silhouette_score
    print("""################## %s metrics #####################""" % name)
    if len(np.unique(pred_labels)) >= 2:
        silh_co = silhouette_score(X, pred_labels)
        if true_labels is not None:
            h_score = homogeneity_score(true_labels, pred_labels)
            c_score = completeness_score(true_labels, pred_labels)
            vm_score = v_measure_score(true_labels, pred_labels)
            adj_r_score = adjusted_rand_score(true_labels, pred_labels)
            adj_mut_info_score = adjusted_mutual_info_score(true_labels, pred_labels)
            metrics = {"Silhouette Coefficient": silh_co,
                       "Estimated number of clusters": n_clusters_,
                       "Homogeneity": h_score,
                       "Completeness": c_score,
                       "V-measure": vm_score,
                       "Adjusted Rand Index": adj_r_score,
                       "Adjusted Mutual Information": adj_mut_info_score}
            for k, v in metrics.items():
                print("\t%s: %0.3f" % (k, v))
            return metrics
        metrics = {"Silhouette Coefficient": silh_co,
                   "Estimated number of clusters": n_clusters_}
        for k, v in metrics.items():
            print("\t%s: %0.3f" % (k, v))
        return metrics
    else:
        print("\t# of predicted labels is {}, can not produce metrics. \n".format(np.unique(pred_labels)))
_get_cluster_metrics方法计算度量,如homogeneity_score、completeness_score、v_measure_score、adjusted_rand_score、adjusted_mutual_info_score和silhouette_score。这些指标将帮助您评估集群的分离程度,并衡量集群内部和之间的相似性。
行动中的 k-均值算法
您现在可以应用KMeans算法来看看它是如何工作的:
from sklearn.cluster import KMeans
estimators = [{'estimator': KMeans, 'args':(), 'kwargs':{'n_clusters': 4}}]
unsupervised_learner = Unsupervised_AutoML(estimators)
可以看到estimators:
unsupervised_learner.estimators
这些将输出以下内容:
[{'args': (),
 'estimator': sklearn.cluster.k_means_.KMeans,
 'kwargs': {'n_clusters': 4}}]
现在可以调用fit_predict获取predictions和performance_metrics:
predictions, performance_metrics = unsupervised_learner.fit_predict(X, y)
指标将被写入控制台:
################## KMeans metrics #####################
  Silhouette Coefficient: 0.631
  Estimated number of clusters: 4.000
  Homogeneity: 0.951
  Completeness: 0.951
  V-measure: 0.951
  Adjusted Rand Index: 0.966
  Adjusted Mutual Information: 0.950
您可以随时稍后打印指标:
pprint(performance_metrics)
这将输出估计器的名称及其度量:
{'KMeans': {'Silhouette Coefficient': 0.9280431207593165, 'Estimated number of clusters': 4, 'Homogeneity': 1.0, 'Completeness': 1.0, 'V-measure': 1.0, 'Adjusted Rand Index': 1.0, 'Adjusted Mutual Information': 1.0}}
让我们添加另一个类方法来绘制给定估计量和预测标签的聚类:
# plot_clusters will visualize the clusters given predicted labels
def plot_clusters(self, estimator, X, labels, plot_kwargs):
    palette = sns.color_palette('deep', np.unique(labels).max() + 1)
    colors = [palette[x] if x >= 0 else (0.0, 0.0, 0.0) for x in labels]
    plt.scatter(X[:, 0], X[:, 1], c=colors, **plot_kwargs)
    plt.title('{} Clusters'.format(str(estimator.__name__)), fontsize=14)
    plt.show()
让我们看看用法:
plot_kwargs = {'s': 12, 'linewidths': 0.1}
unsupervised_learner.plot_clusters(KMeans,
                                   X,
                                   unsupervised_learner.predictions[0]['KMeans'],
                                   plot_kwargs)
从前面的块中可以得到下面的图:

在本例中,聚类大小均匀,并且彼此明显分开,但是当您进行这种探索性分析时,您应该尝试不同的超参数并检查结果。
在本章的后面,您将编写一个包装函数来应用一系列聚类算法及其超参数来检查结果。现在,让我们再看一个 k-means 不太好用的例子。
当数据集中的聚类具有不同的统计属性(如方差差异)时,k 均值将无法正确识别聚类:
X, y = make_blobs(n_samples=2000, centers=5, cluster_std=[1.7, 0.6, 0.8, 1.0, 1.2], random_state=220)
# Plot sample data
plt.scatter(X[:, 0], X[:, 1], **plot_kwargs)
plt.show()
从前面的代码中,我们得到了下面的图:

虽然这个样本数据集是用五个中心生成的,但这并不明显,可能还有四个聚类:
from sklearn.cluster import KMeans
estimators = [{'estimator': KMeans, 'args':(), 'kwargs':{'n_clusters': 4}}]
unsupervised_learner = Unsupervised_AutoML(estimators)
predictions, performance_metrics = unsupervised_learner.fit_predict(X, y)
控制台中的指标如下:
################## KMeans metrics #####################
  Silhouette Coefficient: 0.549
  Estimated number of clusters: 4.000
  Homogeneity: 0.729
  Completeness: 0.873
  V-measure: 0.795
  Adjusted Rand Index: 0.702
  Adjusted Mutual Information: 0.729
KMeans集群绘制如下:
plot_kwargs = {'s': 12, 'linewidths': 0.1}
unsupervised_learner.plot_clusters(KMeans,
                                   X,
                                   unsupervised_learner.predictions[0]['KMeans'],
                                   plot_kwargs)
从前面的代码中,我们得到了下面的图:

在这个例子中,红色(深灰色)和底部绿色簇(浅灰色)之间的点似乎形成了一个大簇。K-means 是根据质心周围点的平均值计算质心。在这里,你需要有一个不同的方法。
正在运行的数据库扫描算法
DBSCAN 是一种能够处理非平坦几何和不均匀簇大小的聚类算法。让我们看看它能做什么:
from sklearn.cluster import DBSCAN
estimators = [{'estimator': DBSCAN, 'args':(), 'kwargs':{'eps': 0.5}}]
unsupervised_learner = Unsupervised_AutoML(estimators)
predictions, performance_metrics = unsupervised_learner.fit_predict(X, y)
控制台中的指标如下:
################## DBSCAN metrics #####################
  Silhouette Coefficient: 0.231
  Estimated number of clusters: 12.000
  Homogeneity: 0.794
  Completeness: 0.800
  V-measure: 0.797
  Adjusted Rand Index: 0.737
  Adjusted Mutual Information: 0.792
DBSCAN集群绘制如下:
plot_kwargs = {'s': 12, 'linewidths': 0.1}
unsupervised_learner.plot_clusters(DBSCAN,
                                   X,
                                   unsupervised_learner.predictions[0]['DBSCAN'],
                                   plot_kwargs)
从前面的代码中,我们得到了下面的图:

k-means 案例中红色(深灰色)和底绿色(浅灰色)簇之间的冲突似乎已经消失,但这里有趣的是,一些小簇出现了,一些点没有根据它们的距离分配给任何簇。
DBSCAN 有eps(epsilon)超参数,它与点在同一邻域的接近度有关;您可以使用该参数来查看算法的行为。
当您在不太了解数据的情况下进行这种探索性分析时,视觉线索总是很重要的,因为指标可能会误导您,因为不是每个聚类算法都可以使用类似的指标进行评估。
行动中的凝聚聚类算法
我们的最后一次尝试是使用凝聚聚类算法:
from sklearn.cluster import AgglomerativeClustering
estimators = [{'estimator': AgglomerativeClustering, 'args':(), 'kwargs':{'n_clusters': 4, 'linkage': 'ward'}}]
unsupervised_learner = Unsupervised_AutoML(estimators)
predictions, performance_metrics = unsupervised_learner.fit_predict(X, y)
控制台中的指标如下:
################## AgglomerativeClustering metrics #####################
  Silhouette Coefficient: 0.546
  Estimated number of clusters: 4.000
  Homogeneity: 0.751
  Completeness: 0.905
  V-measure: 0.820
  Adjusted Rand Index: 0.719
  Adjusted Mutual Information: 0.750
AgglomerativeClustering集群绘制如下:
plot_kwargs = {'s': 12, 'linewidths': 0.1}
unsupervised_learner.plot_clusters(AgglomerativeClustering,
                                   X,
                                   unsupervised_learner.predictions[0]['AgglomerativeClustering'],
                                   plot_kwargs)
从前面的代码中,我们得到了下面的图:

AgglomerativeClustering在本例中表现类似 k 均值,略有不同。
无监督学习的简单自动化
您应该自动化整个发现过程,以尝试具有不同超参数设置的不同聚类算法。下面的代码将向您展示一种简单的方法:
# You will create a list of algorithms to test
from sklearn.cluster import MeanShift, estimate_bandwidth, SpectralClustering
from hdbscan import HDBSCAN
# bandwidth estimate for MeanShift algorithm to work properly
bandwidth = estimate_bandwidth(X, quantile=0.3, n_samples=100)
estimators = [{'estimator': KMeans, 'args': (), 'kwargs': {'n_clusters': 5}},
                         {'estimator': DBSCAN, 'args': (), 'kwargs': {'eps': 0.5}},
                         {'estimator': AgglomerativeClustering, 'args': (), 'kwargs': {'n_clusters': 5, 'linkage': 'ward'}},
                         {'estimator': MeanShift, 'args': (), 'kwargs': {'cluster_all': False, "bandwidth": bandwidth, "bin_seeding": True}},
                         {'estimator': SpectralClustering, 'args': (), 'kwargs': {'n_clusters':5}},
                         {'estimator': HDBSCAN, 'args': (), 'kwargs': {'min_cluster_size':15}}]
unsupervised_learner = Unsupervised_AutoML(estimators)
predictions, performance_metrics = unsupervised_learner.fit_predict(X, y)
您将在控制台中看到以下指标:
################## KMeans metrics #####################
  Silhouette Coefficient: 0.592
  Estimated number of clusters: 5.000
  Homogeneity: 0.881
  Completeness: 0.882
  V-measure: 0.882
  Adjusted Rand Index: 0.886
  Adjusted Mutual Information: 0.881
################## DBSCAN metrics #####################
  Silhouette Coefficient: 0.417
  Estimated number of clusters: 5.000
  ...
################## AgglomerativeClustering metrics #####################
  Silhouette Coefficient: 0.581
  Estimated number of clusters: 5.000
  ...
################## MeanShift metrics #####################
  Silhouette Coefficient: 0.472
  Estimated number of clusters: 3.000
  ...
################## SpectralClustering metrics #####################
  Silhouette Coefficient: 0.420
  Estimated number of clusters: 5.000
  ...
################## HDBSCAN metrics #####################
  Silhouette Coefficient: 0.468
  Estimated number of clusters: 6.000
  ...
您可以稍后打印标签和指标,因为每个算法都有一个标签和指标:
pprint(predictions)
[{'KMeans': array([3, 1, 4, ..., 0, 1, 2], dtype=int32)},
 {'DBSCAN': array([ 0, 0, 0, ..., 2, -1, 1])},
 {'AgglomerativeClustering': array([2, 4, 0, ..., 3, 2, 1])},
 {'MeanShift': array([0, 0, 0, ..., 1, 0, 1])},
 {'SpectralClustering': array([4, 2, 1, ..., 0, 1, 3], dtype=int32)},
 {'HDBSCAN': array([ 4, 2, 3, ..., 1, -1, 0])}]
pprint(performance_metrics)
{'AgglomerativeClustering': {'Adjusted Mutual Information': 0.8989601162598674,
                             'Adjusted Rand Index': 0.9074196173180163,
                             ...},
 'DBSCAN': {'Adjusted Mutual Information': 0.5694008711591612,
            'Adjusted Rand Index': 0.4685215791890368,
            ...},
 'HDBSCAN': {'Adjusted Mutual Information': 0.7857262723310214,
             'Adjusted Rand Index': 0.7907512089039799,
             ...},
 'KMeans': {'Adjusted Mutual Information': 0.8806038790635883,
            'Adjusted Rand Index': 0.8862210038915361,
            ...},
 'MeanShift': {'Adjusted Mutual Information': 0.45701704058584275,
               'Adjusted Rand Index': 0.4043364504640998,
               ...},
 'SpectralClustering': {'Adjusted Mutual Information': 0.7628653432724043,
                        'Adjusted Rand Index': 0.7111907598912597,
                        ...}}
您可以使用plot_clusters方法以同样的方式可视化预测。让我们编写另一个类方法,它将为您在实验中使用的所有估算器绘制聚类图:
def plot_all_clusters(self, estimators, labels, X, plot_kwargs):
    fig = plt.figure()
    for i, algorithm in enumerate(labels):
        quotinent = np.divide(len(estimators), 2)
        # Simple logic to decide row and column size of the figure
        if isinstance(quotinent, int):
            dim_1 = 2
            dim_2 = quotinent
        else:
            dim_1 = np.ceil(quotinent)
            dim_2 = 3
        palette = sns.color_palette('deep',
                                    np.unique(algorithm[estimators[i]['estimator'].__name__]).max() + 1)
        colors = [palette[x] if x >= 0 else (0.0, 0.0, 0.0) for x in
                  algorithm[estimators[i]['estimator'].__name__]]
        plt.subplot(dim_1, dim_2, i + 1)
        plt.scatter(X[:, 0], X[:, 1], c=colors, **plot_kwargs)
        plt.title('{} Clusters'.format(str(estimators[i]['estimator'].__name__)), fontsize=8)
    plt.show()
让我们看看用法:
plot_kwargs = {'s': 12, 'linewidths': 0.1}
unsupervised_learner.plot_all_clusters(estimators, unsupervised_learner.predictions, X, plot_kwargs)
我们从前面的代码块中得到如下图:

Top row, starting from left: KMeans, DBSCAN, AgglomerativeClustering Bottom row, starting from left: MeanShift, SpectralClustering, HDBSCAN
可视化高维数据集
可视化检查三维以上的数据集怎么样?为了直观地检查数据集,您需要有最大三个维度;如果没有,则需要使用特定的方法来降维。这通常通过应用主成分分析 ( 主成分分析)或 t-SNE 算法来实现。
以下代码将加载Breast Cancer Wisconsin Diagnostic数据集,这是 ML 教程中常用的数据集:
# Wisconsin Breast Cancer Diagnostic Dataset
from sklearn.datasets import load_breast_cancer
import pandas as pd
data = load_breast_cancer()
X = data.data
df = pd.DataFrame(data.data, columns=data.feature_names)
df.head()
控制台中的输出如下:
 mean radius mean texture mean perimeter mean area mean smoothness \
0 17.99 10.38 122.80 1001.0 0.11840 
1 20.57 17.77 132.90 1326.0 0.08474 
2 19.69 21.25 130.00 1203.0 0.10960 
3 11.42 20.38 77.58 386.1 0.14250 
4 20.29 14.34 135.10 1297.0 0.10030 
...
 mean fractal dimension ... worst radius \
0 0.07871 ... 25.38 
1 0.05667 ... 24.99 
2 0.05999 ... 23.57 
3 0.09744 ... 14.91 
4 0.05883 ... 22.54 
...
 worst fractal dimension 
0 0.11890 
1 0.08902 
2 0.08758 
3 0.17300 
4 0.07678 
您有 30 种不同的特征可以用来了解给定患者肿瘤的不同特征。
df.describe()将显示每个特征的描述性统计数据:
df.describe()
       mean radius mean texture mean perimeter mean area \
count 569.000000 569.000000 569.000000 569.000000 
mean 14.127292 19.289649 91.969033 654.889104 
std 3.524049 4.301036 24.298981 351.914129 
min 6.981000 9.710000 43.790000 143.500000 
25% 11.700000 16.170000 75.170000 420.300000 
50% 13.370000 18.840000 86.240000 551.100000 
75% 15.780000 21.800000 104.100000 782.700000 
max 28.110000 39.280000 188.500000 2501.000000 
...
       mean symmetry mean fractal dimension ... \
count 569.000000 569.000000 ... 
mean 0.181162 0.062798 ... 
std 0.027414 0.007060 ... 
min 0.106000 0.049960 ... 
25% 0.161900 0.057700 ... 
50% 0.179200 0.061540 ... 
75% 0.195700 0.066120 ... 
max 0.304000 0.097440 ... 
...
       worst concave points worst symmetry worst fractal dimension 
count 569.000000 569.000000 569.000000 
mean 0.114606 0.290076 0.083946 
std 0.065732 0.061867 0.018061 
min 0.000000 0.156500 0.055040 
25% 0.064930 0.250400 0.071460 
50% 0.099930 0.282200 0.080040 
75% 0.161400 0.317900 0.092080 
max 0.291000 0.663800 0.207500 
[8 rows x 30 columns]
让我们看看缩放前后的结果。以下代码片段将使主成分分析适合原始数据。
作用中的主成分分析
下面的代码块向您展示了如何使用两个组件应用主成分分析并可视化结果:
# PCA
from sklearn.decomposition import PCA
pca = PCA(n_components=2, whiten=True)
pca = pca.fit_transform(df)
plt.scatter(pca[:, 0], pca[:, 1], c=data.target, cmap="RdBu_r", edgecolor="Red", alpha=0.35)
plt.colorbar()
plt.title('PCA, n_components=2')
plt.show()
从前面的代码中,我们得到了下面的图:

Plot of PCA, n_components=2
在这里,你可以看到红色类(深灰色)非常浓缩成一个特定的区域,很难分开类。差异会扭曲我们的观点,缩放比例有助于:
# Preprocess data.
scaler = StandardScaler()
scaler.fit(df)
preprocessed_data = scaler.transform(df)
scaled_features_df = pd.DataFrame(preprocessed_data, index=df.index, columns=df.columns)
应用StandardScaler对数据进行预处理后,数据集具有单位方差:
scaled_features_df.describe()
        mean radius mean texture mean perimeter mean area \
count 5.690000e+02 5.690000e+02 5.690000e+02 5.690000e+02 
mean -3.162867e-15 -6.530609e-15 -7.078891e-16 -8.799835e-16 
std 1.000880e+00 1.000880e+00 1.000880e+00 1.000880e+00 
min -2.029648e+00 -2.229249e+00 -1.984504e+00 -1.454443e+00 
25% -6.893853e-01 -7.259631e-01 -6.919555e-01 -6.671955e-01 
50% -2.150816e-01 -1.046362e-01 -2.359800e-01 -2.951869e-01 
75% 4.693926e-01 5.841756e-01 4.996769e-01 3.635073e-01 
max 3.971288e+00 4.651889e+00 3.976130e+00 5.250529e+00 
...
       mean symmetry mean fractal dimension ... \
count 5.690000e+02 5.690000e+02 ... 
mean -1.971670e-15 -1.453631e-15 ... 
std 1.000880e+00 1.000880e+00 ... 
min -2.744117e+00 -1.819865e+00 ... 
25% -7.032397e-01 -7.226392e-01 ... 
50% -7.162650e-02 -1.782793e-01 ... 
75% 5.307792e-01 4.709834e-01 ... 
max 4.484751e+00 4.910919e+00 ... 
...
       worst concave points worst symmetry worst fractal dimension 
count 5.690000e+02 5.690000e+02 5.690000e+02 
mean -1.412656e-16 -2.289567e-15 2.575171e-15 
std 1.000880e+00 1.000880e+00 1.000880e+00 
min -1.745063e+00 -2.160960e+00 -1.601839e+00 
25% -7.563999e-01 -6.418637e-01 -6.919118e-01 
50% -2.234689e-01 -1.274095e-01 -2.164441e-01 
75% 7.125100e-01 4.501382e-01 4.507624e-01 
max 2.685877e+00 6.046041e+00 6.846856e+00 
[8 rows x 30 columns]
应用主成分分析,查看前两个主要成分是否足以分离标签:
# PCA
from sklearn.decomposition import PCA
pca = PCA(n_components=2, whiten=True)
pca = pca.fit_transform(scaled_features_df)
plt.scatter(pca[:, 0], pca[:, 1], c=data.target, cmap="RdBu_r", edgecolor="Red", alpha=0.35)
plt.colorbar()
plt.title('PCA, n_components=2')
plt.show()
我们从前面的代码中获得以下输出:

PCA, n_components=2, after scaling
这似乎很有趣,因为不同标签的例子大多是用前两个主成分分开的。
SNE 行动
你也可以尝试 t-SNE 来可视化高维数据。首先,TSNE将应用于原始数据:
# TSNE
from sklearn.manifold import TSNE
tsne = TSNE(verbose=1, perplexity=40, n_iter=4000)
tsne = tsne.fit_transform(df)
控制台中的输出如下:
[t-SNE] Computing 121 nearest neighbors...
[t-SNE] Indexed 569 samples in 0.000s...
[t-SNE] Computed neighbors for 569 samples in 0.010s...
[t-SNE] Computed conditional probabilities for sample 569 / 569
[t-SNE] Mean sigma: 33.679703
[t-SNE] KL divergence after 250 iterations with early exaggeration: 48.886528
[t-SNE] Error after 1600 iterations: 0.210506
绘制结果如下:
plt.scatter(tsne[:, 0], tsne[:, 1], c=data.target, cmap="winter", edgecolor="None", alpha=0.35)
plt.colorbar()
plt.title('t-SNE')
plt.show()
我们从前面的代码中获得以下输出:

Plot of TSNE
对缩放数据应用TSNE如下:
tsne = TSNE(verbose=1, perplexity=40, n_iter=4000)
tsne = tsne.fit_transform(scaled_features_df)
控制台中的输出如下:
[t-SNE] Computing 121 nearest neighbors...
[t-SNE] Indexed 569 samples in 0.001s...
[t-SNE] Computed neighbors for 569 samples in 0.018s...
[t-SNE] Computed conditional probabilities for sample 569 / 569
[t-SNE] Mean sigma: 1.522404
[t-SNE] KL divergence after 250 iterations with early exaggeration: 66.959343
[t-SNE] Error after 1700 iterations: 0.875110
绘制结果如下:
plt.scatter(tsne[:, 0], tsne[:, 1], c=data.target, cmap="winter", edgecolor="None", alpha=0.35)
plt.colorbar()
plt.title('t-SNE')
plt.show()
我们从前面的代码中获得以下输出:

TSNE after scaling
将简单的组件添加到一起以改进流水线
让我们对fit_predict方法进行一些调整,在您的流水线中包含一个分解器,以便您可以在必要时可视化高维数据:
def fit_predict(self, X, y=None, scaler=True, decomposer={'name': PCA, 'args':[], 'kwargs': {'n_components': 2}}):
    """
    fit_predict will train given estimator(s) and predict cluster membership for each sample
    """
    shape = X.shape
    df_type = isinstance(X, pd.core.frame.DataFrame)
    if df_type:
        column_names = X.columns
        index = X.index
    if scaler == True:
        from sklearn.preprocessing import StandardScaler
        scaler = StandardScaler()
        X = scaler.fit_transform(X)
        if df_type:
            X = pd.DataFrame(X, index=index, columns=column_names)
    if decomposer is not None:
        X = decomposer['name'](*decomposer['args'], **decomposer['kwargs']).fit_transform(X)
        if df_type:
            if decomposer['name'].__name__ == 'PCA':
                X = pd.DataFrame(X, index=index, columns=['component_' + str(i + 1) for i in
                                                          range(decomposer['kwargs']['n_components'])])
            else:
                X = pd.DataFrame(X, index=index, columns=['component_1', 'component_2'])
        # if dimensionality reduction is applied, then n_components will be set accordingly in hyperparameter configuration
        for estimator in self.estimators:
            if 'n_clusters' in estimator['kwargs'].keys():
                if decomposer['name'].__name__ == 'PCA':
                    estimator['kwargs']['n_clusters'] = decomposer['kwargs']['n_components']
                else:
                    estimator['kwargs']['n_clusters'] = 2
    # This dictionary will hold predictions for each estimator
    predictions = []
    performance_metrics = {}
    for estimator in self.estimators:
        labels = estimator['estimator'](*estimator['args'], **estimator['kwargs']).fit_predict(X)
        estimator['estimator'].n_clusters_ = len(np.unique(labels))
        metrics = self._get_cluster_metrics(estimator['estimator'].__name__, estimator['estimator'].n_clusters_, X, labels, y)
        predictions.append({estimator['estimator'].__name__: labels})
        performance_metrics[estimator['estimator'].__name__] = metrics
    self.predictions = predictions
    self.performance_metrics = performance_metrics
    return predictions, performance_metrics
现在,您可以将fit_predict应用到您的数据集。下面的代码块向您展示了一个用法示例:
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering, MeanShift, estimate_bandwidth, SpectralClustering
from hdbscan import HDBSCAN
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
X = data.data
y = data.target
# Necessary for bandwidth
bandwidth = estimate_bandwidth(X, quantile=0.1, n_samples=100)
estimators = [{'estimator': KMeans, 'args': (), 'kwargs': {'n_clusters': 5}},
                         {'estimator': DBSCAN, 'args': (), 'kwargs': {'eps': 0.3}},
                         {'estimator': AgglomerativeClustering, 'args': (), 'kwargs': {'n_clusters': 5, 'linkage': 'ward'}},
                         {'estimator': MeanShift, 'args': (), 'kwargs': {'cluster_all': False, "bandwidth": bandwidth, "bin_seeding": True}},
                         {'estimator': SpectralClustering, 'args': (), 'kwargs': {'n_clusters':5}},
                         {'estimator': HDBSCAN, 'args': (), 'kwargs': {'min_cluster_size':15}}]
unsupervised_learner = Unsupervised_AutoML(estimators)
predictions, performance_metrics = unsupervised_learner.fit_predict(X, y, decomposer=None)
自动无监督学习是一个高度实验性的过程,尤其是如果你对你的数据不太了解的话。作为练习,您可以扩展Unsupervised_AutoML类,尝试为每个算法设置多个超参数集,并将结果可视化。
摘要
在本章中,您了解了为给定问题选择合适的 ML 流水线的许多不同方面。
计算复杂性、训练和评分时间的差异、线性与非线性以及算法、特定的特征转换都是有效的考虑因素,从这些角度来看您的数据非常有用。
通过实践各种用例,您对选择合适的模型以及机器学习流水线如何工作有了更好的理解。你开始触及表面,这一章是扩展这些技能的良好起点。
在下一章中,您将学习优化超参数,并将了解更高级的概念,例如基于贝叶斯的超参数优化。
五、超参数优化
auto-sklearn 库使用贝叶斯优化来调整机器学习 ( ML )流水线的超参数。您将学习贝叶斯优化的内部工作原理,但是让我们首先回顾一下数学优化的基础。
简单来说,优化就是选择最佳值来最小化或最大化给定的函数。如果我们的目标是最小化,一个函数被称为损失函数或成本函数。如果你试图最大化它,那么它被称为实用功能或健身功能。例如,当您构建 ML 模型时,损失函数可以帮助您在训练阶段最小化预测误差。
当你从更广的角度来看整个过程时,会有很多变量起作用。
首先,你可以在一个系统上决定问题的类型,比如无监督的,有监督的,半监督的,或者强化学习的问题。您可以根据数据大小和复杂性决定硬件和软件配置。然后你可以选择合适的语言或库用于你的实验。从一组可用的转换器和估算器中,您可以选择其中的一个子集,用于培训、验证和测试阶段。
所有这些都可以称为配置参数,为开发 ML 流水线设置场景。
其次,变压器和估计器在训练阶段有自己的参数需要计算,例如线性模型中的系数,或者创建多项式和交互特征的次数参数。
例如,ML 算法通常被分类为参数的或非参数的。如果一个算法有固定数量的参数,这意味着函数形式是已知的,那么它就是一个参数;如果不是,那么它被称为非参数,你的数据将塑造函数的形式。
第三,除了参数之外,你需要在训练开始之前设置超参数来指导变压器和估计器参数的估计。
超参数特别重要,因为您的流水线的性能将取决于它们,并且由于有许多超参数,每个超参数可以取一个范围的值,您很快就会意识到这是一个优化问题。
给定超参数和它们可以取值的范围,也就是您的搜索空间,您如何高效地找到性能最好的 ML 流水线?实际上,性能最好的 ML 流水线是交叉验证分数最高的流水线。
本章将涵盖以下主题:
- ML 实验的配置空间
 - 最大似然模型参数和超参数
 - 什么是热启动,它如何帮助参数优化
 - 基于贝叶斯的超参数调整
 - 示例系统
 
技术要求
你可以在书的仓库的Chapter 05文件夹中找到所有的代码示例。
超参数
为了更好地理解这个过程,您将从简单的 Branin 函数开始,该函数有 3 个全局最小值:

下面的代码片段向您展示了 Branin 函数的最小化:
import numpy as np
def branin(x):
    # Branin function has 2 dimensions and it has 3 global mimima
    x1 = x[0]
    x2 = x[1]
    # Global minimum is f(x*)=0.397887 at points (-pi, 12.275), (pi,2.275) and (9.42478, 2.475)
    # Recommended values of a, b, c, r, s and t for Branin function
    a = 1
    b = 5.1 / (4 * np.pi**2)
    c = 5\. / np.pi
    r = 6.
    s = 10.
    t = 1 / (8 * np.pi)
    # Calculating separate parts of the function first for verbosity
    p1 = a * (x2 - (b * x1**2) + (c * x1) - r)**2
    p2 = s * (1-t) * np.cos(x1)
    p3 = s
    # Calculating result
    ret = p1 + p2 + p3
    return ret
# minimize function from scipy.optimize will minimize a scalar function with one or more variables
from scipy.optimize import minimize
x = [5.6, 3.2]
res = minimize(branin, x)
print(res)
执行前面的代码片段将产生以下输出:
fun: 0.3978873577297417
hess_inv: array([[0.10409341, -0.0808961],
[-0.0808961, 0.56160622]])
jac: array([3.57627869e-07, -1.19209290e-07])
message: 'Optimization terminated successfully.'
nfev: 36
nit: 5
njev: 9
status: 0
success: True
x: array([3.14159268, 2.27499994])
优化成功终止,全局最小值可以在 Branin 函数的(3.14159268、2.27499994)处找到。你的优化问题有很多解算器可以用,比如 BFGS 、 L-BFGS-B 、 SLSQP ,这些会有不同的特点,比如一致性和复杂性。通过例子练习会让你熟悉其中的一些,并为进一步探索打开空间。
让我们回顾一下 ML 问题的优化基础。下面的公式显示了大多数 ML 问题可以归结为什么:

在这个方程中,你有损失函数和正则项来防止过度拟合。由 w 表示的权重是您在训练过程中试图学习的内容,这些是前面提到的学习算法的参数。除了这些参数之外,一般还需要定义超参数,比如学习速率和提前停止条件,这些都会影响学习行为。
你有没有注意到损失函数中的 α 和 β ?这些是训练前需要设置的参数,也是超参数。
超参数帮助您在模型偏差和模型方差之间保持健康的平衡。
让我们看一下sklearn中估计器参数的一个简单例子:
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.get_params()
输出如下所示:
{'C': 1.0,
 'class_weight': None,
 'dual': False,
 'fit_intercept': True,
 'intercept_scaling': 1,
 'max_iter': 100,
 'multi_class': 'ovr',
 'n_jobs': 1,
 'penalty': 'l2',
 'random_state': None,
 'solver': 'liblinear',
 'tol': 0.0001,
 'verbose': 0,
 'warm_start': False}
这里有 14 个超参数,如果你考虑一下可能的组合,你会意识到搜索空间有多大。您的目标是从所有超参数集中获得最佳交叉验证分数。
LogisticRegression的重要超参数之一是C,它控制正则化的强度。值反过来影响正则化强度,这意味着较高的值表示较弱的正则化。
即使您是正在使用的算法的专家,正确设置超参数对于从业者的经验来说也是实验性的和主观的。你需要找到比启发式方法更好的方法来找到一组接近最优或最优的超参数。
例如,可以使用GridSearchCV或RandomizedSearchCV在sklearn中搜索超参数空间:
GridSearchCV从给定的超参数和它们可以取的一系列值生成候选集。假设您有以下参数网格:
# Hyperparameters
param_grid = [ {'C': [0.001, 0.01, 0.1, 1, 10, 20, 50, 100],
                'penalty': ['l1', 'l2']} ]
然后GridSearhCV会生成以下参数:
'params': [{'C': 0.001, 'penalty': 'l1'},
  {'C': 0.001, 'penalty': 'l2'},
  {'C': 0.01, 'penalty': 'l1'},
  {'C': 0.01, 'penalty': 'l2'},
  {'C': 0.1, 'penalty': 'l1'},
  {'C': 0.1, 'penalty': 'l2'},
  {'C': 1, 'penalty': 'l1'},
  {'C': 1, 'penalty': 'l2'},
  {'C': 10, 'penalty': 'l1'},
  {'C': 10, 'penalty': 'l2'},
  {'C': 20, 'penalty': 'l1'},
  {'C': 20, 'penalty': 'l2'},
  {'C': 50, 'penalty': 'l1'},
  {'C': 50, 'penalty': 'l2'},
  {'C': 100, 'penalty': 'l1'},
  {'C': 100, 'penalty': 'l2'}]
它将执行详尽的搜索,以找到最佳的交叉验证分数。
RandomizedSearchCV执行搜索的方式与GridSearchCV不同。它不是彻底搜索超参数空间,而是从指定的分布中采样参数设置。您应该按照以下方式构建参数网格:
# Hyperparameters
param_grid = {'C': sp_randint(1, 100),
                'penalty': ['l1', 'l2']}
你注意到sp_randint了吗?它将允许RandomizedSearchCV从均匀分布中提取随机变量,参数将如下创建:
'params': [{'C': 6, 'penalty': 'l2'},
  {'C': 97, 'penalty': 'l2'},
  {'C': 92, 'penalty': 'l2'},
  {'C': 62, 'penalty': 'l1'},
  {'C': 63, 'penalty': 'l2'},
  {'C': 5, 'penalty': 'l2'},
  {'C': 7, 'penalty': 'l1'},
  {'C': 45, 'penalty': 'l1'},
  {'C': 77, 'penalty': 'l2'},
  {'C': 12, 'penalty': 'l1'},
  {'C': 72, 'penalty': 'l2'},
  {'C': 28, 'penalty': 'l1'},
  {'C': 7, 'penalty': 'l2'},
  {'C': 65, 'penalty': 'l1'},
  {'C': 32, 'penalty': 'l1'},
  {'C': 84, 'penalty': 'l1'},
  {'C': 27, 'penalty': 'l1'},
  {'C': 12, 'penalty': 'l1'},
  {'C': 21, 'penalty': 'l1'},
  {'C': 65, 'penalty': 'l1'}],
让我们看一个用法的例子,都是针对GridSearchCV和RandomizedSearchCV的。
以下片段向您展示了GridSearchCV:
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
# Hyperparameters
param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 20, 50, 100],
                'penalty': ['l1', 'l2']}
from sklearn.model_selection import GridSearchCV
n_folds = 5
estimator = GridSearchCV(log_reg,param_grid, cv=n_folds)
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
Y = iris.target
estimator.fit(X, Y)
您将看到以下输出:
GridSearchCV(cv=5, error_score='raise',
       estimator=LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False),
       fit_params=None, iid=True, n_jobs=1,
       param_grid=[{'C': [0.001, 0.01, 0.1, 1, 10, 20, 50, 100], 'penalty': ['l1', 'l2']}],
       pre_dispatch='2*n_jobs', refit=True, return_train_score=True,
       scoring=None, verbose=0)
训练完成后,您可以看到性能最佳的估计器设置:
estimator.best_estimator_
前面的代码将生成以下输出:
LogisticRegression(C=10, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l1', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
你也可以看到最好的分数:
estimator.best_score_
输出如下:
0.98
也可以通过勾选cv_results_查看所有结果:
estimator.cv_results_
这将为您提供每次培训的各种指标:
{'mean_fit_time': array([0.00039144, 0.00042701, 0.00036378, 0.00043044, 0.00145531,
        0.00046387, 0.00670047, 0.00056334, 0.00890565, 0.00064907,
        0.00916181, 0.00063758, 0.01110044, 0.00076027, 0.01196856,
        0.00084472]),
 'mean_score_time': array([0.00017729, 0.00018134, 0.00016704, 0.00016623, 0.00017071,
        0.00016556, 0.00024438, 0.00017123, 0.00020232, 0.00018559,
        0.00020504, 0.00016532, 0.00024428, 0.00019045, 0.00023465,
        0.00023274]),
 'mean_test_score': array([0.33333333, 0.40666667, 0.33333333, 0.66666667, 0.77333333,
        0.82 , 0.96 , 0.96 , 0.98 , 0.96666667,
        0.96666667, 0.96666667, 0.96666667, 0.97333333, 0.96 ,
        0.98 ]),
 'mean_train_score': array([0.33333333, 0.40166667, 0.33333333, 0.66666667, 0.775 ,
        0.83166667, 0.96333333, 0.96333333, 0.97333333, 0.97333333,
        0.97333333, 0.97666667, 0.975 , 0.97833333, 0.975 ,
        0.98 ]),
 'param_C': masked_array(data=[0.001, 0.001, 0.01, 0.01, 0.1, 0.1, 1, 1, 10, 10, 20,
                    20, 50, 50, 100, 100],
              mask=[False, False, False, False, False, False, False, False,
                    False, False, False, False, False, False, False, False],
        fill_value='?',
             dtype=object),
 'param_penalty': masked_array(data=['l1', 'l2', 'l1', 'l2', 'l1', 'l2', 'l1', 'l2', 'l1',
                    'l2', 'l1', 'l2', 'l1', 'l2', 'l1', 'l2'],
              mask=[False, False, False, False, False, False, False, False,
                    False, False, False, False, False, False, False, False],
        fill_value='?',
             dtype=object),
 'params': [{'C': 0.001, 'penalty': 'l1'},
  {'C': 0.001, 'penalty': 'l2'},
  {'C': 0.01, 'penalty': 'l1'},
  {'C': 0.01, 'penalty': 'l2'},
  {'C': 0.1, 'penalty': 'l1'},
  {'C': 0.1, 'penalty': 'l2'},
  {'C': 1, 'penalty': 'l1'},
  {'C': 1, 'penalty': 'l2'},
  {'C': 10, 'penalty': 'l1'},
  {'C': 10, 'penalty': 'l2'},
  {'C': 20, 'penalty': 'l1'},
  {'C': 20, 'penalty': 'l2'},
  {'C': 50, 'penalty': 'l1'},
  {'C': 50, 'penalty': 'l2'},
  {'C': 100, 'penalty': 'l1'},
  {'C': 100, 'penalty': 'l2'}],
 'rank_test_score': array([15, 14, 15, 13, 12, 11, 8, 8, 1, 4, 4, 4, 4, 3, 8, 1],
       dtype=int32),
 'split0_test_score': array([0.33333333, 0.36666667, 0.33333333, 0.66666667, 0.7 ,
        0.76666667, 1\. , 1\. , 1\. , 1\. ,
        1\. , 1\. , 1\. , 1\. , 0.96666667,
        1\. ]),
 'split0_train_score': array([0.33333333, 0.41666667, 0.33333333, 0.66666667, 0.775 ,
        0.825 , 0.95 , 0.95 , 0.95 , 0.96666667,
        0.95 , 0.975 , 0.95833333, 0.975 , 0.95833333,
        0.975 ]),
 'split1_test_score': array([0.33333333, 0.46666667, 0.33333333, 0.66666667, 0.8 ,
        0.86666667, 0.96666667, 0.96666667, 1\. , 1\. ,
        0.96666667, 1\. , 0.96666667, 1\. , 0.96666667,
        1\. ]),
 'split1_train_score': array([0.33333333, 0.35833333, 0.33333333, 0.66666667, 0.775 ,
        0.825 , 0.95833333, 0.96666667, 0.975 , 0.96666667,
        0.975 , 0.975 , 0.975 , 0.975 , 0.975 ,
        0.975 ]),
 'split2_test_score': array([0.33333333, 0.36666667, 0.33333333, 0.66666667, 0.8 ,
        0.83333333, 0.93333333, 0.93333333, 0.96666667, 0.93333333,
        0.93333333, 0.93333333, 0.93333333, 0.93333333, 0.93333333,
        0.96666667]),
 'split2_train_score': array([0.33333333, 0.41666667, 0.33333333, 0.66666667, 0.76666667,
        0.83333333, 0.96666667, 0.96666667, 0.975 , 0.975 ,
        0.975 , 0.98333333, 0.975 , 0.98333333, 0.975 ,
        0.98333333]),
 'split3_test_score': array([0.33333333, 0.46666667, 0.33333333, 0.66666667, 0.8 ,
        0.83333333, 0.9 , 0.9 , 0.93333333, 0.9 ,
        0.93333333, 0.9 , 0.93333333, 0.93333333, 0.93333333,
        0.93333333]),
 'split3_train_score': array([0.33333333, 0.39166667, 0.33333333, 0.66666667, 0.775 ,
        0.84166667, 0.975 , 0.975 , 0.99166667, 0.98333333,
        0.99166667, 0.98333333, 0.99166667, 0.98333333, 0.99166667,
        0.99166667]),
 'split4_test_score': array([0.33333333, 0.36666667, 0.33333333, 0.66666667, 0.76666667,
        0.8 , 1\. , 1\. , 1\. , 1\. ,
        1\. , 1\. , 1\. , 1\. , 1\. ,
        1\. ]),
 'split4_train_score': array([0.33333333, 0.425 , 0.33333333, 0.66666667, 0.78333333,
        0.83333333, 0.96666667, 0.95833333, 0.975 , 0.975 ,
        0.975 , 0.96666667, 0.975 , 0.975 , 0.975 ,
        0.975 ]),
 'std_fit_time': array([7.66660734e-05, 3.32198455e-05, 1.98168153e-05, 6.91923414e-06,
        4.74922317e-04, 2.65661212e-05, 1.03221712e-03, 3.79795334e-05,
        1.86899641e-03, 8.53752397e-05, 1.93386463e-03, 2.95752073e-05,
        2.91377734e-03, 5.70420424e-05, 3.59721435e-03, 9.67829087e-05]),
 'std_score_time': array([1.28883712e-05, 2.39771817e-05, 4.81959487e-06, 2.47955322e-06,
        1.34236224e-05, 2.41545203e-06, 5.64869920e-05, 8.94803700e-06,
        4.10209125e-05, 3.35513820e-05, 3.04168290e-05, 2.87924369e-06,
        4.91685012e-05, 1.62987656e-05, 4.23611246e-05, 7.26868455e-05]),
 'std_test_score': array([0\. , 0.04898979, 0\. , 0\. , 0.03887301,
        0.03399346, 0.03887301, 0.03887301, 0.02666667, 0.0421637 ,
        0.02981424, 0.0421637 , 0.02981424, 0.03265986, 0.02494438,
        0.02666667]),
 'std_train_score': array([0\. , 0.02438123, 0\. , 0\. , 0.00527046,
        0.0062361 , 0.00849837, 0.00849837, 0.01333333, 0.0062361 ,
        0.01333333, 0.0062361 , 0.01054093, 0.00408248, 0.01054093,
        0.00666667])}
让我们看看它是如何工作的RandomizedSearchCV:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint as sp_randint
# Hyperparameters
param_grid = {'C': sp_randint(1, 100),
                'penalty': ['l1', 'l2']}
n_iter_search = 20
n_folds = 5
estimator = RandomizedSearchCV(log_reg, param_distributions=param_grid, n_iter=n_iter_search, cv=n_folds)
estimator.fit(X, Y)
前面的代码生成类似于GridSearchCV的如下输出:
RandomizedSearchCV(cv=5, error_score='raise',
          estimator=LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False),
          fit_params=None, iid=True, n_iter=20, n_jobs=1,
          param_distributions={'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x1176d4c88>, 'penalty': ['l1', 'l2']},
          pre_dispatch='2*n_jobs', random_state=None, refit=True,
          return_train_score=True, scoring=None, verbose=0)
我们也来看看best_estimator_:
estimator.best_estimator_
上述代码生成以下输出:
LogisticRegression(C = 95, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty ='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose = 0, warm_start=False)
estimator.best_score_显示以下输出:
0.98
RandomizedSearchCV具有相同的最佳得分,但这里需要注意的是,表现最佳的估计器设置具有C = 95,这是一个很难找到的值,因为人们在手动构建参数网格时通常会尝试取整值,如 10、100 或 1000。
你也可以用estimator.cv_results_同样检查交叉验证的结果:
{'mean_fit_time': array([0.0091342 , 0.00065241, 0.00873041, 0.00068126, 0.00082703,
        0.01093817, 0.00067267, 0.00961967, 0.00883713, 0.00069351,
        0.01048965, 0.00068388, 0.01074204, 0.0090354 , 0.00983639,
        0.01081419, 0.01014266, 0.00067706, 0.01015086, 0.00067825]),
 'mean_score_time': array([0.00026116, 0.0001647 , 0.00020576, 0.00017738, 0.00022368,
        0.00023923, 0.00016236, 0.00017295, 0.00026078, 0.00021319,
        0.00028219, 0.00018024, 0.00027289, 0.00025878, 0.00020723,
        0.00020337, 0.00023756, 0.00017438, 0.00028505, 0.0001936 ]),
 'mean_test_score': array([0.96666667, 0.97333333, 0.97333333, 0.98 , 0.97333333,
        0.96666667, 0.97333333, 0.96666667, 0.98 , 0.97333333,
        0.96666667, 0.98 , 0.96666667, 0.96666667, 0.96666667,
        0.96666667, 0.96666667, 0.98 , 0.96666667, 0.96666667]),
 'mean_train_score': array([0.97333333, 0.97833333, 0.97333333, 0.98 , 0.97833333,
        0.975 , 0.97833333, 0.975 , 0.97333333, 0.97833333,
        0.975 , 0.98 , 0.975 , 0.97333333, 0.975 ,
        0.975 , 0.975 , 0.97833333, 0.975 , 0.97666667]),
 'param_C': masked_array(data=[20, 53, 5, 95, 50, 71, 41, 43, 8, 30, 70, 91, 53, 15,
                    35, 41, 56, 82, 90, 27],
              mask=[False, False, False, False, False, False, False, False,
                    False, False, False, False, False, False, False, False,
                    False, False, False, False],
        fill_value='?',
             dtype=object),
 'param_penalty': masked_array(data=['l1', 'l2', 'l1', 'l2', 'l2', 'l1', 'l2', 'l1', 'l1',
                    'l2', 'l1', 'l2', 'l1', 'l1', 'l1', 'l1', 'l1', 'l2',
                    'l1', 'l2'],
              mask=[False, False, False, False, False, False, False, False,
                    False, False, False, False, False, False, False, False,
                    False, False, False, False],
        fill_value='?',
             dtype=object),
 'params': [{'C': 20, 'penalty': 'l1'},
  {'C': 53, 'penalty': 'l2'},
  {'C': 5, 'penalty': 'l1'},
  {'C': 95, 'penalty': 'l2'},
  {'C': 50, 'penalty': 'l2'},
  {'C': 71, 'penalty': 'l1'},
  {'C': 41, 'penalty': 'l2'},
  {'C': 43, 'penalty': 'l1'},
  {'C': 8, 'penalty': 'l1'},
  {'C': 30, 'penalty': 'l2'},
  {'C': 70, 'penalty': 'l1'},
  {'C': 91, 'penalty': 'l2'},
  {'C': 53, 'penalty': 'l1'},
  {'C': 15, 'penalty': 'l1'},
  {'C': 35, 'penalty': 'l1'},
  {'C': 41, 'penalty': 'l1'},
  {'C': 56, 'penalty': 'l1'},
  {'C': 82, 'penalty': 'l2'},
  {'C': 90, 'penalty': 'l1'},
  {'C': 27, 'penalty': 'l2'}],
 'rank_test_score': array([10, 5, 5, 1, 5, 10, 5, 10, 1, 5, 10, 1, 10, 10, 10, 10, 10,
         1, 10, 10], dtype=int32),
 'split0_test_score': array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
        1., 1., 1.]),
 'split0_train_score': array([0.95 , 0.975 , 0.95833333, 0.975 , 0.975,
        0.95833333, 0.975 , 0.95833333, 0.95833333, 0.975 ,
        0.95833333, 0.975 , 0.95833333, 0.95 , 0.95833333,
        0.95833333, 0.95833333, 0.975 , 0.95833333, 0.975 ]),
 'split1_test_score': array([0.96666667, 1\. , 1\. , 1\. , 1\. ,
        0.96666667, 1\. , 0.96666667, 1\. , 1\. ,
        0.96666667, 1\. , 0.96666667, 0.96666667, 0.96666667,
        0.96666667, 0.96666667, 1\. , 0.96666667, 1\. ]),
 'split1_train_score': array([0.975, 0.975, 0.975, 0.975, 0.975, 0.975, 0.975, 0.975, 0.975,
        0.975, 0.975, 0.975, 0.975, 0.975, 0.975, 0.975, 0.975, 0.975,
        0.975, 0.975]),
 'split2_test_score': array([0.93333333, 0.93333333, 0.93333333, 0.96666667, 0.93333333,
        0.93333333, 0.93333333, 0.93333333, 0.96666667, 0.93333333,
        0.93333333, 0.96666667, 0.93333333, 0.93333333, 0.93333333,
        0.93333333, 0.93333333, 0.96666667, 0.93333333, 0.93333333]),
 'split2_train_score': array([0.975 , 0.98333333, 0.975 , 0.98333333, 0.98333333,
        0.975 , 0.98333333, 0.975 , 0.975 , 0.98333333,
        0.975 , 0.98333333, 0.975 , 0.975 , 0.975 ,
        0.975 , 0.975 , 0.98333333, 0.975 , 0.98333333]),
 'split3_test_score': array([0.93333333, 0.93333333, 0.93333333, 0.93333333, 0.93333333,
        0.93333333, 0.93333333, 0.93333333, 0.93333333, 0.93333333,
        0.93333333, 0.93333333, 0.93333333, 0.93333333, 0.93333333,
        0.93333333, 0.93333333, 0.93333333, 0.93333333, 0.9 ]),
 'split3_train_score': array([0.99166667, 0.98333333, 0.98333333, 0.99166667, 0.98333333,
        0.99166667, 0.98333333, 0.99166667, 0.98333333, 0.98333333,
        0.99166667, 0.99166667, 0.99166667, 0.99166667, 0.99166667,
        0.99166667, 0.99166667, 0.98333333, 0.99166667, 0.98333333]),
 'split4_test_score': array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
        1., 1., 1.]),
 'split4_train_score': array([0.975 , 0.975 , 0.975 , 0.975 , 0.975 ,
        0.975 , 0.975 , 0.975 , 0.975 , 0.975 ,
        0.975 , 0.975 , 0.975 , 0.975 , 0.975 ,
        0.975 , 0.975 , 0.975 , 0.975 , 0.96666667]),
 'std_fit_time': array([2.16497645e-03, 5.39653699e-05, 1.00355397e-03, 4.75298306e-05,
        9.75692490e-05, 2.63689357e-03, 7.04799517e-05, 2.52499464e-03,
        1.92020413e-03, 6.05031761e-05, 1.78589024e-03, 5.85074724e-05,
        2.28621528e-03, 2.19771432e-03, 1.96957384e-03, 3.06769107e-03,
        1.15194163e-03, 2.10475943e-05, 1.33958298e-03, 4.09795418e-05]),
 'std_score_time': array([4.62378644e-05, 1.66142000e-06, 3.40806829e-05, 1.73623737e-05,
        5.26490415e-05, 4.75790783e-05, 1.48510089e-06, 7.53432889e-06,
        3.86445261e-05, 8.16042958e-05, 4.98746594e-05, 1.93474877e-05,
        2.82650630e-05, 2.54787261e-05, 2.55031663e-05, 3.09080976e-05,
        2.99830109e-05, 7.89824294e-06, 2.02431836e-05, 4.25877252e-05]),
 'std_test_score': array([0.02981424, 0.03265986, 0.03265986, 0.02666667, 0.03265986,
        0.02981424, 0.03265986, 0.02981424, 0.02666667, 0.03265986,
        0.02981424, 0.02666667, 0.02981424, 0.02981424, 0.02981424,
        0.02981424, 0.02981424, 0.02666667, 0.02981424, 0.0421637 ]),
 'std_train_score': array([0.01333333, 0.00408248, 0.00816497, 0.00666667, 0.00408248,
        0.01054093, 0.00408248, 0.01054093, 0.00816497, 0.00408248,
        0.01054093, 0.00666667, 0.01054093, 0.01333333, 0.01054093,
        0.01054093, 0.01054093, 0.00408248, 0.01054093, 0.0062361 ])}
交叉验证结果可能看起来很混乱,但您可以将其导入pandas数据框:
import pandas as pd
df = pd.DataFrame(estimator.cv_results_)
df.head()
我们可以看到如下几条记录:
mean_fit_time mean_score_time mean_test_score mean_train_score param_C \
0 0.009134 0.000261 0.966667 0.973333 20 
1 0.000652 0.000165 0.973333 0.978333 53 
2 0.008730 0.000206 0.973333 0.973333 5 
3 0.000681 0.000177 0.980000 0.980000 95 
4 0.000827 0.000224 0.973333 0.978333 50 
  param_penalty params rank_test_score \
0 l1 {'C': 20, 'penalty': 'l1'} 10 
1 l2 {'C': 53, 'penalty': 'l2'} 5 
2 l1 {'C': 5, 'penalty': 'l1'} 5 
3 l2 {'C': 95, 'penalty': 'l2'} 1 
4 l2 {'C': 50, 'penalty': 'l2'} 5 
   split0_test_score split0_train_score ... split2_test_score \
0 1.0 0.950000 ... 0.933333 
1 1.0 0.975000 ... 0.933333 
2 1.0 0.958333 ... 0.933333 
3 1.0 0.975000 ... 0.966667 
4 1.0 0.975000 ... 0.933333 
   split2_train_score split3_test_score split3_train_score \
0 0.975000 0.933333 0.991667 
1 0.983333 0.933333 0.983333 
2 0.975000 0.933333 0.983333 
3 0.983333 0.933333 0.991667 
4 0.983333 0.933333 0.983333 
   split4_test_score split4_train_score std_fit_time std_score_time \
0 1.0 0.975 0.002165 0.000046 
1 1.0 0.975 0.000054 0.000002 
2 1.0 0.975 0.001004 0.000034 
3 1.0 0.975 0.000048 0.000017 
4 1.0 0.975 0.000098 0.000053 
   std_test_score std_train_score 
0 0.029814 0.013333 
1 0.032660 0.004082 
2 0.032660 0.008165 
3 0.026667 0.006667 
4 0.032660 0.004082 
[5 rows x 22 columns]
您可以过滤数据帧,查看mean_test_score处于最大值的位置:
df[df['mean_test_score'] == df['mean_test_score'].max()]
这将输出以下内容:
    mean_fit_time mean_score_time mean_test_score mean_train_score param_C \
3 0.000681 0.000177 0.98 0.980000 95 
8 0.008837 0.000261 0.98 0.973333 8 
11 0.000684 0.000180 0.98 0.980000 91 
17 0.000677 0.000174 0.98 0.978333 82 
   param_penalty params rank_test_score \
3 l2 {'C': 95, 'penalty': 'l2'} 1 
8 l1 {'C': 8, 'penalty': 'l1'} 1 
11 l2 {'C': 91, 'penalty': 'l2'} 1 
17 l2 {'C': 82, 'penalty': 'l2'} 1 
    split0_test_score split0_train_score ... split2_test_score \
3 1.0 0.975000 ... 0.966667 
8 1.0 0.958333 ... 0.966667 
11 1.0 0.975000 ... 0.966667 
17 1.0 0.975000 ... 0.966667 
    split2_train_score split3_test_score split3_train_score \
3 0.983333 0.933333 0.991667 
8 0.975000 0.933333 0.983333 
11 0.983333 0.933333 0.991667 
17 0.983333 0.933333 0.983333 
    split4_test_score split4_train_score std_fit_time std_score_time \
3 1.0 0.975 0.000048 0.000017 
8 1.0 0.975 0.001920 0.000039 
11 1.0 0.975 0.000059 0.000019 
17 1.0 0.975 0.000021 0.000008 
    std_test_score std_train_score 
3 0.026667 0.006667 
8 0.026667 0.008165 
11 0.026667 0.006667 
17 0.026667 0.004082 
[4 rows x 22 columns]
作为练习,您可以使用以下超参数为GradientBoostingClassifier创建一个参数网格,对GridSearchCV和RandomizedSearchCV进行实验:
learning_rate(默认值= 0.1)—提高学习率n_estimators(默认值= 100)—适合的提升树的数量max_depth(默认值= 3)—最大树深
热启动
就自动化 ML ( 自动化)流水线而言,超参数搜索空间可以增长得非常快,在有限的时间和资源下,穷举搜索变得不切实际。您需要更智能的方法来执行这项任务,尤其是如果您有一个大型数据集,并且有一个复杂的模型在处理它。如果你发现自己处于这种情况下,GridSeachCV实例穷举搜索将是不可行的,或者RandomizedSearchCV的随机参数绘制可能不会在有限的时间内给你最好的结果。
热启动的基本思想是使用从先前的训练跑步中获得的信息来为下一次训练跑步确定更聪明的起点。
例如,LogisticRegression有一个warm_start参数,默认设置为False。以下示例显示了第一次训练的时间,以及参数更新后设置为False的时间:
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression(C=10, tol=0.00001)
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
Y = iris.target
from time import time
start = time()
log_reg.fit(X, Y)
end = time()
print("Time: {}".format(end - start))
# Time: 0.0009272098541259766
log_reg.set_params(C=20)
# LogisticRegression(C=100, class_weight=None, dual=False, fit_intercept=True,
# intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
# penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
# verbose=0, warm_start=False)
start = time()
log_reg.fit(X, Y)
end = time()
print("Time: {}".format(end - start))
# Time: 0.0012941360473632812
LogisticRegression的默认解算器是liblinear,它将在每次新拟合之前重新初始化权重,但是其他解算器如lbfgs、newton-cg、sag和saga可以利用warm_start并通过使用来自先前拟合的信息来减少计算时间。
下面的代码片段向您展示了它在实践中是如何工作的一个小例子:
log_reg = LogisticRegression(C=10, solver='sag', warm_start=True, max_iter=10000)
start = time()
log_reg.fit(X, Y)
end = time()
print("Time: {}".format(end - start))
# Time: 0.043714046478271484
log_reg.set_params(C=20)
start = time()
log_reg.fit(X, Y)
end = time()
print("Time: {}".format(end - start))
# Time: 0.020781755447387695
基于贝叶斯的超参数调整
当涉及到基于模型的超参数调整时,有几种方法可以使用,这些方法在基于顺序模型的全局优化 ( SMBO )下结合使用。
当你想到GridSearchCV或RandomizedSearchCV时,你可能会理所当然地觉得它们交叉验证超参数的方式不是很聪明。两者都预先定义了要在训练期间验证的超参数集,并且都不是为了从训练期间可能获得的信息中获益而设计的。如果您能够找到一种方法,从基于模型性能的超参数验证的先前迭代中学习,那么您就会知道哪个超参数集可能在下一次迭代中给出更好的性能。
SMBO 方法源于这种推理,基于贝叶斯的超参数优化就是其中之一。
基于序列模型的算法配置 ( SMAC )是一个很棒的库,利用贝叶斯优化来配置给定 ML 算法的超参数,非常好用。
以下代码片段向您展示了如何优化您在 SMAC 开始时使用的branin函数:
from smac.facade.func_facade import fmin_smac
x, cost, _ = fmin_smac(func=branin, # function
                           x0=[3.2, 4.5], # default configuration
                           bounds=[(-5, 10), (0, 15)], # limits
                           maxfun=500, # maximum number of evaluations
                           rng=3) # random seed
print(x, cost)
# [3.12848204 2.33810374] 0.4015064637498025
示例系统
在本节中,您将编写一个包装函数来优化 XGBoost 算法超参数,以提高Breast Cancer Wisconsin数据集的性能:
# Importing necessary libraries
import numpy as np
from xgboost import XGBClassifier
from sklearn import datasets
from sklearn.model_selection import cross_val_score
# Importing ConfigSpace and different types of parameters
from smac.configspace import ConfigurationSpace
from ConfigSpace.hyperparameters import CategoricalHyperparameter, \
    UniformFloatHyperparameter, UniformIntegerHyperparameter
from ConfigSpace.conditions import InCondition
# Import SMAC-utilities
from smac.tae.execute_func import ExecuteTAFuncDict
from smac.scenario.scenario import Scenario
from smac.facade.smac_facade import SMAC
# Creating configuration space.
# Configuration space will hold all of your hyperparameters
cs = ConfigurationSpace()
# Defining hyperparameters and range of values that they can take
learning_rate = UniformFloatHyperparameter("learning_rate", 0.001, 0.1, default_value=0.1)
n_estimators = UniformIntegerHyperparameter("n_estimators", 100, 200, default_value=100)
# Adding hyperparameters to configuration space
cs.add_hyperparameters([learning_rate, n_estimators])
# Loading data set
wbc_dataset = datasets.load_breast_cancer()
# Creating function to cross validate XGBoost classifier given the configuration space
def xgboost_from_cfg(cfg):
    """ Creates a XGBoost based on a configuration and evaluates it on the
    Wisconsin Breast Cancer-dataset using cross-validation.
    Parameters:
    -----------
    cfg: Configuration (ConfigSpace.ConfigurationSpace.Configuration)
        Configuration containing the parameters.
        Configurations are indexable!
    Returns:
    --------
    A crossvalidated mean score for the svm on the loaded data-set.
    """
    cfg = {k: cfg[k] for k in cfg if cfg[k]}
    clf = XGBClassifier(**cfg, eval_metric='auc', early_stopping_rounds=50, random_state=42)
    scores = cross_val_score(clf, wbc_dataset.data, wbc_dataset.target, cv=5)
    return 1 - np.mean(scores) # Minimize!
# Creating Scenario object
scenario = Scenario({"run_obj": "quality",
                     "runcount-limit": 200, # maximum function evaluations
                     "cs": cs, # configuration space
                     "deterministic": "true"
                     })
# SMAC object handles bayesian optimization loop
print("Please wait until optimization is finished")
smac = SMAC(scenario=scenario, rng=np.random.RandomState(42),
        tae_runner=xgboost_from_cfg)
incumbent = smac.optimize()
# Let's see the best performing hyperparameter values
print(incumbent)
# Configuration:
# learning_rate, Value: 0.08815217130807515
# n_estimators, Value: 196
# You can see the errpr rate of optimized hyperparameters
inc_value = xgboost_from_cfg(incumbent)
print("Optimized Value: %.2f" % (inc_value))
# 0.02
太好了。现在您知道如何创建您的配置空间,添加您的超参数,并为每个超参数定义值范围。配置完成后,您已经看到了如何创建一个场景对象,并使用 SMAC 优化给定估计器的超参数。
您可以使用 SMAC 对象获取运行历史,并查看每个配置的成本:
param_1 = []
param_2 = []
costs = []
for k,v in smac.runhistory.config_ids.items():
    param_1.append(k._values['learning_rate'])
    param_2.append(k._values['n_estimators'])
    costs.append(smac.runhistory.cost_per_config[v])
print(len(param_1), len(param_2), len(costs))
import matplotlib.pyplot as plt
import matplotlib.cm as cm
sc = plt.scatter(param_1, param_2, c=costs)
plt.colorbar(sc)
plt.show()
下图显示了learning_rate和n_estimators在优化过程中取的不同值及其相关成本:

可以看到最佳配置为learning_rate~ 0.09,n_estimators~ 200。
摘要
在本章中,您学习了模型参数、超参数和配置空间。让我们快速回顾一下:
- 模型参数:你可以把这些作为训练时要学习的参数
 - 模型超参数:这些是你应该在训练开始前定义的参数
 - 配置空间参数:这些参数是指用于主持实验的环境的任何其他参数
 
您已经了解了常见的超参数优化方法,如网格搜索和随机搜索。网格搜索和随机搜索不使用从以前的训练运行中产生的信息,这是基于贝叶斯的优化方法解决的一个缺点。
基于贝叶斯的优化方法利用先前训练运行的信息来决定下一次训练运行的超参数值,并以更智能的方式在超参数空间中导航。SMAC 是 auto-sklearn 用来优化给定估计量的超参数的工具,本章向您展示了如何在自己的 ML 流水线中使用这种方法。
六、创建自动流水线
前几章重点介绍了机器学习 ( ML )项目中需要执行的不同阶段。ML 模型要成功执行并产生结果,必须将许多移动部件捆绑在一起。将 ML 过程的不同部分连接在一起的过程被称为流水线。流水线是一个广义的概念,但对于数据科学家来说是非常重要的概念。在软件工程中,人们构建流水线来开发从源代码到部署的软件。类似地,在 ML 中,创建了一个流水线,允许数据从原始格式流向一些有用的信息。它提供了构造多 ML 并行流水线系统的机制,以便比较几种 ML 方法的结果。
流水线的每一级都被馈送来自其前一级的已处理数据;也就是说,处理单元的输出被提供作为其下一步的输入。数据在流水线中流动就像水在流水线中流动一样。掌握流水线的概念是创建无错误的 ML 模型的有力方法,流水线构成了构建 AutoML 系统的关键要素。在本章中,我们将讨论以下主题:
- ML 流水线介绍
 - 构建简单的流水线
 - 功能变压器
 - 使用弱学习者和集合构建复杂的流水线
 
我们将在下一节开始介绍 ML 流水线。
技术要求
所有代码示例都可以在本章 GitHub 链接的Chapter 06文件夹中找到。
机器学习流水线介绍
通常,ML 算法需要干净的数据来检测数据中的一些模式,并在新的数据集上进行预测。然而,在现实世界的应用程序中,数据通常不准备直接输入到 ML 算法中。类似地,ML 模型的输出只是数字或字符,在现实世界中执行某些动作时需要对其进行处理。为了实现这一点,ML 模型必须部署在生产环境中。将原始数据转换为可用信息的整个框架是使用 ML 流水线执行的。
以下是 ML 流水线的高级图示:

我们将把上图所示的模块分解如下:
- 
数据摄取:是获取数据,导入数据使用的过程。数据可以来源于多个系统,如企业资源规划 ( ERP )软件、客户关系管理 ( CRM )软件、web 应用。数据提取可以是实时的,也可以是批量的。有时,获取数据是一个棘手的部分,也是最具挑战性的步骤之一,因为我们需要具备良好的业务和数据理解能力。
 - 
数据准备:我们在第三章、数据预处理中研究了各种数据准备技术。有几种方法可以将数据预处理成适合构建模型的形式。真实世界的数据往往是倾斜的——有缺失的数据,有时会很嘈杂。因此,有必要对数据进行预处理,使其干净并经过转换,这样它就可以通过 ML 算法运行了。
 - 
ML 模型训练:它涉及使用各种 ML 技术来理解数据中的本质特征,进行预测,或者从中得出见解。通常,最大似然算法已经被编码并作为应用编程接口或编程接口可用。我们需要承担的最重要的责任是调整超参数。使用超参数并优化它们以创建最佳拟合模型是模型训练阶段最关键和最复杂的部分。
 - 
模型评估:有多种标准可以用来评估模型。它是统计方法和商业规则的结合。在 AutoML 流水线中,评估主要基于各种统计和数学度量。如果一个 AutoML 系统是为某些特定的业务领域或用例开发的,那么业务规则也可以嵌入到系统中来评估模型的正确性。
 - 
再培训:我们为用例创建的第一个模型往往不是最好的模型。它被认为是一个基线模型,我们试图通过反复训练来提高模型的精度。
 - 
部署:最后一步是部署模型,包括将模型应用和迁移到业务运营中供他们使用。部署阶段高度依赖于组织拥有的信息技术基础设施和软件能力。
 
正如我们所看到的,为了从 ML 模型中得到结果,我们需要执行几个阶段。scikit-learn 为我们提供了一个流水线功能,可以用来创建几个复杂的流水线。在构建 AutoML 系统时,流水线将非常复杂,因为必须捕获许多不同的场景。然而,如果我们知道如何对数据进行预处理,利用最大似然算法并应用各种评估指标,流水线就是给这些部分赋予形状的问题。
让我们使用 scikit-learn 设计一个非常简单的流水线。
简单的流水线
我们将首先导入一个名为Iris的数据集,该数据集已经存在于 scikit-learn 的样本数据集库中(http://sci kit-learn . org/stable/auto _ examples/datasets/plot _ iris _ dataset . html)。数据集由四个要素组成,有 150 行。我们将在流水线中开发以下步骤,使用Iris数据集训练我们的模型。问题陈述是使用四个不同的特征来预测一个Iris数据的种类:

在这个流水线中,我们将使用MinMaxScaler方法来缩放输入数据,并使用逻辑回归来预测Iris的种类。然后,将根据精度测量结果对模型进行评估:
- 第一步是从 scikit-learn 导入各种库,这些库将提供完成我们任务的方法。我们在前面几章中已经了解了这一切。唯一的补充是来自
sklearn.pipeline的Pipeline法。这将为我们提供创建 ML 流水线所需的必要方法: 
from sklearn.datasets import load_iris
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
- 下一步是加载
iris数据,并将其拆分为训练和测试数据集。在这个例子中,我们将使用 80%的数据集来训练模型,剩下的 20%用来测试模型的准确性。我们可以使用shape功能查看数据集的维度: 
# Load and split the data
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)
X_train.shape
- 下面的结果显示了具有
4列和120行的训练数据集,相当于Iris数据集的 80%,如预期的那样: 

- 接下来,我们打印数据集以浏览数据:
 
print(X_train)
上述代码提供了以下输出:

- 下一步是创建流水线。流水线对象采用(键、值)对的形式。Key 是具有特定步骤名称的字符串,value 是函数或实际方法的名称。在下面的代码片段中,我们将
MinMaxScaler()方法命名为minmax,将LogisticRegression(random_state=42)命名为lr: 
pipe_lr = Pipeline([('minmax', MinMaxScaler()),
 ('lr', LogisticRegression(random_state=42))])
- 然后,我们将流水线对象(
pipe_lr)拟合到训练数据集: 
pipe_lr.fit(X_train, y_train)
- 当我们执行前面的代码时,我们得到以下输出,它显示了所构建的拟合模型的最终结构:
 

- 最后一步是使用
score方法在test数据集上对模型进行评分: 
score = pipe_lr.score(X_test, y_test)
print('Logistic Regression pipeline test accuracy: %.3f' % score)
从下面的结果可以看出,模型的准确率为0.900,为 90%:

在前面的例子中,我们创建了一个流水线,它由两个步骤组成,即minmax缩放和LogisticRegression。当我们在pipe_lr流水线上执行fit方法时,MinMaxScaler对输入数据执行fit和transform方法,并将其传递给估计器,估计器是逻辑回归模型。流水线中的这些中间步骤被称为变压器,最后一步是估计器。
变压器用于数据预处理,有fit和transform两种方法。fit方法用于从训练数据中找到参数,transform方法用于将数据预处理技术应用于数据集。
估计器用于创建机器学习模型,有两种方法,fit和predict。fit方法用于训练最大似然模型,而predict方法用于将训练好的模型应用于测试或新的数据集。
下图总结了这一概念:

我们只需要调用流水线的拟合方法来训练模型,调用预测方法来创建预测。Rest 所有功能即 Fit 和 Transform 封装在流水线的功能中,并按上图所示执行。
有时,我们需要编写一些自定义函数来执行自定义转换。下一节是关于函数转换器,它可以帮助我们实现这个定制的功能。
函数转换器
A FunctionTransformer用于定义一个用户自定义函数,该函数消耗来自流水线的数据,并将该函数的结果返回到流水线的下一阶段。这用于无状态转换,例如取数字的平方或对数,定义自定义缩放函数,等等。
在下面的例子中,我们将使用CustomLog函数和预定义的预处理方法StandardScaler来构建流水线:
- 我们导入所有需要的库,就像我们在前面的例子中所做的那样。这里唯一增加的是
sklearn.preprocessing库中的FunctionTransformer方法。此方法用于执行自定义转换器函数,并将其缝合到流水线中的其他阶段: 
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.preprocessing import StandardScaler
- 在下面的代码片段中,我们将定义一个自定义函数,它返回一个数字
X的日志: 
def CustomLog(X):
    return np.log(X)
- 
接下来,我们将定义一个名为
PreprocData的数据预处理函数,它接受数据集的输入数据(X)和目标(Y)。对于这个例子来说,Y是不必要的,因为我们不打算构建一个监督模型,而只是演示一个数据预处理流水线。然而,在现实世界中,我们可以直接使用这个函数来创建一个有监督的 ML 模型。 - 
这里,我们使用
make_pipeline函数来创建流水线。我们在前面的例子中使用了pipeline函数,其中我们必须为数据预处理或 ML 函数定义名称。使用make_pipeline函数的优势在于它会自动生成函数的名称或键: 
def PreprocData(X, Y):
pipe = make_pipeline(
 FunctionTransformer(CustomLog),StandardScaler()
 )
 X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
 pipe.fit(X_train, Y_train)
 return pipe.transform(X_test), Y_test
- 当我们准备好流水线后,我们可以加载
Iris数据集。我们打印输入数据X来看一下数据: 
iris = load_iris()
X, Y = iris.data, iris.target
print(X)
上面的代码打印了以下输出:

- 接下来,我们将通过传递
iris数据来调用PreprocData函数。返回的结果是一个经过转换的数据集,首先使用我们的CustomLog函数进行处理,然后使用StandardScaler数据预处理方法进行处理: 
X_transformed, Y_transformed = PreprocData(X, Y)
print(X_transformed)
- 前面的数据转换任务产生以下转换后的数据结果:
 

我们现在需要为一个 AutoML 系统构建各种复杂的流水线。在下一节中,我们将使用几个数据预处理步骤和 ML 算法创建一个复杂的流水线。
复杂的流水线
在本节中,我们将使用鸢尾花的四种不同特征来确定预测鸢尾花种类的最佳分类器。我们将结合使用四种不同的数据预处理技术以及四种不同的 ML 算法来完成这项任务。以下是该作业的流水线设计:

我们将按如下方式进行:
- 我们从导入任务所需的各种库和函数开始:
 
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn import svm
from sklearn import tree
from sklearn.pipeline import Pipeline
- 接下来,我们加载
Iris数据集,并将其拆分为train和test数据集。X_train和Y_train数据集将用于训练不同的模型,X_test和Y_test将用于测试训练的模型: 
# Load and split the data
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)
- 接下来,我们将创建四个不同的流水线,每个模型一个。在 SVM 模型的流水线中,
pipe_svm,我们将首先使用StandardScaler缩放数字输入,然后使用主成分分析 ( 主成分分析)创建主成分。最后,使用该预处理数据集构建支持向量机 ( SVM )模型。 - 同样,我们将构建一个流水线来创建名为
pipe_knn的 KNN 模型。在执行KNeighborsClassifier创建 KNN 模型之前,仅使用StandardScaler对数据进行预处理。 - 然后,我们创建一个流水线来构建决策树模型。我们使用
StandardScaler和MinMaxScaler方法对DecisionTreeClassifier方法使用的数据进行预处理。 - 使用流水线创建的最后一个模型是随机森林模型,其中只有
StandardScaler用于预处理RandomForestClassifier方法要使用的数据。 
以下是用于创建这四个不同流水线的代码片段,这四个流水线用于创建四个不同的模型:
# Construct svm pipeline
pipe_svm = Pipeline([('ss1', StandardScaler()),
      ('pca', PCA(n_components=2)),
      ('svm', svm.SVC(random_state=42))])
# Construct knn pipeline
pipe_knn = Pipeline([('ss2', StandardScaler()),
      ('knn', KNeighborsClassifier(n_neighbors=6, metric='euclidean'))])
# Construct DT pipeline
pipe_dt = Pipeline([('ss3', StandardScaler()),
      ('minmax', MinMaxScaler()),
      ('dt', tree.DecisionTreeClassifier(random_state=42))])
# Construct Random Forest pipeline
num_trees = 100
max_features = 1
pipe_rf = Pipeline([('ss4', StandardScaler()),
      ('pca', PCA(n_components=2)),
      ('rf', RandomForestClassifier(n_estimators=num_trees, max_features=max_features))])
- 接下来,我们需要将流水线的名称存储在字典中,用于显示结果:
 
pipe_dic = {0: 'K Nearest Neighbours', 1: 'Decision Tree', 2:'Random Forest', 3:'Support Vector Machines'}
- 然后,我们将列出迭代执行这些流水线的四个流水线:
 
pipelines = [pipe_knn, pipe_dt,pipe_rf,pipe_svm]
- 现在,我们已经准备好了整个流水线的复杂结构。剩下的事情就是将数据与流水线匹配,评估结果,并选择最佳模型。
 
在下面的代码片段中,我们将四个流水线中的每一个迭代地适应训练数据集:
# Fit the pipelines
for pipe in pipelines:
  pipe.fit(X_train, y_train)
- 一旦模型拟合成功执行,我们将使用以下代码片段检查四个模型的准确性:
 
# Compare accuracies
for idx, val in enumerate(pipelines):
  print('%s pipeline test accuracy: %.3f' % (pipe_dic[idx], val.score(X_test, y_test)))
- 我们可以从以下结果中注意到,k 近邻和决策树模型以 100%的完美准确率领先。这太难以置信了,可能是使用小数据集和/或过度拟合的结果:
 

- 我们可以使用两个获胜模型中的任意一个,即 k 近邻 ( KNN )或决策树模型进行部署。我们可以使用下面的代码片段来实现这一点:
 
best_accuracy = 0
best_classifier = 0
best_pipeline = ''
for idx, val in enumerate(pipelines):
 if val.score(X_test, y_test) > best_accuracy:
 best_accuracy = val.score(X_test, y_test)
 best_pipeline = val
 best_classifier = idx
print('%s Classifier has the best accuracy of %.2f' % (pipe_dic[best_classifier],best_accuracy))
- 由于 k 近邻和决策树的精度相似,KNN 被选为最佳模型,因为它是流水线中的第一个模型。但是,在这个阶段,我们还可以使用一些业务规则或访问执行成本来决定最佳模型:
 

摘要
这一章是为 ML 系统构建流水线的草图——它只是冰山一角。建造流水线非常复杂。然而,一旦开发出来,它会让开发人员的生活更加舒适。它降低了制定不同模型的复杂性,因此成为创建自动语言系统所需的基本概念。我们在本章中描述的概念为您创建流水线奠定了基础。当您在本章中构建流水线时,您一定已经理解了使用流水线后模型构建过程的结构有多好。
下一章将总结我们到目前为止的学习。它还将为您提供几个建议,这些建议将有助于设计自动语言系统和成功执行数据科学项目。
七、深度学习
人工智能 ( AI )的下一步更多的是自动化。在这本书里,我们已经介绍了自动机器学习 ( AutoML )的一些基础知识。还有一个人工智能领域刚刚开始出现在多个用例中,并且需要极其自动化地应用。人工智能领域的这一领域被称为深度学习 ( DL )。DL 正处于机器能做什么的临界点。它可以比机器学习 ( ML )做得更多,更轻松,精度更好。DL 算法可以自己学习数据集的关键特征,可以调整权重以创建更好的模型,等等。下行链路网络的应用非常广泛。
随着深度学习的出现,图像、语音和视频识别领域的研究人员和实践者正在看到一些可操作的结果。它帮助人工智能接近了成为机器人大脑的最初目标。它还可以在不断增长的物联网 ( 物联网)领域发挥作用。对于企业来说,DL 已经在简化客户服务和协助许多人力密集型任务的自动化方面发挥了重要作用。到目前为止,你可能已经遇到了由 DL 驱动的机器人试图回答你的产品查询或帮助你预订你最喜欢的比萨饼订单。
DL 也在医药和医疗保健领域释放发展。如果机器开始通过阅读 x 光和核磁共振扫描来诊断你的疾病,不要感到惊讶。预计 DL 将解决人类拥有的许多谜团,并自动化以前不可能完成的大量手动任务。很有趣,不是吗?让我们揭开 DL 的一些深层秘密。
在本章中,我们将了解:
- 前馈神经网络
 - 自动编码器
 - 深度卷积网络
 
在这一章中,我们将尝试举例说明 DL 的一些基本概念。我们这里的重点不是阻止你用方程、数学公式和导数来描述。虽然它们是基本概念,但它们可能是压倒性的。相反,我们将以说明性的方式向您介绍这些概念,并帮助您编写第一个 DL 代码。
神经网络是 DL 网络的前身,它们构成了 DL 框架的构件。神经网络背后的基本思想是创建一个模拟我们生物大脑工作的计算系统。
技术要求
所有代码示例都可以在 GitHub 的Chapter 07文件夹中找到。
神经网络综述
神经网络的最佳定义是由第一批神经计算机科学家之一的发明家罗伯特·赫克特-尼尔森博士在 1989 年 2 月人工智能专家莫林·考迪尔的《神经网络入门:第一部分》中提供的:
...a computing system made up of a number of simple, highly interconnected processing elements, which process information by their dynamic state response to external inputs.
神经网络的基本构件是神经元,它们被组织在不同的层中。分层架构将每一层的神经元连接到下一层的神经元。神经网络至少有三层。输入层连接到一个或多个隐藏层,其中通过加权链接系统建立连接。最后一个隐藏层连接到产生任务结果的输出层。
下图说明了一个三层神经网络:

这个网络也被称为全连接人工神经网络或前馈神经网络 ( FNN )。还有一种神经网络架构,将结果传播回去学习和调整权重,称为前馈神经网络,反向传播,如下图所示:

让我们以综合的方式检查神经网络的每个单元。
神经元
神经元是神经网络的基本单位。每个神经元都有多个输入、一个处理器和一个输出。神经元的处理是从累加所有输入开始的,根据激活函数,它会发出一个信号,传播给其他神经元或作为输出信号。这有助于神经元学习。神经元最基本的形式被称为感知器。感知器的二进制输出为 0 或 1。
典型的神经元如下所示:

我们可以看到,神经元有几个输入。对于每个输入连接,都有一个与特定连接相关联的权重。当神经元被激活时,状态是通过将输入与相应连接的权重相乘来计算的。数学上,它可以用下面的函数来表示:

其中 x i 为包含偏差的输入值。
偏差是一个额外的输入,一个神经元。偏差有自己的连接权重,偏差的值总是 1。这是为了确保即使没有输入,即输入值为 0,神经元也会激活。
一旦计算出状态,该值就通过一个激活函数来规范化结果。
激活功能
激活函数用于使用加权输入的先验线性组合产生非线性决策。我们将讨论主要用于 DL 应用程序的四种不同类型的激活函数:
- 步骤
 - 乙状结肠的
 - ReLU
 - 双曲正切
 
阶跃函数
在阶跃函数中,如果输入加权和的值大于某个阈值,则神经元被激活。两个选项如下:

如果 x ,即输入值的加权和大于或等于 0,则激活 1,否则激活 0。下图说明了阶跃函数:

下一个广泛使用的激活函数是 sigmoid 函数。
sigmoid 函数
sigmoid 函数定义为:

这里, x 是输入值的加权和的值。我们已经在逻辑回归中看到了这个函数。当 x 低于零度时,它下降,高于零度时,它接近于 1。与阶跃函数不同,它是一个非线性激活函数。它主要用于神经网络的输出层,当我们在分类任务中试图预测概率时。下图说明了 sigmoid 函数:

我们讨论的下一个激活函数是 ReLU ,它广泛用于神经网络的隐藏层。
ReLU 函数
研究人员发现,使用整流线性单元 ( ReLU )函数的神经网络比其他非线性函数(如 sigmoid 和 tanh)训练得更快,并且精度没有显著下降。所以, ReLU 功能是最重要的激活功能之一。如果 x 为正,则给出 x 的输出,否则为 0。
其定义如下:
A(x) =最大值(0,x)
ReLU 函数如下图所示:

ReLU is a non-linear function and the combination of ReLU functions are also non-linear. The range of ReLU is from 0 to infinity.
接下来,我们讨论 tanh 函数,它与 sigmoid 函数非常相似,但这里的值低于 0。
正切函数
tanh 函数也被称为双曲正切函数。值从-1 到+1。
其数学公式如下:

tanh 函数的输出以零为中心。它也是一个非线性函数,因此可以用于堆叠不同的层。tanh 功能如下图所示:

现在我们已经对什么是神经网络、神经网络的结构及其不同的组成部分有了一些了解,让我们使用 Keras 创建一个前馈神经网络。
一种基于 Keras 的前馈神经网络
Keras 是一个 DL 库,最初是基于 Python 构建的,运行在 TensorFlow 或 antao 之上。开发它是为了让 DL 实现更快:
- 我们在您的操作系统的命令提示符下使用以下命令调用
install keras: 
pip install keras
- 我们从导入
numpy和pandas库进行数据操作开始。此外,我们设置了一个seed,允许我们重现脚本的结果: 
import numpy as np
import pandas as pd
numpy.random.seed(8)
- 接下来,分别从
keras.models和keras.layers导入序列模型和密集层。Keras 模型被定义为一系列层。顺序构造允许用户配置和添加层。密集层允许用户构建完全连接的网络: 
from keras.models import Sequential
from keras.layers import Dense
- 然后加载
HR磨损数据集,该数据集有 14,999 行和 10 列。salary和sales属性是一次性编码的,由 Keras 用于构建 DL 模型: 
#load hrdataset
hr_data = pd.read_csv('data/hr.csv', header=0)
# split into input (X) and output (Y) variables
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
X = data_trnsf.drop('left', axis=1)
X.columns
以下是前面代码的输出:

- 然后以 70:30 的比例分割数据集,以训练和测试模型:
 
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, data_trnsf.left, test_size=0.3, random_state=42)
- 接下来,我们开始创建一个完全连接的神经网络,使用三层定义一个顺序模型。第一层是输入层。在这一层中,我们使用
input_dim参数、神经元数量和激活函数来定义输入特征的数量。我们将预处理数据集X_train中的 20 个输入要素的input_dim参数设置为20。第一层的神经元数量被指定为12。目标是预测员工流失,这是一个二元分类问题。因此,我们在前两层使用整流器activation功能将非线性引入模型并激活神经元。第二层为隐藏层,配置10神经元。第三层是输出层,有一个神经元具有sigmoid激活功能。这确保了二进制分类任务的输出介于 0 和 1 之间: 
# create model
model = Sequential()
model.add(Dense(12, input_dim=20, activation='relu'))
model.add(Dense(10, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
- 一旦我们配置了模型,下一步就是编译模型。在编译过程中,我们指定了
loss功能、optimizer和metrics。由于我们处理的是两类问题,我们将loss功能指定为binary_crossentropy。我们声明adam是用于本练习的优化器。优化算法的选择对模型的良好性能至关重要。adam优化器是随机梯度下降算法的扩展,是广泛使用的优化器: 
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
- 接下来,我们使用
fit方法将模型拟合到训练集。纪元用于指定向前和向后传递的次数。batch_size参数用于声明每个纪元中要使用的训练示例的数量。在我们的示例中,我们将epochs的编号指定为100,其中batch_size为10: 
# Fit the model
X_train = np.array(X_train)
model.fit(X_train, Y_train, epochs=100, batch_size=10)
- 一旦我们执行了前面的代码片段,执行就开始了,我们可以看到进度,如下图所示。一旦达到模型的
fit方法中指定的时期数,处理停止: 

- 最后一步是评估模型。我们在前面的评估指标中指定了准确性。在最后一步,我们使用以下代码片段检索模型的准确性:
 
# evaluate the model
scores = model.evaluate(X_train, Y_train)
print("%s: %.4f%%" % (model.metrics_names[1], scores[1]*100))
X_test = np.array(X_test)
scores = model.evaluate(X_test, Y_test)
print("%s: %.4f%%" % (model.metrics_names[1], scores[1]*100))
下面的结果显示模型精度为93.56%,测试精度为93.20%,这是一个相当不错的结果。我们可能会得到与根据种子描述的结果略有不同的结果:

Data scaling is often required to obtain good results from neural networks.
在下一节中,我们将讨论自动编码器,这是一种无监督的 DL 技术,广泛用于非线性降维。
自动编码器
自动编码器是一种可用于无监督学习的 DL。它类似于我们之前研究的其他降维技术,如主成分分析 ( 主成分分析)。然而,PCA 使用线性变换将数据从较高维度投影到较低维度,但是自动编码器使用非线性变换。
在自动编码器中,其结构由两部分组成:
- 编码器:该部分将输入压缩成较少的元素或比特数。输入被压缩到最大点,称为潜伏空间或瓶颈。这些压缩位被称为编码位。
 - 解码器:解码器尝试根据编码的位重建输入。如果解码器能够从编码的比特中再现精确的输入,那么我们可以说有一个完美的编码。然而,这是一个理想的案例场景,并不总是发生。重构误差提供了一种测量解码器重构努力和判断自动编码器精度的方法。
 
现在我们已经对自动编码器的结构有了一些了解,让我们使用下图将其可视化:

有不同类型的自动编码器,例如:
- 香草自动编码器:有两层神经网络架构,一个隐藏层。
 - 稀疏自动编码器:用于学习数据的稀疏表示。这里,对其损失函数施加约束,以约束自动编码器的重构。
 - 去噪自动编码器:在这些自动编码器中,引入了部分损坏的输入。这样做是为了防止自动编码器简单地学习输入的结构,并迫使网络发现更健壮的特征来学习输入模式。
 
异常检测是自动编码器最广泛使用的用例之一。这是一个从已知中发现未知的过程。在异常检测练习中,输入数据总是有一个类,即已知的。当自动编码器通过重构输入数据来学习数据模式时,它往往会发现可能是数据异常的未知模式:
- 与前面的示例一样,这里我们使用以下代码片段导入所需的库:
 
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from keras.models import Model, load_model
from keras.layers import Input, Dense
np.random.seed(8)
- 接下来,加载用于使用自动编码器构建异常检测器的数据集。演示使用的数据摘自 1974 年《汽车趋势》美国杂志;它包括 32 辆汽车(1973–74 款)的油耗和汽车设计与性能的 10 个方面。数据集做了一些修改,引入了一些数据异常:
 
# load autodesign
auto_data = pd.read_csv('data/auto_design.csv')
# split into input (X) and output (Y) variables
X =auto_data.drop('Unnamed: 0', 1)
from sklearn.model_selection import train_test_split
X_train, X_test = train_test_split(X, test_size=0.3, random_state=42)
print(X_train)
X_train.shape
对于前面的代码,我们得到以下输出:

- 接下来,定义输入维度。由于有 12 个不同的输入特征,并且我们计划使用自动编码器中的所有特征,我们将输入神经元的数量定义为
12。这嵌入在输入层中,如下面的代码片段所示: 
input_dim = X_train.shape[1]
encoding_dim = 12
input_layer = Input(shape=(input_dim, ))
- 接下来,我们创建一个编码器和解码器。编码器中使用了 ReLU 函数,这是一个非线性激活函数。编码层被传递到解码器,在那里它试图重建输入数据模式:
 
encoded = Dense(encoding_dim, activation='relu')(input_layer)
decoded = Dense(12, activation='linear')(encoded)
- 下面的模型将输入映射到它的重构,这是在解码器层
decoded中完成的。接下来,使用compile方法定义optimizer和loss功能。adadelta优化器使用指数衰减的梯度平均值,是一种高度自适应的学习速率方法。重建是一个线性过程,在解码器中使用线性激活函数来定义。loss定义为mse,均方误差,我们在第 2 章Python 机器学习入门中研究过: 
autoencoder = Model(input_layer, decoded)
autoencoder.compile(optimizer='adadelta', loss='mse')
- 接下来,训练数据
X_train被装入自动编码器。让我们用4的batch_size为100时期训练我们的自动编码器,并观察它是否达到稳定的列车或测试损失值: 
X_train = np.array(X_train)
autoencoder.fit(X_train, X_train,epochs=100,batch_size=4)
- 一旦我们执行了前面的代码片段,执行就开始了,我们可以在下面的截图中看到进度。一旦达到模型的
fit方法中指定的时期数,处理停止: 

- 一旦模型被拟合,我们通过将相同的
X_train数据集传递给自动编码器的predict方法来预测输入值。接下来,我们计算mse值,以了解自动编码器是否能够正确重建数据集,以及重建误差有多大: 
predictions = autoencoder.predict(X_train)
mse = np.mean(np.power(X_train - predictions, 2), axis=1)
- 我们可以绘制
mse来查看重建误差和它无法正确重建的输入数据的索引: 
plt.plot(mse)
- 从下图中我们可以观察到,自动编码器无法正确重建数据集的第 16 条记录。重建误差对于记录来说太高了,这是一种异常。第 13 条记录也有一个小的重建误差,这可能也是一个异常:
 

在下一节中,我们将重点介绍卷积神经网络 ( CNN ),它们广泛用于图像处理和图像识别。
卷积神经网络
这一部分将集中讨论美国有线电视新闻网的架构。虽然有线电视新闻网是一个可能不会在一章中完全涵盖的话题,但我们将重点关注一个有线电视新闻网可以让您轻松开始使用有线电视新闻网的重要因素。在我们的讨论中,我们将使用 Keras 包,使用包中的样本MNIST数据集创建 CNN。
当我们听到有线电视新闻网这个术语时,脑海中浮现的问题是,为什么是有线电视新闻网?我们将尝试用一个简短的解释来回答这个问题。
为什么是 CNN?
我们在前面的章节中讨论了前馈神经网络。尽管它们功能强大,但它们的主要缺点之一是 FNN 忽略了输入数据的结构。所有输入网络的数据必须首先转换成 1D 数字阵列。然而,对于像图像这样的高维数组,很难处理这种转换。保持图像的结构是至关重要的,因为有很多隐藏的信息存储在里面,这是美国有线电视新闻网进入画面的地方。美国有线电视新闻网在处理图像时会考虑图像的结构。
我们的下一个问题是困难项——卷积。这是什么?
什么是卷积?
卷积是一种特殊的数学运算,涉及两个函数f和g的乘法,以产生一个新的修正函数(f * g)。例如,图像(假设函数f)与过滤器函数g之间的卷积将产生图像的新版本。
我们刚刚讨论了过滤器,所以让我们试着理解什么是过滤器。
什么是过滤器?
美国有线电视新闻网使用过滤器来识别图像中的特征,如边缘、线条或拱门。他们在图像中寻找某些重要的模式或特征。过滤器用于搜索图像中的某些特征,并检测图像是否包含这些特征。在图像的不同位置应用滤镜,直到它覆盖整个图像。滤镜在卷积层中形成关键元素,这是有线电视新闻网的一个重要步骤。
美国有线电视新闻网主要有四个不同的层次:
- 卷积层
 - 图层继电器
 - 汇集层
 - 全连接层
 
我们来讨论卷积层,这是 CNN 的第一个阶段。
卷积层
这是在输入图像和我们刚才讨论的滤波器之间进行卷积运算的层。执行此步骤是为了减小图像的整体大小,以便更容易在下一层处理图像。让我们用一个简单的问题来理解这一层的功能,如何识别狗还是猫?当我们看到它们时,它会在一秒钟内自然而然地出现。我们不会分析它们的所有特征来确定狗是狗还是猫是猫。
我们识别重要的特征,如它们的眼睛、耳朵或尾巴,然后识别生物。这也是在卷积层中所做的。在这一层中,重要的特征被识别,其余的都被忽略。过滤器在图像中移动,以检测图像中的基本特征。移动过滤器的过程称为步。
卷积层的结果然后通过非线性激活函数传递,例如 ReLU 函数。
ReLU 层
这个额外的步骤被应用于卷积层,以在卷积特征图中引入非线性。我们在前面的章节中学习了 ReLU 函数。图像具有高度非线性的模式。当我们应用卷积时,它有可能变成线性的,因为有像乘法和求和这样的线性运算。因此,非线性激活函数,如 ReLU,被用来保持图像中的非线性。
有线电视新闻网的下一个阶段是汇集层。
汇集层
通过应用汇集功能,汇集层进一步减小了要素表示的大小。有不同种类的池功能,如average、min、max。最大池被广泛使用,因为它倾向于为每个步幅保持特征图的max值。这类似于卷积层,其中我们有一个滑动窗口,该窗口在特征地图上滑动以找到每个步幅内的max值。池层中的窗口大小通常小于卷积层中使用的窗口大小。
然后,合并后的要素地图将展平为 1D 表示,用于完全连接的图层。
完全连接的层
在一个 CNN 中可以有多个卷积、ReLU 和池运算。然而,总有最后一个单独的阶段是完全连接的层。全连接层是我们前面讨论过的前馈神经网络。这一步的目的是对image数据集进行不同的预测,比如对图像进行分类。
下图展示了美国有线电视新闻网基本架构的最终图片:

现在我们了解了一些关于有线电视新闻网的基础知识,让我们使用 Keras 创建一个有线电视新闻网。我们将使用喀拉斯本身的MNIST数据集。MNIST数据集是一个众所周知的书面数字数据集。数据集已经被分成一个训练集和测试集。它有大约 7 万张图片。每幅图像的灰度为 28 * 28。
本节的完整代码可以在本书的代码库中找到。我们将演示重要的代码部分。
我们开始构建一个 Keras DL 模型,通过定义一个顺序模型:
- 如上所述,顺序方法使我们能够在按顺序执行的其他方法之上添加一层。添加的第一层是由
Conv2D方法定义的卷积层。由于MNIST数据集由 2D 图像组成,我们添加了 2D 卷积层。kernel_size参数用于定义滤镜的大小,步长用于定义移动窗口。 - 喀拉斯没有不同的 ReLU 层。然而,我们可以在卷积层本身定义激活函数。我们为这个任务选择的激活函数是 ReLU。
 - 第三步是使用
MaxPooling2D方法添加一个最大池层。pool_size定义为 2 * 2,汇聚层的步长为 2 * 2。 - 数据扁平化的第四步是增加
Flatten2D方法。最后一层是全连接层,其定义类似于前馈神经网络: 
#Model Definition
cnn_model = Sequential()
cnn_model.add(Conv2D(32, kernel_size=(5, 5), strides=(1, 1),activation='relu',input_shape=input))
cnn_model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
cnn_model.add(Flatten())
cnn_model.add(Dense(1000, activation='relu'))
cnn_model.add(Dense(num_classes, activation='softmax'))
- 下面的代码片段类似于我们看到的其他 Keras 代码。我们首先需要用
loss函数、optimizer和metrics编译模型。然后,使用模型拟合方法中的重要参数batch_size和epochs用训练集拟合模型: 
cnn_model.compile(loss=keras.losses.categorical_crossentropy,
 optimizer=keras.optimizers.Adam(),
 metrics=['accuracy'])
 cnn_model.fit(x_train, y_train,
 batch_size=10,
 epochs=10,
 verbose=1,
 validation_data=(x_test, y_test))
- 一旦我们执行了前面的代码,我们需要等待一段时间才能得到结果。由于训练数据集庞大且图像复杂,仅完成 10 个时期就需要大量时间:
 

- 训练完成后,我们可以使用以下代码片段来评估模型的准确性:
 
model_score = cnn_model.evaluate(x_test, y_test, verbose=0)
print('Loss on the test data:', model_score[0])
print('Model accuracy on the test data:', model_score[1])
该模型在测试数据上的0.9875准确度令人印象深刻,为 98.75%:

摘要
在这一章中,我们向您介绍了神经网络和深度学习的世界。我们讨论了不同的激活函数、神经网络的结构,并使用 Keras 演示了一个前馈神经网络。
深度学习本身就是一个话题,有几本深度学习的书都有深度聚焦。本章的目的是为您提供一个探索深度学习的开端,因为它是机器学习自动化的下一个前沿。我们见证了自动编码器的降维能力。此外,中枢神经系统具有强大的特征处理机制,是构建未来自动化系统的重要组成部分。
我们将在下一章中结束我们的讨论,在这一章中,我们将回顾到目前为止所涵盖的内容、接下来的步骤以及创建完整的机器学习系统所需的技能。
八、ML 和数据科学项目的关键方面
如果你已经走了这么远,拍拍自己的肩膀。这并不是把自己当成一个机器学习 ( ML )专家,而是承认你为学习自动化 ML ( AutoML )工作流所做的工作。你现在已经准备好应用这些技术来解决你的问题了!
在这一章中,你将回顾你在各个章节中所学的内容,并将你的学习放在一个更广阔的视角中。
我们将在讨论中涵盖以下主题:
- 作为搜索的机器学习
 - ML 中的权衡
 - 典型数据科学项目的参与模型
 - 参与模式的各个阶段
 
作为搜索的机器学习
在前面的章节中,您已经看到了许多不同的技术应用于建模问题,其中大多数技术虽然看起来很简单,但是包含了许多最终影响您工作结果的参数。许多建模问题需要将 AutoML 表示为一个搜索问题,在大多数情况下,只能找到次优解。
从更广泛的意义上说,建模只是输入数据和输出数据之间的映射。因此,您将能够推断新输入数据到达未知输出的输出。为了实现您的目标,您需要思考您的实验设计,并相应地配置您的环境,因为您真的不知道什么是性能最好的 ML 流水线——但是让我们停下来一秒钟,退后一步。
实现高性能系统实际上是从系统架构的一些基本选择开始的,这些选择将允许您设计和交付成功的数据科学解决方案。您应该开始考虑的事情之一是您的系统硬件和软件配置,例如服务器类型、特定于 CPU 或 GPU 的要求、内存和磁盘要求、软件要求等。由于您将处理更大的数据集,因此您的配置将更加重要。此时所做的选择将决定数据科学堆栈的性能,该堆栈可能包含以下一些软件框架和库:
- 特定的软件发行版,如 Anaconda
 - 数据处理框架,如 Hadoop、Apache Spark、Kafka
 - 特定任务库,如 scikit-learn、XGBoost、TensorFlow/Keras、PyTorch
 - 数据管理系统,如 MongoDB、Neo4j、Apache Cassandra 和 MySQL
 
这不是一个详尽的列表,但是,即使范围如此有限,这也是一个需要消化的大量信息。理想情况下,您应该至少熟悉每一个在典型架构中扮演的角色,一旦您开始构建系统,这些选择将随着您为不同的用例实现系统而变得清晰。
一旦你有了这些部分并顺利工作,你就可以开始考虑如何移动数据并将其输入到你的 ML 流水线中。
当您处于数据处理和建模阶段时,选项非常多,正如前面提到的,每个方法都有自己的一组参数。下面向您展示了到目前为止您所练习的典型流程:

让我们考虑一个用于预测金融市场走势的文本处理流水线,看看在这样的设置中,这些步骤意味着什么。
首先,你会有各种各样的数据源;这些可能包括来自以下方面的数据:
- 金融交易所
 - 公司公告和文件
 - 提供一般新闻和财经新闻的通讯社
 - 来自政府机构的宏观经济数据
 - 监管报告
 - 推特等社交网络
 
下图显示了几个不同的数据源:

当您存储了这样的数据时,您需要处理这些数据以便在建模阶段使用,因为 ML 模型需要数值矢量化输入。
第一步是特征选择,这可能包括以下步骤:
- 确定每个单词是否会被视为不同的特征;这就是俗称的词汇袋
 - 确定名词短语、动词短语或命名实体是否可以用作特征
 - 将单词归类为有代表性的概念
 - 创建 n 克,这是一个连续的项目序列
 
下图帮助我们理解不同的功能选择:

一旦你决定了你要使用什么特征,你就会考虑减少维数来避免维数灾难。此阶段可能包括以下步骤:
- 简单的过滤操作,例如只包括前 100 个概念
 - 设置阈值以仅包括出现次数超过给定阈值的单词
 - 使用由领域专家创建的预定义字典来过滤输入数据
 - 标准操作,如删除停止词和引理化
 
下图有助于您理解特征选择和表示的不同方法:

降维也是特征表示后可以返回的一个阶段。一旦有了数字特征向量,就可以应用主成分分析 ( 主成分分析)等方法进一步降维。让我们看看特征表示的典型操作:
- 将词汇表中的单词表示为频率向量,其中每个单词将被分配一个出现次数
 - 二进制化频率向量,以便每个值都是 0 或 1
 - 使用术语频率-逆文档频率 ( tf-idf )编码将单词表示为它们在文档集合中对整个文档的相对重要性,即语料库
 - 使用分布式表示,如 Word2Vec 或 Doc2Vec
 
因此,您可以将完整的数字解释如下:

完成数据处理部分后,您可以开始建模,在这种情况下,您将有许多不同的算法可供选择。在文本挖掘中,以下是最常用的算法:
- 深度神经网络,特别是长短期记忆 ( LSTM )网络,这是一种特殊类型的递归神经网络 ( RNNs )
 - 支持向量机
 - 决策树
 - 全体
 - 回归算法
 - 朴素贝叶斯
 
下图显示了可以使用的几种不同算法:

每个算法都有自己的参数空间,参数主要有两种类型:
- 训练开始前设置的超参数
 - 在训练中学习的模型参数
 
目标是优化超参数,以便您的模型参数将为您提供最佳的泛化性能。搜索空间通常非常大,您已经看到了一些方法,例如贝叶斯优化,它们以高效的方式探索空间。
例如,让我们只看一下 XGBoost 算法的参数,它主导着 ML 应用程序。
如果运行以下几行,您将看到模型参数的解释:
import xgboost as xgb
classifier = xgb.XGBClassifier()
classifier?
这为您提供了以下输出:
Type: XGBClassifier
String form:
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
 colsample_bytree=1, g <...> reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
 silent=True, subsample=1)
File: ~/anaconda/lib/python3.6/site-packages/xgboost/sklearn.py
Docstring: 
Implementation of the scikit-learn API for XGBoost classification.
 Parameters
----------
max_depth : int
 Maximum tree depth for base learners.
learning_rate : float
 Boosting learning rate (xgb's "eta")
n_estimators : int
 Number of boosted trees to fit.
silent : boolean
 Whether to print messages while running boosting.
objective : string or callable
 Specify the learning task and the corresponding learning objective or
 a custom objective function to be used (see note below).
booster: string
 Specify which booster to use: gbtree, gblinear or dart.
nthread : int
 Number of parallel threads used to run xgboost. (Deprecated, please use n_jobs)
n_jobs : int
 Number of parallel threads used to run xgboost. (replaces nthread)
gamma : float
 Minimum loss reduction required to make a further partition on a leaf node of the tree.
min_child_weight : int
 Minimum sum of instance weight(hessian) needed in a child.
max_delta_step : int
 Maximum delta step we allow each tree's weight estimation to be.
subsample : float
 Subsample ratio of the training instance.
colsample_bytree : float
 Subsample ratio of columns when constructing each tree.
colsample_bylevel : float
 Subsample ratio of columns for each split, in each level.
reg_alpha : float (xgb's alpha)
 L1 regularization term on weights
reg_lambda : float (xgb's lambda)
 L2 regularization term on weights
scale_pos_weight : float
 Balancing of positive and negative weights.
base_score:
 The initial prediction score of all instances, global bias.
seed : int
 Random number seed. (Deprecated, please use random_state)
random_state : int
 Random number seed. (replaces seed)
missing : float, optional
 Value in the data which needs to be present as a missing value. If
 None, defaults to np.nan.
**kwargs : dict, optional
 Keyword arguments for XGBoost Booster object. Full documentation of parameters can
 be found here: https://github.com/dmlc/xgboost/blob/master/doc/parameter.md.
 Attempting to set a parameter via the constructor args and **kwargs dict simultaneously
 will result in a TypeError.
 Note:
 **kwargs is unsupported by Sklearn. We do not guarantee that parameters passed via
 this argument will interact properly with Sklearn.
要优化像 XGBoost 这样的超参数,您需要选择一种方法,如网格搜索、随机搜索、贝叶斯优化或进化优化。在实践中,贝叶斯优化为优化最大似然算法的超参数产生了良好的结果。
以下流程图显示了优化中使用的常见超参数和方法:

最终,您可以选择性能最好的 ML 流水线,也可以创建自己的集成来利用多个流水线。
掌握这些流水线需要您熟悉每一步,并在整个搜索空间中正确导航,以获得可接受的、接近最佳的解决方案。
机器学习中的权衡
主要有两个方面需要考虑:
- 训练时间
 - 得分时间
 
当你开发你的流水线时,两者都将作为约束。
让我们想想训练和得分时间给积分榜带来的限制。对训练时间的要求通常会决定你将包括在候选人名单中的算法。例如,逻辑回归和支持向量机 ( 支持向量机)是快速训练算法,这可能对您很重要,尤其是如果您正在使用大数据快速原型化想法。他们得分也很快。两者都有不同的实现,解算器也有不同的选项,这使得这两者对许多 ML 用例来说都很方便。
然而,对于像深度神经网络这样的东西,训练和评分时间是非常有限的限制,因为你可能无法忍受长达一周的训练时间或超过一秒钟的评分时间。你可以通过拥有更强大的硬件资源来提高训练和得分时间,但这可能会导致你的账单飙升,这取决于你的网络的复杂性。除了算法的选择,训练时间也高度依赖于你的超参数空间,因为它可能会导致它更长。
另一个问题是可伸缩性,这与您的数据大小有关,您需要确保您的流水线的可伸缩性与数据的增长速度相匹配。此时,您应该考虑对多线程、多核、并行或分布式训练的支持等因素。
典型数据科学项目的参与模型
当你开始学习任何新的东西,或者在现有知识的基础上发展时,了解事情的背景和故事如何随着时间的推移而演变是很重要的,要知道当前的趋势是传统报告、商业智能 ( 商业智能)和分析的自然演变。这就是为什么最初的章节向您介绍了 ML 流水线的背景和基础,例如数据预处理、自动算法选择和超参数优化。
由于 AutoML 流水线的高度实验性,有许多概念连同它们的实际例子一起被解释。
高级分析和 ML 用来解决问题的想法不一定是新的,但它们现在才可用,因为人们可以轻松获得更便宜的硬件和软件资源。您可以使用更先进的技术来解决以前无法解决的一些问题。
在本书中,您已经学习了开发 ML 流水线的各个方面。然而,在现实项目中,开发 ML 流水线只是变量之一。操作 ML 流水线至关重要,因为只有成功部署和监控这些流水线,您才能从开发良好的流水线中获益。许多公司都有针对 ML 的软件即服务 ( SaaS )产品,它们旨在抽象出生产环境中管理 ML 流水线的低级复杂性。
在下一节中,我们将讨论数据科学项目的不同阶段,以便您在处理端到端数据科学项目的成功交付时,能够准确地理解建模落在哪里。
参与模式的各个阶段
一个非常著名的建模过程是用于数据挖掘和预测分析项目的 CRISP-DM,它包括六个步骤:
- 商业理解
 - 数据理解
 - 数据准备
 - 建模
 - 估价
 - 部署
 
这些阶段中的每一个都是相互跟随的,并且它们中的一些通过向前一个阶段提供反馈以递归的方式发生。部署阶段在模型监控和维护方面尤为重要,这是本章的重点。
让我们快速了解一下每个阶段及其在整个过程中的目的。
商业理解
这是一个阶段,在这个阶段中,您完全专注于项目目标、范围、资源、限制、迭代和检查点方面的业务目标。
你试图从商业角度理解整个情况,并从技术角度来阐述问题。在你的组织中,不同的内部利益相关者之间可能会有相互竞争的目标,你应该意识到它们并找到最佳位置。例如,在供应链管理中,产品供应组织试图将其库存保持在最佳水平,而销售组织希望根据非常乐观的销售预测为即将推出的产品建立过量库存。您应该知道谁将从您将实现的 ML 和数据科学能力中受益。
在此阶段,您通常会尝试解决以下问题:
- 熟悉当前的决策过程,并将它们进一步划分为不同场景的独立用例。
 - 就人力资源达成一致后,确定要使用的数据源(例如数据提取、数据湖、运营数据库等)。
 - 找出假设,并试图用可用的数据来验证它们,在继续前进之前,将事实与观点/直觉分开。
 - 就可交付成果以及在整个项目中用于交付这些成果的工具/技术达成一致。
 - 决定哪些评估指标/关键绩效指标将用于 ML 模型和业务成果。你应该始终与商业目标保持一致。
 - 识别可能导致延迟或项目失败的风险,并向风险承担者阐明这些风险。ML 项目的成功本质上是概率性的,不像大多数人习惯使用的 BI 工作。
 
数据理解
在这一阶段,您将了解在整个项目中使用的数据源。
在此阶段,您通常会尝试解决以下问题:
- 
清除数据访问和授权问题。
 - 
将数据加载到首选平台进行初步分析。
 - 
了解敏感信息并执行必要的操作,如匿名化或删除敏感数据。
 - 
确定要使用的数据集。
 - 
识别数据模式并获取字段描述。
 - 
确定每个数据集的数量并识别差异。例如,检查不同表中的变量是否具有相同的数据类型,例如,一个变量在一个表中可以是整数类型,在另一个表中可以是十进制类型。
 - 
探索样本数据集,例如生成基本的包括计数、平均值、标准偏差、百分位数,以及检查变量分布。
 - 
熟悉数据的收集方式,了解数据收集过程中是否存在可能的测量误差。
 - 
在牢记统计谚语的同时研究相关性——相关性并不意味着因果关系。
 - 
检测噪音,如异常值,并就如何对待它们达成一致。
 - 
通过检查数据集的不同属性,确保样本数据集代表总体。例如,由于数据的可用性,对不同类别的数据是否有任何偏斜。
 - 
及时执行多重质量检查,以确保数据质量。例如,您可以识别和澄清错误和缺失的值,例如日期列中的值 100,000,000,000,或者平均值为 23.4 的值 25,000。
 - 
决定用作测试数据的数据量,并将其与训练数据分开。
 
最终,这类实践将引导您在几次参与后创建自动化的数据准备流程和数据质量报告。
数据准备
在此阶段,您将通过连接不同的数据源、清理、格式化和工程要素来创建最终数据集,以便在建模阶段使用。
在此阶段,您通常会尝试解决以下问题:
- 
识别用于模型构建的相关数据集。
 - 
记录数据连接和聚合以构建最终数据集。
 - 
编写带有有用参数的函数,以便在项目后期具有清理和格式化数据集的灵活性,例如通过 x%移除异常值,或者使用平均值、中值或最频繁值来输入缺失值。
 - 
相应地对待异常值。
 - 
玩转功能工程方法。
 - 
选择特征。通常,有三种主要的特征选择方法:
- 过滤方法
 - 包装方法
 - 嵌入式方法
 
 - 
确定特征的重要性,列出包含/排除特征的原因。
 - 
商定要构建的数据转换流水线。
 - 
为特定的操作编写定制的转换器,例如通过扩展 Apache Spark 或 scikit-learn 的转换器类从给定的语料库中提取段落、句子和单词,以及类似的评估器。
 - 
对数据集进行适当的版本控制,并撰写附加注释,解释准备过程中涉及的步骤。
 
建模
在这一阶段,您需要考虑对之前阶段创建的最终数据集进行建模的选项。
在此阶段,您通常会尝试解决以下问题:
- 确定最大似然问题的类型,如监督、半监督、无监督和强化学习。
 - 列出符合要求的 ML 模型。
 - 就评估指标达成一致,并关注要点,如类别不平衡,因为它会欺骗准确性等指标。如果数据集不平衡,可以参考采样技术来获得平衡的数据集。
 - 确定对假阴性和假阳性的容忍度。
 - 思考如何正确设置交叉验证。
 - 分析性能模型的最重要特征。
 - 分析每个特征的模型敏感度。不同的算法可能会对特征进行不同的排序;这将有助于您理解,如果特征的分布特性随时间变化,模型会如何反应。
 - 微调模型。
 - 确定适合部署的 ML 工作负载类型,例如在线、批处理和流式部署。
 - 在选择算法时,将训练和评分时间考虑在内,因为计算复杂度和数据大小通常是限制因素。
 
估价
这是您审查流程并确保涵盖所有计划项目的阶段。
在此阶段,您通常会尝试解决以下问题:
- 审查所有阶段,并确保要部署的模型流水线正确解决了之前执行的所有问题和步骤
 - 准备一场精彩的演讲,保持简洁明了,简单但不要简单
 - 展示结果,抓住要点,解释你的模型如何满足业务目标
 - 解释它的局限性以及在什么情况下可以南下
 
部署
这是你开始操作你的 ML 模型的阶段。
在此阶段,您通常会尝试解决以下问题:
- 在生产中进行短时间的试驾
 - 监控性能
 - 当模型性能开始下降时,确定改进模型的策略,如重新评估、再培训循环和重新部署循环
 - 准备最终报告和交付件
 
你当然可以扩展每个阶段要解决的项目列表,但是这个列表应该让你对你需要涵盖的内容有一个总体的了解。
在项目时间表的中间,你会有很多次加入一个项目团队。在这种情况下,你通常缺乏项目背景,你需要和关键人物开几次会来了解发生了什么。了解这些阶段将有助于您了解项目的当前阶段,并确定错过了什么或阐明后续步骤。
当你经历这些步骤时,记录下你对每一步的推理和发现是很重要的。当您需要重做一些步骤时,适当的文档会节省您的时间,因为您可能不记得您是如何执行一些分析或数据处理的。
摘要
就像你生活中追求的其他事情一样,实践是唯一能帮助你提高开发 ML 流水线技能的东西。您需要花费相当多的时间使用许多不同的技术和算法来处理各种问题和数据集。
尤其是在真实项目中,你可能不会遇到类似的问题,每个项目都需要你有不同的方法。您将很快意识到,重要的不仅仅是建模,而是理解这些技术如何相互集成,并在企业软件架构中很好地发挥作用。
通过学习 AutoML 系统,您向前迈进了一大步,并对 AutoML 流水线有了更好的理解。您肯定应该努力学习更多关于其他方面的知识,例如您感兴趣的领域中特定于领域的应用程序、应用程序体系结构、生产环境和模型维护技术。
感谢您抽出时间浏览内容,我们真诚地希望您和我们一样喜欢阅读这本书!

                    
                
。
,其中
为数值的平均值,σ为数值的标准差。
                
            
        
浙公网安备 33010602011771号