使用 TextBlob 增强您的 Python 字符串
在一行代码中获得更多关于文本的见解!
动机
数据不仅包含数字,还包含文本。了解如何快速处理文本将有助于您更快地分析数据,并从数据中获得更多见解。
文本处理不需要很难。如果我们只需要在一行代码中找到文本的情感、标记文本、找到单词和名词短语的频率,或者纠正拼写,这不是很好吗?这时 TextBlob 就派上用场了。
什么是 TextBlob?
TextBlob 旨在通过一个熟悉的界面提供对常见文本处理操作的访问。您可以将 TextBlob 对象视为学习如何进行自然语言处理的 Python 字符串。
NLTK 提供了一些方法来完成这些任务,但是您可能需要调用几个类来完成不同的任务。但是使用 TextBlob,您需要做的就是使用TextBlob(text)
来访问 TextBlob 的不同方法!
使用安装 TextBlob
pip install -U textblob
python -m textblob.download_corpora
现在,我们需要做的就是用TextBlob
对象包装文本来增强我们的字符串。
让我们看看我们可以用增压弦做什么。
单词标记化
我们将借用我的文章如何在生活不给你喘息的时候学习数据科学中的一些句子来学习如何使用 TextBlob。
单词标记化根据某些分隔符将一段文本分割成单个单词。
而不是根据不同的分隔符如“,”或“.”来分割字符串或者空格,我们只需要用blob.words
来修饰我们的句子!
WordList(['When', 'I', 'was', 'about', 'to', 'give', 'up', 'I', 'told', 'myself', 'to', 'keep', 'going', 'It', 'is', 'not', 'about', 'working', 'harder', 'it', 'is', 'about', 'working', 'smarter'])
WordList
可以作为 Python 列表使用。要访问第一个单词,请使用
>>> blob.words[0]
'When'
名词短语抽取
名词短语是以一个名词为中心的一组两个或多个单词(例如,“狗”、“女孩”、“男人”),包括修饰语(例如,“the”、“a”、“None of”)。例如,“女孩”不是名词短语,但“漂亮的女孩”是名词短语。
有时候,提取句子中的所有名词短语而不是提取单个名词对我们来说很重要。TextBlob 让我们可以轻松做到这一点
WordList(['learning strategies'])
正如我们所看到的,只有“学习策略”是从句子中提取的,因为它是句子中唯一的名词短语。
情感分析
我们还可以使用blob.sentiment
提取句子的情感
Sentiment(polarity=0.15833333333333333, subjectivity=0.48333333333333334)
极性是一个位于(-1,1)范围内的浮点数。如果极性低于 0,句子的否定性大于肯定性。如果极性在 0 以上,则该句肯定多于否定。因为我们的极性是 0.15,所以它是正的多于负的。
主观性是指个人观点。主观性是一个位于(0,1)范围内的浮点数。如果主观性值大于 0.5,则句子的主观性大于客观性,反之亦然。由于句子的主观性是 0.48,所以客观大于主观。
词汇化
词汇化是一组单词的词典形式。例如,‘eats’,‘eat’,‘eating’都是‘eat’这个词的形式。词汇化是在应用其他 NLP 模型之前处理文本的一种流行方法。
为了使单词词条化,将单词放在Word
对象中,而不是TextBlob
know
that
i
can
complete
these
things
in
a
short
amount
of
time
make
me
excite
and
be
in
the
flow
厉害!一些单词被转换成它们的字典形式。‘知’转换为’知’,‘激’转换为’激’,‘使’转换为’使’。现在,NLP 模型不会将“知道”和“知道”视为两个不同的单词!
拼写纠正
有时,我们可能会在文章中发现一两个拼写错误。如果我们分析 Twitter 等社交媒体的文本,这个拼写错误就更常见了。拼写错误会使我们的 NLP 模型很难知道单词learnin
和单词learning
基本上是同一个单词!
TextBlob 还允许我们用一行代码来纠正拼写
我故意拼错了句子中的几个单词,比如neded, swicth,
和learnin.
让我们看看 TextBlob 如何纠正这些单词
TextBlob("I just need to switch my learning strategics.")
相当酷!所有的拼写错误都以合理的方式得到了纠正。
我们也可以用Word().spellcheck()
纠正个别单词
[('learning', 0.25384615384615383),
('warning', 0.24615384615384617),
('margin', 0.2153846153846154),
('latin', 0.13076923076923078),
('martin', 0.05384615384615385),
('lain', 0.046153846153846156),
('earning', 0.038461538461538464),
('marin', 0.007692307692307693),
('darwin', 0.007692307692307693)]
拼写错误的单词lanin
可能是许多事物的拼写错误,因此 TextBlob 给了我们不同的结果,其中learning
是正确率最高的单词。
词频
有时候,知道某个单词在句子中的出现频率是很重要的。TextBlob 允许我们轻松地找到文本中某个单词的频率。
我们将使用我的文章中的所有文字,讲述如何在生活不给你休息的时候学习数据科学。我们将使用报纸刮文章。
pip install newspaper
由于这篇文章是关于我的数据科学之旅,我预计句子中会有很多“我”这个词。让我们用blob.word_counts
检查一下
81
81 是一个很高的数字,但是与我文章中的其他词相比,它有多高呢?让我们用柱状图来形象化它
正如我们所猜测的,像“我”、“to”、“the”、“my”这样的词是文章中最常见的词!
结论
恭喜你!您刚刚学习了如何用 TextBlob 处理 Python 字符串。使用上面列出的所有处理方法的唯一步骤是将字符串包装在TextBlob
对象或Word
对象中。多简单啊!
你可以在 TextBlob 的文档中探索更多处理和分析文本的方法。
本文的代码可以在这里找到
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/khuyentran1401/Data-science/blob/master/nlp/textblob.ipynb)
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上联系我。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
[## 用 Python 模块 Newspaper 和 NLTK 查找文章中的常用词
使用 newspaper3k 和 NLTK 从报纸中提取信息和发现见解的分步指南
towardsdatascience.com](/find-common-words-in-article-with-python-module-newspaper-and-nltk-8c7d6c75733) [## 使用 Python 和情感分析探索和可视化您的 LinkedIn 网络
希望优化您的 LinkedIn 个人资料?为什么不让数据为你服务呢?
towardsdatascience.com](/sentiment-analysis-of-linkedin-messages-3bb152307f84) [## 如何用 Word2Vec 解决类比问题
美国之于加拿大,就像汉堡之于 _?
towardsdatascience.com](/how-to-solve-analogies-with-word2vec-6ebaf2354009) [## PyTorch 是什么?
想想 Numpy,但是有强大的 GPU 加速
towardsdatascience.com](/what-is-pytorch-a84e4559f0e3) [## 自然语言处理中的卷积神经网络
什么是卷积神经网络,如何利用它进行情感分析?
towardsdatascience.com](/convolutional-neural-network-in-natural-language-processing-96d67f91275c)
用蜂鸟增压你的浅 ML 模型
用一行代码弥合传统和深度学习之间的差距
利瓦伊·琼斯在 Unsplash 上的照片
动机
自从 2012 年深度学习的最近一次复兴以来,大量新的 ML 库和框架已经被创建。那些经受住时间考验的公司(PyTorch、Tensorflow、ONNX 等)得到了大型公司的支持,可能不会很快消失。
然而,这也带来了一个问题,因为深度学习社区已经偏离了流行的传统 ML 软件库,如 scikit-learn、XGBoost 和 LightGBM。当公司需要将具有不同软件和硬件假设的多种模型投入生产时,事情变得…令人毛骨悚然。
- 当一些模型是基于张量的,而另一些是基于向量的,你如何保持 ML 推理代码干燥?
- 当基于 GPU 的神经网络开始围绕传统模型运行时,如何保持传统模型的推理运行效率具有竞争力?
寻找统一的模型服务界面
我知道,我知道。在 Kubernetes 中使用微服务可以通过保持事物的解耦在一定程度上解决设计模式问题…如果这是你想要的吗?
但是,我认为这真的忽略了问题。如果您想无缝部署 XGBoost 回归器或全连接 DNN 作为服务的主要输出,该怎么办?当然,你可以热交换你的服务启动的硬件。代码怎么样?
根据模型的类型,您是否打算使用一个精心设计的 if-else 开关来使用一个软件框架而不是另一个?
XGBoost/LightGBM 还不够快吗?
嗯,对于很多用例来说,就是。然而,在需要神经网络的问题和可以用更传统的模型充分解决的问题之间仍然存在巨大的差距。对于更传统的模型,难道你不希望能够使用最新和最伟大的计算框架来支持你的模型的预测吗?这将允许您在需要通过冗余实例将模型向外扩展之前,将模型向上扩展更多。
输入蜂鸟
微软研究院引入了hummingbird
来弥合面向 CPU 的模型和面向张量的模型之间的差距。该库简单地采用任何我们已经训练好的传统模型,并返回一个基于张量计算的模型版本。Hummingbird 旨在解决当前 ML 应用程序的两个核心问题:
- 传统和深度学习软件库对其基本计算单元(向量与张量)有不同的抽象。
- 这种差异的结果是,传统的 ML 库没有获得与硬件加速器(读:GPU)相同的性能增益。
有了蜂鸟,你的 ML 管道将开始看起来更干净。你会知道,不管算法如何,你最终会得到一个通过张量计算来创建预测的模型。不仅如此,这些张量计算将由您的组织可能已经效忠的同一深度学习框架来运行。
所有这些都来自一个函数调用。在我看来,这是一笔不错的交易!
让我们看看它的实际效果。
设置
让我们把这部分去掉。你知道该怎么做。
确保再现性
让我们用宇宙万物的答案来控制 Numpy 和 PyTorch 中的随机性。
召唤一些数据
让我们定义一个快速助手来快速制作一些分类数据,并用它创建一些数据集。
多亏了 Deepnote,我不必占用这个笔记本的一半来为你打印数据形状和分布。这些笔记本配备了功能丰富的变量资源管理器,它提供了大多数关于数据的基本 EDA 风格的问题。
查看本文的原始版本,了解 Deepnote 如何从他们的 Jupyter 风格的笔记本中自动创建漂亮的文章。
把鸟带进来
现在,通过 Hummingbird 将您的模型转换成张量计算的一行程序是convert(clf, 'pytorch')
。
就是这样。那是蜂鸟。
为了使我们的比较更加容易,让我们在此基础上创建一个快速的方法,当它可用时自动将其移动到 GPU。作为一些最终添加的糖,我们将接受一个标志,如果需要的话,强制将模型保存在 CPU 上。但是请记住,对convert()
的单一调用是您需要拥有的与蜂鸟的唯一接口;它在单个函数调用的掩护下完成了它所有的魔法。一旦您得到返回给您的模型,您就像任何其他传统的 pythonic ML 模型一样对它调用predict()
。
拿出你们的手表,时间到了
好了,是时候进行基准测试了!不要担心一连串的导入或方法包装。让我们用%%timeit
魔法命令保持简单。这个神奇的命令将自动多次运行单元中的代码,报告运行时样本的平均值和标准偏差。首先,我们将按原样计时sklearn
模型。然后,我们将看到蜂鸟在 CPU 上的模型和在 GPU 上的模型如何比较。
Original: 136 ms ± 1.59 ms per loop
Hummingbird CPU: 1.81 s ± 16.1 ms per loop
来源:吉菲
呀!
嗯……这真是出乎意料。关于这一点没有两种方法:对于某些数据集,Hummingbird 可能在 CPU 上运行得更慢。这甚至可以在他们当前的一些蜂鸟 Github repo 示例笔记本中看到。此外,我确实有意提到了在某些数据集上运行时较慢*,因为它确实优于其他数据集。*
也就是说,这个副作用不应该让任何人跑向门口——记住图书馆的目标!将模型转换为张量计算的主要原因是利用硬件,而硬件在这方面优于。
剧透提示:我说的是 GPU!这款 Deepnote 笔记本由 NVIDIA T4 张量核心 GPU 驱动。让我们看看模型如何在和上运行。
Original: 136 ms ± 1.59 ms per loop
Hummingbird CPU: 1.81 s ± 16.1 ms per loop
Hummingbird GPU: 36.6 ms ± 65.8 µs per loop
我们走吧!现在,我们不仅比原来有了 73%的平均加速,而且还有一个数量级的更小的方差。运行时的原始标准差是其均值的 1.1%,GPU 运行时的标准差是 0.18%!
来源: Giphy
以速度保证质量
不过,暂时不要激动。你的模型可能拥有世界上最快的运行时间;如果它不能在转换过程中保持精度,它就完全没用了。让我们看看原始模型和两个转换后的模型之间的预测是如何比较的。为此,我们求助于我最喜欢的可视化库之一,seaborn
。
原始模型和蜂鸟模型之间预测差异的分布
有意思…
一点也不差。基于 CPU 的模型的增量分布在零附近相当对称,3σ(注意轴刻度)在 1e-7 附近。基于 GPU 的模型的增量分布也有类似的小偏差,但显示出非零偏差和偏斜!这当然是有趣的行为,激起了好奇的心,但它仍然是一个小细节,除了最精确敏感的模型。
陪审团已经决定:蜂鸟在加速的同时也提供了精确性👍。
看看下面微软的一些大规模比较。🚀
来源:微软研究院
顶端的樱桃
哦,顺便说一句,你还可以自动加入所有未来的计算优化,这些优化来自于成千上万受雇于这些巨型框架的人。随着对不太受欢迎的框架的支持逐渐消失(相信我,这最终会发生),你会舒服地坐着,知道你的每一个模型都运行在良好支持的基于张量的推理框架上。
毕竟,我们从事的是数据科学,而不是运行时优化。利用大人物来完成该领域的工作感觉很好,让我们可以专注于我们的核心竞争力。
结论
正如微软研究院最近的许多其他举措一样,我对蜂鸟感到兴奋。在快速分化的 ML 空间中,这是走向整合的一大步,从混乱中获得一些秩序总是一件好事。我确信,随着时间的推移,他们基于 CPU 的推理的运行时停顿将被消除,同时确保 GPU 的持续优势。当他们进行更新时,我们只需点击几下鼠标,就可以使用支持 GPU 的 Deepnote 笔记本来测试他们的声明!
潜入蜂鸟
使用蜂鸟和 Deepnote
用数据解读更多生活
基于 Neo4j 数据的 LynxKite 超级数据科学
如果您有一个 Neo4j 数据库,那么您已经完成了您的图表之旅中的几个重要步骤:您知道如何将您的业务(的某些方面)建模为一个图表,可能您已经在使用您的数据进行特别的图表查询、本地调查,或者甚至在您的操作中!
但是,就像你不会在面向表的数据科学中直接使用 SQL 数据库一样,如果你想在一个复杂的、迭代的图形数据科学项目中取得成功,你需要的不仅仅是一个图形数据库。
在一个传统的、面向表格的项目中,你可以使用像 Pandas 或 Python 的 Spark 这样的东西,加入像 PyTorch 这样的 ML 框架,或者使用像 RapidMiner 或 Dataiku 这样的集成数据科学工具。但是无论您选择什么工具,您都将使用数据的快照,并且最终会得到一个包含许多相互依赖的操作的复杂工作流。
然而,如果你手头有一个严肃的图形项目,你应该转向 LynxKite !现在,我们的新 Neo4j 连接器让这一切变得非常简单。
为了记录,如果你想做图形数据科学,但是你还没有一个图形数据库,那也完全没问题。您可以直接使用 LynxKite 将传统数据转换成图表。但是这篇文章不是关于这个的。
现在让我们详细考虑一个简单但典型的图表数据科学工作。以 Neo4j 的 Northwind 数据集为例。(只需在你的 Neo4j 浏览器中输入:play northwind-graph
,按照提示操作即可获得。)这是一个代表客户订单历史的图表。它包含客户、订单、产品、产品类别和供应商的节点。我们想要实现的是,根据同一顾客购买产品的频率,识别出迎合相似“口味”的产品组。
获取数据
首先,让我们将数据导入 LynxKite!使用新的 Neo4j 导入框非常简单:
(截图作者)
你只需在你的 LynxKite 工作区上放下一个[Import from Neo4j](https://lynxkite.com/docs/latest/user-guide/#import-from-neo4j)
框,设置好你的连接参数,按下Import
,你就可以开始了。
如果保留查询的默认值,则会导入所有数据。在本例中,我们通过添加 WHERE 子句修改了节点查询:现在我们只对订单和产品感兴趣。
以下是导入图形的一个小示例:
(截图作者)
在左边,您可以看到两个订单,右边是订购的产品。
图形转换
您很少想要在一个图形上运行固定图形算法,因为它在您的生产系统中是可用的。通常你想要一些结构上的改变,你想要实际的算法在一个修改过的图上运行。
值得注意的是,这些步骤构成了数据科学项目不可或缺的一部分,我们经常希望对它们进行大量迭代。在 LynxKite 中,这些步骤是您的分析工作流中的一等公民。您可以轻松地更改细节,并可以轻松地重新运行整个下游分析。如果你直接在一个 graph DB 中这样做,那么你将不得不在每次迭代中经历痛苦的清理和图形重建。
在我们的具体例子中,我们想从一个订单-产品二分图发展到一个只包含产品的图,其中边代表购买了两种相关产品的客户的数量。我们执行以下转换步骤:
- 我们根据客户 id 合并订单:对于这个分析,我们不关心在同一个订单中购买了哪些产品,只关心相同的客户。
- 我们添加反向边,然后计算三元闭包。这样,我们在产品中得到我们想要的边,除了我们还为每个产品创建了一串循环边。所以我们删除了它们,并且只保留代表产品的节点,而不是(合并的)订单。
- 最后,我们将所有平行边合并成单条边,并保留原始平行边的基数作为边属性。我们也只保留代表足够频繁的共享客户的边。
我们可以通过在工作区放几个盒子来完成以上所有的工作。以下是图形转换的完整流程:
(截图作者)
这是结果图的一个示例:
(作者截图*)*
查找和标记聚类
现在我们已经有了想要的图,对它运行一个聚类算法是很简单的,我们只需放入一个[Find modular clustering](https://lynxkite.com/docs/latest/user-guide/#find-modular-clustering)
框:
(作者截图*)*
好吧,我们后来做了一点调整:我们删除了非常小的集群,我们将产品的边复制到产品集群,这样我们就可以看到不同集群之间的相关性。以下是完整的流程:
(截图作者)
我们基本上完成了,但让我们再做一个整容改进!让我们为每个集群找出最重要的产品,并使用该产品的名称作为我们集群的标签。这很简单:我们只需在产品图上计算 PageRank,并为所有集群取最高 PageRank 产品的名称:
(截图作者)
现在是时候惊叹我们(不那么)努力的成果了!这是一个可视化图,显示了所有有趣的带标签的集群(右侧),以及一个集群的成员(左侧):
(截图作者)
有趣的是,几乎所有的集群都是由某种奶酪主导的…我一直都知道,奶酪就是石头!
回到 Neo4j
为了结束这个循环,让我们将分析结果返回到数据库中!我们需要两个简单的准备步骤。我们为聚合标签属性取了一个更好的名字,这是我们希望在数据库中看到的。然后,我们将它从集群移至其所有成员。最后,我们应用一个 Neo4j 导出框:
(截图作者)
事实上,集群标签如预期的那样出现在 Neo4j 中!
(截图作者)
在这种情况下,我们想要将顶点属性导出回数据库,但是也可以用边属性做同样的事情,或者你甚至可以将一个完整的图形推送到数据库。
全部用代码编写
我们的经验是,迭代的、探索性的图形数据科学可以在 UI 上更有效地完成:更容易改变事物,在任何点检查值和元数据,不需要记住参数名,等等。也就是说,有些人更喜欢编码,这也是数据科学管道自动化生产部署的唯一合理方式。
对于这些情况,我们提供了强大而简单的 Python API 。仅为了演示,无需进一步解释,请参见下面执行上述完整分析的 python 代码。
概述
我们在 Northwind 数据集上进行了简单但有趣的分析。但这里的要点不是关于这个分析的具体细节,也不是结果。我们想要展示的是,为什么即使是一个相当简单的项目也需要一些步骤和功能,而这些步骤和功能在一个实时的、事务性的图形数据库中是不可能或非常麻烦的。
但是我们带来了好消息!现在有了我们的新连接器,在 Neo4j 实例上使用免费、开源的 LynxKite 来进行高效、舒适的图形数据科学变得非常容易!
通过提升模型提升客户接触点
介绍一种预测个体治疗效果的强大方法,使用 pandas 和 XGBoost 的 Python 中的合成数据。
图片来自 Pixabay
在这篇文章中,我将介绍提升建模的概念,并举例说明为什么它是数据科学家增加业务价值的工具箱中的一个重要部分。然后,我将展示一种构建提升模型的简单方法,并使用 Python 中的合成数据演示几个提升模型评估指标。
这篇文章在 Github 上以 Jupyter 笔记本的形式发布。
介绍
在机器学习可以为企业创造价值的无数方式中,与监督分类或回归等方法相比,提升建模是鲜为人知的方式之一。但是对于许多用例来说,它可能是最有效的建模技术。在任何情况下,企业可以针对不同的客户有选择地采取代价高昂的行动,以期影响他们的行为,提升建模应该是确定战略的有力候选。这是因为提升建模旨在找到受行动影响最大的客户子集。识别这个细分市场对于在商业策略中最大化投资回报是很重要的。
例如,在提供优惠券时,企业可能会遭受收入损失:如果客户购买并使用优惠券,收入会因优惠券的价值而减少。但是,如果优惠券说服顾客购买,而他们本来不会购买,那么它可能仍然是值得的。在提升建模的术语中,这些类型的客户被称为“可说服的”,提升建模将事情分解为有“待遇”和没有“待遇”的客户行为,在本例中,待遇是接收优惠券。
经易和弗罗斯特许可使用的图片(2018)
提升建模的目标,也称为净提升或增量响应建模,是为了确定“可说服的”,而不是在“确定的事情”和“失败的原因”上浪费精力,并避免打扰“睡觉的狗”,或那些对治疗有负面反应的人,如果他们存在的话。提升建模已经在许多领域得到应用,包括营销,这里展示的经典用例,以及讨债和政治活动。
隆起建模的数据:实验是关键
现在我们知道了隆起建模的目标,我们如何实现它呢?构建提升模型的典型起点是来自随机对照实验的数据集:我们需要一个代表治疗组和未接受治疗的对照组中所有不同类型客户的样本。如果治疗组中购买的顾客比例明显高于对照组,我们知道促销是“起作用的”,因为它鼓励所有顾客平均购买。这就是所谓的平均治疗效果(ATE)。量化 ATE 是 A/B 测试的典型结果。
然而,可能只有治疗组中的一部分客户对我们观察到的大部分 ATE 负责。举个极端的例子,也许治疗组中一半的顾客负责整个 ATE,而促销对另一半没有影响。如果我们有办法提前确定哪些顾客更容易接受治疗,那么我们就能把资源集中在他们身上,而不是把时间浪费在那些治疗效果很小或没有效果的人身上。我们可能需要找到其他促销活动来吸引不响应者。在确定因人而异的可变治疗效果的过程中,以这些人拥有的不同特质为条件,我们在寻找个体治疗效果(ITE),也称为条件平均治疗效果(凯特)。这就是机器学习和预测建模发挥作用的地方。
模型的力学
构建提升模型的经典技术是为单个客户预测如果他们得到治疗,他们购买的可能性,以及如果他们没有得到治疗,他们购买的可能性。然后将这两个概率相减以获得提升:如果给予治疗,购买的可能性会增加多少?这可以通过两种方式实现,在这两种情况下,模型的二元响应变量是顾客在治疗后是否进行了购买:
- 将治疗组和对照组集合成一个数据集,并训练一个单一模型,其中治疗是二元特征。在推断阶段,模型用于对每个实例进行两次预测,第一次处理= 1,第二次处理= 0。这被称为“S-Learner”方法,因为它使用单一模型。
- 为治疗组和对照组训练单独的模型。在推理阶段,治疗和控制模型都用于获得每个实例的预测。这被称为“T-Learner”方法,因为它使用了两种模型。
下面的示意图总结了这两种方法:
这些方法在关于隆起建模和因果推断的文献中有广泛记载(Lee 等人,2013 年 Gutierrez 和 Gerardy,2016 年)。它们具有相对简单和直观的优势,可以使用许多数据科学家熟悉的二进制分类建模技术以及 SAS 等企业软件中的专用包来实现(Lee et al. 2013)。与此同时,因果推理是机器学习中一个活跃的研究领域,其他方法可能会实现更好的模型性能。不同的方法包括旨在提升的基于树的模型(Gutierrez 和 Gerardy 在 2016 年进行了审查)、目标变量转换(Yi 和 Frost 2018 年)以及其他最近的创新,如 X-Learner (Kunzel 等人,2019 年)。
在所有种类中,隆起建模面临一个基本挑战。目标是为单个客户预测治疗后的购买可能性,以及未治疗时的购买可能性,以计算提价。但在现实中,我们从来没有观察到一个人既接受治疗又没有接受治疗的结果,因为这是不可能的!有人要么接受治疗,要么不接受。在数学建模中,如果我们不能观察到我们感兴趣的所有结果,这通常是一个问题。这一挑战说明了提升建模的反事实性质,以及随机实验对了解所有类型客户的美食的重要性。
图片来自 Pixabay
Gutierrez 和 Gerardy (2016)总结了这一挑战,并指出了前进的方向:
估算客户提升既是一个因果推理,也是一个机器学习问题。这是一个因果推理问题,因为人们需要估计两个结果之间的差异,这两个结果对一个人来说是互斥的(要么人我收到了一封促销电子邮件,要么没有收到)。为了克服这种反事实的性质,提升建模主要依赖于随机实验,即随机分配客户接受治疗(治疗组)或不接受治疗(对照组)。隆起建模也是一个机器学习问题,因为人们需要训练不同的模型,并根据一些性能指标选择产生最可靠隆起预测的模型。这需要明智的交叉验证策略以及潜在的功能工程。
让我们通过构建一个 S-Learner 模型并对其进行评估,使用一个示例数据集来探索这些概念。
*# load packages* import numpy **as** np
import pandas **as** pd
from statsmodels.stats.proportion import proportions_ztest
import sklearn **as** sk
from sklearn.metrics import auc
import xgboost **as** xgb
import matplotlib **as** mpl
import matplotlib.pyplot **as** plt
**%**matplotlib inline
import pickle
示例数据集
建立提升模型最直接的方法是从随机对照实验的数据开始。这样,治疗组和对照组都应该有一个具有代表性的顾客群体样本。在设计的实验之外,如果一个自然控制群体作为企业正常经营的一部分存在,准实验数据可能是可用的。治疗组和对照组也可以通过一种称为倾向评分匹配的技术来近似,该技术在 causaml 包中可用,该包还提供了一套提升建模工具(causaml)。
在这里,我们使用来自最近出版物(赵等人,2020 年)的合成数据,这些数据在这里公开提供。这些数据模拟了在治疗组和对照组之间平均分配的设计实验。我们只加载该数据集中的前 10,000 行,这是“100 次试验(用不同的随机种子重复)”中的第一次。数据集的构建使得一些特征可以预测结果,一些特征不提供信息,一些特征可以具体预测治疗效果。
我们感兴趣的列是treatment_group_key
,它标识客户是否接受了治疗;conversion
,如果客户进行了购买,则为 1;如果没有,则为 0;以及 36 个合成特征,它们都以x
开头。在真实数据中,这些特征可能对应于客户购买历史、人口统计数据以及数据科学家可能设计的其他数量,假设它们在建模提升中有用。
让我们加载数据并简单地研究一下。
df **=** pd.read_csv('data/uplift_synthetic_data_100trials.csv', nrows**=**10000)df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000 entries, 0 to 9999
Data columns (total 43 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Unnamed: 0 10000 non-null int64
1 trial_id 10000 non-null int64
2 treatment_group_key 10000 non-null object
3 conversion 10000 non-null int64
4 control_conversion_prob 10000 non-null float64
5 treatment1_conversion_prob 10000 non-null float64
6 treatment1_true_effect 10000 non-null float64
7 x1_informative 10000 non-null float64
8 x2_informative 10000 non-null float64
9 x3_informative 10000 non-null float64
...
42 x36_uplift_increase 10000 non-null float64
dtypes: float64(39), int64(3), object(1)
memory usage: 3.3+ MBdf.head()
这一万条记录中,有多少是治疗组的,有多少是对照组的?
df['treatment_group_key'].value_counts()control 5000
treatment1 5000
Name: treatment_group_key, dtype: int64
有五五分成。让我们将治疗变量编码为二进制 0/1:
df['treatment_group_key'] **=** df['treatment_group_key'].map(
arg**=**{'control':0, 'treatment1':1})
分析实验结果
总转化率是多少?
df['conversion'].mean()0.3191
与对照组相比,治疗组的转化率是多少?
exp_results_df **=** \
df.groupby('treatment_group_key').agg({'conversion':['mean', 'sum',
'count']})
exp_results_df
(exp_results_df.loc[1,('conversion', 'mean')] \
**-** exp_results_df.loc[0,('conversion', 'mean')]).round(4)0.1042
治疗组的转化率(37%)明显高于对照组(27%),表明该治疗在鼓励转化率方面是有效的:ate 为阳性,约为 10%。
在真实数据中,差异通常不是很大,通常进行显著性检验来确定 A/B 检验的结果。
proportions_ztest(count**=**exp_results_df[('conversion', 'sum')],
nobs**=**exp_results_df[('conversion', 'count')])(-11.177190529878043, 5.273302441543889e-29)
p 值是从比例测试中返回的第二个数量,它比 0.05 小得多,或者比用于确定显著性的任何其他阈值都小得多。所以我们知道有一个重要的 ATE。这是隆起建模的典型起点。如果我们观察到这种处理没有增加转化率,虽然理论上有可能使用提升模型找到一个有说服力的客户群,但实际上这可能不值得努力。这可能取决于手头的具体问题。
然而,在我们的案例中,在观察到显著的治疗效果后,我们继续使用隆起建模来寻找 CATE,看看我们是否能确定那些有说服力的证据。
构建提升模型
这里我将使用一个XGBClassifier
来训练一个 S-Learner;即包括所有特征的单个模型,其中治疗指示也是一个特征。我将数据分成训练集和验证集(80/20 分割),以便尽早停止。我还将使用验证集来说明模型评估指标。在一个真实的项目中,应该从这个过程中保留一个测试集,测试集上的评估度量将用于模型的最终评估。
train, valid **=** sk.model_selection.train_test_split(
df, test_size**=**0.2,random_state**=**42)**print**(train.shape, valid.shape)(8000, 43) (2000, 43)
将功能指定为列表。这包括治疗栏和所有功能,从第 8 栏开始:
features **=** ['treatment_group_key'] **+** df.columns.tolist()[7:]**print**(features)['treatment_group_key', 'x1_informative', 'x2_informative', 'x3_informative',... 'x36_uplift_increase']
组合用于训练 XGBoost 分类器的训练集和验证集:
X_train **=** train[features]
y_train **=** train['conversion']
X_valid **=** valid[features]
y_valid **=** valid['conversion']eval_set **=** [(X_train, y_train), (X_valid, y_valid)]
现在是实例化和训练模型的时候了。
model **=** xgb.XGBClassifier(learning_rate **=** 0.1,
max_depth **=** 6,
min_child_weight **=** 100,
objective **=** 'binary:logistic',
seed **=** 42,
gamma **=** 0.1,
silent **=** True,
n_jobs**=**2)**%%**time
model.fit(X_train, y_train, eval_set**=**eval_set,\
eval_metric**=**"auc", verbose**=**True, early_stopping_rounds**=**30)[0] validation_0-auc:0.693049 validation_1-auc:0.648941
Multiple eval metrics have been passed: 'validation_1-auc' will be used for early stopping.
Will train until validation_1-auc hasn't improved in 30 rounds.
[1] validation_0-auc:0.718238 validation_1-auc:0.656877
[2] validation_0-auc:0.72416 validation_1-auc:0.667244
[3] validation_0-auc:0.727643 validation_1-auc:0.669992
...
[99] validation_0-auc:0.852237 validation_1-auc:0.762969
CPU times: user 6.7 s, sys: 87.8 ms, total: 6.79 s
Wall time: 3.48 s
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0.1,
learning_rate=0.1, max_delta_step=0, max_depth=6,
min_child_weight=100, missing=None, n_estimators=10,
n_jobs=2, nthread=None, objective='binary:logistic',
random_state=0, reg_alpha=0, reg_lambda=1,
scale_pos_weight=1, seed=42, silent=True, subsample=1,
verbosity=1)
训练过程完成,我们可以看到该模型具有相当高的验证 AUC。训练 AUC 甚至比这更高,这意味着从技术上来说模型是过度拟合的。通常我会进行超参数搜索,但我发现这里使用的值可以提供合理的结果,以便用该数据集说明隆起建模。
作为一个实用的旁注,我发现在某些情况下,当使用 T-Learner(这里没有显示)时,在计算提升时,过度适应训练集可能会导致意想不到的结果。根据我的经验,这个问题可以通过减少max_depth
或增加XGBClassifier
中的min_child_weight
来解决,换句话说就是减少过度拟合的数量。
在模型构建中要考虑的另一点是特征选择,我在这里省略了。在隆起建模的上下文中,可以使用下面在验证集上介绍的隆起模型评估度量作为选择特征的方式,例如通过递归特征消除。隆起模型的特征选择也是最近研究的主题,包括本文所用数据集的来源(赵等人,2020)。
模型评估
现在,我们有了提升模型。如果你已经熟悉二进制分类,那么为一个 S 型学习者建立模型是非常简单的。为了实际计算给定数据集的提升,使用这种方法,我们需要对模型进行两次评分,一次处理= 1,另一次处理= 0,然后减去这两次得到提升。在这里,我们对验证集执行此操作,然后绘制提升分数的直方图。
X_valid_0 **=** X_valid.copy(); X_valid_0['treatment_group_key'] **=** 0
X_valid_1 **=** X_valid.copy(); X_valid_1['treatment_group_key'] **=** 1
Uplift **=** model.predict_proba(X_valid_1)[:,1]\
**-** model.predict_proba(X_valid_0)[:,1]mpl.rcParams['figure.dpi'] **=** 200
mpl.rcParams['figure.figsize'] **=** (6,4)
plt.hist(Uplift, bins**=**50)
plt.xlabel('Uplift score')
plt.ylabel('Number of observations in validation set')
隆起的分布大部分是正的,这是有意义的,因为我们从对实验的分析中知道,平均而言,治疗鼓励转化。然而,一些例子有负面的提升,意味着治疗实际上不鼓励一些人的 T2 转变。换句话说,在这些数据中似乎存在一种睡狗效应。
现在的主要问题是:我们应该相信这些结果吗?我们怎么知道这个模型有多好?提升模型评估的指标比监督学习中使用的典型指标更复杂,例如用于分类任务的 ROC AUC 或用于回归的 RMSE。一般来说,对于不同范围的预测上升分数,上升评价指标对治疗组和对照组之间的转换率进行了比较。对于那些具有高提升分数的患者,我们期望看到治疗组和对照组之间的较大差异,而那些具有较低提升分数的患者应该具有较小的差异,或者甚至在对照组中具有较大的转化率(即负差异)。
分位数度量
评估提升模型的一种流行方法是使用分位数图。这将给出模型是否“工作”的快速视觉印象,在倾斜真实隆起的意义上。为了创建分位数图表,我们从验证集的提升预测开始,并根据这些分数将实例分成分位数。分位数的数量取决于我们有多少数据,尽管实际上 10 是一个非常典型的数字(十分位数)。然后,在每个箱中,我们会发现治疗组和对照组的转换率的差异。如果模型运行良好,我们应该在最高的十分位数中看到较大的正差异,在最低的十分位数中减少到小的或负的差异(即治疗率类似于控制率,或低于控制率)。换句话说,随着预测的隆起增加,从对照组到治疗组的真实隆起也应该增加。
获取分数分位数
从验证数据创建一个新的DataFrame
,以添加提升分数和分位数。
Uplift.shape(2000,)valid.shape(2000, 43)valid_w_score **=** valid.copy()
valid_w_score['Uplift score'] **=** Upliftvalid_w_score.head()
检查验证集的治疗组和对照组是否大致平衡(它们应该是平衡的,因为我们使用了随机训练/验证分割,但检查总是好的):
valid_w_score['treatment_group_key'].value_counts()0 1011
1 989
Name: treatment_group_key, dtype: int64
现在,使用整个验证集(治疗组和对照组一起),为提升分数分位数制作标签。我们将检查治疗组和对照组在分位数内是否平衡,因为我们将按分位数和治疗方法分割数据以创建图表。Pandas 有一个方便的功能,可以根据输入序列中的一个观察值属于哪个分位数来生成一系列标签。
score_quantiles, score_quantile_bins **=** \
pd.qcut(x**=**valid_w_score['Uplift score'],
q**=**10,
retbins**=**True,
duplicates**=**'drop')
从这个函数中,我们得到一个列,指示每个实例属于哪个分位数,由 bin 边缘表示:
score_quantiles.head()6252 (-0.00339, 0.0186]
4684 (-0.114, -0.00339]
1731 (0.201, 0.398]
4742 (0.121, 0.148]
4521 (0.0391, 0.0548]
Name: Uplift score, dtype: category
Categories (10, interval[float64]): [(-0.114, -0.00339] < (-0.00339, 0.0186]...
我们还在score_quantile_bins
中获得了所有面元边缘的列表,但是我们在这里不需要它。现在,让我们将分数分位数添加到数据帧中,以便我们可以使用它进行分析。
valid_w_score['Quantile bin'] **=** score_quantiles
valid_w_score[[
'treatment_group_key', 'conversion', 'Uplift score',
'Quantile bin']].head(10)
使用 groupby/count 和一些多指数魔术,检查分位数区间内的处理和对照观察值是否相似:
count_by_quantile_and_treatment **=** valid_w_score.groupby(
['Quantile bin', 'treatment_group_key']) \
['treatment_group_key'].count()
count_by_quantile_and_treatment **= \** count_by_quantile_and_treatment.unstack(**-**1)
count_by_quantile_and_treatment
count_by_quantile_and_treatment.plot.barh()
plt.xlabel('Number of observations')
不太精确地说,就治疗和控制的比例而言,分数分位数似乎并不不平衡;它们在每个箱中是相似的。这是意料之中的,因为我们正在处理来自随机实验的数据,然而再次检查这样的假设是好的。
上升分位数图表
上升分位数图。我们将首先创建一个可用于治疗组的遮罩:
validation_treatment_mask **=** \
valid_w_score['treatment_group_key'] **==** 1
然后,我们分别为治疗组和对照组获得提升分数分位数内的转换率:
treatment_by_quantile **=** valid_w_score[validation_treatment_mask]\
.groupby('Quantile bin')['conversion'].mean()
control_by_quantile **=** valid_w_score[**~**validation_treatment_mask]\
.groupby('Quantile bin')['conversion'].mean()
最后,我们计算它们的差异,这是每个分数分位数内的真实提升。
true_uplift_by_quantile **=** treatment_by_quantile **-** \
control_by_quantile
true_uplift_by_quantile.head(5)Quantile bin
(-0.114, -0.00339] -0.017486
(-0.00339, 0.0186] 0.034343
(0.0186, 0.0391] -0.004600
(0.0391, 0.0548] 0.021554
(0.0548, 0.0726] 0.133929
Name: conversion, dtype: float64
现在我们有了绘制上升分位数图表所需的所有信息。
true_uplift_by_quantile.plot.barh()
plt.xlabel('True uplift')
上升分位数图表显示,在很大程度上,真实的上升从较低的分数仓增加到较高的分数仓,这是我们期望看到的模型是否有效。因此,看来我们的模型可以有效地细分出更容易对治疗做出反应的客户。在实际项目中,对保留的测试集重复这种分析是很重要的,以确认模型使用的数据根本没有用于模型训练,因为从技术上讲,这种验证集用于模型拟合过程中的早期停止。然而,验证集上的良好性能是一个好迹象,只要测试集与训练集和验证集具有相似的特征,我们就希望在那里看到相似的性能。
我们能从分位数图中学到什么?通过对实验的分析,我们知道 ATE 约为 10%。我们用验证集创建的分位数图告诉我们,通过瞄准提升分数的前十分位数,我们可以实现超过 35%的治疗效果,这是一个显著的增加。接下来的几个十分位数似乎也比 ATE 有更大的治疗效果。根据治疗费用的高低,利用这些信息针对有限的人群可能是有意义的。
我们还可以从对真实隆起的观察中看到睡狗效应的一些支持。分数最低的十分位数,完全由负分组成,实际上有负的真实上升。因此,通过提升分数,瞄准底层 10%的人口,实际上对业务有负面影响。
提升校准
虽然提升分位数图提供了一个定性的快照,告诉我们该模型在细分客户方面是否有效,但我们可以在这个方向上更进一步,并询问该模型在预测提升方面的准确性如何。这是校准过程,为此我们需要分数分位数内的平均预测提升:
predicted_uplift_by_quantile **=** valid_w_score\
.groupby(['Quantile bin'])['Uplift score'].mean()
predicted_uplift_by_quantile.head(5)Quantile bin
(-0.114, -0.00339] -0.035133
(-0.00339, 0.0186] 0.008385
(0.0186, 0.0391] 0.029582
(0.0391, 0.0548] 0.047033
(0.0548, 0.0726] 0.063634
Name: Uplift score, dtype: float32
我们将把这些数据和我们上面计算的真实上升量放在一个DataFrame
中,以创建一个散点图。如果隆起预测是准确的,那么预测隆起和真实隆起的散点图应该接近一对一线。
pred_true_uplift **=** pd.DataFrame({'Predicted Uplift':predicted_uplift_by_quantile,
'True Uplift':true_uplift_by_quantile})
min_on_plot **=** pred_true_uplift.min().min()
max_on_plot **=** pred_true_uplift.max().max()
ax **=** plt.axes()
ax.plot([min_on_plot, max_on_plot], [min_on_plot, max_on_plot],
linestyle**=**'--', color**=**'tab:orange', label**=**'One-one line')
pred_true_uplift.plot.scatter(x**=**'Predicted Uplift', y**=**'True Uplift',
ax**=**ax, label**=**'Model performance')
ax.legend()
从定性角度来看,我们可以从校准图中看到,平均预测上升接近真实上升,按分位数计算。通过计算某种度量,也许是 MAE(平均绝对误差),作为模型拟合优度的度量,可以使这种校准更加精确。
这些基于分位数的分析还有其他一些扩展方式:
- 用于计算升高的治疗= 1 和治疗= 0 的预测值,可以分别在治疗组和对照组中根据这些分数的十分位数分别校准转换率。这将是对这些群体的预测转化概率的校准。
- 误差线可以包含在所有的图上。对于预测的转化概率,可以分别计算治疗组和对照组的每个箱内平均值的标准误差,而对于真实的转化率,可以使用二项式的正态近似值。然后,当减去处理和控制的平均值以获得上升时,将添加基于这些计算的方差,并且可以在每个箱内计算上升的标准误差。
- 在商业情况下,应该知道治疗的成本和转换的预期收入。提升分位数图可以扩展为代表收入的提升,这可以与治疗成本相平衡,以评估商业策略的盈利能力。
累积指标
累积增益图
在实践中使用提升分数时,一种常见的方法是根据客户的提升分数按降序排列客户。我们可以扩展分位数图的概念,通过这种方式,计算通过瞄准特定部分的人口,我们可以获得多少额外的客户(“增量客户”)。
这个想法是累积收益图的基础。Gutierrez 和 Gerardy (2016)给出的累积增益公式为:
其中 Yᵀ 是处理组每个 bin 中的累计转化数,从最高分 bin 开始向下,而 Nᵀ 是以相同方式找到的客户的累计数量; Y^C 和 N^C 是对照组相似的累积和。累积收益有效地衡量了转化概率的累积提升,从最高 bin 开始,乘以治疗组和对照组的总客户数,以估计如果目标客户数达到该数量,将会发生的额外转化数。
为了获得累积收益图的数据,我们需要计算治疗组和对照组中每个分数分位数 bin 中的客户数量(我们在上文中对此进行了可视化,但在此将重新计算)以及转化客户的总数。在这里,我们将使用.iloc[::-1]
将结果颠倒过来,以模拟首先锁定提升分数最高的客户,然后从那里继续下去的策略。
treatment_count_by_quantile **=** valid_w_score[validation_treatment_mask]\
.groupby('Quantile bin').agg({'conversion':['sum', 'count']}).iloc[::**-**1]
control_count_by_quantile **=** valid_w_score[**~**validation_treatment_mask]\
.groupby('Quantile bin').agg({'conversion':['sum', 'count']}).iloc[::**-**1]
下面是治疗组的情况,例如:
treatment_count_by_quantile.head(5)
以及转换和客户总数的累计总和:
treatment_count_by_quantile.cumsum().head(5)
将所有这些放入上面所示的累积增益公式中,我们得到:
cumulative_gain **=** \
((treatment_count_by_quantile[('conversion','sum')].cumsum()
**/** treatment_count_by_quantile[('conversion','count')].cumsum())
**-**
(control_count_by_quantile[('conversion','sum')].cumsum()
**/** control_count_by_quantile[('conversion','count')].cumsum()))\
***** (treatment_count_by_quantile[('conversion','count')].cumsum()
**+** control_count_by_quantile[('conversion','count')].cumsum())
我们现在可以检查和绘制累积增益。
cumulative_gain.round()Quantile bin
(0.201, 0.398] 74.0
(0.148, 0.201] 125.0
(0.121, 0.148] 149.0
(0.0941, 0.121] 172.0
(0.0726, 0.0941] 209.0
(0.0548, 0.0726] 237.0
(0.0391, 0.0548] 242.0
(0.0186, 0.0391] 240.0
(-0.00339, 0.0186] 249.0
(-0.114, -0.00339] 248.0
dtype: float64cumulative_gain.plot.barh()
plt.xlabel('Cumulative gain in converted customers')
累积收益提供了另一种方式来看待提升模型导向战略的潜在影响。如果我们向每位顾客提供这种治疗,我们将增加 248 名转化顾客。然而,我们可以获得 149 名客户,约为最大可能的 60%,通过提升分数,只为前 30%的客户(前 3 个十分位数)提供治疗。这是因为随着我们在列表中的下移,我们的目标客户是预测个体治疗效果较低的客户。在图表中价值较低的容器中,累积的转化数量甚至可能从一个容器下降到另一个容器,因为我们实际上因为瞄准睡觉的狗而失去了客户。
累积增益曲线
上面的各种图表都是了解提升模型性能的信息丰富且直观的方式,但是它们不会立即导致模型性能指标,通过这些指标可以比较不同的建模方法。为了实现这一飞跃,可以将累积增益的思想推广到曲线,类似于如何使用接收器操作特性(ROC)曲线来评估二元分类器。为了得到 ROC 曲线,随着阳性分类的阈值被连续提高以在数据集中包括越来越多的实例,直到全部被包括,计算真阳性率和假阳性率。相比之下,累积增益曲线测量目标人群中的累积转化率增益,如上所述,随着越来越多的人成为目标,根据提升分数递减,直到所有人都成为目标。
增益曲线定义为
其中 t 是客户的指数,从最高提升分数开始向下,其他变量的定义与前面的等式类似。
增益曲线计算是 causaml 软件包的一部分,称为提升曲线(causaml)。在熊猫身上也可以算的相当快。第一步是根据提升分数进行排序:
sorted_valid **=** valid_w_score.sort_values('Uplift score',
ascending**=**False)\
.reset_index(drop**=**True)
sorted_valid[['treatment_group_key', 'conversion',
'Uplift score']].head(10)
在这个DataFrame
上,我们添加了几列,这将使曲线的计算更容易,遵循上面等式中的符号。
sorted_valid['Y_T'] **=** \
(sorted_valid['conversion'] ***** \
sorted_valid['treatment_group_key']).cumsum()
sorted_valid['Y_C'] **=** \
(sorted_valid['conversion'] ***** \
(sorted_valid['treatment_group_key']**==**0)).cumsum()
sorted_valid['N_T'] **=** sorted_valid['treatment_group_key'].cumsum()
sorted_valid['N_C'] **=** \
(sorted_valid['treatment_group_key']**==**0).cumsum()sorted_valid[['treatment_group_key', 'conversion', 'Uplift score',
'Y_T', 'Y_C', 'N_T', 'N_C']].head(10)
现在,增益曲线的计算可以如下进行:
sorted_valid['Gain curve'] **=** (
(sorted_valid['Y_T']**/**sorted_valid['N_T'])
**-**
(sorted_valid['Y_C']**/**sorted_valid['N_C'])
) ***** (sorted_valid['N_T'] **+** sorted_valid['N_C'])
让我们来看看增益曲线。
sorted_valid['Gain curve'].plot()
plt.xlabel('Number of customers treated')
plt.ylabel('Gain in conversion')
增益曲线看起来与上面的增益图非常相似(如果它被翻转过来),只是更连续,并且基本上讲述了相同的故事。然而,该曲线的一个优点是,类似于 ROC 曲线,我们可以计算曲线下的面积,解释为更大的面积意味着更好的模型:我们希望能够通过瞄准尽可能少的客户来获得尽可能多的客户。如果我们完全知道谁会对治疗做出积极反应,我们将只治疗那些会做出积极反应的人,上面绘制的增益曲线最初的斜率为 1,然后趋于平稳,如果有睡着的狗,可能会下降。这将导致增益曲线具有陡峭的初始斜率,并尽可能长时间地保持较高,从而导致曲线下的较大区域。
在计算 AUC 之前,归一化数据可能是有益的。如图所示,增益曲线在 x 轴和 y 轴上都有客户单位。这对于用真实世界的数量来可视化事物是很有好处的。然而,如果我们想要评估验证和测试集的性能,例如,这些曲线下的区域可能不可比较,因为这些数据集可能具有不同数量的观察值。我们可以通过缩放曲线来解决这个问题,使 x 轴和 y 轴的最大值都为 1。
缩放的 x 轴代表目标人群的比例:
gain_x **=** sorted_valid.index.values **+** 1
gain_x **=** gain_x**/**gain_x.max()
**print**(gain_x[:3])
**print**(gain_x[**-**3:])[0.0005 0.001 0.0015]
[0.999 0.9995 1\. ]
缩放后的 y 轴是治疗整个人群的收益比例:
gain_y **=** (
sorted_valid['Gain curve']
**/**
sorted_valid['Gain curve'].tail(1).values
).values
**print**(gain_y[:3])
**print**(gain_y[**-**3:])[nan 0\. 0.]
[1.00802087 1.00534686 1\. ]
注意归一化增益曲线中的第一个条目是NaN
;在开始时至少会有一个,因为至少在第一次观察时,n^t_t或 n^c_t会是零,导致被零除的误差。因此,我们将从 x 和 y 向量中删除条目,以消除NaN
,如果数据集足够大,这应该不是问题。**
nan_mask **=** np.isnan(gain_y)
gain_x **=** gain_x[**~**nan_mask]
gain_y **=** gain_y[**~**nan_mask]
**print**(gain_y[:3])
**print**(gain_y[**-**3:])[0\. 0\. 0.00805023]
[1.00802087 1.00534686 1\. ]
现在我们可以绘制归一化增益曲线,以及计算的 AUC。对此,我们将添加一条一对一的线。类似于 ROC 曲线上的一对一直线的解释,这里这对应于我们通过随机对待顾客而在理论上预期的收益曲线:我们通过对待所有顾客而获得的收益部分根据对待的部分和 ATE 而增加。
mpl.rcParams['font.size'] **=** 8
gain_auc **=** auc(gain_x, gain_y)
ax **=** plt.axes()
ax.plot(gain_x, gain_y,
label**=**'Normalized gain curve, AUC {}'.format(gain_auc.round(2)))
ax.plot([0, 1], [0, 1],
'--', color**=**'tab:orange',
label**=**'Random treatment')
ax.set_aspect('equal')
ax.legend()
ax.set_xlabel('Fraction of population treated')
ax.set_ylabel('Fraction of gain in converted customers')
ax.grid()
注意,与 ROC 曲线不同,增益曲线在 y 轴上实际上可以超过 1.0。这是因为,如果我们能避免惹麻烦,我们可能会赢得比请客更多的顾客。
此处计算的 AUC 提供了一种通用方法来比较不同模型和数据集之间的提升模型性能,如给定应用的训练、验证和测试集。归一化增益曲线可以一起绘制,并以监督分类问题中比较 ROC AUCs 的相同方式比较它们的 AUC。感兴趣的读者可能希望开发一个 T-Learner 模型,并与这里作为练习显示的 S-Learner 结果进行比较。
结论
隆起建模的目标是创建个体治疗效果的预测模型。这种模型允许数据科学家将人群分为更可能对治疗有反应的群体和不太可能对治疗有反应的群体。以此为目标,各种建模技术得到了发展;隆起建模继续受到积极的研究兴趣。提升模型的评估不像监督分类或回归模型那样简单,因为它需要单独考虑和比较治疗组和对照组。然而,开源 Python 包(CausalML、Pylift)已经被创建来促进隆起模型的开发和评估。这里使用 Python 和 pandas 演示了几种有用的提升评估技术,其中一些在那些包中可用。
感谢阅读!如果您对使用提升建模解决业务用例感兴趣,包括考虑财务影响、模型可解释性和生产中的监控,请参见我的后续帖子帮助晚期借款人在 Tala 使用提升建模还款。
感谢 Pierre Gutierrez 和 Robert Yi 提供的意见和反馈
参考
causaml:一个 Python 包,提供了一套隆起建模和因果推理方法,使用基于最近研究的机器学习算法。访问时间是 2020 年 7 月 5 日。
古铁雷斯,皮埃尔和让-伊夫·杰拉德迪,2016。因果推理和隆起模型:文献综述。JMLR:研讨会和会议记录 67:1–13。
Kunzel,Soren R .等人,2019 年。使用机器学习评估异质治疗效果的金属学习者。PNAS 2019 年 3 月 5 日 115(10)4156–4165
Lee,Taiyeong 等人,2013 年使用 SAS Enterprise Miner 进行增量响应建模。2013 年 SAS 全球论坛:数据挖掘和文本分析。
Pylift:一个提升库,主要提供(1)快速提升建模实现和(2)评估工具。访问时间是 2020 年 7 月 5 日。
易、罗伯特、威尔·弗罗斯特,2018。Pylift:用于隆起建模的快速 Python 包。访问时间是 2020 年 7 月 5 日。
原载于 2020 年 7 月 8 日 https://www.steveklosterman.com。
使用 GPU 加速游戏开发使用 Godot 引擎、Kompute 框架和 Vulkan SDK 加速机器学习
实践教程
一个实践教程,教你如何使用 Godot 游戏引擎和 Kompute 框架将 GPU 优化的 ML 代码集成到你的游戏开发工作流程中
作者图片
最近,世界已经看到了游戏行业和人工智能领域的各种定义性里程碑。仅在几周内,我们已经看到了游戏行业的重大财务公告,包括Unity 13 亿美元的 IPO 和Epic Games 17.8 亿美元的投资。人工智能领域也一直在赶上它的宣传,在 2020 年达到 600 多亿美元的市场,并在人工智能和游戏的交叉领域带来了令人兴奋的应用,包括 AlphaGo 击败冠军 Lee Sedol ,以及深度学习驱动的游戏,如人工智能地牢(以及更多应用)。
本文从技术上深入探讨了应用人工智能和游戏开发这两个领域之间的交集。我们专门研究如何利用跨供应商/移动 GPU 框架的能力来加速机器学习和高级数据处理用例的处理。
本教程的简单游戏界面(图片由作者提供)
在本教程中,你将学习如何使用 Kompute 框架 在流行的开源 Godot 游戏引擎 内部构建 GPU 优化代码。
您将了解如何通过 Godot 游戏引擎在游戏开发中利用机器学习和高级 GPU 计算。
除了编程经验之外,不需要任何背景知识**,但是如果你对参考的底层 AI / GPU 计算概念感兴趣,我们建议查看我们以前的文章,“移动中的机器学习&跨供应商 GPU 使用 Kompute & Vulkan 变得简单”。**
你可以在 资源库的 示例文件夹中找到完整的代码,以及 GDNative 库 实现和 Godot 自定义模块 实现。
Godot 游戏引擎
图片来自 Godot 开源 GitHub 库
拥有超过 30k github 明星和超过 1k 贡献者的 Godot 是最受欢迎的 OSS 游戏引擎。Godot 迎合了 2D 和 3D 开发的需求,已经被广泛用于手机、桌面、主机和网络兼容游戏/应用。Godot 是用 C++构建的,这使得它又快又轻——它只有 40MB 的下载空间。
Godot 通过健壮的设计原则和对高级语言的支持,包括其领域特定语言 GdScript ,对于新手来说非常直观,具有类似 Python 的语法,使其非常容易采用。也可以使用 C#、Python、可视化脚本、C++等进行开发。
在本教程中,我们将使用编辑器构建一个游戏,使用 GdScript 触发 ML 训练/推理,使用 C++开发底层的核心处理组件。
进入 Kompute & Vulkan SDK
与 Khronos 成员一起玩“瓦尔多在哪里”(图片由 Vincent Hindriksen 通过 StreamHPC 提供)
Vulkan 是一个由 Khronos Group 领导的开源项目,Khronos Group 是一个由大量技术公司组成的财团,他们聚集在一起,致力于定义和推进移动和桌面媒体(和计算)技术的开放标准。
大量引人注目的(和新的)框架已经采用 Vulkan 作为他们的核心 GPU 处理 SDK。Godot 引擎本身正在进行一个重大的 4.0 更新,将把 Vulkan 作为它的核心渲染引擎。
正如您所想象的,Vulkan SDK 提供了对 GPU 的非常低级的访问,这允许非常专业的优化。这对于数据处理和 GPU 开发人员来说是一笔巨大的财富——主要缺点是冗长,需要 500–2000 多行代码才能获得编写应用程序逻辑所需的基本样板文件。这可能导致昂贵的开发周期和错误,从而导致更大的问题。这是我们启动 Kompute 项目的主要动机之一。
Kompute 是一个构建在 Vulkan SDK 之上的框架,专门用于扩展其计算能力,作为一个简单易用、高度优化、移动友好的通用 GPU 计算框架。
Kompute 文档(图片由作者提供)
Kompute 不是为了隐藏任何核心 Vulkan 概念而构建的——核心 Vulkan API 设计得非常好。相反,它通过 BYOV(自带 Vulkan)设计增强了 Vulkan 的计算能力,通过减少所需的样板代码和自动化编写 Vulkan 应用程序中涉及的一些更常见的工作流,使开发人员能够进行开发。
对于想了解更多信息的新开发人员来说,它为开始使用 GPU 计算提供了坚实的基础。对于更高级的 Vulkan 开发人员,Kompute 允许他们将其集成到现有的 Vulkan 应用程序中,并通过在需要时访问所有 Vulkan 内部来执行非常精细的优化。该项目是完全开源的,我们欢迎错误报告、文档扩展、新的例子或建议——请随时在回购中提出问题。
游戏开发中的人工智能
在本帖中,我们将基于我们在“移动设备中的机器学习&跨厂商 GPU 使用 Kompute & Vulkan 简化”文章中创建的机器学习用例。我们不会像在那篇文章中那样详细地讨论基础概念,但是我们仍然会在这一节中介绍所需的高级直觉。
首先,我们需要一个允许我们公开机器学习逻辑的接口,这主要需要两个功能:
train(…)
—允许机器学习模型学习从提供的输入预测输出的功能predict(...)
—预测未知实例输出的函数。这可以在下图中概述的两个工作流程中看到。
数据科学流程(图片由作者提供)
特别是在游戏开发中,这也将是机器学习工作流的一种常见模式,对于预测性和解释性建模用例都是如此。这通常包括利用用户与游戏本身直接(或间接)互动时产生的数据。这些数据可以作为机器学习模型的训练特征。新模型的训练可以通过数据科学家执行的手动“离线”工作流来执行,或者通过自动触发再训练模型来执行。
Godot 中的机器学习项目
我们将首先提供一个关于我们的 Kompute 绑定将如何在我们的 Godot 游戏中使用的高级概述。我们将创建一个简单的项目,并训练我们创建的 ML 模型,它将在 GPU 中运行。在本节中,我们已经可以访问定制的 KomputeModelML Godot 类——关于如何构建它并将其导入 Godot 项目的详细信息将在后面的章节中介绍。
新项目屏幕(图片由作者提供)
为此我们使用的是 Godot 版本 3.2.3 稳定版 。创建一个新项目和一个新的 2D 场景。您应该会看到一个空白项目,带有一个顶级 2D 节点,如左图所示。
现在我们可以开始给游戏添加资源了。我们将从创建一个简单的 UI 界面/菜单开始,该界面/菜单由机器学习模型的输入组成,它反映了前面的架构图中涵盖的工作流。
我们将有两个用于数据X_i
和X_j
的输入行编辑文本框,以及一个用于Y
预期预测的输入行编辑文本框。下图显示了用于构建 UI 的节点结构。您还可以通过导入project.godot
文件来访问 repo 中的 full godot 项目。节点将被引用以读取输入数据并显示输出预测(和学习的参数)。
我们现在将能够在 Godot 中添加 GdScript 代码,这将允许我们读取输入,训练模型并执行预测——该脚本是在Parent
节点下创建的。
下面是我们用来执行处理的完整脚本,它使用了我们在下一节中构建的KomputeModelML
自定义 Godot 类。我们将在下面分解代码的各个不同部分。
首先,我们定义变量来简化 Godot 编辑器节点的引用。这可以通过美元符号语法$NODE/PATH
来实现,如下文所示。
下面的compute_ml()
函数包含机器学习训练和预测的逻辑。我们首先使用 Godot 编辑器节点引用从文本框中读取输入。
我们现在可以从 C++类绑定中创建一个实例,我们将在下一节中构建这个实例。这个类公开了我们将用于机器学习推理的训练和预测函数。
我们现在可以通过传递输入和预期的预测来训练我们的模型。下面的 ML 模型是逻辑回归模型,它将调整其内部参数以最佳拟合输入和输出,从而产生一个能够预测未知数据点的模型。
既然我们已经训练了我们的模型,我们可以对看不见的数据点进行预测。为了简单起见,我们将传递用于测试的相同输入,但是您可以传递全新的数组,并查看它会产生什么样的预测。然后,我们可以在我们定义的preds_node
引用节点变量中显示结果,这将显示在显示屏上。
最后,我们还想显示学习到的参数,在这种情况下,它包括w1
、w2
和bias
。我们能够在各自的标签中显示权重和偏差。
最后要设置的是将“Kompute Train & Predict”按钮连接到compute_ml
功能,这可以通过编辑器设置一个指向功能本身的信号来完成。
在我们的脚本中设置 compute_ml 方法的信号(图片由作者提供)
一旦这些都设置好了,我们就可以运行游戏,并使用提供的输入来触发训练和预测。然后,我们可以看到学习参数以及预测输出。当我们修改输入y
、xi
和xj
时,也可以看到学习参数是如何变化的。
Kompute ML Godot 界面及结果参数(图片由作者提供)
Kompute ML 实现
既然对游戏中发生的事情有了一个直觉,我们就能够使用 Kompute 框架 编写我们的底层 C++类来创建 Godot 绑定。
Kompute 建筑设计(图片由作者提供)
我们将遵循 Kompute 的设计原则,这些原则在显示不同组件的附图中有所概述。我们将按照此工作流程在 GPU 中加载数据并执行培训。
下面列出了 Godot 类绑定实现的头文件,我们将对其进行详细分析。正如你所看到的,创建一个带有绑定的 C++类是非常直观的,你可以看到我们在上面的 Godot GdScript 中调用的相同的函数。
KomputeLogisticRegression.hpp 实现
类头文件的初始部分包括:
- 导入包含我们将在这个项目中使用的所有 Kompute 依赖项的
Kompute.hpp
头文件 - 顶级
Godot.hpp
导入是确保所有 Godot 组件可用所必需的。 - Node2D 是我们将要继承的资源,但是你可以从继承树中的其他类继承,这取决于你计划如何使用你的定制 Godot 类
- 对于跨应用传递的数据,我们将使用 Godot
Array
来处理 GdScript 和 Naive C++之间的传输,以及 KomputeTensor
来处理 GPU 数据管理。 - GODOT_CLASS 宏定义通过添加额外的 GODOT 相关功能来扩展该类。正如我们将在下面看到的,当构建为 Godot 定制模块时,您将需要使用 GDCLASS。
遵循基本功能,我们必须定义核心逻辑:
void train(Array y, Array xI, Array xJ)
—使用逻辑回归模型的 GPU 本机代码来训练机器学习模型。它接受输入数组X
,以及包含预期输出的数组y
。Array predict(Array xI, Array xJ)
—执行推理请求。在这种实现中,它不使用 GPU 代码,因为通常在推理端通过并行化获得的性能收益较少。然而,如果并行处理多个输入,仍有预期的性能增益(此功能允许)。Array get_params()
—以[ <weight_1>, <weight_2>, <bias> ]
的格式返回包含学习参数的数组。
然后,我们可以声明将在 C++和高级 GdScript Godot 引擎之间绑定的方法,这些方法通常可供编辑器和更广泛的游戏访问。下面我们将简要地看一下注册一个函数所需的代码。
对于数据管理,我们将使用 Kompute 张量和数组——在这种情况下,我们只需要“学习”和“坚持”我们的逻辑回归模型的权重和偏差。
最后,我们还定义了着色器代码,它基本上就是将在 GPU 内部作为机器码执行的代码。Kompute 允许我们传递包含代码的字符串,但是对于生产部署,可以将着色器转换为二进制文件,也可以使用可用的实用程序转换为头文件。
如果你对完整的实现感兴趣,你可以在 gdnative 实现和定制模块实现文件夹中找到所有的文件。此外,如果你对这些技术的理论和潜在的基本概念感兴趣,这将在我们之前的帖子中全面介绍。
编译并集成到 Godot 中
现在我们有了 GPU 优化的机器学习模型的基础代码,我们现在可以在 Godot 游戏引擎中运行它了。Godot 允许我们通过两种主要方式将 C++代码添加到我们的项目中:
- GdNative 库构建指令 —在 Godot 中,您可以添加自己的“GdNative 脚本”,这些脚本基本上是绑定到 GdScript 的 C++类,这意味着这些脚本可以在项目中动态使用和引用。这种方法适用于标准的 Godot 安装,不需要像第二种方法那样重新编译完整的编辑器。
- 自定义模块 构建指令 —在 Godot 中只有底层核心类是“核心”组件的一部分;其他的一切——UI、编辑器、GdScript 语言、网络功能——都是定制模型。编写一个定制模块很容易,这就是我们能够公开一些核心 Kompute 功能的方式。该选项要求用定制模块重新编译完整的 Godot C++项目。
一旦您通过指令设置了这些方法中的一个,您将能够从 Godot 内部访问自定义对象。每种方法之间存在一些细微的实现差异。
我们不会在博文中涉及具体的构建细节,但是你可以在上面的 GdNative 库/自定义模块链接中找到确切的代码和构建说明。
下一步是什么?
恭喜你,你一路走到了最后!虽然这篇文章涵盖了广泛的主题,但是也有大量的概念被浏览过。其中包括底层 Vulkan 概念、GPU 计算基础、机器学习最佳实践和更高级的 Kompute 概念。幸运的是,网上有资源可以扩展你在这些方面的知识。以下是我推荐的一些进一步阅读的链接:
- "利用 Kompute 简化移动设备中的机器学习&跨供应商 GPU&Vulkan文章,深入探讨理论和概念
- Kompute 文档了解更多细节和更多示例
- 机器学习工程师时事通讯如果你想了解关于机器学习的最新文章
- 令人敬畏的生产机器学习开源工具列表,用于部署、监控、版本化和扩展您的机器学习
- FastAI 的 ML for Coders 课程简介进一步学习机器学习概念
- Vulkan SDK 教程深入了解底层 Vulkan 组件
利用 Dask 调整增压超参数
Dask 将 scikit-learn 参数搜索速度提高了 16 倍以上,Spark 提高了 4 倍以上
声明:我是土星云的高级数据科学家——我们用 Python 和 Dask 让企业数据科学变得快速而简单。
超参数调整是构建机器学习模型的一个关键部分,通常也是痛苦的部分。从你的模型中挤出每一点性能可能意味着数百万美元广告收入的差异,或者医疗保健模型中患者的生死。即使你的模型需要一分钟来训练,你也可能要等几个小时来完成网格搜索(想想 10x10 的网格,交叉验证,等等。).每次你等待一个搜索完成都会打破一个迭代周期,并且增加你的模型产生价值的时间。简而言之:
- 更快的运行时间意味着更多的迭代,以在截止日期之前提高准确性
- 更快的运行时间意味着更快的交付,因此您可以处理另一个项目
- 这两个要点都意味着将价值提升到您组织的底线
在这篇文章中,我们将看到如何通过在 Saturn Cloud 上用 Dask 代码 替换 scikit-learn 管道中的几行代码,将超参数搜索的速度提高超过16 倍。这使得传统的通宵参数搜索变成了几秒钟的等待。我们还尝试用 Apache Spark 进行类似的网格搜索,这需要更多的代码修改,但仍然比 Dask 慢得多。****
一、Dask 是什么?
Dask 是一个灵活、健壮的并行计算框架,内置于 Python 中,并为 Python 服务。它可以处理常见的数据结构,比如数组和数据帧,但是也可以用来并行处理那些不适合的复杂操作。事实上,并行数组和数据帧实际上是熟悉的numpy
和pandas
对象的集合,并且具有匹配的 API。通过这种方式,数据科学家不需要学习全新的框架,就可以在大数据上执行他们的代码。
实验设置
我们将使用公开可用的纽约出租车数据集并训练一个线性回归模型,该模型可以使用与乘客接送相关的属性来预测出租车乘坐的费用金额。
我们将首先使用单节点 Python 包(pandas
和scikit-learn
)完成数据加载和网格搜索,然后强调使用 Dask 或 Spark 并行化网格搜索需要做哪些更改。所有三个工作负载使用相同的数据执行相同的网格搜索,我们在整篇文章中将它们称为单节点(对于单节点 Python)、 Dask (对于 Dask 集群)和 Spark (对于 Spark 集群)。
五金器具
对于所有任务,我们都使用 AWS 的 r 5.2x 大型实例(8 个内核,64GB RAM)。对于 Python,我们只使用一个节点,对于 Spark 和 Dask,我们在具有不同数量工作节点的集群上运行工作负载,以跟踪运行时(3、10 和 20)。
Spark 集群使用亚马逊 EMR 管理,而 Dask 集群使用土星云管理。
给我看看结果吧!
如果您想跳过代码并查看性能改进,请跳到结果部分。
单节点工作流
首先,加载数据!我们随机抽取数据进行基准测试。
然后,创建一些特征:
我们正在使用一个 scikit-learn 弹性网模型,它可以基于l1_ratio
参数执行 L1、L2 和弹性网正则化。我们还将尝试几个alpha
的值,创建一个包含 404 个商品和三重交叉验证的网格,从而得到 1212 个适合搜索的模型。
当我们训练一个线性模型时,我们需要一次性编码分类特征并缩放数字特征。注意定义GridSearchCV
时的n_jobs=-1
参数指示 scikit-learn 在机器中的所有内核上并行化模型训练(这还没有使用 Dask,因为单节点并行化是 scikit-learn 自带的)。
最后,我们可以运行网格搜索并检索最佳分数:
如果你在和我们一样大小的机器上运行这个,大约需要 3 个小时。
顺便来看看
Dask 在单节点上运行良好,并且可以扩展到具有数千个节点的集群。要开始使用 Dask,我们需要初始化一个客户端,在这种情况下,我们将使用 Saturn Cloud 的SaturnCluster
设置我们的集群。
在读取数据时,我们需要做的唯一改变是使用dask.dataframe
包,而不是pandas
。所有特征工程代码保持完全相同,因为 Dask 数据帧实现了 pandas API。
然后创建我们的管道和网格搜索:
注意 Dask 有几个不同的预处理和GridSearchCV
类,用于加速预处理和避免网格搜索过程中不必要的重新计算。pipeline 和 estimator ( ElasticNet
)类直接来自 scikit-learn。
我们可以像使用单节点 scikit-learn 一样使用网格搜索:
使用 20 个节点运行这个网格搜索的运行时间为1114】分钟!只需更改大约 10 行代码,性能就提高了 16 倍。让我夸张地再说一遍。
改 10 行代码 16 倍提升!
火花
Apache Spark 是 Scala 内置的大数据处理引擎,带有一个 Python 接口,向下调用 Scala/JVM 代码。它是 Hadoop 处理生态系统中的一个主要部分,围绕 MapReduce 范式构建,具有数据帧接口以及机器学习。
为了使用 Spark 运行我们的工作负载,我们需要重构我们的 Python 代码,以使用 Spark 的 DataFrame 以及 Spark ML 管道、预处理和模型类。
特征工程看起来与熊猫略有不同:
然后,我们设置我们的预处理管道和网格搜索。Spark ML 希望所有的特性都在一个向量列中,所以我们使用VectorAssembler
来收集所有处理过的列。
然后,我们运行网格搜索,得到最佳结果:
在 20 个节点的集群上,这大约需要 47 分钟。
下面是一个并列的网格搜索代码,让您感受一下 Dask 在这个例子中有多简单:
代码量:Dask(左)和 Spark(右)
结果
我们用 404 个配置和 3 倍交叉验证进行了超参数搜索,使用弹性网络模型从纽约出租车数据集预测出租车出行持续时间。我们从单节点 Python 实现开始,并使用 Dask 和 Spark 将其转移到集群。从下表中我们可以看到,Dask 搜索比单节点和 Spark 集群版本快得多,同时只需要最少的代码更改。
我们在不同规模的集群上运行了 Dask 和 Spark 工作负载,以了解更大的集群如何缩短参数搜索时间:
与 Spark 和 Dask 集群相比的单节点 scikit-learn
为什么 Dask 这么快?
Dask 跨集群中的节点和核心并行化模型拟合,因此当向群集中添加更多机器时,您可以预期近似线性的加速。并行化工作会带来一些开销,这就是为什么我们通过添加 19 台机器获得了 16 倍的性能提升。
在这个例子中,Spark 的速度要慢得多,因为 Spark 网格搜索实现并没有并行化网格,它只是并行化模型拟合。这变成了连续的网格搜索,每个 fit 中的部分在集群中并行化。有一个项目 joblibspark 正在积极开发中,该项目旨在 spark 集群上并行化 scikit-learn 管道。在撰写本文时,我们无法成功运行 joblibspark。
所有代码都可以在这里获得。
您需要更快的超参数搜索吗?
是啊!你可以用土星云在几秒钟内启动 Dask 集群。Saturn 处理所有工具基础设施、安全性和部署方面的问题,让您立即开始使用 Dask。点击在你的 AWS 账户中免费试用土星!
用 Python 为 MS SQL Server 增压
如何使用 Python 来自动化 SQL 的一切
在工作中,我大量使用 SQL**。它并不是没有令人讨厌的细微差别和限制,但归根结底,它是所有数据职业的基础。因此,对于任何在数据领域工作的人来说,这是绝对必要的。精通 SQL 非常重要。**
虽然 SQL 很棒,但为什么要满足于棒呢?为什么我们不给 SQL** 增压?**
SQL 的局限性源于它是一种声明性语言,这意味着我们告诉 SQL 我们想要什么,然后 SQL 将从指定的数据库中获取这些信息。对于许多数据提取或简单的数据操作任务,这是所有需要的。
但是如果我们想要更多呢?
这正是我将在本文中向您展示的。
Axel Antas-Bergkvist 在 Unsplash 上拍摄的照片
它始于一个基础
点击此处查看完整脚本
这段代码是增强 MS SQL server 的基础。一旦构建完成,我们就可以从 Python 连接到 SQL,只需:
sql = Sql('database123')
简单对吗?有几件事情正在进行,所以让我们剖析代码。
class Sql:
首先要注意的是,我们将它包含在一个类中。我发现这种方法是合理的,因为在这种格式中,我们可以为这个特定的数据库添加或删除进程。一旦看到这是如何工作的,这就更有意义了。
我们初始化我们的类:
def __init__(self, database, server="XXVIR00012,55000"):
我和我的同事几乎总是连接到同一个服务器。所以我将这个公共服务器名设置为server
的默认参数。
您的服务器名称可以在“连接到服务器”对话框中找到,也可以在 MS SQL Server Management Studio 的窗口顶部找到:
接下来,我们建立与 SQL 的连接:
self.cnxn = pyodbc.connect("Driver={SQL Server Native Client 11.0};"
"Server="+self.server+";"
"Database="+self.database+";"
"Trusted_Connection=yes;")
多亏了 pyodbc 模块,这变得非常容易。我们简单地将一个连接字符串传递给pyodbc.connect(...)
函数,更多细节可以在这里找到。
最后,我喜欢在Sql
类中创建一个查询字符串,它随着传递给该类的每个查询而更新:
self.query = "-- {}\n\n-- Made in Python".format(datetime.now()
.strftime("%d/%m/%Y"))
这允许我们记录我们的代码,同时也作为一个可读性更强的输出,提供给那些更愿意阅读 SQL 代码的同事。
注意,我将在下面的代码片段中排除对 *self.query*
部分代码的更新,如果需要,请查看提供的链接中的完整代码。
积木
有几个基本功能我觉得非常有用,几乎每天都在使用。这些都侧重于将数据传入或传出数据库。
让我们从以下目录开始:
对于我们当前的项目,我们需要:
- 将这些文件导入 SQL server
- 将它们合并到一个表中
- 基于列中的类别动态创建多个表
在进一步充实我们的 SQL 类之后,这将变得像下面这样简单:
让我们从头开始。
推送数据帧
点击这里查看完整的脚本,或者这个脚本可以维护数据类型(但是偶尔会在不太干净的数据集上抛出错误)
这个函数包含在我们的 SQL 类中。它允许我们轻松地将 Pandas 数据帧推送到 SQL 数据库。
这在需要上传大量文件时特别有用。然而,允许 Python 将数据导入 SQL 的真正力量来自于 Python 的灵活性。
将十几个 Excel 工作簿中的特定选项卡导入 SQL 可能是一场噩梦。但是使用 Python 很容易。现在我们已经构建了一个函数,允许我们使用 Python 来读取这些选项卡,并为我们将它们导入 SQL。
指南
点击此处查看完整脚本
该功能实际上用于union
和drop
功能。它只是让我们尽可能简单地执行 SQL 代码。
response
参数为我们提供了将查询输出提取到数据帧的选项。允许我们从generic_jan
表中的colX
中提取所有唯一值,只需使用:
sets = list(sql.manual("SELECT colX AS 'category' FROM generic_jan GROUP BY colX", response=True)['category'])
联盟
现在我们已经构建了manual
函数,创建union
函数很容易:
点击此处查看完整脚本
这只是循环遍历我们通过table_list
提供的表名列表,为所有给定的表名构建一个联合查询。然后用self.manual(query)
执行。
滴
我们能够将大量的表上传到 SQL Server。这很好,但会很快使我们的数据库人满为患。为了解决这个问题,我们将创建一个drop
函数:
点击此处查看完整脚本
同样,由于有了manual
功能,这个功能非常简单。这允许我们通过向tables
提供一个字符串来删除单个表,或者通过向tables
提供一个表名列表来删除多个表。
当将这些极其简单的函数结合起来时,我们可以利用 Python 的优势来大规模扩展 SQL Server 的功能。
如果你有兴趣了解更多,请查看pysqlplus
的 GitHub 回购。这是一个小项目,只是为了增强我自己的工作场所与 MS SQL Server 的交互而构建的。
尽管如此,我希望它可以帮助其他一些用户找到将 Python 集成到他们的 SQL 例程中的方法。我个人几乎每天都在使用它,它是如此简单却又无比强大。
感谢阅读!
如果你有兴趣学习更多关于 Python 和云中 SQL 的知识,可以看看我在 Coursera 上发表的关于 IBMs 可扩展数据科学基础课程的评论,点击这里 :
我的想法是,在 IBM 的高级数据中,对可伸缩数据科学基础中的关键概念进行分解…
towardsdatascience.com](/a-summary-of-the-advanced-data-science-with-ibm-specialization-1-4-5caf48c011df)**
经典数据的超密集编码
使用具有双振幅编码的维数减少
我之前的一篇文章题为“ 130,780 点量子分类”,其中的电路使用 20 个量子位来映射所有数据,这导致了一个 Twitter 帖子,激励我思考我可以减少多少量子位计数。老实说,这条赛道并没有引起太多的注意;我专注于用一个相当大的真实数据集获得准确的分类结果。然而,公平地说,第一种有效的编码不一定是最佳编码策略。
介绍
量子计算的众多挑战之一是将经典数据映射到量子位,也就是“量子位”。在我的上一篇文章中,数据集有五个特性(也就是数据列)和四个类。最简单的方法是将每一类的每个特征映射到一个量子位,结果是 5×4 = 20 个量子位。我已经考虑过在每个量子位上映射两个特征的可能性,但是,我还是保持简单来验证算法是否真的有效。
顺便说一下,请记住,每个量子位代表数千行经典数据。每个向量代表一个类别中一列的标准化平均值。
原始电路
虽然你看不到下面的 20 个数据量子位,但你看到 5 个数据量子位(最上面的 5 个量子位)被重置,每个重复使用 3 次。这是因为当你在电路中添加量子位时,模拟器的速度会明显变慢,直到两个小时后的某个时候达到运行时错误(至少对于 IBM Q 体验来说)。然而,重置和重复使用更少的量子位,可以完成更多的计算。
与我已经发表的所有其他电路相比,这个电路的一个独特反馈是,OpenQASM 子例程的使用将电路压缩得如此之多,以至于它似乎实际上没有做太多事情,尤其是没有做任何“量子”的事情。此外,虽然“设置”盒仅仅是重置和重用量子位,但“测试”盒隐藏了非常量子交换测试。因此,即使子例程使编码变得更简单和容易,我将放弃它们,以便未来的电路公开显示它们的组件门。
对于那些没有读过我上一篇文章的人来说,所有进行交换测试的电路都必须在模拟器上运行的原因是因为 Fredkin gates 转换成了大量的门。真实量子位的有限连通性加剧了电路深度的问题,这导致 CNOTs 在已经很重要的步数上增加了更多的电路深度。当测量开始时,退相干已经使结果变得毫无价值。
降维
数据集的每一列都是数据的一个维度,所以我们从五个维度开始。然而,一个量子位不是五维的,所以我们需要某种降维策略来将这个数据集映射到少于 20 个量子位。使用旋转和量子态层析成像,我们可以将数据映射到 10 个量子位,这是一个显著的减少。然而,如果我们使用经典的预处理,我们甚至可以做得更好。
这个算法很松散地受到了主成分分析(PCA) 的启发。你可能看不到任何相似之处,但思考如何将数据集缩减为二维帮助我进一步将五维数据缩减为一维。
双振幅编码
如果你看了视频,“量子机器学习- 24 -编码经典信息,”魏特克博士描述了我的编码策略的一个变种。然而,我实际上并没有对振幅进行编码,因为我并没有测量编码的量子位。此外,振幅在布洛赫球的顶部和底部附近不太敏感,而在中部附近更敏感,这似乎会扭曲交换测试的结果。
相反,我用圆周率的分数。我希望每次旋转的量是一致的,不管我们在布洛赫球的哪个位置。
算法
我不可否认不是数学家,所以这个算法的降维部分可能有捷径。正如之前我的上一篇文章中所展示的,除了别的以外,我专注于让我的算法工作。如果它们有效,我会发布我所拥有的,然后在后续项目中进行优化。
- 对于每个类,使用公式((平均值-最小值)/(最大值-最小值))对每个要素(列)的平均值进行归一化,其中最大值和最小值是全局最大值和最小值
- 对于每个类别,将上述标准化平均值相加
- 使用步骤 1 中的公式归一化步骤 2 中的总和;全局最大值和全局最小值再次来自整个数据集,因此我们可能映射的所有测试值将保持在 0 到 1 的范围内
- 将每个归一化总和乘以 pi
该算法的最终结果是每个类由一个值表示,范围从圆周率值的 0%到 100%。
问题
对于四个类,我们可以使用 U3 门来映射每个量子位的两个类。这是本文顶部显示的电路。每个量子位围绕 y 轴有一个 0 到π的旋转,围绕 z 轴有一个 0 到π的旋转。
问题是这些数据被压缩得太多了,没有用。或者说,至少,我还没想好怎么用。在 Twitter 上,我把它比作使用 ZIP 文件,你必须用它提取内容,然后才能使用。
然而,通过量子态断层扫描提取内容并不成功,否则我会在这里展示这个电路。此外,如果没有其他原因,量子 ZIP 文件对增加的电路深度没有意义。从最少数量的有用量子位开始是有意义的。
最佳选择
对于给定数据集中的四类数据,所需数据量子位的最小数量似乎是四,或者每个数据量子位一类。我们可以用 RY 门代替 U3 门。
前一篇文章的电路的目标是量子分类,所以这里是该算法的修订版。前四个量子位代表四类数据,中间四个量子位代表我们想要分类的新数据点,底部四个量子位是用于交换测试的 ancilla 量子位。
测试数据量子位可能看起来是多余的,但这种冗余是必要的,因为我们正在运行四个不同的交换测试。在这种情况下,我们可以在一个模拟量子位上使用重置门,但我宁愿明确指出,每个类别比较都需要它自己的类别数据量子位、测试数据量子位和辅助量子位。
结果
当量子状态相同时,交换测试以 1 的概率测量|0 >,当量子状态最大程度地相反时(在 Bloch 球的相对侧),以 0.5 的概率测量|0>。例如,测量值为 0.9 表示各州之间距离相对较近。因此,测量|0 >的概率最高的类是最接近测试数据点的类。
如果您进行传统的后处理,测量|0 >的最高概率是类 0,这很好,因为这是我获取测试数据点的地方。为了完全公开,数据,甚至类,有很多重叠,所以很容易得到不正确的结果。但是,因为是数据有重叠,所以使用 scikit-learn 或其他方法,我们应该会看到同样不正确的结果。
结论
本文顶部的电路显示了仅映射到两个量子位的 130,780 个数据点。虽然我目前还没有一种算法可以处理这种程度的压缩,但这并不是不可能的。
使用稍微少一点的压缩,并将相同的数据映射到仅四个量子位,我们仍然可以运行诸如量子分类之类的算法。这与我之前实现的 20 量子位相比是一个巨大的减少。
未来的工作
我仍在考虑使用最大限度压缩数据的方法,并希望在未来的文章中分享对此的感悟。我也期待有一天在真正的硬件上运行这个算法。尽管我现在可以在 ibmq_16_melbourne 上运行它,但没有办法优化电路的量子位连接,结果将是一个退相干(如果这是一个真实的词)的混乱。
承认
感谢 IBM 一如既往的为量子位,无论是真实的还是模拟的。本文所有图片均来自 IBM Q Experience。也感谢量子直觉 ( @explore_quantum )和量子史蒂夫(@史蒂夫 _quantum )没有对将 130780 个数据点映射到 20 个量子位留下深刻印象。
使用 Google Colab 为您的 Jupyter 笔记本提供超强动力
您的 Jupyter 笔记本体验即将升级
Google Colab 是一个免费的基于浏览器的笔记本环境,完全运行在云中。它有一个类似于谷歌文档的界面,它是一个游戏改变者。
首先,如果你以前没有用过 Colab,那你就错过了。以下是几个原因:
- 无服务器: Colab 允许你在浏览器中立即弹出一个完全无服务器的 Jupyter 笔记本。这意味着您不必担心配置硬件、您的 Python 版本和路径,或者您是在 Windows、MacOS 甚至是手机上!
- **分享:**你可以无缝分享笔记本,就像一个谷歌文档。
- 免费 GPU: 一键免费 GPU。对我来说太棒了,因为我有一台 MacBookAir,所以肯定没有 GPU。
对于超级大国来说:
- 版本控制:你的笔记本上是否堆满了大量注释掉的代码?这也扰乱了你的思维。使用 Colab,您可以随意删除任何代码,因为每次保存时,Colab 都会为您保留一个版本,您可以恢复到该版本。cmd/ctrl+G 打开您的修订历史,您可以命名您过去的修订。这样你就可以随时删除旧代码,保持笔记本整洁。
- 键盘快捷键:键盘快捷键很多。只需在工具栏的工具选项下进行检查。更好的是,它们都是完全可配置的。我经常使用快捷键来添加新的单元格。
- **把“ *#@title 这是一个块”*放在一个代码单元格的顶部:**你的单元格刚有了标题就变得可压缩了。这是清理笔记本的好方法。
- **将“###节标题”放入一个文本单元格:**它将使所有内容直到下一个带有“# # #”的文本框都折叠起来,因此您可以折叠整个节。
- **表单:**您可以添加滑块、下拉框和其他表单元素,无需任何代码,只需点击悬停在代码单元格右上角的三个点,然后点击“添加表单”。这使得其他想要与您的笔记本进行交互的人无需编辑代码就可以这样做。
使用表单滑块允许用户调整模型参数。
- seed bank**😗*谷歌在 colabs 中托管了一个完整的深度学习和数据科学概念证明库,你只需点击一个按钮就可以打开并运行它。 GAN 合成器有人吗?
- 使用 GPU: 进入运行时- >更改运行时类型,选择 GPU。瞧啊。你现在有一个图形处理器。对于深度学习和一些可并行化的数据科学算法是必不可少的(XGBoost 有人吗??)同时,您可以添加更多内存。单击更改运行时类型后,可以选择切换到更多 RAM。
- Colab Pro: Colab 确实有超时和使用限制(它们相当大,比如一天十个小时)。但是,如果你发现自己遇到了这些问题,你可以每月支付 10 美元购买 Colab Pro,它可以延长运行时间,并更好地访问更强大的 GPU。这是我刚刚为我的深度学习项目买的,太棒了!
- **评论功能:**你知道如何在谷歌文档上发表评论吗?现在你也可以在 Colab 上这样做了!这很好,因为笔记本实际上一半是编码环境,一半是报告。轻松地与他人分享您的发现,他们可以直接在您的笔记本上发表评论!
- **自动完成:**难以记住所有那些 Matplotlib 或 NumPy 函数和函数参数?好吧,Colab 可以自动完成,所以你只需要开始输入;当你输入的时候,它会显示出剩下的函数名,参数和任何文档。
有几件事我希望他们会补充…
- 自动格式化:如果你能按下“ctrl + f”键,你的代码就会自动格式化,那不是很好吗?
- 类似 vscode 的 Git 指示:看看自上次提交以来您修改和添加了哪些行不是很好吗?像这样:
- Linter :当我在做一些 Linter 知道会出错或者只是糟糕/无用代码的事情时,我很想得到那些红色的曲线。
Colab 的缺点是什么?
- 我最近在一个深度学习项目上工作了 40+小时;随着代码库变得越来越大(深度学习比数据科学更容易发生这种情况),用笔记本管理变得越来越困难。
- 如果你使用太多的 RAM 或 GPU,你的会话就会崩溃,你将不得不重新运行所有的代码。它也会在休眠一段时间后超时。
- 如果你想访问谷歌硬盘上的文件,每次安装硬盘时都必须这样做。它会变得很烦人。
考虑到这一切,如果你以前从未使用过,Colab 绝对是一个更高的水平;如果你有,这些功能会给你超能力!
每个人的监督和非监督学习
不管你是技术天才还是街舞者,任何人都可以理解机器学习
来源:https://www.pexels.com/@freestockpro
不管你是技术天才还是街舞者,听说了机器学习并出于好奇或原始的激情决定去寻找它。以错误的方式被引入这些概念,感觉就像从直升机上被丢进了大海。我是以不太友好的方式被引入这些概念的,相信我,这一点也不好玩,这就是为什么我花时间以一种简单、初学者友好和吸引人的方式写这篇文章。
在这篇文章中,在保持事情简单的同时,我将稍微深入一点,明确解释监督和非监督机器学习的确切含义,这样即使是初学者或非技术人员也可以消化信息,而不会因其复杂性而便秘。
说完这些,让我们来看看是什么把我们带到这里的。
机器学习 大致分为 2 大类:有监督和无监督机器学习。
什么是监督学习?
插图:DAVIDE BONAZZI/@SALZMANART
有监督的机器学习包括使用明确标记的数据训练计算机系统。标记数据在这里是指输入已经用相应的期望输出标签进行了标记。机器学习算法(模型)通过迭代过程从这些标记的数据中学习,然后使其能够执行未来预测。例如,这里有一个类比可以帮助你理解这个概念。
可以把监督学习算法想象成这样的学生,他们被给予大量的实践问题(数据),并被指示通过找到这些问题中的信息和它们的相关答案(输出)之间的模式来找到解决这些问题的方法。
在上面的场景中,目标变成了能够找到最有效的数据(练习题)以提供给最有效的算法(学习风格),从而获得最佳的表现(答案)
监督学习问题被进一步分成两个子类— 分类和回归。这两个子类之间的唯一区别是算法旨在预测的输出或目标的类型,这将在下面解释。
1.分类问题
在分类中,目标是识别一个对象(输入)属于哪个类别。例如,我们可能有兴趣确定图像是否包含狗或猫,颜色红色或黑色,电子邮件垃圾邮件或真实邮件,患者是否携带艾滋病毒。在上面的例子中,分类目标只取 2 个类的分类问题被称为二元分类问题。另一方面,如果目标输出取 2 个以上的值,这是一个多类分类问题。比如说;分类花的类型,分类汽车的类型等
2.回归问题
截图来源:https://www . slide share . net/gabrielspmoreira/introduction-to-data-science-123459159
回归问题是当要预测的输出变量是数值时。这与上面看到的分类相反。让我们举个例子,你可能会对确定伊斯坦布尔的房价、生活在昆布的男人的体重、喀麦隆教师的薪水(T21)等感兴趣。价格、权重、和薪水是上图所示的数值,纵轴表示房价。
趣味问答 这只是为了刺激和巩固回归和分类的区别。如果你准备好了,那我们走吧。
以下哪项是分类问题?
1.通过一个人的笔迹来预测他/她的性别。根据面积预测房价
3。预测明年季风是否正常
4。预测下个月一张音乐专辑的销量
在这篇文章的最后,我将提供答案,所以保持冷静,让我们继续前进。
用于分类和回归的监督机器学习算法。
下面概述的处理回归和分类任务的不同算法类似于不同的学习风格,每个学生都使用不同的学习风格在上面分类下的类比中得出答案。这些算法中的哪一个在特定的分类和回归问题中表现得最好,由数据科学家来决定。
信守我让事情变得简单的承诺。以下是根据问题的具体特征来解决监督机器学习问题的最常用算法列表。
支持向量机
线性回归
逻辑回归
朴素贝叶斯
线性判别分析
决策树
随机森林
K-最近邻算法
神经网络(多层感知器)
相似性学习
有关不同算法的更多信息,请查看 scikit-learn 网站 以及本文末尾的参考链接。
分类问题的一些有趣应用是在**垃圾邮件检测、图像识别、语音识别。**你还可以查看一下机器学习的一些现实生活中的应用概述这里
在我们继续之前,让我们冷静一下这个令人震惊的事实…
法老拉美西斯二世是唯一一位获得护照并登上飞往法国的飞机的埃及法老。
来源:https://www . ancient-origins . net/history-named-people/mummy-passport-0010944
什么是无监督机器学习?
来源:https://www.pexels.com/@alittleimagery
与有监督的机器学习相反,在无监督的机器学习中,模型被输入没有**人类预定义标签的数据。**在数据中寻找隐藏的结构、模式或关系,这取决于算法。
让我与你分享这个类比。
想象一下,你对游泳一无所知,不幸的是,你的朋友带你去参加泳池派对,故意把你推进去。你需要弄清楚如何游泳,并让自己离开那个冰冷的游泳池。
以此类推,你就是模型(算法),池就是数据。没有游泳教练教你游泳,因此得名无人监管。
就像监督学习一样,非监督学习可以分为两种类型:聚类和关联技术。
1.聚类分析技术
在聚类分析中,该算法使用未标记的数据,其目的是根据某些相似性或不相似性标准将数据分成称为簇的组。相似的数据点被分组在同一个聚类下,如上图所示。
聚类可以分为排他聚类、重叠聚类、层次聚类、概率聚类在本文中我不会深入讨论这些,但是可以在文章末尾的链接中找到更多信息。
以下是机器学习中最常用的一些聚类算法。
***层次聚类
- K-means 聚类
- K-NN (k 近邻)
*主成分分析
奇异值分解
独立成分分析
对于好奇的读者来说,请参考文章末尾的链接以获得关于这些技术的更多信息。
2.关联规则技术
在关联问题中,模型学习数据之间的关系,然后得出某些规则。这种无监督学习技术是关于在大型数据库中发现变量之间的 有趣关系 。例如,买新房子的人很可能会买新家具,买牙刷的人很可能会买牙膏,等等。
已经提出了许多产生关联规则的算法。一些众所周知的算法是:
*** Apriori 算法
- Eclat 算法和
频繁模式增长*
下面包含解释个别技术的链接。
无监督学习技术的应用。
无监督学习在 e 探索性分析 中非常有用,因为它可以自动识别数据中的结构。例如,通过基因表达测量对癌症患者分组,根据浏览和购买历史对购物者分组,通过电影观众给出的评级对电影分组。
这篇关于机器学习的 文章 提供了机器学习的一些有趣的日常现实应用。
测验的答案:预测一个人的性别,预测明年季风是否正常都是分类任务。另外两个是回归。
我知道你答对了!!
希望你喜欢读这篇文章,就像我喜欢写它一样。非常感谢您的建议和意见。
参考文献。
在最近的演讲活动中,我遇到了试图从概念上解释机器学习而不…
towardsdatascience.com](/explaining-machine-learning-in-laymans-terms-9b92284bdad4)
https://www.guru99.com/unsupervised-machine-learning.html#7
https://www.wikiwand.com/en/Unsupervised_learning
https://medium . com/@ mandysdana/machine-learning-types-of-class ification-9497 BD 4 F2 e 14
https://medium . com/datadriveninvestor/class ification-algorithms-in-machine-learning-85 c0ab 65 ff 4
https://towardsdatascience . com/unsupervised-learning-and-data-clustering-eeecb 78 b 42 a
https://medium . com/@ mandysdana/machine-learning-types-of-class ification-9497 BD 4 F2 e 14
https://medium . com/datadriveninvestor/class ification-algorithms-in-machine-learning-85 c 0 ab 65 ff 4
https://towards data science . com/unsupervised-learning-and-data-clustering-eeecb 78 b 42 a
https://www.wikiwand.com/en/Unsupervised_learning
监督和非监督学习识别客户
解决同一问题的两种不同方法。
欢迎来到我在 Udacity 的数据科学家纳米学位的最后一个项目。我正在经历职业转变,从学术界(我有天体物理学博士学位)转向工业界,这个纳米学位是我朝着那个方向迈出的一步。
我选择这个项目是因为涉及到两种不同类型的机器学习算法应用于相同的数据。还因为这是各种行业都很常见的问题。这个项目的数据和大纲是由贝塔斯曼子公司 Arvato Financial Solutions 提供的。所有的工作都在这个环节中完成。
目标
我们希望利用无监督和有监督的机器学习技术和人口统计数据,为德国的一家邮购销售公司识别新客户。
数据
该数据包括 4 个数据集:
- 客户:德国一家邮购销售公司的客户的人口统计数据,包括 3 列,提供关于客户的广泛信息(191 652 x 369)。
- AZDIAS:德国一般人口的人口统计信息(891 211 x 366)。
- MAILOUT-TRAIN:公司营销活动目标的人口统计信息-TRAIN 集,包括一个回应列,该列指明每个接收人是否成为公司的客户(42 982 x 367)。
- MAILOUT-TEST:公司营销活动目标的人口统计信息—测试集(42 833x 366)。
人口统计列没有直观的名称,这给分析带来了困难,但是我们可以从 Arvato 提供的元数据中获得每个列的含义。
数据争论
我不得不在数据上争论不休:
- 删除客户数据中多余的列。
我们没有使用这些数据。
2.识别缺失的值。
数据包含许多缺失值,但其中一些是编码的。我必须非常仔细地查看元数据才能发现:
- 所有值=-1 表示未知。
- 对于 16 列,值=0 表示未知。
- 对于 74 列,value=9 表示未知。
- 对于对象列,value=X 或 XX 表示未知。
我必须将所有这些值转换成 NaNs。
3.删除缺少大量值的列。
在下图中,我显示了缺少更多值的列:
有 6 列缺少 50%以上的值。我们删除这些列。
4.删除有大量缺失值的行。
在下图中,我根据缺失数据的百分比显示了行的计数:
大多数行的缺失数据不到 10%,我们只保留那些行。
5.将对象列转换为数字列。
我们有六个非数字列:
- 两个实际上是数字,所以我把它们转换了。
- 一个是约会。我将该列转换为 datetime 类型,然后只保存年份。
- 一个只有两个值。我把它们映射到 0 和 1。
- 剩下的两列是分类的,没有序号,所以我必须通过 Panda 的 get_dummies()方法执行热编码。
6.填充 NaNs。
因为大多数数据是分类的,所以我选择用 mode by 列来填充缺失的值。为了做到这些,我实现了一个估算器()。
7.删除高度相关的列。
按照这里的步骤,我删除了 AZDIAS 和 CUSTOMERS 数据帧中相关性大于 0.95 的列。
8.删除 id 列。
对于模型来说,名为 LNR 的 id 列并不是一个有趣的特性。
步骤 2-8 是在一个名为 clean_data()的函数中实现的。
9.缩放数据。
我们需要缩放数据,以获得机器学习模型的准确性能。我使用了 StandardScaler(),它通过移除平均值并缩放到单位方差来标准化特征。
使用无监督学习识别普通人群中的客户群。
在项目的这一部分,我们执行无监督聚类来识别客户群。
- 主成分分析。
争论之后,我们以 425 列结束。由于这个数字非常大,我们对 AZDIAS 数据集进行了主成分分析,目的是降低维数。
我决定保留 170 个组件,它们占累计解释方差的 80%左右。
2.使聚集
我做了一个 K 均值聚类。首先,我只取了一个数据样本,用不同数量的聚类来拟合算法。我在下面的图中显示了结果:
按照肘方法,我选择保留 16 个集群(我也可以选择 7 个集群,但我决定用 16 个)。
然后,我用 AZDIAS 数据集拟合 K-means 算法,并预测每一行的聚类数。
3.客户数据
我将具有 170 个特征的拟合 PCA 和具有 16 个聚类的拟合 K-means 应用于客户数据,以获得每行的预测聚类数。
为了识别客户群,我们必须比较这些聚类:
很明显,客户子群体主要由集群 10、12 和 2 表示。这三个集群的人是公司的目标。哪些主成分(PC)与这些集群相关联?哪些是代表性最不足的(13、5、6、8)?
我们可以看看这 7 个被标识为代表过多(蓝色)和代表不足(绿色)的集群的第一主成分的值。
很难对情况进行详尽的分析,但有一个明确的结论:
对于 PC # 0,所有代表不足的聚类具有小于 1 的值,而代表过多的聚类具有更大的值。PC # 1 的情况几乎相反。
分析 PC # 0,我们看到与列最相关的是:MOBI_REGIO、PLZ8_ANTG1、KBA13_ANTG1、KBA05_ANTG1、LP_STATUS_FEIN、KBA13_BAUMAX、HH_EINKOMMEN_SCORE、KBA13_ANTG4、KBA13_ANTG3、PLZ8_ANTG3。
作为一个例子,让我们看看 MOBI _ 区域列的哪种值与 PC#0>1(客户)相关,哪些值与 PC#0 相关<1 (non-clients):
In general, we can associate positive values with clients and negative values with no clients. Since all these features are scaled to mean=0 the this means values above and under the mean. The MOBI_REGIO column is related to mobility, high values indicate low mobility. So, people with low mobility are our target.
After repeating this proceeding for the other 9 columns I found out that:
结论— 我们正在寻找高收入、高社会地位、流动性低且附近有 1-2 户人家的人。
使用监督学习预测客户响应概率
在项目的这一部分,我们训练一个分类器来预测测试数据集中的人对活动做出响应的可能性。
- 使用 wrangling 步骤中描述的 clean_data()函数清理训练数据。
- 将特征与目标分开。目标是二进制标签 0 或 1。从下一张图中可以看出,数据是不平衡的:
3.使用标准缩放器()缩放特征
4.训练不同的简单模型,看看它们的表现。
我训练了 5 个模型并计算了 roc_auc。由于数据的不平衡,我使用了这个指标(也因为它是 Kaggle 比赛中用来测试我的结果的指标)。
由于结果的度量,我选择保持梯度推进算法。
5.寻找最佳参数
我使用 GridSearchCV 扫描了以下参数空间。
param_grid = {'max_depth': [3,4,5],
'loss':['deviance', 'exponential'],
'n_estimators': [100, 150, 200]}
我发现最好的参数是 max_depth=4,loss=deviance(默认),n_estimators=100(默认),ROC_AUC=0.779。
6.预测测试数据的标签。
首先,我必须清理测试数据。我使用了带有不删除行选项的 clean_data 函数,还保存了 id 列,因为我在第 7 步中需要它。
我用拟合最佳模型的 predict_proba 方法预测了标签的概率(因为我们使用的是 roc_auc,所以我们对精确值不感兴趣)。
7.将结果上传到 Kaggle 比赛
我取得了 0.77698 的成绩,提交时的第 91 名。
结论
我喜欢这个项目,因为它有很多值得探索的东西。数据争论的步骤真的很长,而且列没有直观的名称这一事实使得一切变得更加困难。幸运的是,大多数特征都是数字,这简化了事情。
无监督模型很容易实现,但需要大量的工作来理解结果。得出一个详尽的结论是困难的,但我对我得出的结论感到高兴:我们正在寻找高收入、高社会地位、流动性低且附近有 1-2 个家庭的房子的人。
监督模型更难,我必须测试不同的模型,寻找最佳参数。上传我的成绩到 Kaggle 后,我得到了 0.77698 分。我对结果很满意,但我知道还有改进的空间。
我能做些什么来改进模型?
-设计一些功能:例如,我可以将年份变量转换为桶
-用平均值或其他技术填充 NaNs
-测试更多的分类器或更大的参数空间
你有其他想法吗?
监督学习算法:解释和简单代码
监督学习算法采用一组已知的输入数据(学习集)和对数据的已知响应(输出),并形成一个模型来生成对新输入数据的响应的合理预测。如果您有正在尝试预测的输出的现有数据,请使用监督学习。
它们可以用于分类或回归。在这篇文章中,我将展示每一个的一些例子。
分类算法
k 最近邻分类器
***1) Import the Classifier* ****from** sklearn.neighbors **import** KNeighborsClassifier***2) Create arrays for the features and the response variable* **
y = df['target'].values
X = df.drop('target', axis=1).values ***3) Create a k-NN classifier with 6 neighbors* **
knn = KNeighborsClassifier(n_neighbors = 6) ***4) Fit the classifier to the data* **
knn.fit(X,y) ***5) make predictions***
new_prediction = knn.predict(X_new)
选择正确的集群数量
***1) Setup arrays to store train and test accuracies*** neighbors = np.arange(1, 9)
train_accuracy = np.empty(len(neighbors))
test_accuracy = np.empty(len(neighbors))***2) Loop over different values of k***
for i, k in enumerate(neighbors): ***3) Setup a k-NN Classifier with k neighbors***
knn = KNeighborsClassifier(n_neighbors=k) ***4) Fit the classifier to the training data***
knn.fit(X_train, y_train) ***5) Compute accuracy on the training set***
train_accuracy[i] = knn.score(X_train, y_train) ***6) Compute accuracy on the testing set***
test_accuracy[i] = knn.score(X_test, y_test)***7) Generate plot***
plt.title('k-NN: Varying Number of Neighbors')
plt.plot(neighbors, test_accuracy, label = 'Testing Accuracy')
plt.plot(neighbors, train_accuracy, label = 'Training Accuracy')
plt.legend()
plt.xlabel('Number of Neighbors')
plt.ylabel('Accuracy')
随机森林分类器
***1) Import the Classifier* from** sklearn.ensemble **import** RandomForestClassifier***2) input data as np array*** y = df[‘target'].values
X = df.drop(‘target', axis=1).values***3) import model and fit(train) data*** rfc = RandomForestClassifier(n_estimators=100)
rfc.fit(X, y)**4) make predictions** pred_rfc = rfc.predict(X_test)
支持向量机(SVM)和判别分析
***1) Import the necessary modules* **
**from** sklearn.linear_model **import** LogisticRegression
**from** sklearn.metrics **import** confusion_matrix, classification_report ***2) Create training and test sets***
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.4, random_state=42) ***3) Create the classifier:* **
logreg = LogisticRegression() ***4) Fit the classifier to the training data* ** logreg.fit(X_train,y_train) ***5) Predict the labels of the test set* **
y_pred = logreg.predict(X_test)
logistic 回归的 ROC 曲线
ROC 曲线是一种图示,其说明了二元分类器系统在其辨别阈值变化时的诊断能力。ROC 曲线是通过在各种阈值设置下绘制真阳性率对假阳性率而创建的。真阳性率也称为灵敏度、召回率或检测概率。假阳性率也称为假警报概率
来自:https://flowing data . com/2014/05/09/type-I-and-ii-errors-simplified/
***1) Import the necessary modules*** from sklearn.metrics import roc_curve ***2) Compute predicted probabilities***
y_pred_prob = logreg.predict_proba(X_test)[:,1] ***3) Generate ROC curve values***
fpr, tpr, thresholds = roc_curve(y_test, y_pred_prob) ***4) Plot ROC curve* **
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr, tpr)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.show()
ROC 曲线下的面积越大,模型越好。
回归算法
线性和非线性回归
***1) Import LinearRegression* **
**from** sklearn **import** linear_model
**from** sklearn.linear_model **import** LinearRegression***2) Create the regressor* **
reg = linear_model.LinearRegression()***3) Create the prediction space* **
prediction_space = np.linspace(min(X_fertility),max(X_fertility)).reshape(-1,1)***4) Fit the model to the data* **
reg.fit(X_fertility,y)***5) Compute predictions***
y_pred = reg.predict(prediction_space)***6) Plot regression line***
plt.plot(prediction_space, y_pred, color='black', linewidth=3) plt.show()
***1) Import Decision Tree Regressor***
from sklearn.tree import DecisionTreeRegressor***2) Create a decision tree regression model* **
decision_tree = DecisionTreeRegressor() ***3) Fit the model to the training features and targets* ** decision_tree.fit(train_features,train_targets) ***4) Check the score on train and test* ** print(decision_tree.score(train_features, train_targets)) print(decision_tree.score(test_features,test_targets))
最后,还有两个回归算法:
高斯过程回归模型(GPR)和支持向量机(SVM)回归
感谢阅读!!
如果你想继续阅读这样的故事,你可以在这里订阅!
如果你想看看这些算法在真实世界的数据集中表现如何,我建议你阅读下一篇文章:
机器学习最流行的数据集之一对应于泰坦尼克号事故
towardsdatascience.com](/feature-engineering-and-algorithm-accuracy-for-the-titanic-dataset-5891cfa5a4ac)
或者阅读这个项目,我正在处理来自我的研究的数据:
探索性数据分析
towardsdatascience.com](/datascience-in-oil-and-gas-engineering-projects-daace6e6c7f)
监督学习,但是更好:半监督学习
来源: Pixabay
为什么半监督学习是 ML 的未来
监督学习是人工智能领域探索的第一种学习类型。自从它的概念提出以来,无数的算法——从简单的逻辑回归到大规模的神经网络——已经被研究以提高准确性和预测能力。
然而,一项重大突破表明,添加无监督数据可以提高泛化能力和性能。事实上,在无数的场景中,带标签的数据并不是现成的。半监督学习可以在标准任务上取得最先进的结果,只需要一小部分标记数据——数百个训练样本。
在我们对半监督学习的探索中,我们将涵盖:
- 半监督学习简介。什么是半监督学习,它与其他学习方法相比如何,半监督学习算法的框架/思想过程是什么?
- 算法:半监督 GANs 。与传统 gan 的比较,半监督 gan 的过程和性能的解释。
- 机器学习的用例与未来。为什么半监督学习会有这么大的需求,在哪里可以应用。
半监督学习简介
半监督学习算法代表了监督和非监督算法之间的中间地带。虽然没有正式定义为机器学习的“第四”元素(监督、非监督、强化),但它将前两者的各个方面结合成了自己的方法。
这些算法对有一些标签的数据进行操作,但大部分是无标签的。传统上,人们要么选择监督路径,只对带有标签的数据进行操作,从而极大地减小数据集的大小;否则,人们会选择无监督的路线并丢弃标签,同时保留数据集的其余部分用于类似聚类的工作。
作者创建的图像。
在真实世界的数据中,情况往往如此。由于标注非常昂贵,尤其是在大多数数据集存在的量级上,大型数据集(尤其是用于企业目的的数据集)可能只有几个标注。例如,考虑确定用户活动是否具有欺诈性。在一百万个用户中,公司知道有一万个用户是这样,但是其他九万个用户可能是恶意的,也可能是良性的。
半监督学习允许我们对这些类型的数据集进行操作,而不必在选择监督学习或无监督学习时进行权衡。
通常,半监督学习算法在这个框架上运行:
- 半监督机器学习算法使用有限的一组标记样本数据来训练自己,从而产生一个“部分训练”的模型。
- 部分训练的模型标记未标记的数据。因为样本标记数据集有许多严重的限制(例如,现实世界数据中的选择偏差),标记的结果被认为是“伪标记”数据。
- 标记和伪标记数据集相结合,创建了一种独特的算法,结合了监督和非监督学习的描述和预测方面。
半监督学习使用分类过程来识别数据资产,并使用聚类过程将其分组为不同的部分。
算法:半监督 GAN
半监督 GAN(Semi-Supervised GAN,缩写为 SGAN)是生成对抗网络架构的变体,用于解决半监督学习问题。
在传统的 GAN 中,训练一个鉴别器来预测图像是真实的(来自数据集)还是伪造的(由生成器模型生成),允许它从图像中学习鉴别特征,即使没有标签。虽然大多数人通常使用 GANs 中的生成器(该生成器已被训练为生成与数据集中的图像相似的图像)来生成真实生成的图像,但也可以通过迁移学习使用鉴别器作为起点来开发同一数据集上的分类器,从而使受监督的任务受益于无监督的训练。由于已经学习了大多数图像特征,所以执行分类的训练时间和准确度将会好得多。
然而,在 SGAN 中,鉴别器同时以两种模式训练:无监督和有监督。
- 在无监督中,鉴别器需要区分真实图像和生成的图像,就像在传统的 GAN 中一样。
- 在【T4 监督】中,鉴别器需要将图像分类到预测问题中的几个类别中,就像在标准的神经网络分类器中一样。
为了同时训练这两种模式,鉴别器必须输出 1 + n 节点的值,其中 1 表示“真或假”节点,而 n 是预测任务中的类别数。
在半监督 GAN 中,鉴别器模型被更新以预测 K+1 个类,其中 K 是预测问题中的类的数量,并且附加的类标签被添加用于新的“假的”类。它包括同时为非监督 GAN 任务和监督分类任务直接训练鉴别器模型。整个数据集可以通过 SGAN 传递-当训练示例有标签时,调整鉴别器的权重,否则,忽略分类任务,鉴别器调整其权重,以更好地区分真实图像和生成的图像。
作者创建的图像。
虽然允许 SGAN 无监督地训练允许模型从非常大的未标记数据集中学习非常有用的特征提取,但监督学习允许模型利用提取的特征并将其用于分类任务。结果是一个分类器,即使在非常非常少的标记样本(几十到几百个)上训练,它也能在像 MNIST 这样的标准问题上取得令人难以置信的结果。
SGAN 巧妙地结合了非监督学习和监督学习的各个方面,以相互加强,允许两个世界的最佳部分一起工作,以最少的标签产生令人难以置信的结果。
机器学习的使用案例和未来
在一个可用数据量不断呈指数增长的时代,无监督的数据根本不能停下来等待标签赶上。无数真实世界的数据场景都是这样出现的——例如,YouTube 视频或网站内容。从爬行引擎和内容聚合系统到图像和语音识别,半监督学习无处不在。
半监督学习结合监督学习和非监督学习(分别)的过度拟合和“欠拟合”趋势的能力创建了一个模型,该模型可以在泛化的同时出色地执行分类任务,给定最少量的标记数据和大量的未标记数据。除了分类任务之外,半监督算法还有其他广泛的用途,如增强聚类和异常检测。尽管该领域本身相对较新,但算法仍在不断被创造和完善,因为它们在当今的数字世界中有着巨大的需求。
半监督学习确实是机器学习的未来。
监督学习是不够的
为了在人工智能方面取得进展,我们的模型需要学会应对混乱的现实世界
谢尔盖·阿库利奇在 Unsplash 上的照片
在 2002 年的一次新闻发布会上,美国国防部长唐纳德·拉姆斯菲尔德解释了情报报告的基本局限性:
“有已知的已知;有些事情我们知道我们知道。我们也知道有已知的未知;也就是说,我们知道有些事情我们不知道。但也有未知的未知——那些我们不知道自己不知道的。如果纵观我们国家和其他自由国家的历史,后一类往往是困难的。”
这句名言蕴含着深刻的智慧,我们可以将其转化为机器学习研究。通常,我们收集一些数据,并将这些数据分成训练集和测试集。一个成功的模型将能够预测测试集中的许多实例:这些是已知的知识。对于测试集中的其他实例,即已知的未知量,就不那么确定了。
然而,在这种传统的、有监督的学习体系中,未知的未知是缺失的。根据定义,只有当 ML 模型被部署到生产中时,我们才会遇到未知的未知,在那里它会遇到真实的世界。当这种情况发生时,事情可能会出错,因为模型不知道正在发生什么,同时又对其预测过于自信。
开集识别
因此,监督学习不一定足以应对现实世界。现实世界简直太乱了,它往往会向我们扔新的东西。
这里有一个聪明的方法来测试这个想法:在 MNIST 数据上训练一个机器学习模型,但在训练期间只给它显示数字 0,1,2,3,4,5。然后,在测试过程中,向它显示整个数字范围。会有怎样的表现?
当在训练期间只看到类的子样本时,根据 MNIST 数据训练的模型的退化。[1]
结果很糟糕(见上面的蓝色曲线)。随着我们在训练过程中遗漏越来越多的类,准确度从接近 1 下降到大约 0.6。研究人员 Lalit Jain 及其合作者在 2014 年展示了这一结果。他们还提出了一种新的 ML 算法,以更好的方式推广到“未知的未知”(上面的红色曲线)——当然,警告是,这些未知的未知实际上是研究人员事先知道的。
开集识别是训练 ML 模型的挑战,这些模型不仅在已知数据上表现良好,还能识别是否有全新的东西扔给它们——并相应地调整预测的不确定性。解决这个问题是在现实世界中部署模型的重要一步。
如果模型能在不确定的时候问我们就更好了。
主动学习
主动学习的想法简单而有力。假设我们有一个大的未标记的数据池,和一个小的已标记的数据样本。然后,在已标记的数据上训练一个 ML 模型,并让该模型对未标记的数据实例进行排序,例如通过它对标签的不确定程度。选择最不确定的实例,由人类标记它们,将它们添加到标记的训练集中,并重新训练。
主动学习循环。[2]
如果未标记池是有限的,只需重复循环,直到标记了足够多的数据。然而,在现实世界中,未标记的池可能是无限的:在现代生产系统中,每秒钟都会产生新数据。
这是所有事情是如何组合在一起的:在现实世界中,我们需要擅长开集识别的模型,擅长识别一个新样本是否是以前从未经历过的,一个未知的未知。我们需要主动学习循环,以便模型可以在人类专家的帮助下快速有效地填补知识空白。这两个领域的进展对于现实世界的 ML 应用至关重要。
最后的想法
我在之前的帖子中提到了伊莱恩·赫尔茨贝格的惨死。2018 年 3 月 18 日晚上,赫尔茨贝格在亚利桑那州坦佩市的一条街道上骑车时,被一辆自动驾驶汽车撞了。尽管汽车模型已经在大量驾驶数据的基础上进行训练,但这些数据错过了足够多的行人骑自行车过马路的例子。对这个模型来说,赫尔茨贝格是一个未知的未知数。
图像数据中的长尾问题示例。[3]
不管我们用多少数据来训练我们的模型,模型总是会遗漏一些东西。这就是著名的长尾问题。然而我们人类是不同的:我们不仅仅是学习模式,我们有一种“为什么”我们看到的事物是这样的感觉。这种从第一原理进行推理的形式使我们能够推广到未知的长尾,这是监督 ML 模型中所缺少的技能。
实际上,一个模型永远不会真正“完成”学习。在现实世界的场景中,学习过程必须无限期地继续下去。监督学习是不够的。此外,模型需要善于识别未知的未知,更好的是,在最需要的时候主动要求我们澄清。
参考资料和进一步阅读
[ 1 ] Jain 等,利用包含概率的多类开集识别
[ 2 落定,主动学习文献综述
[ 3 刘等,开放世界中的大规模长尾识别
为什么真正的人工智能需要的不仅仅是模式识别
towardsdatascience.com](/the-origin-of-intelligent-behavior-3d3f2f659dc2)
监督学习——什么,什么时候,为什么,好与坏(第一部分)
深入回归
深度学习并不总是大数据问题的答案
通常在工作场所,业务利益相关者和经理会将机器学习和大数据与深度学习联系起来。他们通常认为所有数据问题的最佳解决方案是深度学习、人工智能或神经网络,或者这些技术的某种组合(插入你最喜欢的流行语)。作为一个有统计学背景的人,我发现这种观点非常令人沮丧,因为选择的机器学习算法类型应该基于五个关键因素:
选择机器学习算法时的 5 个关键考虑因素
- 数据问题的类型——即监督与非监督
- 数据集内的变量类型,即分类或数值
- 验证统计模型的基本假设
- 由此产生的模型精度
- 精确度与召回率以及灵敏度与特异性之间的平衡(用于分类
在接下来的几个部分和几篇博文中,我将介绍不同类型的监督学习模型,并将上述考虑应用于每一个模型。
监督学习
很多时候,数据问题需要监督学习的应用。这是当你确切地知道你想要预测什么— 目标或因变量,并且有一组自变量或预测变量时,你想要更好地理解它们对目标变量的影响。然后,模型的选择基于将数据中的基本模式映射到依赖于数据分布和变量类型的函数。
名称“监督学习”用于描述这些类型的模型,因为模型在训练集上学习基础模式。迭代/循环的次数决定了模型有机会从过去学习的次数。在随后的每一轮中,模型都会根据在之前的运行中所学到的知识,尝试提高模型的准确性(由用户选择的准确性度量)。模型在达到最大运行次数后或不再能提高模型精度时停止运行(在某些模型中由提前停止指定)。
有两种类型的监督学习算法,如下图所示,其中结果变量的类型决定了您是选择回归还是分类。
图 1:监督学习模型的类型
我们先来钻研一下回归。
回归
人们通常认为一切都是线性回归问题,但事实并非如此。R 回归算法有一套非常严格的假设,必须满足这些假设,才能使最终结果在统计上可靠(*注:我没有写统计意义上的)。
为什么要回归?
回归分析是一种预测算法,常用于时间序列分析和预测。这是因为它模拟了预测变量和因变量之间的潜在关系,并确定了预测变量的值的变化如何解释结果变量的变化。
有几种类型的回归算法,但我将集中在下面的几个。
图 2:回归的类型
在我进一步探索上述回归技术之前。让我们了解一下与回归技术相关的假设。
回归假设
- 回归线的残差呈正态分布— 为确保回归模型的结果有效,残差*(观察值和预测值之间的差值)应遵循正态分布(平均值为零,标准偏差恒定)。残差也称为误差项,它们的分布可以在正态概率图上观察到(Q-Q)。如果大多数残差位于正态对角线上,那么我们可以假设它们是正态的。*
图 3:用于检查正态性的密度图
2。数据集中的变量来自正态分布总体 —数据中的所有变量也应该来自正态分布总体。通常,在统计学中,如果样本量足够大,则可以根据中心极限定理假设正态性。在一些教科书中,它也低至 n = 30。
3。残差是独立的— 检查该假设以避免残差中的自相关*。当数据集中存在自相关时,可能会导致低估标准误差,进而使预测值看起来具有统计学意义,即使它们并不具有统计学意义。*
- **杜宾-沃森测试可用于检查自相关性。零假设是残差不是线性自相关的。德宾-沃森的 d 可以取 2 到 4 之间的任何值,但是 1.5 到 2.5 之间的 d 的经验法则值用于指示残差中缺乏自相关。
- 注意:德宾-沃森测试只能证明直接相邻者之间的线性自相关(一阶效应)。
图 4:残差的随机分布(同方差)
图 5:残差的扇形展开(异方差)
4。数据显示同方差— 当残差沿最佳拟合线均匀分布而不是显示一种模式时,数据中出现同方差(残差具有恒定方差)。当存在异方差时,残差的形状可能呈现扇形(锥形)甚至线性趋势。
- 通过将残差(y 轴)与预测值(x 轴)绘制到散点图上,并寻找点的随机分散而不是任何聚类或模式,可以评估同方差性。异方差的统计检验是戈德菲尔德-匡特检验*。它包括将数据集分成两组,并检查两组之间的残差方差是否相似。*
图 6:所示的正线性关系
5。关系是线性的 —进行线性回归(一元或多元)时,自变量和因变量之间的关系必须是线性的。从视觉上看,这可以通过在每个自变量和因变量对之间创建一个散点图来确定,并检查是否有一条可以通过这些点绘制的直线。
图 7:异常值由实心圆表示
5.没有显著的/有影响的异常值 —异常值通常是数据集中偏离平均值+/- 3 个标准偏差或超出箱线图的点。由于异常值是极值,它们会对最佳拟合线的斜率产生显著影响。
- 异常值可以在箱线图(位于须状物外部的点)或直方图的偏斜度中直观地识别出来。
- 为了确定异常值是否有影响,有两个统计指标可以使用: 1)库克距离和 2)马氏距离
- Cook’s Distance 检查当数据集中的每个观察值被移除时,回归系数如何变化,从而检查它们的影响。数据集中的每个值都被赋予一个库克距离值。厨师的距离越大,观察的影响越大。用于确定某个值是否为异常值的一个典型试探法是检查库克的 D 值是否大于 4/n (其中 n 是数据集的大小)。
- **Mahanalobis 距离测量每个观察值距离大多数数据点(或平均值)有多远(多少标准差),通常用于多元数据集。该距离可以使用具有 n 个自由度的卡方分布来近似计算。计算数据集中每个观察值的 p 值。检查 p 值是否小于显著性水平(即 0.01),以确定它们是否极端。
6.自变量之间没有多重共线性—当自变量彼此相关时,多重共线性出现。如果是这种情况,通常您希望回归模型中只包含变量对中的一个变量。这个假设可以用三种方法来检验。*
*不适用于简单线性回归
- 方差膨胀因子(VIF) : 如果变量的 VIF 值大于 10,则变量是多重共线的。
图 7:波士顿住房数据集的相关图
- 【相关系数® :创建所有变量对之间的相关矩阵。如果相关系数大于或等于-/+ 0.7,则变量高度相关。
- 容差 — 该指标衡量每个自变量如何影响所有其他自变量,由等式 T = 1 — R 平方定义。通过对剩余的独立变量进行回归,可以计算每个独立变量的 r 平方(决定系数)。通常使用 0.1 的容差系数,当 T < 0.1 时,这表明数据集中存在多重共线性。
回归假设概述
让我们通过对波士顿住房数据运行回归诊断来测试我们已经学到的一些概念。
图 8:残差的诊断图
- 残差与拟合值— 该图用于检查线性度。假设这条线是水平的,没有任何明显的图案,我们可以假设它是线性关系。
- 正常 Q-Q 。该图用于检查残差的正态性。由于大部分残差都在对角线上,所以满足这个假设。
- 标度位置(或分布位置)——该图用于检查残差的方差是否均匀(同质方差)。我们正在寻找一条水平线,在这条线的上下有平均分布的残差。在这种情况下,线有一些曲率,这意味着我们有一些异方差。
- 残差 vs 杠杆。此图用于确定有影响的观察结果或那些有杠杆作用的观察结果。图中标记的点似乎是有影响的。通过去除这些,我们也可以解决异方差问题,因为相同的观察值在前面的图中被标记。
现在让我们看看常用于评估模型性能的统计数据。
评估模型性能
有几个指标可以用来衡量回归模型的准确性。我已经描述了我在构建模型时常用的方法。
- R 平方 :决定系数衡量自变量的变化可以解释多少结果变量的变化。更高的 R 平方值被视为反映了更高的模型精度。然而,当模型中有多个预测器时, R 平方不可信。这是因为每个预测值都会影响 R 平方值。在这些情况下,使用调整的 R 平方更可靠。在决定是否信任 R 平方值时,需要该领域的专业知识。例如,在化工厂等受控环境中, R 平方可能相当高(接近 90%);然而,当模拟人类行为时,高的 R 平方值可以代表过拟合*。在这些情况下,最好使用以下任何指标来验证您的模型。*
- RMSE(均方根误差): 计算为预测值与观测值之间方差的平方根(残差)。该值越低,模型拟合得越好。 RMSE 在某些情况下是更好的度量,因为它惩罚大的误差(残差)。
- MAE(平均绝对误差): 这个度量比 RMSE 简单,只是对残差的绝对值求和。它受异常值的影响较小。每个残差对等式中的总误差有成比例的贡献。同样,值越低越好。
我们终于可以深入了解各种类型的回归算法。
最小二乘回归
线性回归,也称为最小二乘回归,使用独立变量来逼近结果变量的最佳拟合线。它是一个试图最小化平方差之和的加权方程,由以下方程表示,其中 y 是预测的因变量,每个 β 是对应于每个自变量或 x 的系数,β₀ 是 y 截距, e 是误差项。
最小二乘回归方程的数学表示
在简单线性回归中,只有一个系数和一个自变量。为多元线性回归中包含的每个自变量( x) 添加一个系数项( β) 。
解释线性回归方程
*让我们从 y 轴截距或常数开始, *β₀.是所有自变量( X) 均为零时 y 的预测值。它通常也是预测的 y. 的平均值
对于连续预测变量的系数*,其解释如下。保持所有其他变量不变, X ₁每增加 1 个单位, y 将增加(如果 β ₁ *为正)*或减少(如果 β ₁ *为负)平均增加 β ₁ 个单位。
*对于线性回归,分类自变量编码为 0 或 1(虚拟变量)。因此,当解释它们的系数时,即 *y 的预测值的差异,一个单位的差异表示从一个类别切换到另一个类别,同时保持所有其他变量不变。
多项式回归
当自变量和因变量之间的关系本质上是非线性的并且包括曲率时,多项式回归可以被认为是一种选择。
图 6:多项式回归示例
在左边的图中,我们可以看到多项式回归(到 2 的数量级)比直线拟合略好。
每个独立变量的多项式回归方程如下所示。对于变量被提升到的每一阶,在等式中为变量添加一个附加项。
继续将方程提升到更高次的多项式以获得最佳拟合可能是诱人的;然而,这会导致过度配合。过度拟合发生在试图解释尽可能多的点时,包括数据集(即训练集)中的随机误差,使得难以推广到其他数据集(即测试集)。
多项式回归方程的数学表示
逐步回归
逐步回归就像它的名字一样,涉及在线性模型中添加(从模型中没有独立变量开始的正向选择)或移除(从完整模型或模型中的所有独立变量开始的反向选择)独立变量,以基于模型评估度量找到因变量的最佳预测子集。
**前向选择在模型评估度量不再有改进时停止(即 R 平方停止增加),而 b 后向选择在最无影响的(即那些对 R 平方有贡献的)独立变量被移除后停止。
R 的回归
线性回归
坚持波士顿住房数据集,我对所有预测变量对中值房价进行了线性回归(“lm”)。请注意,在运行模型之前,我没有“处理”多重共线性、正态性和异常值问题。
*> linearMod <- lm(medv ~ ., data=Boston)
> summary(linearMod)Call:
lm(formula = medv ~ ., data = Boston)Residuals:
Min 1Q Median 3Q Max
-15.595 -2.730 -0.518 1.777 26.199Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.646e+01 5.103e+00 7.144 3.28e-12 ***
crim -1.080e-01 3.286e-02 -3.287 0.001087 **
zn 4.642e-02 1.373e-02 3.382 0.000778 ***
indus 2.056e-02 6.150e-02 0.334 0.738288
chas 2.687e+00 8.616e-01 3.118 0.001925 **
nox -1.777e+01 3.820e+00 -4.651 4.25e-06 ***
rm 3.810e+00 4.179e-01 9.116 < 2e-16 ***
age 6.922e-04 1.321e-02 0.052 0.958229
dis -1.476e+00 1.995e-01 -7.398 6.01e-13 ***
rad 3.060e-01 6.635e-02 4.613 5.07e-06 ***
tax -1.233e-02 3.760e-03 -3.280 0.001112 **
ptratio -9.527e-01 1.308e-01 -7.283 1.31e-12 ***
black 9.312e-03 2.686e-03 3.467 0.000573 ***
lstat -5.248e-01 5.072e-02 -10.347 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1Residual standard error: 4.745 on 492 degrees of freedom
Multiple R-squared: 0.7406, Adjusted R-squared: 0.7338
F-statistic: 108.1 on 13 and 492 DF, p-value: < 2.2e-16*
上面我们可以看到调整后的 R 平方值为 73.38%* ,也就是说房价中位数的这个变化量可以用模型中的预测变量来解释。这是一个非常好的 R 平方值,并且 p 值< 0.05 表示统计显著性。*
接下来,我将确定哪些变量对这个 R 平方统计有贡献。我可以看到,除了印度河和年龄都是非显著贡献者(p 值> 0.05)。
让我们看看如果我运行逐步回归会发生什么。
逐步回归
向后
让我们首先尝试解释对模型的调用。
- 我的因变量(medv)已经针对所有自变量进行了回归。)
- 我使用了逆向回归
- 该模型将被调整以尝试由 1 到 13 个变量组成的模型。
*# Set seed for reproducibility
> set.seed(123)
> # Set up repeated k-fold cross-validation (10 fold)
> train.control <- trainControl(method = "cv", number = 10)
> # Train the model
> step.model <- train(medv ~ ., data=Boston,
+ method = "leapBackward",
+ tuneGrid = data.frame(nvmax = 1:13),
+ trControl = train.control
+ )
> step.model$results
nvmax RMSE Rsquared MAE RMSESD RsquaredSD MAESD
1 1 6.190699 0.5542196 4.512277 0.6480792 0.03585104 0.5143497
2 2 5.537331 0.6393649 3.975099 0.6194193 0.08470812 0.4184383
3 3 5.216943 0.6757786 3.675974 0.7844579 0.09606216 0.3282287
4 4 5.144946 0.6843838 3.621869 0.6838151 0.08435306 0.3033078
5 5 4.998163 0.7010740 3.543459 0.7416779 0.08756225 0.2999431
6 6 5.048162 0.6953426 3.520865 0.7245789 0.08673190 0.2717374
7 7 5.049469 0.6952020 3.500608 0.6987592 0.08276566 0.2597508
8 8 5.008368 0.7004709 3.497253 0.6740135 0.08021108 0.2756674
9 9 5.012961 0.7018661 3.490065 0.6963818 0.08564270 0.2683468
10 10 4.947244 0.7098667 3.447318 0.6896883 0.08705085 0.2222943
**11 11 4.809318 0.7249622 3.359429 0.6861143 0.08341394 0.2409937**
12 12 4.825967 0.7231964 3.365846 0.6808653 0.08309689 0.2442360
13 13 4.829796 0.7227732 3.368084 0.6796633 0.08336179 0.2455718
> step.model$bestTune
nvmax
11 11> summary(step.model$finalModel)
Subset selection object
13 Variables (and intercept)
Forced in Forced out
crim FALSE FALSE
zn FALSE FALSE
indus FALSE FALSE
chas FALSE FALSE
nox FALSE FALSE
rm FALSE FALSE
age FALSE FALSE
dis FALSE FALSE
rad FALSE FALSE
tax FALSE FALSE
ptratio FALSE FALSE
black FALSE FALSE
lstat FALSE FALSE
1 subsets of each size up to 11
Selection Algorithm: backward
crim zn indus chas nox rm age dis rad tax ptratio black lstat
1 ( 1 ) " " " " " " " " " " " " " " " " " " " " " " " " "*"
2 ( 1 ) " " " " " " " " " " "*" " " " " " " " " " " " " "*"
3 ( 1 ) " " " " " " " " " " "*" " " " " " " " " "*" " " "*"
4 ( 1 ) " " " " " " " " " " "*" " " "*" " " " " "*" " " "*"
5 ( 1 ) " " " " " " " " "*" "*" " " "*" " " " " "*" " " "*"
6 ( 1 ) " " " " " " " " "*" "*" " " "*" " " " " "*" "*" "*"
7 ( 1 ) " " " " " " " " "*" "*" " " "*" "*" " " "*" "*" "*"
8 ( 1 ) "*" " " " " " " "*" "*" " " "*" "*" " " "*" "*" "*"
9 ( 1 ) "*" " " " " " " "*" "*" " " "*" "*" "*" "*" "*" "*"
10 ( 1 ) "*" "*" " " " " "*" "*" " " "*" "*" "*" "*" "*" "*"
11 ( 1 ) "*" "*" " " "*" "*" "*" " " "*" "*" "*" "*" "*" "*"*
在上面的例子中,我用 10 倍交叉验证和 13 个最大变量进行了逐步回归。我没有全部使用它们,因为我们知道在之前运行全线性模型时,有两个并不重要。我们可以看到,最佳模型由 11 个变量组成(最低的 RMSE、最低的 MAE 和最高的 R 平方)。
星号()表示变量包含在相应的模型中。最好的 11 变量模型包括除了印度河流域和年龄以外的所有变量,就像完全线性模型一样。*
向前逐步回归给出了与完全线性模型和向后回归相似的结果。
*> set.seed(123)
> train.control <- trainControl(method = "cv", number = 10)
> step.model <- train(medv ~ ., data=Boston,
+ method = "leapForward",
+ tuneGrid = data.frame(nvmax = 1:13),
+ trControl = train.control
+ )
> step.model$results
nvmax RMSE Rsquared MAE RMSESD RsquaredSD MAESD
1 1 6.190699 0.5542196 4.512277 0.6480792 0.03585104 0.5143497
2 2 5.537331 0.6393649 3.975099 0.6194193 0.08470812 0.4184383
3 3 5.216943 0.6757786 3.675974 0.7844579 0.09606216 0.3282287
4 4 5.243481 0.6736566 3.658219 0.7510340 0.08224804 0.3304744
5 5 5.098698 0.6904107 3.568604 0.7354806 0.08392928 0.2726078
6 6 5.045755 0.6953140 3.512300 0.7223225 0.08606320 0.2787373
7 7 4.969144 0.7036333 3.448631 0.7542104 0.08842114 0.2944921
8 8 4.985899 0.7027153 3.456932 0.7099500 0.08432911 0.2895184
9 9 4.985690 0.7030156 3.466382 0.6879376 0.08295944 0.2782166
10 10 4.912877 0.7137519 3.421565 0.6865803 0.07813425 0.2716994
11 11 4.809318 0.7249622 3.359429 0.6861143 0.08341394 0.2409937
12 12 4.825967 0.7231964 3.365846 0.6808653 0.08309689 0.2442360
13 13 4.829796 0.7227732 3.368084 0.6796633 0.08336179 0.2455718
> step.model$bestTune
nvmax
11 11*
正规化
正则化是一种常用于解决回归中可能出现的过度拟合和多重共线性问题的技术。这是通过在目标函数中增加一个惩罚项来实现的,目标函数的目的是减少标准误差。
里脊回归
在岭回归中,增加了一个惩罚项(收缩参数)λ,以减少 β 系数项中的较大变化。 L2 正则化用于骑行回归,目标是通过试图使系数更接近零来最小化系数平方的 s um。
岭回归适用于包含大量变量的小型数据集,具有小到中等的效果。
套索回归
Lasso(最小绝对收缩和选择算子)回归使用 L1 正则化,其中λ被添加到系数的绝对平方和。
Lasso 回归适用于变量较少但对因变量有中到大影响的数据集。
弹性网络回归
ElasticNet 结合了套索和岭回归技术,其中应用了 L1 和 L2 正则化。
选择正则化参数
在决定使用哪个正则化参数时,我们可以基于两种方法:1)选择λ,其中信息标准最小化(如 AIC 或 BIC),侧重于模型拟合;2)运行交叉验证模型,并选择λ值,使交叉验证残差最小化,侧重于预测精度。
回归的利与弊
优点
- 易于理解并向利益相关者展示
- 可用于解释——即每个预测因素对结果变量的相对影响
缺点
- 将所有独立变量标准化(定标和居中)以避免多重共线性非常重要
- 需要检查严格的模型假设
这就是我关于回归的全部内容。我的下一篇博文将是关于分类算法的。