每个程序员应该知道的 50 个算法(二)

原文:annas-archive.org/md5/a52efc405cb495a26c2bdcb2c25f51df

译者:飞龙

协议:CC BY-NC-SA 4.0

第五章:图算法

图提供了一种独特的数据结构表示方式,尤其与结构化或表格数据相比。虽然结构化数据(如数据库)擅长存储和查询静态、统一的信息,但图在捕捉存在于实体之间的复杂关系和模式方面表现出色。以 Facebook 为例,每个用户都是一个节点,每个友谊或互动就是一个连接的边;这种连接的网络最好通过图结构来表示和分析。

在计算领域,某些问题,尤其是涉及关系和连接的问题,更自然地通过图算法来解决。这些算法的核心目标是理解图的结构。理解这一结构意味着要弄清楚数据点(或节点)是如何通过连接(或边)相互关联的,以及如何有效地导航这些连接以提取或分析所需的数据。

在本章中,我们将踏上以下领域的探索之旅:

  • 图表示:捕捉图的各种方式。

  • 网络理论分析:网络结构背后的基础理论。

  • 图遍历:有效遍历图的技巧。

  • 案例研究:通过图算法深入分析欺诈行为。

  • 邻域技术:确定和分析大型图中局部区域的方法。

完成本章后,我们将对图作为数据结构有一个坚实的理解。我们应当能够构建复杂的关系—无论是直接的还是间接的—并将能够使用图算法解决复杂的现实问题。

理解图:简要介绍

在现代数据的广阔互联景观中,超越表格模型的局限,图结构作为强大的工具,能够 encapsulate(封装)复杂的关系。它们的崛起不仅仅是一个趋势,而是对数字世界错综复杂的结构所带来的挑战的回应。图论中的历史性进展,例如莱昂哈德·欧拉(Leonhard Euler)对柯尼斯堡七桥问题的开创性解决方案,为理解复杂关系奠定了基础。欧拉将现实世界问题转化为图形表示的方法,彻底改变了我们看待和操作图的方式。

图:现代数据网络的支柱

图不仅为社交媒体网络和推荐引擎等平台提供了支撑,还作为打开看似无关领域中模式的钥匙,例如道路网络、电路、分子、生态系统,甚至计算机程序中的逻辑流。图的关键在于它们内在的能力,能够表达有形和无形的互动。

那么,为什么这种由节点和边组成的结构在现代计算中如此重要呢?答案就在于图算法。专门为理解和解析关系而设计的这些数学算法,精确地处理连接。它们通过明确的步骤解码图结构,揭示出图的总体特征以及复杂的细节。

在深入探讨图的表示方式之前,首先要建立对其背后机制的基础理解。图结构,根植于数学和计算机科学的丰富土壤,为描绘实体之间关系提供了一种形象的方法。

现实世界的应用

现代数据中日益复杂的模式和连接,在图论中找到了清晰的解释。简单的节点和边背后,蕴藏着解决世界上一些最复杂问题的答案。当图算法的数学精确性遇到现实世界的挑战时,其结果往往会带来令人惊讶的变革性影响。

  • 欺诈检测:在数字金融世界中,欺诈交易往往彼此紧密相连,构成一张微妙的网络,旨在欺骗传统的检测系统。图论被用来识别这些模式。例如,从单一来源到多个账户的相互连接的小额交易突然激增,可能是洗钱的迹象。

    通过将这些交易绘制成图,分析人员可以识别出异常模式,隔离可疑节点,并追溯潜在欺诈的来源,从而确保数字经济的安全。

  • 航空交通管制:天空中充满了忙碌的航班。每架飞机必须在确保与其他航班保持安全距离的同时,穿越复杂的航线迷宫。图算法为天空绘制了路线,将每架飞机视为节点,其航线视为边。2010 年美国空中交通拥堵事件证明了图分析的威力。科学家们使用图论解读了系统性级联延误,提供了优化航班安排的见解,减少未来此类事件的发生。

  • 疾病传播建模:疾病,尤其是传染性疾病的传播,并非随机发生;它们遵循人类互动和流动的隐形轨迹。图论构建了复杂的模型来模拟这些模式。通过将个体视为节点,将他们的互动视为边,流行病学家成功地预测了疾病的传播,识别潜在的热点,并实施及时的干预。例如,在 COVID-19 疫情初期,图算法在预测潜在的爆发聚集区方面发挥了重要作用,帮助指导封锁和其他预防措施。

  • 社交媒体推荐:曾想过像 Facebook 或 Twitter 这样的平台如何推荐朋友或内容吗?这些推荐背后潜藏着巨大的图,代表用户的互动、兴趣和行为。例如,如果两个用户有多个共同的朋友或相似的参与模式,他们可能彼此认识或有相同的兴趣。图算法帮助解码这些连接,使平台能够通过相关推荐增强用户体验。

图的基础:顶点(或节点)

这些是图中的个体实体或数据点。想象一下你 Facebook 列表中的每个朋友都是一个独立的顶点:

  • 边(或链接):顶点之间的连接或关系。当你在 Facebook 上添加朋友时,你的顶点和他们的顶点之间形成一条边。

  • 网络:由顶点和边的互联网形成的更大结构。例如,整个 Facebook,以及其所有用户及其友谊,可以被视为一个巨大的网络。

图 5.1 中,ABC 表示顶点,而连接它们的线条则是边。这是一个图的简单表示,为我们将要探索的更复杂的结构和操作奠定了基础。

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_01.png

图 5.1:简单图的图形表示

图论和网络分析

图论和网络分析虽然交织在一起,但在理解复杂系统方面具有不同的功能。图论是离散数学的一个分支,提供节点(实体)和边(关系)的基本概念,而网络分析则是将这些原理应用于研究和解释现实世界网络的应用。例如,图论可以定义社交媒体平台的结构,其中个体是节点,而友谊是边;相反,网络分析则深入研究这种结构,揭示模式,如影响者中心或孤立社区,从而提供关于用户行为和平台动态的可操作见解。

我们将首先看看如何在数学上和视觉上表示这些图。然后,我们将利用一组被称为“图算法”的关键工具对这些表示进行网络分析。

图的表示方式

图是一种用顶点和边表示数据的结构。一个图被表示为 a[Graph] = (https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_001.png, https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_002.png),其中 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_003.png 表示顶点集合,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_004.png 表示边集合。请注意 a[Graph] 有 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_005.png 个顶点和 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_006.png 条边。需要注意的是,除非另有说明,边可以是双向的,表示连接的顶点之间存在双向关系。

一个顶点,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_007.png,代表一个现实世界的对象,如一个人、一台计算机或一个活动。一个边,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_008.png,连接网络中的两个顶点:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_009.png

上述方程表示,在图中,所有的边属于一个集合,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_004.png,所有的顶点属于另一个集合,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_001.png。请注意,符号‘|’表示某个元素属于特定的集合,从而确保了边、顶点及其相应集合之间关系的清晰性。

一个顶点象征着像个人或计算机等有形实体,而一条边,连接两个顶点,表示它们之间的关系。这种关系可以是个体之间的友谊、在线连接、设备之间的物理连接,或者是像参加会议这样的参与性连接。

图的机制与类型

图有多种类型,每种图都有其独特的属性:

  • 简单图:没有平行边或自环的图。

  • 有向图(DiGraph):每条边都有方向,表示单向关系的图。

  • 无向图:一类边没有特定方向,表示相互关系的图。

  • 加权图:一类每条边都有一个权重的图,通常代表距离、成本等。

在本章中,我们将使用 networkx Python 包来表示图。可以从networkx.org/下载。让我们尝试使用 Python 中的 networkx 包来创建一个简单的图。所谓“简单图”,正如图论中所提到的,是指没有平行边或自环的图。首先,让我们尝试创建一个没有顶点或节点的空图,aGraph

import networkx as nx
graph = nx.Graph() 

让我们添加一个单独的顶点:

graph.add_node("Mike") 

我们还可以通过列表添加一系列顶点:

graph.add_nodes_from(["Amine", "Wassim", "Nick"]) 

我们还可以在现有顶点之间添加一条边,如下所示:

graph.add_edge("Mike", "Amine") 

现在让我们打印边和顶点:

print(graph.nodes())
print(graph.edges()) 
['Mike', 'Amine', 'Wassim', 'Nick']
[('Mike', 'Amine')] 

请注意,如果我们添加一条边,这也会导致添加相关的顶点,如果这些顶点尚不存在,如下所示:

G.add_edge("Amine", "Imran") 

如果我们打印节点列表,观察到的输出如下:

print(graph.edges()) 
[('Mike', 'Amine'), ('Amine', 'Imran')] 

请注意,添加已存在的顶点的请求会被默默忽略。请求会根据我们创建的图的类型被忽略或考虑。

自我中心化网络

许多网络分析的核心概念是自我中心化网络,或简而言之,称为自我网络。想象一下,你不仅想研究一个单独的节点,还想研究它的周围环境。这时,自我网络就派上用场了。

自我网络的基础

对于一个给定的顶点——我们称其为 m——与 m 直接连接的周围节点构成了 m 的直接邻域。这个邻域,加上 m 本身,就构成了 m 的自我网络。在这个语境下:

  • m 被称为 自我

  • 直接连接的节点被称为 一跳邻居 或简单称为 替代节点

一跳、二跳及更远的节点

当我们说“单跳邻居”时,我们指的是与我们关注的节点直接相连的节点。可以把它看作是从一个节点到下一个节点的一步或“一跳”。如果我们考虑两个跳跃之远的节点,那么它们被称为“二跳邻居”,以此类推。这种命名法可以扩展到任意数量的跳跃,帮助我们理解n度邻域。

特定节点3的自我网络如下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_02.png

图 5.2:节点 3 的自我网络,展示了自我和它的单跳邻居

自我网络的应用

自我网络在社会网络分析中被广泛使用。它们对于理解大规模网络中的局部结构至关重要,并能基于个体的即时网络环境提供关于个体行为的见解。

例如,在在线社交平台中,自我网络可以帮助检测有影响力的节点,或理解信息在局部网络区域内的传播模式。

引入网络分析理论

网络分析使我们能够深入研究互联的数据,并将其呈现为网络形式。它涉及研究和应用方法论,以检查这种网络格式中排列的数据。在这里,我们将分解与网络分析相关的核心要素和概念。

网络的核心是“顶点”,它作为基本单元存在。可以把网络想象成一个网状结构;顶点是这个网的节点,而连接它们的边表示不同实体之间的关系。值得注意的是,两个顶点之间可能存在不同的关系,这意味着边可以被标记为表示不同类型的关系。比如,假设有两个人,他们可以通过“朋友”或“同事”关系相连;这两者都是不同的关系,但连接的是同样的个体。

为了充分利用网络分析的潜力,至关重要的是评估顶点在网络中的重要性,尤其是针对具体问题。存在多种技术可以帮助我们确认这一重要性。

让我们来看一下网络分析理论中一些重要的概念。

理解最短路径

在图论中,“路径”被定义为一系列节点,连接起始节点和结束节点,且不经过任何中间节点的重复。简而言之,路径描绘了两者之间的路线。这条路径的“长度”是通过计算其中包含的边数来确定的。在两个节点之间的各种路径中,包含边数最少的路径被称为“最短路径”。

确定最短路径是许多图算法中的一项基本任务。然而,这一任务并非总是简单明了的。随着时间的推移,已经开发了多种算法来解决这个问题,其中最著名的之一就是 20 世纪 50 年代末提出的迪杰斯特拉算法。该算法旨在找出图中的最短距离,并已广泛应用于 GPS 设备等领域,依赖它来推算两个点之间的最短距离。在网络路由领域,迪杰斯特拉方法再次证明了它的宝贵价值。

像谷歌和苹果这样的科技公司正在进行一场持续的竞争,特别是在增强它们的地图服务方面。目标不仅是识别最短路线,还要快速完成,通常在几秒钟内。

在本章的后面,我们将探讨广度优先搜索BFS)算法,这是一种可以作为迪杰斯特拉算法基础的方法。标准 BFS 假设图中任何路径的遍历成本相等。然而,迪杰斯特拉算法考虑了不同的遍历成本。为了将 BFS 转化为迪杰斯特拉算法,我们需要整合这些不同的遍历成本。

最后,尽管迪杰斯特拉算法侧重于从单一源点到所有其他顶点的最短路径识别,但如果目标是确定图中每对顶点之间的最短路径,则弗洛伊德-沃尔沙尔算法更为适合。

创建邻域

在深入研究图算法时,“邻域”这一术语经常出现。那么,在这种情况下,我们所说的“邻域”是什么意思呢?可以把它想象成围绕某个特定节点的紧密社区。这个“社区”由那些直接连接或与焦点节点密切相关的节点组成。

作为类比,想象一个城市地图,其中地标代表节点。位于某一显著地点附近的地标形成其“邻域”。

划分这些邻域的广泛采用的方法是通过k-阶策略。在这里,我们通过确定距离节点k跳远的顶点来界定一个节点的邻域。为了更直观的理解,当k=1时,邻域包含所有与焦点节点直接相连的节点。对于k=2,它扩展到包括那些与这些直接邻居相连的节点,模式继续下去。

想象一个圆圈中的中心点作为我们的目标顶点。当k=1时,任何与这个中心点直接连接的点就是它的邻居。随着k的增加,圆圈的半径增大,包含了距离更远的点。

利用和解释邻域对于图算法来说是重要的,因为它帮助识别关键的分析区域。

让我们看一下创建邻域的各种标准:

  1. 三角形

  2. 密度

让我们更详细地探讨这些内容。

三角形

在广阔的图论世界中,识别那些具有强大互联关系的顶点可以揭示重要的洞察。经典的方法是寻找三角形——即三个节点彼此之间保持直接连接的子图。

我们通过一个实际的应用案例——欺诈检测来探索这个问题,接下来的案例研究中将对此进行更详细的分析。想象一个相互连接的网络——一个“自我网络”——围绕一个中心人物展开——我们称他为马克斯。在这个自我网络中,除了马克斯之外,还有两个人,爱丽丝和鲍勃。现在,这三人形成了一个“三角形”——马克斯是我们的核心人物(或“自我”),而爱丽丝和鲍勃是次要人物(或“他者”)。

这时事情变得有趣了:如果爱丽丝和鲍勃有过欺诈活动的记录,那么这就引起了对马克斯可信度的质疑。这就像发现你的两个亲密朋友曾参与可疑的行为——自然会让你处于审视之下。然而,如果其中只有一个人有可疑的过去,那么马克斯的情况就变得模糊了。我们不能直接给他贴上标签,而需要更深入的调查。

为了更直观地理解,想象马克斯处于一个三角形的中心,爱丽丝和鲍勃位于另外两个顶点。它们之间的相互关系,特别是如果带有负面含义时,可以影响人们对马克斯诚信的看法。

密度

在图论中,密度是衡量网络紧密程度的一个指标。具体来说,它是图中实际存在的边数与最大可能的边数之比。在数学上,对于一个简单的无向图,密度定义为:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_012.png

为了更好地理解这一点,让我们来看一个例子:

假设我们是一个有五个成员的书籍俱乐部成员:爱丽丝、鲍勃、查理、戴夫和伊芙。如果每个成员都认识并与其他每个成员都有互动,那么他们之间将总共有 10 条连接(或边)(爱丽丝-鲍勃、爱丽丝-查理、爱丽丝-戴夫、爱丽丝-伊芙、鲍勃-查理,等等)。在这种情况下,最大可能的连接数或边数是 10。如果这些连接都存在,那么密度就是:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_013.png

这表明一个完全密集或完全连接的网络。

然而,假设爱丽丝只认识鲍勃和查理,鲍勃认识爱丽丝和戴夫,查理只认识爱丽丝。戴夫和伊芙虽然是成员,但尚未与任何人互动。在这种情况下,实际上只有三条连接:爱丽丝-鲍勃、爱丽丝-查理和鲍勃-戴夫。让我们来计算密度:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_014.png

这个值小于 1,表明书籍俱乐部的互动网络并没有完全连接;许多潜在的互动(边)尚未发生。

从本质上讲,接近 1 的密度表示网络连接紧密,而接近 0 的值则表明互动稀疏。理解密度有助于多种场景,从分析社交网络到优化基础设施规划,通过评估系统元素的互联程度。

理解中心性度量

中心性度量提供了一个了解图中各个节点重要性的窗口。可以将中心性看作是识别网络中关键角色或枢纽的工具。例如,在社交环境中,它有助于确定具有影响力的人物或核心人物。在城市规划中,中心性可能表明在交通流动或可达性方面发挥关键作用的建筑物或交汇点。理解中心性至关重要,因为它揭示了网络中对功能、凝聚力或影响力至关重要的节点。

在图分析中最常用的中心性指标包括:

  • 度数:反映一个节点的直接连接。

  • 介数:表示一个节点在两其他节点之间的最短路径上充当桥梁的频率。

  • 紧密度:表示一个节点距离网络中所有其他节点的远近。

  • 特征向量:根据节点连接的质量而非数量来衡量节点的影响力。

注意,中心性度量适用于所有图。正如我们所知,图是对象(顶点或节点)及其关系(边)的通用表示形式,中心性度量有助于识别这些节点在图中的重要性或影响力。回想一下,网络是图的特定实现或应用,通常表示像社交网络、交通系统或通信网络这样的现实世界系统。因此,虽然讨论的中心性度量可以广泛应用于所有类型的图,但它们常常在网络的上下文中得到强调,因为它们在理解和优化现实世界系统中具有实际意义。

让我们更深入地探讨这些指标,以更好地理解它们的实用性和细微差别。

度数

连接到特定顶点的边的数量称为其度数。它可以表示一个特定顶点的连接程度以及其在网络中快速传播消息的能力。

让我们考虑 a[图] = (https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_001.png, https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_016.png),其中 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_017.png 代表一组顶点,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_016.png 代表一组边。回想一下,a[图] 有 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_019.png 个顶点和 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_020.png 条边。如果我们将节点的度数除以 (https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_021.png),这就是度数中心性:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_022.png

现在,让我们看一个具体的例子。考虑以下图:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_03.png

图 5.3:一个示例图,说明了度数和度数中心性的概念

现在,在前面的图中,顶点 C 的度为 4。它的度中心性可以通过以下方式计算:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_023.png

介数

介数中心性是衡量图中顶点重要性的关键指标。在社交媒体背景下,它评估一个人在特定子群体中进行通信的关键角色的可能性。在计算机网络中,当顶点代表计算机时,介数可以提供关于如果某个计算机(或顶点)发生故障时,它对节点间通信的潜在影响的见解。

计算顶点 a 的介数,给定 a[图] = (https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_001.png, https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_016.png),按以下步骤进行:

  1. 计算 a[图] 中每对顶点之间的最短路径。我们可以用 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_026.png 来表示。

  2. https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_027.png中,计算通过顶点 a 的最短路径数量。我们可以用 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_028.png 来表示。

  3. 通过以下方式计算介数:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_029.png

公平性与接近度

在图论中,我们通常希望确定一个特定顶点与其他顶点之间的中心性或距离。一种量化此概念的方法是计算一个称为“公平性”的指标。对于给定的顶点“a”和图“g”,公平性通过将顶点“a”到图中每个其他顶点的距离相加来确定。本质上,它让我们了解一个顶点与其邻居之间的“分布”或“远近”。这个概念与中心性密切相关,中心性衡量的是一个顶点与所有其他顶点之间的整体距离。

相反,接近度可以被视为公平性的对立面。虽然直觉上可能认为接近度是顶点与其他顶点距离之和的负值,但从技术上讲并不准确。接近度衡量的是一个顶点与图中所有其他顶点的接近程度,通常通过取其到其他顶点距离之和的倒数来计算。

公平性和接近度是网络分析中至关重要的指标。它们提供了有关信息如何在网络中流动或某个特定节点可能有多大影响力的洞察。通过理解这些指标,可以深入理解网络结构及其潜在的动态。

特征向量中心性

特征向量中心性是一种评估图中节点重要性的指标。它不仅考虑节点的直接连接数,还考虑这些连接的质量。简而言之,如果一个节点与其他在网络中具有重要地位的节点相连接,那么这个节点就被视为重要。

为了给这个问题增加一些数学背景,假设每个节点 v 都有一个中心性得分 x(v)。对于每个节点 v,它的特征向量中心性是基于它的邻居节点的中心性得分之和,并按一个因子进行缩放!(特征向量的相关特征值):

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_031.png

其中 M(v) 表示 v 的邻居节点。

根据节点的邻居加权节点重要性这一思想,为谷歌开发 PageRank 算法时奠定了基础。该算法为互联网上的每个网页分配一个排名,表示其重要性,且深受特征向量中心性概念的影响。

对于有兴趣了解我们即将展示的瞭望塔示例的读者,理解特征向量中心性的本质将为复杂网络分析技术的运作提供更深入的见解。

使用 Python 计算中心性指标

让我们创建一个网络,然后尝试计算它的中心性指标。

1. 建立基础:库和数据

这包括导入必要的库和定义我们的数据:

import networkx as nx
import matplotlib.pyplot as plt 

对于我们的示例,我们考虑了一组顶点和边:

vertices = range(1, 10)
edges = [(7, 2), (2, 3), (7, 4), (4, 5), (7, 3), (7, 5), (1, 6), (1, 7), (2, 8), (2, 9)] 

在这个设置中,顶点代表网络中的单个点或节点。边则表示这些节点之间的关系或链接。

2. 构建图形

基础设置完成后,我们继续构建我们的图形。这涉及将我们的数据(顶点和边)输入到图结构中:

graph = nx.Graph()
graph.add_nodes_from(vertices)
graph.add_edges_from(edges) 

这里,Graph() 函数初始化一个空图。随后的方法 add_nodes_fromadd_edges_from 将我们的定义的节点和边添加到这个图中。

3. 描绘图像:可视化图形

图形表示通常比原始数据更具表达力。可视化不仅有助于理解,还能提供图形整体结构的快照:

nx.draw(graph, with_labels=True, node_color='y', node_size=800)
plt.show() 

这段代码为我们绘制图形。with_labels=True 方法确保每个节点都有标签,node_color 提供不同的颜色,node_size 调整节点大小以提高可读性。

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_04.png

图 5.4:图形的示意表示,展示了节点及其相互关系

一旦我们的图形建立完成,下一步关键是计算并理解每个节点的中心性度量。正如前面所讨论的,中心性度量衡量了节点在网络中的重要性。

  • 度中心性:这个度量值给出了一个特定节点连接的节点的比例。简单来说,如果一个节点具有高的度中心性,它就与图中的许多其他节点相连。nx.degree_centrality(graph) 函数返回一个字典,字典的键是节点,值是它们相应的度中心性:

    print("Degree Centrality:", nx.degree_centrality(graph)) 
    
    Degree Centrality: {1: 0.25, 2: 0.5, 3: 0.25, 4: 0.25, 5: 0.25, 6: 0.125, 7: 0.625, 8: 0.125, 9: 0.125} 
    
  • 介数中心性:这一度量表示通过特定节点的最短路径数量。具有高介数中心性的节点可以视为图中不同部分之间的“桥梁”或“瓶颈”。函数nx.betweenness_centrality(graph)可以为每个节点计算这一度量:

    print("Betweenness Centrality:", nx.betweenness_centrality(graph)) 
    
    Betweenness Centrality: {1: 0.25, 2: 0.46428571428571425, 3: 0.0, 4: 0.0, 5: 0.0, 6: 0.0, 7: 0.7142857142857142, 8: 0.0, 9: 0.0} 
    
  • 接近中心性:这表示一个节点与图中所有其他节点的接近程度。具有高接近中心性的节点能够迅速与所有其他节点进行互动,因此位于图的中心。这个衡量标准通过nx.closeness_centrality(graph)计算:

    print("Closeness Centrality:", nx.closeness_centrality(graph)) 
    
    Closeness Centrality: {1: 0.5, 2: 0.6153846153846154, 3: 0.5333333333333333, 4: 0.47058823529411764, 5: 0.47058823529411764, 6: 0.34782608695652173, 7: 0.7272727272727273, 8: 0.4, 9: 0.4} 
    
  • 特征向量中心性:与度中心性通过计算直接连接的数量不同,特征向量中心性考虑了这些连接的质量或强度。与其他高得分节点连接的节点会得到加权,从而成为影响力节点的衡量标准。为了便于解释,我们进一步对这些中心性值进行排序:

    eigenvector_centrality = nx.eigenvector_centrality(graph)
    sorted_centrality = sorted((vertex, '{:0.2f}'.format(centrality_val)) 
                               for vertex, centrality_val in eigenvector_centrality.items())
    print("Eigenvector Centrality:", sorted_centrality) 
    
    Eigenvector Centrality: [(1, '0.24'), (2, '0.45'), (3, '0.36'), (4, '0.32'), (5, '0.32'), (6, '0.08'), (7, '0.59'), (8, '0.16'), (9, '0.16')] 
    

请注意,中心性度量值预计将给出图或子图中特定顶点的中心性度量。通过查看图形,标记为7的顶点似乎处于最中心的位置。顶点 7 在所有四个中心性度量中具有最高的值,因此反映了它在此上下文中的重要性。

现在让我们来看一下如何从图中获取信息。图是复杂的数据结构,包含了存储在顶点和边中的大量信息。我们将探讨一些有效的策略,帮助我们高效地在图中导航,以便收集信息来回答查询。

社会网络分析

社会网络分析SNA)作为图论中的一个重要应用脱颖而出。其核心是,当分析符合以下标准时,就可以被认为是社会网络分析:

  • 图中的顶点代表个体。

  • 边表示这些个体之间的社会连接,包括友谊、共同兴趣、家庭纽带、意见差异等。

  • 图分析的主要目标是理解显著的社会背景。

社会网络分析(SNA)一个有趣的方面是它能揭示与犯罪行为相关的模式。通过绘制关系和互动,可以识别出可能表明欺诈行为或异常活动的模式。例如,分析连接模式可能揭示出在特定地点存在不寻常的连接或频繁的互动,暗示潜在的犯罪热点或网络。

LinkedIn 在社会网络分析(SNA)相关新技术的研究和发展中做出了巨大贡献。实际上,LinkedIn 可以被看作是该领域许多算法的开创者。

因此,SNA(社会网络分析)——由于其固有的分布式和互联架构——是图论最强大的应用之一。抽象图形的另一种方式是将其视为一个网络,并应用针对网络设计的算法。这个领域被称为网络分析理论,我们接下来将讨论它。

理解图遍历

为了利用图形,必须从中挖掘信息。图遍历被定义为一种策略,用来确保每个顶点和边都按顺序访问。我们的目标是确保每个顶点和边都被访问一次且仅一次——既不多也不少。大致上,遍历图形搜索其中数据的方式可以有两种不同的方式。

在本章前面我们学到,通过宽度进行遍历叫做广度优先搜索BFS)——通过深度进行遍历叫做深度优先搜索DFS)。让我们逐一了解它们。

BFS

当我们处理的图形(a)具有邻居层级或级别的概念时,BFS 效果最佳。例如,当一个人在 LinkedIn 上的连接以图的形式表示时,首先是一级连接,然后是二级连接,这直接转化为层次。

BFS 算法从根顶点开始,探索邻域中的顶点。然后它移动到下一个邻域层级并重复这个过程。

让我们看一下 BFS 算法。首先,考虑以下无向图:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_05.png

图 5.5:展示个人连接的无向图

构建邻接表

在 Python 中,字典数据结构非常适合表示图的邻接表。下面是我们如何定义一个无向图:

graph={ 'Amin'   : {'Wasim', 'Nick', 'Mike'},
         'Wasim' : {'Imran', 'Amin'},
         'Imran' : {'Wasim','Faras'},
         'Faras' : {'Imran'},
         'Mike'  : {'Amin'},
         'Nick' :  {'Amin'}} 

为了在 Python 中实现它,我们将按如下步骤进行。

我们将首先解释初始化,然后是主循环。

BFS 算法实现

算法实现将涉及两个主要阶段:初始化和主循环。

初始化

我们遍历图形的过程依赖于两个关键的数据结构:

  • visited:一个集合,用于存储我们已探索的所有顶点。它开始时为空。

  • queue:一个列表,用来存储待探索的顶点。最初,它只包含我们的起始顶点。

主循环

BFS 的主要逻辑围绕着逐层探索节点展开:

  1. 从队列中移除第一个节点,并将其视为当前迭代的节点:

    node = queue.pop(0) 
    
  2. 如果节点没有被访问过,标记为已访问并获取其邻居:

    if node not in visited:
        visited.add(node)
        neighbours = graph[node] 
    
  3. 将未访问的邻居添加到队列中:

    for neighbour in neighbours:
        if neighbour not in visited:
            queue.append(neighbour) 
    
  4. 一旦主循环完成,返回visited数据结构,它包含了所有已遍历的节点。

完整的 BFS 代码实现

完整的代码,包括初始化和主循环,将如下所示:

def bfs(graph, start):
    visited = set()
    queue = [start]
    while queue:
        node = queue.pop(0)
        if node not in visited:
            visited.add(node)
            neighbours = graph[node]
            unvisited_neighbours = [neighbour for neighbour in neighbours                                     if neighbour not in visited]
            queue.extend(unvisited_neighbours)
   return visited 

BFS 遍历机制如下:

  1. 过程从第一层开始,由节点“Amin”表示。

  2. 它接着扩展到第二层,访问“Wasim”、“Nick”和“Mike”。

  3. 随后,BFS 深入到第三层和第四层,分别访问“Imran”和“Faras”。

当 BFS 完成遍历时,所有节点都已记录在已访问集合中,队列为空。

使用 BFS 进行特定搜索

为了更好地理解 BFS 的实际操作,我们将使用已实现的函数来查找图中到特定人的路径:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_06.png

图 5.6:使用 BFS 对图进行层级遍历

现在,让我们尝试使用 BFS 从这个图中找到某个特定的人。我们来指定要搜索的数据,并观察结果:

start_node = 'Amin'
print(bfs(graph, start_node)) 
{'Faras', 'Nick', 'Wasim', 'Imran', 'Amin', 'Mike'} 

这表示 BFS 从Amin开始时访问的节点顺序。

现在让我们来看一下 DFS 算法。

DFS

DFS 提供了与 BFS 不同的图遍历方式。BFS 按层级逐层探索图,首先关注邻近的节点,而 DFS 则尽可能深入某条路径,直到回溯再继续探索其他路径。

想象一棵树。从根部开始,DFS 会深入到某个分支的最远叶子节点,标记沿着该分支的所有节点为已访问,然后回溯以类似的方式探索其他分支。这个思想是先到达给定分支上的最远叶节点,再考虑其他分支。“叶子”是指树中没有任何子节点的节点,或者在图的上下文中,指没有被访问的相邻节点。

为了确保遍历不会陷入死循环,特别是在有环图中,DFS 使用了一个布尔标志。该标志表示某个节点是否已经被访问,从而防止算法重新访问已访问的节点,避免陷入无限循环。

为了实现 DFS,我们将使用栈数据结构,这在第二章中已经详细讨论过,算法中的数据结构。记住,栈遵循后进先出LIFO)原则。这与队列不同,队列用于 BFS 时遵循先进先出FIFO)原则:

以下代码用于 DFS:

def dfs(graph, start, visited=None):
    if visited is None:
        visited = set()
    visited.add(start)
    print(start)
    for next in graph[start] - visited:
        dfs(graph, next, visited)
    return visited 

让我们再次使用以下代码来测试先前定义的dfs函数:

graph={ 'Amin' : {'Wasim', 'Nick', 'Mike'},
         'Wasim' : {'Imran', 'Amin'},
         'Imran' : {'Wasim','Faras'},
         'Faras' : {'Imran'},
         'Mike'  :{'Amin'},
         'Nick'  :{'Amin'}} 

如果我们运行此算法,输出将如下所示:

Amin
Wasim
Imran
Faras
Nick
Mike 

让我们来看一下使用 DFS 方法遍历这个图的完整模式:

  1. 迭代从顶层节点 Amin 开始。

  2. 然后,它移动到第二层节点 Wasim。从那里,它继续向下,直到到达末尾,即 Imran 和 Fares 节点。

  3. 完成第一个完整的分支后,它回溯并转到第二层,访问 Nick 和 Mike。

遍历模式如图 5.7所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_07.png

图 5.7:DFS 遍历的可视化表示

注意,DFS 也可以在树中使用。

现在,让我们看一个案例研究,解释我们到目前为止在本章中讨论的概念如何应用于解决现实世界的问题。

案例研究:使用社会网络分析(SNA)进行欺诈检测

介绍

人类天生具有社交性,他们的行为通常反映了他们的社交圈。在欺诈分析领域,一个叫做“同质性”的原则表示个体之间可能会基于共同的属性或行为而建立联系。例如,一个同质网络可能由来自同一故乡、同一大学或有共同爱好的人组成。其基本原则是,个体的行为,包括欺诈行为,可能会受到他们直接联系的影响。这也有时被称为“通过联系而定罪”。

在此背景下,什么是欺诈?

在本案例研究的背景下,欺诈是指可能包括冒充、信用卡盗窃、伪造支票提交或任何其他可以在关系网络中表示和分析的非法活动。为了理解这个过程,首先让我们看一个简单的案例。为此,我们将使用一个包含九个顶点和八条边的网络。在这个网络中,四个顶点是已知的欺诈案例,并被分类为欺诈F)。其余五个人没有欺诈相关的历史,分类为非欺诈NF)。

我们将通过以下步骤编写代码来生成这个图形:

  1. 让我们导入所需的包:

    import networkx as nx
    import matplotlib.pyplot as plt 
    
  2. 定义顶点的数据结构:

    vertices = range(1,10)
    edges= [(7,2), (2,3), (7,4), (4,5), (7,3), (7,5), (1,6),(1,7),(2,8),(2,9)] 
    
  3. 实例化图形:

    graph = nx.Graph() 
    
  4. 现在,绘制图形:

    graph.add_nodes_from(vertices)
    graph.add_edges_from(edges)
    positions = nx.spring_layout(graph) 
    
  5. 让我们定义 NF 节点:

    nx.draw_networkx_nodes(graph, positions, 
                           nodelist=[1, 4, 3, 8, 9], 
                           with_labels=True, 
                           node_color='g', 
                           node_size=1300) 
    
  6. 现在,让我们创建已知涉及欺诈的节点:

    nx.draw_networkx_nodes(graph, positions, 
                           nodelist=[1, 4, 3, 8, 9], 
                           with_labels=True, 
                           node_color='g', 
                           node_size=1300) 
    
  7. 最后,为节点创建标签:

    labels = {1: '1 NF', 2: '2 F', 3: '3 NF', 4: '4 NF', 5: '5 F', 6: '6 F', 7: '7 F', 8: '8 NF', 9: '9 NF'}
    nx.draw_networkx_labels(graph, positions, labels, font_size=16)
    nx.draw_networkx_edges(graph, positions, edges, width=3, alpha=0.5, edge_color='b')
    plt.show() 
    

一旦上述代码运行,它将显示如下图形:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_08.png

图 5.8:初始网络表示,展示了欺诈节点和非欺诈节点

请注意,我们已经进行过详细的分析,将每个节点分类为图形节点或非图形节点。假设我们将添加一个新的顶点,命名为q,如下面的图所示。我们没有关于这个人是否涉及欺诈的先前信息。我们希望根据这个人与社交网络中现有成员的联系,将此人分类为 NF 或 F:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_09.png

图 5.9:向现有网络引入新节点

我们设计了两种方法来将这个新加入的人(由节点q表示)分类为 F 或 NF:

  • 使用一个简单的方法,不使用中心性度量和关于欺诈类型的附加信息

  • 使用一种瞭望塔方法,这是一种高级技术,利用现有节点的中心性度量,以及关于欺诈类型的附加信息

我们将详细讨论每种方法。

进行简单的欺诈分析

简单的欺诈分析技术基于这样一个假设:在一个网络中,个人的行为受到他们所连接的人影响。在一个网络中,两个顶点如果相互连接,它们更可能表现出相似的行为。

基于这个假设,我们将设计一个简单的技术。如果我们想找出某个特定节点a属于F的概率,该概率表示为P(F/q),其计算方法如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_032.png

让我们将其应用到前面的图中,其中Neighborhood[n]表示顶点n的邻域,w(n, n[j])表示nn[j]之间连接的权重。另外,DOS[normalized]是怀疑程度的标准化值,介于 0 和 1 之间。最后,degree[q]是节点q的度数。

概率的计算方法如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_033.png

基于这一分析,这个人涉及欺诈的可能性为 67%。我们需要设置一个阈值。如果阈值是 30%,那么此人超过了阈值,我们可以放心地将其标记为F

请注意,这一过程需要对网络中的每个新节点重复进行。

现在,让我们看看进行欺诈分析的高级方法。

展示瞭望塔欺诈分析方法

之前的简单欺诈分析技术有以下两个局限性:

  • 它并未评估社交网络中每个顶点的重要性。与涉及欺诈的中心节点的连接可能比与一个远离的孤立个体的关系具有不同的含义。

  • 在将某人标记为已知的欺诈案例时,我们不考虑犯罪的严重性。

瞭望塔欺诈分析方法解决了这两个局限性。首先,让我们看一下几个概念。

负面结果评分

如果已知某人涉及欺诈,我们称该人有一个负面结果。并非每个负面结果的严重性相同。已知冒充他人的人,其负面结果会比仅仅尝试用过期的$20 礼品卡以某种创意方式使其有效的人更为严重。

从 1 到 10 的评分中,我们将不同的负面结果评分如下:

负面结果负面结果评分
冒充身份10
参与信用卡盗窃8
虚假支票提交7
犯罪记录6
无记录0

请注意,这些分数将基于我们对欺诈案件及其对历史数据的影响的分析。

怀疑程度

怀疑程度DOS)量化了我们对某人可能涉及欺诈的怀疑程度。DOS 值为 0 表示该人风险较低,DOS 值为 9 表示该人风险较高。

历史数据分析表明,职业诈骗犯在其社交网络中占有重要位置。为了考虑这一点,我们首先计算网络中每个节点的四个中心性指标。然后,我们取这些节点的平均值。这反映了该特定个体在网络中的重要性。

如果与某个节点关联的人涉及诈骗,我们通过使用前述表格中预设的值对该人进行评分,从而展示该负面结果。这是为了确保犯罪的严重性体现在每个个体的 DOS 值中。

最后,我们将中心性指标的平均值与负面结果分数相乘,以获得 DOS 值。然后,通过将其除以网络中最大 DOS 值来标准化 DOS。

让我们计算前述网络中九个节点的 DOS:

节点 1节点 2节点 3节点 4节点 5节点 6节点 7节点 8节点 9
中心度0.250.50.250.250.250.130.630.130.13
介数中心性0.250.4700000.7100
接近度0.50.610.530.470.470.340.720.40.4
特征向量0.240.450.360.320.320.080.590.160.16
中心性指标的平均值0.310.510.290.260.260.140.660.170.17
负面结果分数0600781000
DOS03001.821.16.62500
标准化 DOS00.47000.270.17100

每个节点及其标准化的 DOS 在下图中展示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_10.png

图 5.10:展示具有计算 DOS 值的节点

为了计算新增节点的 DOS,我们将使用以下公式:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_034.png

使用相关值,我们将按如下方式计算 DOS:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_05_035.png

这将显示与该新节点相关的欺诈风险。这意味着,在 0 到 1 的范围内,这个人的 DOS 值为0.42。我们可以为 DOS 创建不同的风险区间,如下所示:

DOS 值风险分类
DOS = 0无风险
0<DOS<=0.10低风险
0.10<DOS<=0.3中等风险
DOS>0.3高风险

根据这些标准,可以看出新的个体是一个高风险人员,应当标记为高风险。

通常,在进行此类分析时不涉及时间维度。但现在有一些先进的技术可以随着时间的推移观察图的增长。这使得研究人员可以查看随着网络演化而变化的顶点关系。尽管对图的时间序列分析会大大增加问题的复杂性,但它可能提供额外的欺诈证据,这在其他情况下是无法获得的。

摘要

在这一章中,我们学习了基于图的算法。本章使用了不同的技术来表示、搜索和处理表示为图的数据。我们还培养了计算两个顶点之间最短距离的技能,并在问题空间中构建了邻域。这些知识将帮助我们使用图论来解决诸如欺诈检测等问题。

在下一章中,我们将重点介绍不同的无监督机器学习算法。本章讨论的许多应用案例技术与无监督学习算法相辅相成,这些内容将在下一章中详细讨论。在数据集中找到欺诈证据就是这样的应用案例之一。

在 Discord 上了解更多

要加入本书的 Discord 社区——在这里你可以分享反馈、向作者提问并了解新版本——请扫描下面的二维码:

packt.link/WHLel

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/QR_Code1955211820597889031.png

第二部分

机器学习算法

本节详细介绍了不同种类的机器学习算法,如无监督机器学习算法和传统的监督学习算法,并且还介绍了自然语言处理算法。本节包含的章节有:

  • 第六章无监督机器学习算法

  • 第七章传统监督学习算法

  • 第八章神经网络算法

  • 第九章自然语言处理算法

  • 第十章理解序列模型

  • 第十一章高级序列建模算法

第六章:无监督机器学习算法

本章讲解的是无监督机器学习算法。我们的目标是在本章结束时,能够理解无监督学习及其基本算法和方法如何有效应用于解决现实世界中的问题。

我们将涵盖以下主题:

  • 介绍无监督学习

  • 理解聚类算法

  • 降维

  • 关联规则挖掘

介绍无监督学习

如果数据不是随机生成的,它往往在多维空间中表现出某些元素之间的模式或关系。无监督学习涉及在数据集中检测并利用这些模式,以便更有效地对其进行结构化和理解。无监督学习算法揭示这些模式,并利用它们作为赋予数据集特定结构的基础。识别这些模式有助于更深入地理解和呈现数据。从原始数据中提取模式有助于更好地理解原始数据。

该概念如图 6.1所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_01.png

图 6.1:使用无监督机器学习从未标记的原始数据中提取模式

在接下来的讨论中,我们将穿越 CRISP-DM 生命周期,这是一种流行的机器学习过程模型。在这个背景下,我们将确定无监督学习适合的位置。举例来说,可以将无监督学习比作侦探在没有预设知识的情况下,通过拼凑线索来形成模式或群组。就像侦探的洞察力对破案至关重要一样,无监督学习在机器学习生命周期中也扮演着关键角色。

无监督学习在数据挖掘生命周期中的作用

首先让我们了解一下典型机器学习过程的不同阶段。为了理解机器学习生命周期的不同阶段,我们将通过使用机器学习进行数据挖掘的例子来学习。数据挖掘是从给定数据集中发现有意义的相关性、模式和趋势的过程。为了讨论使用机器学习进行数据挖掘的不同阶段,本书采用了跨行业数据挖掘标准流程CRISP-DM)。CRISP-DM 由来自不同组织的数据挖掘专家们设计并实现,包括克莱斯勒和 IBM 等知名公司。更多详情请参考www.ibm.com/docs/en/spss-modeler/saas?topic=dm-crisp-help-overview

CRISP-DM 生命周期包含六个不同的阶段,见下图:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_02.png

图 6.2:CRISP-DM 生命周期的不同阶段

让我们逐一解析并探索每个阶段。

阶段 1:业务理解

本阶段的重点是收集需求,并尝试从业务角度深入理解问题。定义问题的范围并根据机器学习适当重新表述它是这一阶段的重要部分。本阶段包括确定目标、定义项目的范围,并理解利益相关者的需求。

需要注意的是,CRISP-DM 生命周期的第一阶段是关于业务理解。它关注的是需要做什么,而不是如何做。

阶段 2:数据理解

本阶段的重点是理解可用于数据挖掘的数据。在此阶段,我们将了解给定的数据集中是否拥有解决第一阶段定义的问题所需的所有信息。我们可以使用数据可视化、仪表板和汇总报告等工具来理解数据中的模式。如本章后面所述,无监督机器学习算法也可以用于发现数据中的模式,并通过详细分析其结构来理解这些模式。

阶段 3:数据准备

这涉及为我们将在阶段 4 中训练的机器学习模型准备数据。根据使用案例和需求,数据准备可能包括去除异常值、归一化、去除空值以及减少数据的维度。后续章节会更详细地讨论这些内容。数据处理和准备好后,通常会以 70-30 的比例进行分割。较大的一部分,称为训练数据,用于训练模型识别各种模式,而较小的一部分,称为测试数据,则保存以评估模型在阶段 5 中对未见数据的表现。还可以选择保留一部分数据用于验证和微调模型,以防止过拟合。

阶段 4:建模

这是我们通过训练模型来形成数据模式的阶段。对于模型训练,我们将使用在阶段 3 中准备的训练数据分区。模型训练包括将我们准备好的数据输入机器学习算法。通过迭代学习,算法识别并学习数据中固有的模式。目标是形成表示数据集中不同变量之间关系和依赖性的模式。我们将在后续章节中讨论这些数学公式的复杂性和性质如何在很大程度上依赖于我们选择的算法——例如,线性回归模型将生成一个线性方程,而决策树模型将构建一个类似树的决策模型。

除了模型训练,模型调优也是 CRISP-DM 生命周期这一阶段的一个组成部分。该过程包括优化学习算法的参数,以提高其性能,从而使预测更为准确。它涉及使用可选的验证集对模型进行微调,帮助调整模型的复杂性,找到从数据中学习与对未见数据进行概括之间的平衡。在机器学习中,验证集是数据集的一部分,用于对预测模型进行精细调整。

它有助于调整模型的复杂性,旨在找到已知数据学习与对未见数据进行概括之间的最佳平衡。这种平衡对于防止过拟合至关重要,过拟合是指模型过于“记住”训练数据,从而在新数据上表现不佳。因此,模型调优不仅可以提升模型的预测能力,还可以确保其稳健性和可靠性。

阶段 5:评估

这一阶段涉及通过使用来自阶段 3 的测试数据来评估最近训练的模型。我们将模型的性能与阶段 1 中设定的基准进行对比。基准在机器学习中的作用是提供一个参考点,可以通过多种方法来确定。它可以通过基本的基于规则的系统、简单的统计模型、随机机会,甚至是基于人类专家的表现来建立。基准的目的是提供一个最小的性能门槛,我们的机器学习模型应该超越这个门槛。基准充当比较的标准,给我们提供一个期望的参考点。如果模型的评估结果与阶段 1 中最初定义的期望一致,我们就可以继续。如果不一致,我们必须重新审视并迭代所有之前的阶段,从阶段 1 开始。

阶段 6:部署

一旦评估阶段(阶段 5)结束,我们就会检查训练好的模型的表现是否达到或超过了既定的期望。需要记住的是,成功的评估并不自动意味着可以部署。模型在我们的测试数据上表现良好,但这并不是判断模型是否准备好解决实际问题的唯一标准,如阶段 1 所定义的那样。我们必须考虑诸如模型在从未见过的新数据上的表现、如何与现有系统集成以及如何处理未预见的边缘情况等因素。因此,只有当这些广泛的评估得到了令人满意的验证时,我们才能自信地将模型部署到生产环境中,在那里它开始为我们预先定义的问题提供可用的解决方案。

CRISP-DM 生命周期的第 2 阶段(数据理解)和第 3 阶段(数据准备)都是关于理解数据并为训练模型做准备。这些阶段涉及数据处理。一些组织为此数据工程阶段配备了专门的人员。

很明显,提出问题解决方案的过程是完全数据驱动的。结合有监督和无监督机器学习来制定可行的解决方案。本章专注于解决方案中的无监督学习部分。

数据工程包括第 2 阶段和第 3 阶段,是机器学习中最耗时的部分。它可能占据典型机器学习ML)项目的 70% 的时间和资源(Data Management in Machine Learning: Challenges, Techniques, and Systems, Cody 等人,SIGMOD ‘17:2017 年 ACM 国际数据管理会议论文集,2017 年 5 月)。无监督学习算法在数据工程中可以发挥重要作用。

以下部分提供了关于无监督算法的更多详细信息。

当前无监督学习的研究趋势

机器学习研究领域经历了显著的变化。早期的重点主要集中在有监督学习技术上。这些方法对推理任务立即有效,提供了明显的优势,如节省时间、降低成本和提高预测准确性。

相反,无监督机器学习算法的内在能力最近才开始引起关注。与有监督的对应方法不同,无监督技术在没有直接指令或先入为主的假设下运作。它们擅长探索数据中更广泛的“维度”或方面,从而使数据集的审查更加全面。

为了澄清,在机器学习术语中,“特征”是被观察现象的个体可度量的属性或特征。例如,在一个涉及客户信息的数据集中,特征可能包括客户的年龄、购买历史或浏览行为。“标签”则代表我们希望模型根据这些特征预测的结果。

虽然有监督学习主要集中于建立这些特征与特定标签之间的关系,但无监督学习并不局限于预先确定的标签。相反,它可以更深入地挖掘,发现各种特征之间的复杂模式,这些模式在使用有监督方法时可能会被忽视。这使得无监督学习在应用中具有更广阔和灵活的潜力。

然而,无监督学习固有的灵活性带来了一个挑战。由于探索空间更大,它往往会导致更高的计算需求,从而带来更高的成本和更长的处理时间。此外,由于其探索性特点,管理无监督学习任务的规模或“范围”可能更为复杂。然而,能够挖掘数据中隐藏的模式或关联,使得无监督学习成为数据驱动洞察的强大工具。

如今,研究趋势正朝着监督学习和无监督学习方法的整合发展。这种结合策略旨在利用两种方法的优势。

现在让我们来看一些实际的例子。

实际示例

目前,无监督学习用于更好地理解数据并为其提供更多结构——例如,它被用于市场细分、数据分类、欺诈检测和市场篮分析(本章稍后讨论)。让我们看一下无监督学习在市场细分中的应用示例。

使用无监督学习进行市场细分

无监督学习作为市场细分的强大工具。市场细分是指根据共享特征将目标市场划分为不同组的过程,使公司能够量身定制其营销策略和信息,有效地接触并吸引特定的客户群体。用于分组目标市场的特征可能包括人口统计、行为或地理相似性。通过利用算法和统计技术,它使企业能够从客户数据中提取有意义的见解,识别隐藏的模式,并根据客户行为、偏好或特征的相似性将其分为不同的群体。这种数据驱动的方法使营销人员能够制定量身定制的策略、提高客户定位能力,并增强整体营销效果。

理解聚类算法

无监督学习中最简单且最强大的技术之一是通过聚类算法将相似的模式组合在一起。它用于理解与我们要解决的问题相关的特定数据方面。聚类算法寻找数据项中的自然分组。由于该分组不基于任何目标或假设,因此它被归类为无监督学习技术。

以一个充满书籍的巨大图书馆为例。每本书代表一个数据点——包含诸如类型、作者、出版年份等多种属性。现在,想象一个图书管理员(聚类算法),他被要求对这些书籍进行分类。在没有预设类别或说明的情况下,图书管理员开始根据书籍的属性对其进行分类——所有的侦探小说放在一起,经典文学放在一起,同一作者的书籍放在一起,等等。这就是我们所说的“自然组”,即那些共享相似特征的数据项被聚集在一起。

各种聚类算法所创建的分组是基于在问题空间中找到各个数据点之间的相似性。请注意,在机器学习的背景下,数据点是存在于多维空间中的一组测量值或观察结果。简单来说,它是帮助机器了解其所要完成任务的单一信息。确定数据点之间相似性的最佳方法因问题而异,且取决于我们所处理问题的性质。让我们看看可以用来计算数据点之间相似性的各种方法。

量化相似性

无监督学习技术,如聚类算法,通过在给定问题空间内确定各种数据点之间的相似性,能够有效工作。这些算法的有效性在很大程度上取决于我们是否能够正确地衡量这些相似性,在机器学习术语中,这些常常被称为“距离度量”。那么,究竟什么是距离度量呢?

本质上,距离度量是一个数学公式或方法,用于计算两个数据点之间的“距离”或相似度。在这个背景下,重要的是要理解,“距离”并不指物理距离,而是基于数据点的特征或属性来衡量相似性或差异性。

在聚类中,我们可以讨论两种主要的距离类型:簇间距离和簇内距离。簇间距离指的是不同簇(或数据点组)之间的距离。与此相对,簇内距离指的是同一簇内(或者说,同一组内)数据点之间的距离。一个好的聚类算法的目标是最大化簇间距离(确保每个簇彼此独立),同时最小化簇内距离(确保同一簇内的数据点尽可能相似)。以下是三种最常用的量化相似性的方法:

  • 欧几里得距离度量

  • 曼哈顿距离度量

  • 余弦距离度量

让我们更详细地了解这些距离度量。

欧几里得距离

不同点之间的距离可以量化两个数据点之间的相似性,并广泛应用于无监督机器学习技术,如聚类。欧几里得距离是最常用且最简单的距离度量。这里所说的“距离”量化的是两个数据点在多维空间中相似或不同的程度,这对于理解数据点的分组至关重要。最简单且最广泛使用的距离度量之一就是欧几里得距离。

欧几里得距离可以被认为是三维空间中两个点之间的直线距离,类似于我们在现实世界中测量距离的方式。例如,考虑地图上的两座城市;欧几里得距离就是这两座城市之间的“鸟飞直线”距离,即从城市 A 到城市 B 的直线距离,忽略了任何可能的障碍物,如山脉或河流。

类似地,在我们数据的多维空间中,欧几里得距离计算的是两个数据点之间最短的“直线”距离。通过这种方式,它提供了一个量化的度量,表示数据点之间的相似性或远近,基于它们的特征或属性。例如,假设我们有两个点,A(1,1)B(4,4),它们位于二维空间中,如下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_03.png

图 6.3:计算两个给定点之间的欧几里得距离

要计算AB之间的距离——即d(A,B),我们可以使用以下勾股定理公式:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_001.png

注意,这个计算适用于二维问题空间。对于n维问题空间,我们可以按照以下方式计算两个点AB之间的距离:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_002.png

曼哈顿距离

在许多情况下,使用欧几里得距离度量两点之间的最短距离并不能真正代表两点之间的相似性或接近性——例如,如果两个数据点代表地图上的位置,那么通过陆地交通工具(如汽车或出租车)从 A 点到 B 点的实际距离将比通过欧几里得距离计算的距离更远。想象一下一个繁忙的城市网格,在这里你不能像欧几里得距离那样穿越建筑物从一个点到另一个点,而是必须在街道网格中导航。曼哈顿距离就像是这种现实世界中的导航——它计算的是沿着这些网格线从点 A 到点 B 所走的总距离。

对于类似的情况,我们使用曼哈顿距离,它估算了在城市街道上沿网格状街道从起点到目的地时两点之间的距离。与欧几里得距离等直线距离度量不同,曼哈顿距离在这种情况下能更准确地反映两个位置之间的实际距离。曼哈顿距离和欧几里得距离度量之间的比较如下面的图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_04.png

图 6.4:计算两个点之间的曼哈顿距离

请注意,在图中,这些点之间的曼哈顿距离表示为严格沿着该图网格线的折线路径。相比之下,欧几里得距离则显示为从 A 点到 B 点的直线。显然,曼哈顿距离总是等于或大于对应的欧几里得距离。

余弦距离

尽管欧几里得和曼哈顿距离度量在简单的低维空间中表现良好,但随着我们进入更加复杂的“高维”环境,它们的效果会减少。“高维”空间指的是包含大量特征或变量的数据集。随着维度(特征)的增加,使用欧几里得和曼哈顿距离计算的距离变得越来越没有意义,且计算负担更加繁重。

为了解决这个问题,我们在高维情况下使用“余弦距离”度量。该度量通过评估由两个数据点与原点连接所形成的角度的余弦来工作。在这里,重要的不是两点之间的物理距离,而是它们形成的角度。

如果数据点在多维空间中距离较近,它们将形成一个较小的角度,无论涉及多少维度。相反,如果数据点之间相距较远,产生的角度会更大。因此,余弦距离在高维数据中提供了一个更细致的相似度度量,帮助我们更好地理解复杂的数据模式:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_05.png

图 6.5:计算余弦距离

文本数据几乎可以被视为一个高维空间。这源于文本数据的独特性质,每个独特的单词可以被视为一个不同的维度或特征。由于余弦距离度量在高维空间中表现得非常好,因此它在处理文本数据时是一个不错的选择。

请注意,在前面的图中,A(2,5)B(4.4)之间的角度余弦就是由图 6.5中的 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_003.png 表示的余弦距离。这些点之间的参考点是原点——即X(0,0)。但实际上,问题空间中的任何点都可以作为参考数据点,不一定非得是原点。

现在,让我们来看看最流行的无监督机器学习技术之一——即 k-means 聚类算法。

k-means 聚类算法

k-means 聚类算法得名于创建“k”个聚类并使用均值或平均值来确定数据点之间的“接近度”的过程。“均值”一词指的是计算每个聚类的质心或“中心点”的方法,质心本质上是聚类内所有数据点的平均值。换句话说,算法会计算聚类内每个特征的均值,从而得出一个新的数据点——质心。然后,这个质心作为衡量其他数据点“接近度”的参考点。

k-means 的流行源于其可扩展性和速度。该算法计算高效,因为它使用一个简单的迭代过程,其中聚类的质心会反复调整,直到它们成为聚类成员的代表。正是这种简单性使得该算法在处理大规模数据集时尤其快速且可扩展。

然而,k-means 算法的一个显著限制是它无法独立地确定最佳的聚类数“k”。理想的“k”依赖于给定数据集中的自然分组。这个限制背后的设计理念是保持算法的简洁和快速,因此假设有一个外部机制来计算“k”。根据问题的上下文,“k”可以直接确定。例如,如果任务是将一组数据科学学生分成两个聚类,一个专注于数据科学技能,另一个专注于编程技能,那么“k”自然为二。然而,对于那些“k”的值不容易显现的问题,可能需要通过试错的迭代过程,或基于启发式的方法,来估算最适合的数据集聚类数。

k-means 聚类的逻辑

在这一部分,我们将深入探讨 k-means 聚类算法的工作原理。我们将逐步拆解它的操作过程,以帮助你清晰地理解其机制和应用。本节描述了 k-means 聚类算法的逻辑。

初始化

为了进行分组,k-means 算法使用距离度量来找出数据点之间的相似性或接近度。在使用 k-means 算法之前,需要选择最合适的距离度量。默认情况下,会使用欧几里得距离度量。然而,根据数据的性质和需求,你可能会发现其他距离度量,如曼哈顿距离或余弦相似度,更为合适。此外,如果数据集存在离群值,则需要设计一个机制来确定哪些标准需要被识别并从数据集中去除离群值。

有多种统计方法可用于异常值检测,如 Z-score 方法或 四分位距 (IQR) 方法。现在让我们来看一下 k-means 算法中涉及的不同步骤。

k-means 算法的步骤

k-means 聚类算法涉及的步骤如下:

第 1 步我们选择簇的数量,k
第 2 步在数据点中,我们随机选择 k 个点作为簇中心。
第 3 步基于选定的距离度量,我们迭代地计算问题空间中每个点到每个 k 个簇中心的距离。根据数据集的大小,这可能是一个耗时的步骤——例如,如果簇中有 10,000 个点,且 k = 3,这意味着需要计算 30,000 个距离。
第 4 步我们将问题空间中的每个数据点分配到最近的簇中心。
第 5 步现在,我们问题空间中的每个数据点都有一个分配的簇中心。但我们还没有完成,因为初始簇中心的选择是基于随机选择的。我们需要验证当前随机选择的簇中心是否实际上是每个簇的质心。我们通过计算每个 k 个簇的组成数据点的均值来重新计算簇中心。这一步解释了为什么这个算法叫做 k-means。
第 6 步如果在第 5 步中簇中心发生了变化,这意味着我们需要重新计算每个数据点的簇分配。为此,我们将回到第 3 步,重复进行计算密集型的步骤。如果簇中心没有发生变化,或者我们预定的停止条件(例如最大迭代次数)已经满足,那么我们就完成了。

以下图显示了在二维问题空间中运行 k-means 算法的结果:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_06.png

图 6.6:k-means 聚类结果 (a) 聚类前的数据点;(b) 运行 k-means 聚类算法后的结果簇

注意,在这种情况下,运行 k-means 后得到的两个簇是明显区分开的。现在我们来看看 k-means 算法的停止条件。

停止条件

在像 k-means 这样的无监督学习算法中,停止条件在确定算法何时应该停止迭代过程中起着至关重要的作用。对于 k-means 算法,默认的停止条件是在第 5 步中簇中心不再发生移动。但是,与许多其他算法一样,k-means 算法可能需要较长的时间才能收敛,尤其是在处理大数据集和高维问题空间时。

我们可以显式地定义停止条件,而不是等待算法收敛,具体如下:

  • 通过指定最大执行时间:

    • 停止条件t>t[max],其中 t 是当前执行时间,t[max] 是我们为算法设置的最大执行时间。
  • 通过指定最大迭代次数:

    • 停止条件:如果 m>m[max],其中 m 是当前迭代次数,m[max] 是我们为算法设置的最大迭代次数。

编写 k-means 算法

我们将在您提供的一个简单二维数据集上执行 k-means 聚类,数据集包含两个特征 xy。假设你看到夜晚花园里散布的萤火虫群体。你的任务是根据它们之间的接近程度将这些萤火虫分组。这就是 k-means 聚类的本质,它是一个流行的无监督学习算法。

我们给定了一个数据集,就像我们的花园一样,数据点绘制在二维空间中。我们的数据点由 xy 坐标表示:

import pandas as pd
dataset = pd.DataFrame({
    'x': [11, 21, 28, 17, 29, 33, 24, 45, 45, 52, 51, 52, 55, 53, 55, 61, 62, 70, 72, 10],
    'y': [39, 36, 30, 52, 53, 46, 55, 59, 63, 70, 66, 63, 58, 23, 14, 8, 18, 7, 24, 10]
}) 

我们的任务是使用 k-means 算法对这些数据点进行聚类。

首先,我们导入所需的库:

from sklearn import cluster
import matplotlib.pyplot as plt 

接下来,我们将通过指定聚类数(k)来初始化 KMeans 类。对于这个例子,假设我们想将数据分成 3 个聚类:

kmeans = cluster.KMeans(n_clusters=2) 

现在,我们用数据集来训练我们的KMeans模型。值得一提的是,这个模型只需要特征矩阵(x),而不需要目标向量(y),因为它是一个无监督学习算法:

kmeans.fit(dataset) 

现在,让我们来看看标签和聚类中心:

labels = labels = kmeans.labels_
centers = kmeans.cluster_centers_
print(labels) 
[0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0] 
print(centers) 
[[16.77777778 48.88888889]
 [57.09090909 15.09090909]] 

最后,为了可视化我们的聚类,我们绘制了数据点,并根据其分配的聚类为其着色。聚类的中心,也称为质心,也被绘制出来:

plt.scatter(dataset['x'], dataset['y'], c=labels)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], s=300, c='red')
plt.show() 

在图中,彩色的点表示我们的数据点及其相应的聚类,而红色的点表示每个聚类的质心。

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_07.png

图 6.7:k-means 聚类结果

注意,图中较大的点是由 k-means 算法确定的质心。

k-means 聚类的限制

k-means 算法设计简单且快速。由于其设计上的故意简化,它具有以下限制:

  • k-means 聚类的最大限制是初始聚类数必须预先确定。

  • 聚类中心的初始分配是随机的。这意味着每次运行算法时,可能会给出略微不同的聚类结果。

  • 每个数据点只能分配到一个聚类。

  • k-means 聚类对离群点敏感。

现在让我们来看看另一种无监督机器学习技术——层次聚类。

层次聚类

K 均值聚类使用自上而下的方法,因为我们从最重要的数据点开始,即聚类中心。还有一种聚类方法,与自上而下的方式不同,我们从底部开始算法。在这里,底部指的是问题空间中的每个单独数据点。解决方案是随着算法向上推进,逐步将相似的数据点聚在一起,直到达到聚类中心。这种自底向上的方法由层次聚类算法使用,且在本节中有详细讨论。

层次聚类的步骤

层次聚类包括以下步骤:

  1. 我们为问题空间中的每个数据点创建一个单独的聚类。如果问题空间包含 100 个数据点,则会从 100 个聚类开始。

  2. 我们只将彼此最接近的点分为一组。

  3. 我们检查停止条件;如果停止条件尚未满足,则重复步骤 2。

结果得到的聚类结构称为树状图

在树状图中,垂直线的高度决定了项目之间的相似度,如下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_08.png

图 6.8:层次聚类

请注意,停止条件在图 6.8中以虚线表示。

编写层次聚类算法

让我们学习如何在 Python 中编写层次聚类算法:

  1. 我们首先从sklearn.cluster库中导入AgglomerativeClustering,同时导入pandasnumpy包:

    from sklearn.cluster import AgglomerativeClustering
    import pandas as pd
    import numpy as np 
    
  2. 然后我们将在二维问题空间中创建 20 个数据点:

    dataset = pd.DataFrame({
        'x': [11, 11, 20, 12, 16, 33, 24, 14, 45, 52, 51, 52, 55, 53, 55, 61, 62, 70, 72, 10],
        'y': [39, 36, 30, 52, 53, 46, 55, 59, 12, 15, 16, 18, 11, 23, 14, 8, 18, 7, 24, 70]
    }) 
    
  3. 接下来,我们通过指定超参数来创建层次聚类。需要注意的是,超参数指的是在训练过程前设置的机器学习模型的配置参数,它会影响模型的行为和性能。我们使用fit_predict函数来实际处理算法:

    cluster = AgglomerativeClustering(n_clusters=2, affinity='euclidean', linkage='ward')
    cluster.fit_predict(dataset) 
    
  4. 现在让我们来看看每个数据点与所创建的两个聚类的关联:

    print(cluster.labels_) 
    
    [0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0] 
    

你可以看到,层次聚类和k-均值算法的聚类分配非常相似。

k-均值聚类算法相比,层次聚类算法具有其独特的优势和缺点。一个关键的优势是,层次聚类不需要事先指定聚类数量,这与k-均值不同。

当数据没有明确提示最佳聚类数量时,这个特性非常有用。层次聚类还提供了树状图,这是一种类似树的图示,对于可视化数据的嵌套分组以及理解层次结构非常有帮助。

然而,层次聚类也有其缺点。它比k-均值更耗费计算资源,因此不适合处理大数据集。

理解 DBSCAN

基于密度的空间应用聚类与噪声 (DBSCAN) 是一种无监督学习技术,它基于数据点的密度进行聚类。基本思想是基于这样的假设:如果我们将数据点在一个拥挤或高密度的空间中聚集在一起,我们可以实现有意义的聚类。

这种聚类方法有两个重要的含义:

  • 基于这个思想,算法可能会将无论其形状或模式如何的点聚集在一起。这种方法有助于创建任意形状的簇。这里的“形状”指的是多维空间中数据点的模式或分布。这个能力非常有用,因为现实世界中的数据通常是复杂且非线性的,而能够创建任意形状的簇可以更准确地表示和理解这些数据。

  • 与 k-means 算法不同,我们不需要指定簇的数量,算法可以检测到数据中适当的分组数量。

以下步骤涉及 DBSCAN 算法:

  1. 算法在每个数据点周围建立一个邻域。在此上下文中,“邻域”是指一个区域,其中其他数据点会被检查与感兴趣点的接近程度。这是通过计算在一个通常由变量eps表示的距离范围内的数据点数量来实现的。在此设置中,eps变量指定了两个数据点之间的最大距离,超过该距离则不被视为在同一邻域内。默认情况下,这个距离是通过欧几里得距离度量来确定的。

  2. 接下来,算法对每个数据点的密度进行量化。它使用一个名为min_samples的变量,表示一个数据点被视为“核心实例”所需的最小其他数据点数量,这些数据点应该位于数据点的eps距离内。简单来说,核心实例是一个密集地被其他数据点包围的数据点。从逻辑上讲,数据点密度高的区域将有更多的核心实例。

  3. 每个已识别的邻域标识一个簇。需要特别注意的是,围绕一个核心实例(一个在其“eps”距离内有最小数量其他数据点的数据点)的邻域可能包含其他核心实例。这意味着核心实例并非专属于某一个簇,而是由于其与多个数据点的接近,可能有助于形成多个簇。因此,这些簇的边界可能会重叠,从而导致一个复杂的、相互连接的簇结构。

  4. 任何不是核心实例的数据点,或者不在核心实例邻域内的数据点,都被视为离群点。

让我们看看如何在 Python 中使用 DBSCAN 创建簇。

在 Python 中使用 DBSCAN 创建簇

首先,我们将从sklearn库中导入必要的函数:

from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons 

让我们使用DBSCAN来解决一个稍微复杂一点的聚类问题,这个问题涉及被称为“半月形”结构的数据。在这个上下文中,“半月形”是指两组数据点,形状像新月,每个半月代表一个独特的簇。这样的数据集构成了挑战,因为簇是不可线性分割的,意味着无法用一条直线轻松地将不同的组分开。

这是“非线性类边界”概念的应用。与可以由直线表示的线性类边界不同,非线性类边界更加复杂,通常需要曲线或多维面来准确分隔不同的类或簇。

为了生成这个半月形数据集,我们可以利用make_moons()函数。这个函数生成一个类似两个半月的螺旋图案。可以根据我们的需求调整月形的“噪声”程度和生成的样本数量。

生成的数据集如下所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_09.png

图 6.9:用于 DBSCAN 的数据

为了使用 DBSCAN,我们需要提供epsmin_samples参数,正如前文所讨论的:

from matplotlib import pyplot
from pandas import DataFrame
# generate 2d classification dataset
X, y = make_moons (n_samples=1000, noise=0.05)
# scatter plot, dots colored by class value
df = DataFrame (dict (x=X[,0], y=X[,1], label=y))
colors = {0: 'red', 1:'blue'}
fig, ax = pyplot.subplots()
grouped = df.groupby('label')
for key, group in grouped:
    group.plot(ax=ax, kind='scatter', x='x', y='y', label=key, color-colors[key])
pyplot.show() 

评估聚类

良好质量的聚类目标是,属于不同簇的数据点应该是可以区分的。这意味着以下几点:

  • 属于同一簇的数据点应该尽可能相似。

  • 属于不同簇的数据点应该尽可能不同。

人类直觉可以通过可视化簇来评估聚类结果,但也有数学方法可以量化聚类的质量。这些方法不仅测量每个簇的紧密度(凝聚度)和不同簇之间的分离度,还提供了一种数值化的客观评估聚类质量的方法。轮廓分析就是一种技术,它比较由 k-means 算法创建的簇的紧密度和分离度。它是一个量化簇中凝聚度和分离度的度量指标。虽然这种技术在 k-means 的背景下有提及,但事实上它是可以推广的,适用于评估任何聚类算法的结果,而不仅限于 k-means。

轮廓分析为每个数据点分配一个分数,称为轮廓系数,范围从 0 到 1。它本质上衡量了同一簇内的每个数据点与相邻簇中数据点的距离。

聚类的应用

聚类被广泛应用于需要发现数据集潜在模式的场合。

在政府应用场景中,聚类可以用于以下方面:

  • 犯罪热点分析:将聚类应用于地理位置数据、事件报告和其他相关特征。这有助于识别犯罪高发地区,使执法机构能够优化巡逻路线并更有效地部署资源。

  • 人口社会分析:聚类可以分析诸如年龄、收入、教育和职业等人口统计数据。这有助于了解不同地区的社会经济组成,为公共政策和社会服务提供信息。

在市场研究中,聚类可以用于以下方面:

  • 市场细分:通过对消费者数据(包括消费习惯、产品偏好和生活方式指标)进行聚类,企业可以识别出不同的市场细分群体。这有助于量身定制产品开发和营销策略。

  • 定向广告:聚类有助于分析客户的在线行为,包括浏览模式、点击率和购买历史。这使得公司能够为每个客户群体创建个性化广告,从而提高参与度和转化率。

  • 客户分类:通过聚类,企业可以根据客户与产品或服务的互动、反馈和忠诚度对客户进行分类。这有助于了解客户行为、预测趋势并制定客户保持策略。

主成分分析PCA)也用于一般性的数据探索以及去除实时数据中的噪声,例如股市交易。在这种情况下,“噪声”指的是可能掩盖数据中潜在模式或趋势的随机或不规则波动。PCA 帮助过滤这些不规则波动,使数据分析和解释更加清晰。

降维

我们数据中的每个特征对应于我们问题空间中的一个维度。通过减少特征的数量,使我们的问题空间变得更简单,称为降维。它可以通过以下两种方式之一来实现:

  • 特征选择:选择在我们试图解决的问题背景下重要的特征集合

  • 特征聚合:通过以下算法之一将两个或多个特征合并以减少维度:

    • PCA:一种线性无监督机器学习算法

    • 线性判别分析LDA):一种线性有监督机器学习算法

    • KPCA:一种非线性算法

让我们更深入地了解其中一种流行的降维算法——主成分分析(PCA),并详细探讨。

主成分分析

PCA 是一种无监督机器学习方法,通常用于通过线性变换的过程来减少数据集的维度。简单来说,它是一种通过关注数据中最重要的部分来简化数据的方式,这些部分是通过它们的方差来识别的。

考虑一个数据集的图形表示,其中每个数据点都绘制在多维空间中。PCA 帮助识别主成分,这些主成分是数据变化最大的方向。在图 6.10中,我们看到了其中的两个,PC1 和 PC2。这些主成分展示了数据点分布的整体“形状”。

每个主成分对应一个新的较小维度,捕捉尽可能多的信息。从实际角度来看,这些主成分可以视为原始数据的摘要指标,使得数据更易于管理和分析。例如,在一个关于顾客行为的大型数据集中,PCA 可以帮助我们识别出定义大多数顾客行为的关键驱动因素(主成分)。

确定这些主成分的系数涉及计算数据协方差矩阵的特征向量和特征值,这是我们在后续章节中将深入探讨的话题。这些系数作为每个原始特征在新组件空间中的权重,定义了每个特征对主成分的贡献。

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_10.png

图 6.10:主成分分析

进一步说明,假设你有一个包含国家经济各个方面的数据集,例如 GDP、就业率、通货膨胀等。这些数据庞大且多维。在这里,PCA 允许你将这些多个维度简化为两个主成分,PC1 和 PC2。这些成分将包含最关键的信息,同时丢弃噪声或不太重要的细节。

结果图表,以 PC1 和 PC2 作为坐标轴,将为你提供一个更易于解释的经济数据的可视化表示,每个点代表一个经济体的状态,该状态基于其 GDP、就业率和其他因素的组合。

这使得 PCA 成为简化和解释高维数据的宝贵工具。

让我们考虑以下代码:

from sklearn.decomposition import PCA
import pandas as pd
url = "https://storage.googleapis.com/neurals/data/iris.csv"
iris = pd.read_csv(url)
iris
X = iris.drop('Species', axis=1)
pca = PCA(n_components=4)
pca.fit(X) 
 Sepal.Length   Sepal.Width    Petal.Length    Petal.Width    Species
0    5.1    3.5    1.4    0.2    setosa
1    4.9    3.0    1.4    0.2    setosa
2    4.7    3.2    1.3    0.2    setosa
3    4.6    3.1    1.5    0.2    setosa
4    5.0    3.6    1.4    0.2    setosa
...    ...    ...    ...    ...    ...
145    6.7    3.0    5.2    2.3    virginica
146    6.3    2.5    5.0    1.9    virginica
147    6.5    3.0    5.2    2.0    virginica
148    6.2    3.4    5.4    2.3    virginica
149    5.9    3.0    5.1    1.8    virginica 
X = iris.drop('Species', axis=1)
pca = PCA(n_components=4)
pca.fit(X) 
PCA(n_components=4) 

现在让我们打印出我们 PCA 模型的系数:

pca_df=(pd.DataFrame(pca.components_,columns=X.columns))
pca_df 

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_11.png

图 6.11:突出显示 PCA 模型系数的图示

请注意,原始 DataFrame 有四个特征:Sepal.LengthSepal.WidthPetal.LengthPetal.Width。前面的 DataFrame 指定了四个主成分 PC1、PC2、PC3 和 PC4 的系数——例如,第一行指定了 PC1 的系数,可用于替代原始的四个变量。

在这里需要注意的是,主成分的数量(在本例中为四个:PC1、PC2、PC3 和 PC4)不一定像我们之前的经济学例子那样必须是两个。主成分的数量是我们根据愿意处理数据的复杂度来选择的。我们选择的主成分越多,我们就能保留原始数据更多的方差,但这也会增加复杂度。

根据这些系数,我们可以计算输入数据框X的 PCA 组件:

X['PC1'] = X['Sepal.Length']* pca_df['Sepal.Length'][0] + X['Sepal.Width']* pca_df['Sepal.Width'][0]+ X['Petal.Length']* pca_df['Petal.Length'][0]+X['Petal.Width']* pca_df['Petal.Width'][0]
X['PC2'] = X['Sepal.Length']* pca_df['Sepal.Length'][1] + X['Sepal.Width']* pca_df['Sepal.Width'][1]+ X['Petal.Length']* pca_df['Petal.Length'][1]+X['Petal.Width']* pca_df['Petal.Width'][1]
X['PC3'] = X['Sepal.Length']* pca_df['Sepal.Length'][2] + X['Sepal.Width']* pca_df['Sepal.Width'][2]+ X['Petal.Length']* pca_df['Petal.Length'][2]+X['Petal.Width']* pca_df['Petal.Width'][2]
X['PC4'] = X['Sepal.Length']* pca_df['Sepal.Length'][3] + X['Sepal.Width']* pca_df['Sepal.Width'][3]+ X['Petal.Length']* pca_df['Petal.Length'][3]+X['Petal.Width']* pca_df['Petal.Width'][3]
X 

现在让我们在计算 PCA 组件后打印X

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_12.png

图 6.12:PCA 组件的打印计算结果

现在让我们打印方差比率,并尝试理解使用 PCA 的含义:

print(pca.explained_variance_ratio_) 
[0.92461872 0.05306648 0.01710261 0.00521218] 

方差比率表示以下内容:

  • 如果我们选择用 PC1 替换原始的四个特征,那么我们将能够捕获原始变量方差的约 92.3%。由于未捕获原始四个特征的 100%方差,我们会引入一些近似。

  • 如果我们选择用 PC1 和 PC2 替换原始的四个特征,那么我们将捕获原始变量方差的额外 5.3%。

  • 如果我们选择用 PC1、PC2 和 PC3 替换原始的四个特征,那么我们将再捕获原始变量方差的 0.017%。

  • 如果我们选择用四个主成分替换原始的四个特征,那么我们将捕获原始变量方差的 100%(92.4 + 0.053 + 0.017 + 0.005),但用四个主成分替换四个原始特征是没有意义的,因为我们根本没有降低维度,什么也没实现。接下来,让我们看看 PCA 的局限性。

PCA 的局限性

尽管 PCA 有很多优点,但它也存在局限性,如下所述:

  • 首先,PCA 在处理连续变量时最为有效,因为它的基本数学原理是为处理数值数据而设计的。它处理类别变量时效果较差,而类别变量通常出现在包括性别、国籍或产品类型等属性的数据集中。例如,如果你正在分析一个包含数字响应(如年龄或收入)和类别响应(如偏好或选择的选项)的调查数据集,PCA 对于类别数据并不适用。

  • 此外,PCA 通过在较低维空间中创建原始高维数据的近似来运作。虽然这种降维简化了数据处理和操作,但它也带来了代价:一些信息的丢失。这是一个在每个使用案例中需要仔细评估的权衡。例如,如果你处理的是一个生物医学数据集,其中每个特征代表特定的基因标记,那么使用 PCA 可能会导致丢失对某种疾病的诊断或治疗至关重要的信息。

因此,虽然 PCA(主成分分析)在处理具有许多相互关联的数值变量的大型数据集时是一个强大的降维工具,但其局限性需要仔细考虑,以确保它是某个特定应用的正确选择。

关联规则挖掘

特定数据集中的模式是需要发现、理解并挖掘出其包含信息的宝藏。有一类重要的算法专注于给定数据集中的模式分析。这类算法中比较流行的一种被称为关联规则挖掘算法,它为我们提供了以下功能:

  • 能够衡量模式的频率

  • 能够建立因果关系模式

  • 通过将模式的准确性与随机猜测进行比较,量化模式的有用性

现在我们将看一些关联规则挖掘的例子。

使用示例

关联规则挖掘用于我们试图调查数据集不同变量之间的因果关系时。以下是它能帮助回答的典型问题:

  • 哪些湿度、云量和温度值可能导致明天下雨?

  • 什么类型的保险索赔可能表明欺诈行为?

  • 哪些药物组合可能会导致患者出现并发症?

正如这些例子所示,关联规则挖掘在商业智能、医疗保健和环境研究等领域有广泛的应用。这一算法是数据科学家工具包中的强大工具,能够将复杂的模式转化为跨多个领域的可操作洞察。

市场篮子分析

推荐引擎是本书第十二章《推荐引擎》中广泛讨论的一个重要主题,是个性化用户体验的强大工具。然而,还有一种更简单且有效的生成推荐的方法,称为市场篮子分析。市场篮子分析基于哪些商品经常一起购买的信息。与更复杂的推荐引擎不同,这种方法不考虑用户特定数据或用户表达的个别商品偏好。在这里必须做出区分。推荐引擎通常根据用户的过去行为、偏好以及大量其他用户特定信息创建个性化建议。相反,市场篮子分析只关注购买的商品组合,而不考虑购买者是谁或他们的个别偏好。

市场篮分析的一个主要优势是数据收集的相对简便性。收集全面的用户偏好数据可能复杂且耗时。然而,关于商品共同购买的数据通常可以直接从交易记录中提取,这使得市场篮分析成为商家进入推荐领域的一个便捷起点。例如,当我们在沃尔玛购物时,就会生成这类数据,而不需要任何特殊技术来获取这些数据。

所谓的“特殊技术”,是指额外的步骤,如进行用户调查、使用追踪 Cookie 或构建复杂的数据管道。相反,这些数据是作为销售过程的副产品随时可用的。当这些数据在一段时间内收集时,就称为跨国数据

当关联规则分析应用于便利店、超市和快餐连锁店的购物车跨国数据时,这被称为市场篮分析。它衡量一起购买一组商品的条件概率,有助于回答以下问题:

  • 商品应如何在货架上摆放?

  • 商品应该如何在营销目录中展示?

  • 基于用户的购买模式,应该推荐什么?

由于市场篮分析能够估计商品之间的关联性,因此它常常用于大宗零售市场,如超市、便利店、药店和快餐连锁店。市场篮分析的优点在于结果几乎是自解释的,意味着商业用户可以轻松理解。

我们来看一个典型的大型超市。商店中所有独特的商品可以用一个集合表示,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_004.png = {item[1], item[2], . . . , item[m]}。因此,如果该超市销售 500 种不同的商品,那么https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_005.png将是一个包含 500 个元素的集合。

人们会在这家商店购买商品。每次有人购买商品并在收银台付款时,该商品就会被添加到某个特定交易的商品集合中,这个集合被称为商品集。在特定时间段内,这些交易会被汇总到一个集合中,表示为https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_006.png,其中https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_007.png = {t[1], t[2], . . . , t[n]}。

我们来看以下仅由四个交易组成的简单交易数据。这些交易汇总在下面的表格中:

t[1]门柱,护垫
t[2]球棒,门柱,护垫,头盔
t[3]头盔,球
t[4]球棒,护垫,头盔

让我们更详细地看这个例子:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_008.png = {球棒,门柱,护垫,头盔,球},表示商店中所有独特的商品。

让我们考虑来自https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_009.png的一个事务 t[3]。注意,在 t[3] 中购买的商品可以用物品集 t[3] = {头盔, 球} 表示,意味着顾客购买了两件商品。这个集合被称为物品集,因为它包含了单个事务中购买的所有商品。由于这个物品集有两个商品,所以物品集 t[3] 的大小被认为是二。这个术语让我们能够更有效地分类和分析购买模式。

关联规则挖掘

一个关联规则从数学上描述了涉及多个事务的物品之间的关系。它通过研究两个物品集之间的关系,以 XY 的形式进行描述,其中 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_010.pnghttps://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_011.png。此外,XY 是不重叠的物品集;这意味着 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_012.png

一个关联规则可以通过以下形式来描述:

{头盔, } ⇒ {自行车}

这里, {头盔, } 是 X, {自行车} 是 Y

让我们来看看不同类型的关联规则。

规则的类型

运行关联分析算法通常会从事务数据集中生成大量规则。它们中的大多数是无用的。为了挑选出能提供有用信息的规则,我们可以将它们分类为以下三种类型:

  • 微不足道的

  • 无法解释

  • 可执行的

让我们更详细地看一下这些类型。

微不足道的规则

在生成的大量规则中,许多从数据中推导出的规则是无用的,因为它们总结了有关业务的常识。这些被称为微不足道的规则。即使微不足道规则的置信度很高,它们仍然无用,不能用于任何基于数据的决策。请注意,这里“置信度”指的是在关联分析中使用的一种度量,量化了在另一个事件(A)已经发生的情况下,某个特定事件(比如 B)发生的概率。我们可以安全地忽略所有微不足道的规则。

以下是一些微不足道规则的例子:

  • 任何从高楼大厦跳下来的人很可能会死。

  • 更努力工作可以在考试中获得更好的成绩。

  • 当温度下降时,取暖器的销售量会上升。

  • 在高速公路上超速驾驶会导致发生事故的几率增加。

无法解释的规则

在运行关联规则算法之后生成的规则中,那些没有明显解释的规则是最难使用的。请注意,只有当一个规则可以帮助我们发现并理解一个新的模式,并最终可能导致某个特定的行动时,才是有用的。如果不能解释为什么事件 X 导致事件 Y,那么它就是一个无法解释的规则,因为它只不过是一个数学公式,最终探索的是两个无关和独立事件之间没有意义的关系。

以下是一些无法解释的规则的例子:

  • 穿红色衬衫的人在考试中往往表现更好。

  • 绿色自行车更容易被盗。

  • 买泡菜的人也会购买尿布。

可操作规则

可操作规则是我们寻找的黄金规则。它们为业务所理解并能带来洞察。当向熟悉业务领域的观众展示时,它们可以帮助我们发现事件的可能原因。例如,可操作规则可能基于当前的购买模式,建议在商店中为特定产品选择最佳陈列位置。它们还可以建议将哪些物品放在一起,以最大化销售机会,因为用户倾向于将这些物品一起购买。

以下是可操作规则及其相应行动的示例:

  • 规则 1:向用户社交媒体账户展示广告会提高销售的可能性。

  • 可操作项:建议广告产品的替代方式。

  • 规则 2:创建更多的价格点可以提高销售的可能性。

  • 可操作项:某一商品可以进行促销,而另一商品的价格则可以提高。

现在让我们来看一下如何对规则进行排序。

排序规则

关联规则有三种衡量方式:

  • 物品的支持度(频率)

  • 信心

  • 提升度

让我们更详细地了解一下它们。

支持度

支持度度量是一个数值,用于量化我们在数据集中寻找的模式的频率。它是通过首先计算我们感兴趣的模式出现的次数,然后将其除以所有交易的总数来计算的。

让我们来看一下针对特定项目集[a]的公式:

numItemset[a] = 包含项目集*[a]的交易数量

num[total] = 交易总数

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_013.png

仅通过查看支持度,我们就可以了解模式出现的稀有性。低支持度意味着我们在寻找一个稀有事件。在商业环境中,这些稀有事件可能是异常情况或离群值,可能具有重要的意义。例如,它们可能代表不寻常的客户行为或独特的销售趋势,这可能标志着需要战略关注的机会或威胁。

例如,如果项目集[a] = {头盔, 球}出现在六笔交易中的两笔中,那么支持度*(项目集*[a]) = 2/6 = 0.33

信心

信心是一个数值,它通过计算条件概率量化了我们可以多大程度地将左侧(X)与右侧(Y)关联。它计算了在事件X发生的情况下,事件X导致事件Y发生的概率。

数学上,考虑规则XY

该规则的信心表示为信心(XY ),其测量方式如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_014.png

让我们看一个例子。考虑以下规则:

{头盔, } ⇒ {球门柱}

该规则的信心通过以下公式计算:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_015.png

这意味着,如果某人购物篮中有{头盔,球},则有 0.5 或 50%的概率他们还会购买搭配的球棒。

提升度

估算规则质量的另一种方法是计算提升度。提升度返回一个数字,量化规则在预测结果时相比仅假设方程右侧结果所取得的改进。“改进”指的是规则在预测结果时,相比于基线或默认方法所带来的增强或改善程度。它表示规则提供的预测比仅依赖方程右侧假设所获得的预测更准确或更具洞察力的程度。如果XY项集是独立的,则提升度按如下方式计算:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_016.png

关联分析的算法

在本节中,我们将探索以下两种可用于关联分析的算法:

  • Apriori 算法:由 Agrawal, R. 和 Srikant 于 1994 年提出。

  • FP-growth 算法:由 Han 等人于 2001 年提出的改进方案。

让我们逐一看一下这些算法。

Apriori 算法

Apriori 算法是一种迭代和多阶段的算法,用于生成关联规则。它基于生成与测试方法。

在执行 Apriori 算法之前,我们需要定义两个变量:支持度[阈值]和置信度[阈值]。

该算法包括以下两个阶段:

  • 候选生成阶段:它生成候选项集,其中包含所有支持度[阈值]以上的项集。

  • 筛选阶段:它会过滤掉所有低于期望置信度[阈值]的规则。

经过筛选后,得到的规则即为答案。

Apriori 算法的局限性

Apriori 算法的主要瓶颈是在第一阶段生成候选规则——例如,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_017.png = {项[1],项[2],…,项[m]}可以产生 2^m 个可能的项集。由于其多阶段设计,它首先生成这些项集,然后再寻找频繁项集。这个限制是一个巨大的性能瓶颈,使得 Apriori 算法不适用于较大的项集,因为它在找到频繁项集之前会生成过多的项集,这会影响所需时间。

现在让我们深入了解 FP-growth 算法。

FP-growth 算法

频繁模式增长FP-growth)算法是对 Apriori 算法的改进。它首先展示频繁事务 FP 树,这是一棵有序树。它包括两个步骤:

  • 填充 FP 树

  • 挖掘频繁模式

让我们逐步看这些步骤。

填充 FP 树

让我们考虑下表所示的交易数据。首先,我们将其表示为稀疏矩阵:

ID球棒球票护垫头盔
101100
211110
300011
410110

现在,让我们计算每个项的频率并按频率降序排列:

频率
护垫3
头盔3
球棒2
门柱2
1

现在,让我们根据频率重新排列基于交易的数据:

ID原始项重新排序的项
t1门柱,护垫护垫,门柱
t2球棒,门柱,护垫,头盔头盔,护垫,门柱,球棒
t3头盔,球头盔,球
t4球棒,护垫,头盔头盔,护垫,球棒

为了构建 FP 树,让我们从 FP 树的第一分支开始。FP 树以作为根节点。为了构建树,我们可以用节点表示每个项,如下图所示(这里显示的是 t[1]的树表示)。

请注意,每个节点的标签是项的名称,频率则附加在冒号后面。同时请注意,护垫项的频率为 1:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_13.png

图 6.13:FP 树表示第一个交易

运用相同的模式,让我们绘制所有四个交易,从而得到完整的 FP 树。FP 树有四个叶节点,每个叶节点表示与四个交易相关联的项集。请注意,我们需要计算每个项的频率,并在多次使用时增加其频率——例如,当将 t[2]添加到 FP 树时,头盔的频率增加到 2。同样,在添加 t[4]时,它的频率再次增加到 3。

得到的树如以下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_06_14.png

图 6.14:FP 树表示所有交易

请注意,前面的图示中生成的 FP 树是一个有序树。这引出了 FP-growth 树的第二阶段:挖掘频繁模式。

挖掘频繁模式

FP-growth 过程的第二阶段专注于从 FP 树中挖掘频繁模式。创建有序树是一个故意的步骤,旨在生成一种便于在寻找这些频繁模式时轻松导航的数据结构。

我们从一个叶节点开始这段旅程,这是一个终点节点,并向上遍历。例如,让我们从一个叶节点项目“球棒”开始。我们接下来的任务是找出“球棒”的条件模式基。术语“条件模式基”可能听起来很复杂,但它仅仅是指从特定叶节点项目到树根的所有路径的集合。对于我们的项目“球棒”,条件模式基将包括从“球棒”节点到树顶部的所有路径。此时,理解有序和无序树之间的差异变得至关重要。在有序树(如 FP 树)中,项目遵循固定顺序,简化了模式挖掘过程。无序树不提供这种结构化设置,这可能使频繁模式的发现更具挑战性。

当计算“球棒”的条件模式基时,我们实质上是映射从“球棒”节点到根的所有路径。这些路径显示了在交易中与“球棒”经常共现的项目。实质上,我们正在遵循与“球棒”相关的树的“分支”,以理解它与其他项目的关系。这种视觉上的说明阐明了我们从哪里获取这些信息以及 FP 树如何帮助阐明交易数据中的频繁模式。球棒的条件模式基如下所示:

曲棍球: 1护具: 1头盔: 1
护腕: 1头盔: 1

球棒频繁模式将如下所示:

{曲棍球, 护具, 头盔}: 球棒

{护腕, 头盔}: 球棒

使用 FP-growth 的代码

让我们看看如何使用 Python 中的 FP-growth 算法生成关联规则。为此,我们将使用pyfpgrowth包。首先,如果我们以前从未使用过pyfpgrowth,让我们先安装它:

!pip install pyfpgrowth 

然后,让我们导入我们实现此算法所需的包:

import pandas as pd
import numpy as np
import pyfpgrowth as fp 

现在我们将创建输入数据,形式为transactionSet

dict1 = {
    'id':[0,1,2,3],
    'items':[["wickets","pads"],
    ["bat","wickets","pads","helmet"],
    ["helmet","pad"],
    ["bat","pads","helmet"]]
 }
transactionSet = pd.DataFrame(dict1) 
 id    items
0    0    [wickets, pads]
1    1    [bat, wickets, pads, helmet]
2    2    [helmet, pad]
3    3    [bat, pads, helmet] 

一旦生成了输入数据,我们将基于我们在find_frequent_patterns()中传递的参数生成图案。请注意,传递给此函数的第二个参数是最小支持度,在本例中为 1:

patterns = fp.find_frequent_patterns(transactionSet['items'],1) 

图案已生成。现在让我们打印这些图案。这些图案列出了项目组合及其支持度:

patterns 
{('pad',): 1,
 ('helmet', 'pad'): 1,
 ('wickets',): 2,
 ('pads', 'wickets'): 2,
 ('bat', 'wickets'): 1,
 ('helmet', 'wickets'): 1,
 ('bat', 'pads', 'wickets'): 1,
 ('helmet', 'pads', 'wickets'): 1,
 ('bat', 'helmet', 'wickets'): 1,
 ('bat', 'helmet', 'pads', 'wickets'): 1,
 ('bat',): 2,
 ('bat', 'helmet'): 2,
 ('bat', 'pads'): 2,
 ('bat', 'helmet', 'pads'): 2,
 ('pads',): 3,
 ('helmet',): 3,
 ('helmet', 'pads'): 2} 

现在让我们生成规则:

rules = fp.generate_association_rules(patterns,0.3)
rules 
{('helmet',): (('pads',), 0.6666666666666666),
 ('pad',): (('helmet',), 1.0),
 ('pads',): (('helmet',), 0.6666666666666666),
 ('wickets',): (('bat', 'helmet', 'pads'), 0.5),
 ('bat',): (('helmet', 'pads'), 1.0),
 ('bat', 'pads'): (('helmet',), 1.0),
 ('bat', 'wickets'): (('helmet', 'pads'), 1.0),
 ('pads', 'wickets'): (('bat', 'helmet'), 0.5),
 ('helmet', 'pads'): (('bat',), 1.0),
 ('helmet', 'wickets'): (('bat', 'pads'), 1.0),
 ('bat', 'helmet'): (('pads',), 1.0),
 ('bat', 'helmet', 'pads'): (('wickets',), 0.5),
 ('bat', 'helmet', 'wickets'): (('pads',), 1.0),
 ('bat', 'pads', 'wickets'): (('helmet',), 1.0),
 ('helmet', 'pads', 'wickets'): (('bat',), 1.0)} 

每条规则都有左手边和右手边,用冒号(:)分隔。它还为我们提供了数据集中每条规则的支持度。

摘要

在本章中,我们探讨了各种无监督机器学习技术。我们研究了在何种情况下尝试降低我们试图解决的问题的维度是一个好主意,以及如何通过不同的方法实现这一点。我们还研究了无监督机器学习技术在哪些实际示例中可能非常有帮助,包括市场篮分析。

在下一章,我们将探讨各种监督学习技术。我们将从线性回归开始,然后介绍更复杂的监督学习算法,如基于决策树的算法、SVM 和 XGBoost。我们还将学习朴素贝叶斯算法,它最适用于非结构化文本数据。

在 Discord 上了解更多信息

要加入本书的 Discord 社区——在这里你可以分享反馈、向作者提问并了解新版本——请扫描下面的二维码:

packt.link/WHLel

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/QR_Code1955211820597889031.png

第七章:传统的监督式学习算法

人工智能是新的电力。

—Andrew Ng

第七章,我们将关注监督式机器学习算法。这些算法的特点是依赖有标签的数据进行模型训练,其本质多样且灵活。让我们来看看一些实例,如决策树、支持向量机SVMs)和线性回归等,它们都属于监督学习范畴。

随着我们对这个领域的深入探讨,需要指出的是,本章并未涉及神经网络,尽管它是监督式机器学习中的一个重要类别。鉴于神经网络的复杂性以及该领域的快速发展,神经网络值得深入探讨,我们将在接下来的三章中展开。神经网络的广泛内容需要不止一章来充分讨论它们的复杂性和潜力。

在本章中,我们将深入探讨监督式机器学习的核心要素,重点介绍分类器和回归器。我们将通过实际问题作为案例,探索它们的能力。将展示六种不同的分类算法,接着介绍三种回归技术。最后,我们将比较它们的结果,总结本次讨论的关键要点。

本章的总体目标是帮助你理解不同类型的监督式机器学习技术,并了解哪些监督式机器学习技术最适用于特定类别的问题。

本章将讨论以下概念:

  • 理解监督式机器学习

  • 理解分类算法

  • 评估分类器性能的方法

  • 理解回归算法

  • 评估回归算法性能的方法

我们先来了解监督式机器学习的基本概念。

理解监督式机器学习

机器学习侧重于利用数据驱动的方法来创建自主系统,这些系统可以帮助我们在有无人工监督的情况下做出决策。为了创建这些自主系统,机器学习使用一组算法和方法来发现并构建数据中的可重复模式。机器学习中最流行和最强大的方法之一是监督式机器学习方法。在监督式机器学习中,算法会接收一组输入,称为特征,以及它们对应的输出,称为标签。这些特征通常包括结构化数据,如用户资料、历史销售数据或传感器测量值,而标签通常代表我们希望预测的具体结果,如客户购买习惯或产品质量评分。通过给定的数据集,监督式机器学习算法用于训练一个模型,捕捉特征和标签之间通过数学公式表示的复杂关系。这个训练好的模型是用于预测的基本工具。

监督学习中从现有数据中学习的能力类似于人类大脑从经验中学习的能力。这种学习能力利用了人类大脑的一个特性,是将决策能力和智能引入机器的基本方式。

让我们考虑一个示例,我们希望使用监督式机器学习技术训练一个模型,能够将一组电子邮件分类为合法邮件(称为合法)和垃圾邮件(称为垃圾邮件)。为了开始,我们需要从过去的例子中获取数据,以便机器可以学习什么类型的邮件内容应被分类为垃圾邮件。

使用文本数据进行的基于内容的学习任务是一个复杂的过程,并通过其中一种监督式机器学习算法实现。在本示例中,可以用于训练模型的一些监督式机器学习算法示例包括决策树和朴素贝叶斯分类器,我们将在本章稍后讨论。

目前,我们将重点讨论如何构建监督式机器学习问题。

构建监督式机器学习问题

在深入讨论监督式机器学习算法的细节之前,让我们先定义一些基本的监督式机器学习术语:

术语解释
标签标签是我们模型需要预测的变量。监督式机器学习模型中只能有一个标签。
特征用于预测标签的输入变量集称为特征。
特征工程转换特征以准备它们用于选择的监督式机器学习算法的过程称为特征工程。
特征向量在将输入提供给监督学习算法之前,所有的特征都会组合成一个叫做特征向量的数据结构。
历史数据用于构建标签与特征之间关系的数据,称为历史数据。历史数据包含实例。
训练/测试数据历史数据通过实例分为两部分——较大的数据集称为训练数据,较小的数据集称为测试数据。
模型一种数学形式,用于表达最能捕捉标签与特征之间关系的模式。
训练使用训练数据创建模型。
测试使用测试数据评估训练模型的质量。
预测使用我们训练过的模型来估计标签的过程。在这个语境中,“预测”是模型的最终输出,指定了一个确切的结果。与“预测概率”不同,后者不是给出一个具体的结果,而是提供每个潜在结果的统计可能性。

一个训练过的监督学习模型能够通过根据特征来估计标签,从而进行预测。

让我们引入本章中将用于讨论机器学习技术的符号:

变量含义
y实际标签
ý预测标签
d实例总数
b训练实例的数量
c测试实例的数量
X_train训练特征向量

请注意,在这个语境中,“实例”指的是我们数据集中的单个实例。每个实例包含一组特征(输入数据)和一个对应的标签(我们要预测的结果)。

让我们深入探讨我们已经引入的一些术语的实际应用。考虑一个特征向量,本质上是一个数据结构,包含了所有的特征。

比如,如果我们有“n”个特征和“b”个训练实例,我们表示这个训练特征向量为 X_train。因此,如果我们的训练数据集包含五个实例和五个变量或特征,X_train 将有五行——每个实例一行,总共有 25 个元素(5 个实例 x 5 个特征)。

在这个语境中,X_train 是一个特定术语,表示我们的训练数据集。这个数据集中的每个实例是特征和其对应标签的组合。我们用上标来表示特定实例的行号。因此,我们数据集中的单个实例表示为 (X^((1)), y^((1))),其中 X^((1)) 表示第一个实例的特征,而 y^((1)) 是它的对应标签。

因此,我们的完整标注数据集 D 可以表示为 D = {( X((1))*,y*((1))), (y ((2))*,y*((2))), …… , (X((d))*,y*((d)))},其中 D 表示总的样本数量。

我们将D划分为两个子集——训练集D[train]和测试集D[test]。训练集D[train]可以表示为D[train] = {(X^((1)), y((1))*),(X*((2)), y((2))*),……,(X*((b)), y^((b)))},其中‘b’是训练示例的数量。

训练模型的主要目标是确保训练集中的任何第i个样本的预测目标值('ý')与实际标签('y')尽可能一致。这确保了模型的预测反映了样本中呈现的真实结果。

现在,让我们看看一些术语是如何在实际中被构建的。

正如我们讨论过的,特征向量被定义为一个数据结构,其中存储了所有特征。

如果特征的数量是n,训练样本的数量是b,则X_train表示训练特征向量。

对于训练数据集,特征向量由X_train表示。如果训练数据集中有b个样本,那么X_train将有b行。如果有n个变量,那么训练数据集的维度将是n x b

我们将使用上标表示训练示例的行号。

我们的标签数据集中的这个特定示例由*(Features*^((1)), label^((1))) = (X^((1)), y^((1))*)表示。

因此,我们的标签数据集表示为D = {(X^((1)), y((1))*),(X*((2)), y((2))*),……,(X*((d)), y^((d)))}

我们将其分为两部分——D[train] 和 D[test]。

所以,我们的训练集可以表示为D[train] = {(X^((1)), y((1))*),(X*((2)), y((2))*),……,(X*((b)), y^((b)))}

训练模型的目标是,对于训练集中的任何第i个样本,预测的目标值应该尽可能接近样本中的实际值。换句话说:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_001.png

因此,我们的测试集可以表示为D[test] = {X^((1)), y^((1))), (X^((2)), y((2))*),…,(X*(©), y^(©))}

标签的值通过一个向量Y表示:

Y ={y^((1)), y^((2)), …, y^((m))}*。

让我们通过一个例子来说明这些概念。

假设我们正在进行一个项目,目的是根据各种特征预测房价,比如卧室数量、房屋面积和房龄。我们将如何将机器学习术语应用到这个现实场景中呢?

在这种情况下,我们的“特征”将是卧室数量、房屋面积和房龄。假设我们有 50 个样本(即 50 个不同的房子,并且我们拥有这些房子的一些特征以及对应的价格)。我们可以将它们表示为一个训练特征向量,叫做X_train

X_train变成一个包含 50 行(每个房子的行)和 3 列(每个特征:卧室数、面积和房龄)的表格。这是一个 50 x 3 的矩阵,保存了我们所有的特征数据。

单个房屋的特征集和价格可能表示为*((X*((i))*,y*((i)))),其中X((i))包含第*i*个房屋的特征,*y*((i))是其实际价格。

我们的整个数据集D可以表示为D = {(X((1))*,y*((1))), (X((2))*,y*((2))), … , ((X((50))*,y*((50))))}

假设我们使用 40 个房屋进行训练,剩余的 10 个用于测试。我们的训练集D[train]将是前 40 个样本:{(X((1))*,y*((1))), (X((2))*,y*((2))), … , ((X((40))*,y*((40))))}

训练模型后,我们的目标是预测房价 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_002.png,这些预测结果应与所有训练集中房屋的实际价格 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_003.png紧密匹配。

我们的测试集D[test]由剩余的 10 个样本组成:{(X((41))*,y*((41))), (X((42))*,y*((42))), … , (X((50))*,y*((50))))}

最后,我们有Y向量,包含了我们所有实际的房价:Y ={ y^((1)), y^((2)), …, y^((50))}

通过这个具体的例子,我们可以看到在使用监督学习预测房价时,这些概念和方程是如何转化为实践的。

理解启用条件

一个监督学习算法需要满足某些条件才能执行。启用条件是确保监督学习算法有效性的某些前提条件。这些启用条件如下:

  • 足够的示例:监督学习算法需要足够的示例来训练模型。当我们有确凿证据表明兴趣模式在数据集中得到了充分表示时,我们就认为有足够的示例。

  • 历史数据中的模式:用于训练模型的示例需要具有某种模式。我们关注的事件发生的可能性应该依赖于模式、趋势和事件的组合。在我们的模型中,标签在数学上表示了我们关注的事件。如果没有这些,我们所处理的数据就是随机数据,无法用于训练模型。

  • 有效假设:当我们使用示例训练监督学习模型时,我们期望适用于这些示例的假设在未来也能有效。让我们看一个实际的例子。如果我们想为政府训练一个可以预测学生签证是否会被批准的机器学习模型,那么理解是,当该模型用于预测时,法律和政策不会发生变化。如果在训练模型后执行了新政策或新法律,模型可能需要重新训练以纳入这些新信息。

让我们探讨如何区分分类器和回归器。

区分分类器和回归器

在机器学习模型中,标签可以是类别变量或连续变量。连续变量是数值变量,可以在两个值之间具有无限多个值,而类别变量是定性变量,可以分类为不同的类别。标签的类型决定了我们使用的有监督机器学习模型的类型。从根本上讲,我们有两种类型的有监督机器学习模型:

  • 分类器:如果标签是类别变量,则机器学习模型被称为分类器。分类器可以用来回答以下类型的业务问题:

    • 这种异常的组织生长是恶性肿瘤吗?

    • 根据当前的天气条件,明天会下雨吗?

    • 根据某位特定申请人的资料,是否应批准他们的按揭申请?

  • 回归器:如果标签是连续变量,则我们训练一个回归器。回归器可以用来回答以下类型的业务问题:

    • 根据当前的天气状况,明天会下多少雨?

    • 给定某一特定房屋的特征,价格会是多少?

让我们更详细地看看分类器和回归器。

理解分类算法

在有监督机器学习中,如果标签是类别变量,则模型被归类为分类器。回忆一下,模型本质上是从训练数据中学习到的数学表示:

  • 历史数据被称为已标记数据

  • 需要预测标签的生产数据被称为未标记数据

使用训练过的模型准确地标记未标记的数据是分类算法的真正力量。分类器预测未标记数据的标签,以回答特定的业务问题。

在我们展示分类算法的细节之前,让我们先展示一个业务问题,作为分类器的挑战。然后我们将使用六种不同的算法回答相同的挑战,这将帮助我们比较它们的方法论、思路和性能。

展示分类器挑战

我们将首先展示一个常见问题,作为挑战测试六种不同的分类算法。这个常见问题在本章中被称为分类器挑战。使用所有六个分类器来解决相同的问题将帮助我们从两个方面进行改进:

  • 所有输入变量需要处理并组装成一个复杂的数据结构,称为特征向量。使用相同的特征向量帮助我们避免为所有六种算法重复数据准备。

  • 由于使用相同的特征向量作为输入,我们可以准确比较各种算法的性能。

分类器挑战是预测一个人是否会进行购买的可能性。在零售行业,能够帮助最大化销售的一件事就是更好地理解顾客的行为。这可以通过分析历史数据中发现的模式来实现。我们先陈述问题。

问题陈述

给定历史数据,我们能否训练一个二元分类器,预测一个特定用户是否最终会根据其个人资料购买产品?

首先,让我们探索可用的标记数据集来解决这个问题:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_004.png

请注意,x 是实数集合的一个成员。https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_005.png 表示它是一个具有 b 个实时特征的向量。https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_006.png 意味着它是一个二元变量,因为我们处理的是一个二分类问题。输出可以是 01,每个数字代表一个不同的类别。

对于这个具体的示例,当 y = 1 时,我们称其为正类,当 y = 0 时,我们称其为负类。为了使其更具体,当 y 等于 1 时,我们处理的是正类,意味着用户可能会进行购买。相反,当 y 等于 0 时,它表示负类,意味着用户不太可能购买任何东西。该模型将帮助我们根据用户的历史行为预测未来的用户行为。

尽管正类和负类的水平可以任意选择,但一个好的做法是将正类定义为感兴趣的事件。如果我们尝试为银行标记欺诈交易,那么正类(即 y = 1)应该是欺诈交易,而不是反过来。

现在,让我们来看以下内容:

  • 实际标签,用 y 表示

  • 预测标签,用 ý 表示

请注意,对于我们的分类器挑战,示例中标签的实际值用 y 来表示。如果在我们的示例中,有人购买了一个商品,我们就说 y = 1。预测值用 ý 来表示。输入特征向量 x 的维度将等于输入变量的数量。我们希望确定给定特定输入时,用户购买商品的概率。

所以,我们希望确定在给定特征向量 x 的特定值时,y = 1 的概率。从数学上讲,我们可以这样表示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_007.png

请注意,表达式 P(y = 1|x) 表示在事件 x 发生的条件下,事件 y 等于 1 的条件概率。换句话说,它表示在特定条件 x 的知识或存在下,结果 y 为真的概率,即 y 为正类的概率。

现在,让我们看看如何处理和组装特征向量 x 中的不同输入变量。使用处理管道组装 x 不同部分的方法将在下一部分中更详细地讨论。

使用数据处理管道进行特征工程

为所选机器学习算法准备数据的过程称为特征工程,是机器学习生命周期中的关键部分。特征工程可以分为不同的阶段或阶段。用于处理数据的多阶段处理代码统称为数据管道。尽可能使用标准处理步骤构建数据管道,可以使其可重用,并减少训练模型所需的工作量。通过使用更多经过充分测试的软件模块,代码的质量也得到了提升。

除了特征工程,值得注意的是,数据清洗也是此过程中的一个关键部分。这涉及到解决异常值检测和缺失值处理等问题。例如,异常值检测可以帮助你识别并处理那些可能会负面影响模型性能的异常数据点。同样,缺失值处理是用来填充或处理数据集中的缺失数据点,确保模型能够获得完整的数据视图。这些步骤是数据管道中不可或缺的一部分,有助于提高机器学习模型的可靠性和准确性。

让我们为分类器挑战设计一个可重用的处理管道。如前所述,我们将一次性准备数据,然后用于所有分类器。

导入数据

让我们首先导入必要的库:

import numpy as np
import sklearn,sklearn.tree
import matplotlib.pyplot as plt
import pandas as pd
import sklearn.metrics as metrics
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, StandardScaler 

请注意,我们将使用 Python 中的pandas库,它是一个强大的开源数据操作与分析工具,提供高性能的数据结构和数据分析工具。我们还将使用sklearn,它提供了一套全面的工具和算法,用于各种机器学习任务。

导入数据

该问题的标签数据包含了示例,存储在一个名为Social_Network_Ads.csvCSV格式文件中。我们从读取这个文件开始:

# Importing the dataset
dataset = pd.read_csv('https://storage.googleapis.com/neurals/data/Social_Network_Ads.csv') 

这个文件可以从storage.googleapis.com/neurals/data/Social_Network_Ads.csv下载。

特征选择

选择与我们想要解决的问题相关的特征的过程称为特征选择。这是特征工程中的一个重要部分。

一旦文件导入后,我们会删除User ID列,该列用于识别个人,在训练模型时应该排除。通常,User ID是一个唯一标识每个人的字段,但对我们试图建模的模式或趋势没有实际意义。

因此,在训练机器学习模型之前,通常的做法是删除这些列:

dataset = dataset.drop(columns=['User ID']) 

现在,让我们使用head命令预览数据集,它将打印出该数据集的前五行:

dataset.head(5) 

数据集如下所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_01.png

图 7.1: 示例数据集

现在,让我们看看如何进一步处理输入数据集。

独热编码

许多机器学习模型在所有特征都作为连续变量表示时表现最好。这一要求意味着我们需要一种方法将类别特征转换为连续特征。一种常见的技术是‘独热编码’。

在我们的背景下,Gender特征是类别型的,我们的目标是使用独热编码将其转换为连续变量。但是,究竟什么是独热编码呢?

独热编码是一种将类别变量转换为机器学习算法能更好理解的格式的过程。它通过为原始特征中的每个类别创建新的二进制特征来实现这一点。例如,如果我们对‘Gender’应用独热编码,它将生成两个新特征:MaleFemale。如果性别是Male,则‘Male’特征为 1(表示真),‘Female’特征为 0(表示假),反之亦然。

现在,让我们将这个独热编码过程应用到我们的‘Gender’特征上,并继续我们的模型准备过程:

enc = sklearn.preprocessing.OneHotEncoder() 

drop='first'参数表示应删除‘Gender’特征中的第一个类别。

首先,让我们对‘Gender’进行独热编码:

enc.fit(dataset.iloc[:,[0]])
onehotlabels = enc.transform(dataset.iloc[:,[0]]).toarray() 

在这里,我们使用fit_transform方法对‘Gender’列应用独热编码。reshape(-1, 1)函数用于确保数据符合编码器所期望的正确二维格式。toarray()函数用于将输出的稀疏矩阵转换为密集的numpy数组,以便后续更方便地处理。

接下来,让我们将编码后的Gender重新添加到数据框中:

genders = pd.DataFrame({'Female': onehotlabels[:, 0], 'Male': onehotlabels[:, 1]}) 

请注意,这行代码将编码后的‘Gender’数据重新添加到数据框中。由于我们设置了drop='first',假设‘Male’类别被视为第一个类别,那么我们新添加的列‘Female’将具有1的值(表示女性),如果性别是男性,则为0

然后,我们从数据框中删除原始的Gender列,因为它已经被我们的新Female列所替代:

result = pd.concat([genders,dataset.iloc[:,1:]], axis=1, sort=False) 

一旦转换完成,让我们再次查看数据集:

result.head(5) 

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_02.png

图 7.2: 在这里添加标题…

请注意,为了将变量从类别变量转换为连续变量,独热编码已将Gender转换为两个单独的列——MaleFemale

让我们来看看如何指定特征和标签。

指定特征和标签

让我们指定特征和标签。在本书中,我们将使用y表示标签,使用X表示特征集:

y=result['Purchased']
X=result.drop(columns=['Purchased']) 

X表示特征向量,包含我们需要用于训练模型的所有输入变量。

将数据集划分为测试集和训练集

接下来,我们将把数据集分成两部分:70%用于训练,30%用于测试。这个划分的理由是,在机器学习实践中,有一个经验法则,我们希望数据集的大部分用于训练模型,使其能够从各种示例中有效学习。这里的 70%便是为了这个目的。然而,我们也需要确保我们的模型能够很好地推广到未见过的数据,而不仅仅是记住训练集。为了评估这一点,我们将把 30%的数据保留用于测试。这部分数据在训练过程中不会被使用,作为衡量训练模型性能和其对新、未见数据进行预测能力的基准:

X_train, X_test, y_train, y_test = train_test_split(X, y,test_size = 0.25, random_state = 0) 

这已经创建了以下四个数据结构:

  • X_train:包含训练数据特征的数据结构

  • X_test:包含测试数据集特征的数据结构

  • y_train:包含训练数据集中标签值的向量

  • y_test:包含测试数据集标签值的向量

现在让我们对数据集应用特征归一化。

特征缩放

在准备我们的数据集以供机器学习模型使用时,一个重要步骤是特征归一化,也称为缩放。在许多机器学习算法中,特征变量被缩放到统一的范围,通常是从 0 到 1,这可以通过确保没有单一特征因其尺度过大而主导其他特征,从而提高模型的性能。

这个过程还可以帮助算法更快地收敛到解决方案。现在,让我们对数据集应用这一变换,以获得最佳结果。

首先,我们初始化一个StandardScaler类的实例,它将用于执行缩放操作:

# Feature Scaling
sc = StandardScaler() 

然后,我们使用fit_transform方法。这个变换会对特征进行缩放,使它们的均值为0,标准差为1,这就是标准化缩放的本质。转换后的数据存储在X_train_scaled变量中:

X_train = sc.fit_transform(X_train) 

接下来,我们将应用transform方法,它将相同的变换(如前面的代码所示)应用于测试数据集X_test

X_test = sc.transform(X_test) 

在我们缩放数据后,它已经准备好作为不同分类器的输入,这些分类器将在后续章节中展示。

评估分类器

一旦模型训练完成,我们需要评估其性能。为此,我们将使用以下过程:

  1. 我们将把标记数据集分为两部分——训练分区和测试分区。我们将使用测试分区来评估训练好的模型。

  2. 我们将使用测试分区的特征来生成每一行的标签。这是我们的预测标签集。

  3. 我们将比较预测标签集与实际标签,以评估模型。

除非我们试图解决一些非常简单的问题,否则在评估模型时会出现一些误分类。我们如何解读这些误分类来确定模型质量,取决于我们选择使用哪些性能指标。

一旦我们得到了实际标签和预测标签,就可以使用一系列性能指标来评估模型。

量化模型的最佳指标将取决于我们要解决的业务问题的需求,以及训练数据集的特点。

现在让我们来看一下混淆矩阵。

混淆矩阵

混淆矩阵用于总结分类器评估的结果。二分类器的混淆矩阵如下所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_03.png

图 7.3:混淆矩阵

如果我们训练的分类器标签有两个级别,那么它就是一个二分类器。监督式机器学习的第一个关键应用——特别是二分类器——是在第一次世界大战期间,用于区分飞机和飞鸟。

分类可以分为以下四类:

  • 真正阳性 (TPs):正确分类为正类的正类

  • 真阴性 (TNs):正确分类为负类的负类

  • 假阳性 (FPs):实际上是负类的被分类为正类

  • 假阴性 (FNs):实际上是正类的被分类为负类

让我们看看如何使用这四个类别来创建各种性能指标。

混淆矩阵通过详细列出正确和错误预测的数量,提供了模型性能的全面快照。它列举了 TPs、TNs、FPs 和 FNs。在这些中,正确分类指的是模型正确识别类别的实例,即 TPs 和 TNs。模型的准确率是指这些正确分类(TPs 和 TNs)在所有预测中所占的比例,可以直接从这个混淆矩阵中计算得到。混淆矩阵通过计算 TPs、TNs、FPs 和 FNs,告诉你正确分类和误分类的数量。模型准确率定义为所有预测中正确分类所占的比例,可以从混淆矩阵中轻松看到,如下所示。

当我们的数据中有近似相等数量的正例和负例示例时——这种情况称为平衡类——准确率指标可以提供一个有价值的模型性能度量。换句话说,准确率是模型正确预测数量与总预测数量的比率。例如,如果我们的模型在 100 个测试实例中正确识别了 90 个,无论它们是正例还是负例,其准确率将为 90%。这个度量可以让我们大致了解模型在两个类别中的整体表现。如果我们的数据具有平衡的类别(即正例总数大致等于负例总数),那么准确率将为我们提供对训练模型质量的良好洞察。准确率是在所有预测中分类正确的比例。

在数学上:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_008.png

理解召回率和精确度

在计算准确率时,我们不区分 TP 和 TN。通过准确率评估模型是直接的,但是当数据存在不平衡类时,它将无法准确量化训练模型的质量。当数据存在不平衡类时,另外两个指标可以更好地量化训练模型的质量,即召回率和精确度。我们将利用一个流行的钻石采矿过程的例子来解释这两个额外指标的概念。

几个世纪以来,冲积钻石采矿一直是从全世界各地河床沙中提取钻石的最流行方式之一。数千年的侵蚀已知将钻石从其主要矿床冲刷到世界各地的河床中。为了采矿钻石,人们在一个大型露天矿坑中从河岸收集沙子。经过大量冲洗后,矿坑中留下大量岩石。

绝大多数这些冲刷过的岩石只是普通的石头。将其中一块岩石识别为钻石是罕见但非常重要的事件。在我们的场景中,一家矿山的所有者正在尝试使用计算机视觉来识别哪些冲刷过的岩石是普通的石头,哪些是钻石。他们正在利用形状、颜色和反射通过计算机视觉对冲刷过的岩石进行分类。

在这个例子的背景下:

TP一块正确识别为钻石的冲刷过的岩石
TN一块正确识别为石头的冲刷过的岩石
FP一块错误地识别为钻石的石头
FN一颗错误地识别为石头的钻石

让我们在心中记住这个从矿山中提取钻石的过程,来解释召回率和精确度:

  • 召回率:这计算了命中率,即在大量事件库中识别出目标事件的比例。换句话说,这个指标衡量了我们找到或“命中”大部分目标事件的能力,并尽可能减少未被识别的事件。在识别一堆普通石头中的钻石的背景下,召回率是量化寻宝成功率的指标。对于某个装满了洗净石头的坑,召回率将是识别到的钻石数量与坑内钻石总数的比例:

    https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_009.png

    假设坑内有 10 颗钻石,每颗价值 $1,000。我们的机器学习算法能够识别出其中的九颗。那么,召回率就是 9/10 = 0.90

    因此,我们能够找回 90% 的宝藏。按美元计算,我们识别出了 $9,000 的宝藏,而总值为 $10,000。

  • 精准率:精准率只关注由训练好的模型标记为正类的数据点,并丢弃其他所有数据。如果我们只筛选出模型标记为正类的事件(即 TPs 和 FPs),然后计算其准确性,这就是精准率。

    现在,让我们在钻石开采的背景下探讨精准率。假设我们希望使用计算机视觉技术从一堆洗净的石头中识别出钻石,并将其发送给客户。

    这个过程应该是自动化的。最坏的情况是算法错误地将一块石头分类为钻石,最终客户收到这块石头并因此被收费。因此,显然,为了使这个过程可行,精准率应该很高。

    针对钻石开采的例子:

    https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_010.png

理解召回率和精准率之间的权衡

使用分类器做出决策涉及一个两步过程。首先,分类器生成一个介于 0 到 1 之间的决策分数。然后,它应用一个决策阈值来确定每个数据点的类别。分数高于阈值的数据点被分配为正类,而分数低于阈值的数据点则被分配为负类。这两个步骤可以如下解释:

  1. 分类器生成一个决策分数,这是一个介于 0 到 1 之间的数字。

  2. 分类器使用一个叫做决策阈值的参数值,将当前数据点分配到两个类别之一。任何决策(分数 > 决策阈值)的数据点被预测为正类,而任何决策(分数 < 决策阈值)的数据点被预测为负类。

想象一个情境,你在经营一个钻石矿。你的任务是从一堆普通的石头中识别出珍贵的钻石。为了简化这个过程,你开发了一个机器学习分类器。该分类器检查每一块石头,分配一个介于 0 到 1 之间的决策分数,最后根据这个分数和预设的决策阈值对石头进行分类。

决策分数本质上表示分类器对某块岩石确实是钻石的信心,分数接近 1 的岩石很可能是钻石。而决策阈值是一个预定义的截止点,决定岩石的最终分类。分数高于阈值的岩石被分类为钻石(正类),而低于阈值的岩石则被丢弃为普通岩石(负类)。

现在,假设所有岩石按其决策分数升序排列,如图 7.4所示。最左侧的岩石分数最低,最不可能是钻石,而最右侧的岩石分数最高,最可能是钻石。在理想情况下,决策阈值右侧的每块岩石都会是钻石,而左侧的每块岩石都是普通石头。

考虑如图 7.4所示的情况,其中决策阈值处于中央。在决策边界的右侧,我们发现三个实际的钻石(TPs)和一个被错误标记为钻石的普通岩石(FPs)。在左侧,我们有两个正确识别的普通岩石(TNs)和两个被错误分类为普通岩石的钻石(FNs)。

因此,在决策阈值的左侧,你会发现两个正确分类和两个误分类。它们分别是 2 个真正负类(TNs)和 2 个假负类(FNs)。

让我们计算图 7.4中的召回率和精确率:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_011.png

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_012.png

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_04.png

图 7.4:精确率/召回率权衡:岩石按分类器分数排序

处于决策阈值之上的被认为是钻石。

请注意,阈值越高,精确率越高,但召回率越低。

调整决策阈值会影响精确率和召回率之间的权衡。如果我们将阈值向右移动(如图 7.5所示),我们增加了将岩石分类为钻石的标准,从而提高了精确率,但降低了召回率:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_05.png

图 7.5:精确率/召回率权衡:岩石按分类器分数排序

处于决策阈值之上的被认为是钻石。请注意,阈值越高,精确率越高,但召回率越低。

图 7.6中,我们降低了决策阈值。换句话说,我们降低了将岩石分类为钻石的标准。因此,假负类(即漏掉的宝石)会减少,但假正类(即错误信号)会增加。因此,如果我们降低阈值(如图 7.6所示),我们放宽了钻石分类的标准,增加了召回率,但降低了精确率:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_06.png

图 7.6:精确度/召回率权衡:岩石根据其分类器得分排序

那些高于决策阈值的被视为钻石。请注意,阈值越高,精确度越高,但召回率越低。

所以,调整决策边界的值就是在召回率和精确度之间权衡。我们增加决策边界以获得更好的精确度,同时可以预期更多的召回率;我们降低决策边界以获得更好的召回率,但可能会牺牲精确度。

让我们绘制一张精确度与召回率的图表,以便更好地理解两者之间的权衡:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_07.png

图 7.7:精确度与召回率的关系

召回率和精确度的最佳选择是什么?

增加召回率是通过降低我们用来识别数据点为正样本的标准来实现的。精确度预计会降低,但如上图所示,当精确度降到约 0.8 时,下降幅度非常明显。这是我们可以选择合适的召回率和精确度值的点。在上面的图表中,如果我们选择 0.8 作为召回率,精确度为 0.75。我们可以将其解释为能够标记 80% 的所有目标数据点。在这个精确度水平下,我们对这些数据点的标记准确率为 75%。如果没有特定的业务需求,且只是用于通用场景,这可能是一个合理的折中方案。

展示精确度与召回率之间固有权衡的另一种方法是使用 接收操作特性曲线ROC)。为此,我们先定义两个术语:真正例率TPR)和 假正例率FPR)。

让我们看看 ROC 曲线。为了计算 TPR 和 FPR,我们需要观察坑中的钻石:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_013.png

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_014.png

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_015.png

请注意:

  • TPR 等于召回率或命中率。

  • TNR 可以看作是负事件的召回率或命中率。它决定了我们正确识别负事件的成功率。它也叫做 特异度

  • FPR = 1 – TNR = 1 - 特异度。

很明显,这些图中的 TPR 和 FPR 可以通过以下方式计算:

图号TPRFPR
7.43/5=0.61/3=0.33
7.52/5=0.40/3 = 0
7.65/5 = 11/3 = 0.33

请注意,降低我们的决策阈值会增加 TPR 或召回率。为了从矿山中尽可能多地提取钻石,我们会降低将石头分类为钻石的标准。结果是更多的石头会被错误地分类为钻石,从而增加 FPR。

请注意,一个高质量的分类算法应该能够为每一块岩石提供决策分数,这个分数大致对应岩石成为钻石的概率。该算法的输出如图 7.8所示。钻石应该位于右侧,石块应该位于左侧。在图中,随着我们将决策阈值从0.8降到0.2,我们预计 TRP 和 FPR 会有显著的提升。实际上,TRP 的急剧增加与 FPR 的轻微增加是评估二分类器质量的最佳指标之一,因为该分类算法能够生成与岩石成为钻石的可能性直接相关的决策分数。如果钻石和石块在决策分数轴上随机分布,降低决策阈值会同样可能标记为石块或钻石。这将是最差的二分类器,也叫做随机化器:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_08.png

图 7.8:ROC 曲线

理解过拟合

如果一个机器学习模型在开发环境中表现优秀,但在生产环境中明显退化,我们称这个模型为过拟合。这意味着训练出的模型过于贴合训练数据集,模型创建的规则中包含了过多的细节。这表明模型的方差与偏差之间的权衡最能体现这一概念。

在开发机器学习模型时,我们通常会对模型需要捕捉的真实世界现象做出一些简化假设。这些假设对于使建模过程更可控、更简洁至关重要。然而,这些假设的简化会在我们的模型中引入一定程度的“偏差”。

让我们进一步分析一下。偏差是一个量化我们预测值与真实值偏离程度的术语。简单来说,如果我们有较高的偏差,意味着我们的模型预测值与实际值相差较远,这会导致训练数据上较高的误差率。

例如,考虑线性回归模型。它假设输入特征与输出变量之间存在线性关系。然而,在现实世界中,关系可能是非线性的或更加复杂的,这并不总是成立。虽然这个线性假设简化了我们的模型,但它可能会导致较高的偏差,因为它可能无法完全捕捉变量之间的实际关系。

现在,让我们来谈谈“方差”。在机器学习中,方差是指如果我们使用不同的训练数据集,我们的模型预测会发生多大的变化。一个高方差的模型非常注重训练数据,容易从噪声和细节中学习。因此,它在训练数据上的表现非常好,但在未见过的数据或测试数据上的表现较差。这种表现差异通常被称为过拟合。

我们可以通过靶心图来可视化偏差和方差,如图 7.9所示。请注意,靶心的中心是一个完美预测正确值的模型。离靶心较远的射击表示高偏差,而分散较广的射击表示高方差。在理想的情况下,我们希望偏差低、方差低,所有射击都击中靶心。然而,在现实世界中,这是一种权衡。降低偏差会增加方差,而降低方差会增加偏差。

这就是偏差-方差权衡,它是机器学习模型设计中的一个基本概念:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_09.png

图 7.9:偏差和方差的图示

在机器学习模型中,平衡适当的泛化程度是一个微妙的过程。这个平衡,有时也是不平衡的,正是通过偏差-方差权衡来描述的。机器学习中的泛化指的是模型在适应新数据(这些数据来源于与训练数据相同的分布)时的能力。换句话说,一个泛化良好的模型能够将从训练数据中学到的规则有效地应用于新的、未见过的数据。通过使用更简单的假设,可以实现更广泛的规则,从而使模型对训练数据的波动不那么敏感。这意味着模型的方差较低,因为它在不同训练集之间变化不大。

然而,这也有其负面影响。简单的假设意味着模型可能无法完全捕捉数据中的所有复杂关系。这会导致模型始终偏离真实输出,从而增加偏差。

因此,从这个角度看,更高的泛化意味着更低的方差,但更高的偏差。这就是偏差-方差权衡的本质:一个过度泛化的模型(高偏差)可能会过度简化问题,错过重要的模式,而一个泛化不足的模型(高方差)可能会对训练数据进行过拟合,同时捕捉噪声和信号。

在这两种极端之间找到平衡是机器学习中的一个核心挑战,管理这种权衡的能力往往能够决定一个模型是好还是优秀。偏差与方差之间的这种权衡由算法的选择、数据的特征以及各种超参数决定。根据你试图解决的具体问题的需求,达到偏差与方差之间的适当妥协是非常重要的。

现在让我们来看一下如何指定分类器的不同阶段。

指定分类器的阶段

一旦标记数据准备好,分类器的开发就包括训练、评估和部署。这三个实施分类器的阶段在下面的**跨行业数据挖掘标准流程(CRISP-DM)**生命周期图中有所展示(CRISP-DM 生命周期在第五章图算法中有更详细的解释):

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_10.png

图 7.10: CRISP DM 生命周期

在实施分类器模型时,需要考虑几个关键阶段,从彻底理解当前的业务问题开始。这包括识别解决此问题所需的数据,并理解数据的实际背景。收集相关的标记数据后,下一步是将数据集分成两部分:训练集和测试集。训练集通常较大,用于训练模型以理解数据中的模式和关系。另一方面,测试集用于评估模型在未见过的数据上的表现。

为确保这两个数据集能代表整体数据分布,我们将使用随机抽样技术。这样,我们可以合理地期望整个数据集中的模式在训练集和测试集两部分中都有体现。

注意,如图 7.10所示,首先是训练阶段,在该阶段使用训练数据来训练模型。训练阶段结束后,使用测试数据评估训练后的模型。通过不同的性能矩阵来量化训练模型的表现。一旦模型被评估,我们进入模型部署阶段,将训练好的模型部署并用于推断,通过标记未标记数据来解决实际问题。

现在,让我们来看一些分类算法。

在接下来的章节中,我们将介绍以下分类算法:

  • 决策树算法

  • XGBoost 算法

  • 随机森林算法

  • 逻辑回归算法

  • SVM算法

  • 朴素贝叶斯算法

让我们从决策树算法开始。

决策树分类算法

决策树基于递归划分方法(分治法),它生成一组规则用于预测标签。算法从根节点开始,并将其划分为多个分支。内部节点表示对某个属性的测试,测试结果通过分支表示到达下一层级。决策树在叶节点结束,叶节点包含决策。当划分不再改善结果时,过程停止。

现在,让我们深入了解决策树算法的细节。

理解决策树分类算法

决策树分类的独特之处在于生成一组可由人类理解的规则层次结构,这些规则用于在运行时预测标签。该模型的透明性是其一个主要优势,因为它使我们能够理解每个预测背后的推理过程。这个层次结构是通过递归算法形成的,遵循一系列步骤。

首先,让我们通过一个简化的例子来说明这一点。考虑一个预测一个人是否会喜欢某部特定电影的决策树模型。树顶端的决策或“规则”可能是,“这部电影是喜剧吗?”如果答案是“是”,树则分支到下一个规则,比如,“这部电影是否由这个人的最喜欢的演员主演?”如果答案是否,树则分支到另一个规则。每个决策点都会进一步细分,形成规则的树状结构,直到我们得到最终的预测。

通过这一过程,决策树引导我们通过一系列易于理解的逻辑步骤,最终得出预测结果。这种清晰性使得决策树分类器区别于其他机器学习模型。

该算法本质上是递归的。创建这一规则层次结构涉及以下步骤:

  1. 找到最重要的特征:在所有特征中,算法识别出最能区分训练数据集中的数据点与标签之间的特征。计算依据诸如信息增益或基尼不纯度等度量。

  2. 二分:使用最重要的特征,算法创建一个标准,用于将训练数据集分为两个分支:

    • 符合标准的数据点

    • 不符合标准的数据点

  3. 检查叶节点:如果任何结果分支中大部分标签属于同一类别,该分支将成为最终分支,从而形成叶节点。

  4. 检查停止条件并重复:如果提供的停止条件未满足,算法将回到步骤 1进行下一次迭代。否则,模型将标记为已训练,并且结果决策树中最低级别的每个节点将被标记为叶节点。停止条件可以简单地定义为迭代次数,或者使用默认的停止条件,当算法达到每个叶节点的同质性水平时便停止。

决策树算法可以通过以下图示进行解释:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_11.png

图 7.11:决策树

在上面的图中,根节点包含了一些圆圈和叉号。它们仅代表某个特征的两类不同类别。该算法创建了一个标准,试图将圆圈与叉号分开。在每个层级,决策树对数据进行划分,并且这些划分期望在从第 1 层开始越来越均匀。一个完美的分类器的叶节点只包含圆圈或叉号。由于现实世界数据集的内在不可预测性和噪声,训练完美的分类器通常是困难的。

请注意,决策树具有一些关键优势,使其在许多场景中成为首选。决策树分类器的优点在于其可解释性。与许多其他模型不同,它们提供了一套清晰透明的“如果-那么”规则,使得决策过程易于理解和审计。这在医疗保健或金融等领域尤为有利,在这些领域,理解预测背后的逻辑与预测本身一样重要。

此外,决策树对数据的规模不太敏感,并且能够处理分类和数值变量的混合。这使得它们在面对多样化数据类型时成为一个多功能的工具。

因此,尽管训练一个“完美”的决策树分类器可能是困难的,但它们所提供的优势,包括简单性、透明性和灵活性,通常足以克服这一挑战。

我们将使用决策树分类算法来解决分类器挑战问题。

现在,让我们使用决策树分类算法解决我们之前定义的常见问题,预测客户是否最终购买产品:

  1. 首先,让我们实例化决策树分类算法,并使用我们为分类器准备的数据的训练部分来训练一个模型:

    classifier = sklearn.tree.DecisionTreeClassifier(criterion = 'entropy', random_state = 100, max_depth=2) 
    
    DecisionTreeClassifier(criterion = 'entropy', random_state = 100, max_depth=2) 
    
  2. 现在,让我们使用我们训练好的模型来预测标注数据的测试部分的标签。让我们生成一个混淆矩阵,以总结我们训练模型的性能:

    y_pred = classifier.predict(X_test)
    cm = metrics.confusion_matrix(y_test, y_pred) 
    
  3. 这将给出以下输出:

    cm 
    
    array([[64, 4],
           [2, 30]]) 
    
  4. 现在,让我们使用决策树分类算法计算所创建分类器的准确率召回率精确度值:

    accuracy= metrics.accuracy_score(y_test,y_pred)
    recall = metrics.recall_score(y_test,y_pred)
    precision = metrics.precision_score(y_test,y_pred)
    print(accuracy,recall,precision) 
    
  5. 运行上述代码将产生以下输出:

    0.94 0.9375 0.8823529411764706 
    

性能衡量标准帮助我们将不同的训练建模技术相互比较。

现在,让我们来探讨决策树分类器的优缺点。

决策树分类器的优缺点

在本节中,我们将讨论使用决策树分类算法的优缺点。

决策树分类器最显著的优势之一在于其固有的透明性。支配其模型构建的规则是可读且可解释的,这使得它们非常适合那些需要清晰理解决策过程的情境。这种类型的模型通常被称为白盒模型,是需要最小化偏差并最大化透明度的场景中不可或缺的组成部分。这在政府和保险等关键行业中尤其重要,因为这些行业对问责制和可追溯性有着至关重要的要求。

此外,决策树分类器能够很好地处理分类变量。它们的设计天生适合从离散问题空间中提取信息,这使得它们在大多数特征属于特定类别的数据集中特别合适。

另一方面,决策树分类器也确实存在一些局限性。它们最大的挑战是容易过拟合。当决策树深入挖掘时,可能会创建捕捉过多细节的规则,这会导致模型从训练数据中过度泛化,进而在未见过的数据上表现不佳。因此,使用决策树分类器时,实施剪枝等策略以防止过拟合是至关重要的。

决策树分类器的另一个局限性是它们在处理非线性关系时的困难。它们的规则主要是线性的,因此可能无法捕捉到非直线关系的细微差别。因此,虽然决策树为解决问题带来了一些显著的优势,但它们的弱点在选择适当的模型时需要特别谨慎。

用例

决策树分类器可以用于以下用例来分类数据:

  • 抵押贷款申请:训练一个二分类器来判断申请人是否可能违约。

  • 客户细分:将客户分类为高价值客户、中等价值客户和低价值客户,以便根据每个类别定制营销策略。

  • 医学诊断:训练一个分类器来区分良性或恶性肿瘤。

  • 治疗效果分析:训练一个分类器来标记对某种治疗反应积极的患者。

  • 使用决策树进行特征选择:在考察决策树分类器时,另一个值得讨论的方面是它们的特征选择能力。在规则创建过程中,决策树倾向于从数据集中选择一部分特征。决策树这一固有特性在处理特征数目庞大的数据集时尤为有益。

你可能会问,为什么特征选择如此重要?在机器学习中,处理大量特征可能是一个挑战。特征过多可能导致模型复杂,难以解释,甚至由于"维度诅咒"导致性能下降。

通过自动选择最重要的特征子集,决策树可以简化模型,并专注于最相关的预测变量。

值得注意的是,决策树中的特征选择过程并不限于它们自身模型的开发。这个过程的结果也可以作为其他机器学习模型的初步特征选择。这可以提供哪些特征最重要的初步了解,并帮助简化其他机器学习模型的开发。

接下来,让我们深入探讨集成方法。

理解集成方法

在机器学习领域,集成是一种技术,其中多个模型,每个模型都有细微的差异,被创建并结合成一个复合或聚合模型。这些差异可以来自使用不同的模型参数、数据子集,甚至是不同的机器学习算法。

然而,"稍微不同"在这个上下文中是什么意思?在这里,集成中的每个单独模型都是为了独特性而创建的,但不会有根本性的差异。这可以通过调整超参数、在不同的训练数据子集上训练每个模型,或者使用不同的算法来实现。目标是让每个模型捕捉数据的不同方面或细微差别,当它们结合在一起时,可以帮助提升整体的预测能力。

那么,这些模型是如何结合的呢?集成技术涉及一种称为聚合的决策过程,其中个别模型的预测被合并。这可能是一个简单的平均值、一个多数投票,或者是根据所使用的具体集成技术采用更复杂的方法。

至于何时以及为何需要集成方法,当单个模型无法达到较高的准确率时,它们尤其有用。通过组合多个模型,集成可以捕捉更多的复杂性,并通常能获得更好的性能。这是因为集成可以平均化偏差,减少方差,并且不容易过拟合训练数据。

最后,评估一个集成模型的有效性类似于评估单个模型。可以根据问题的性质使用如准确率、精确度、召回率或 F1 分数等指标。关键的不同在于,这些指标是应用于集成的聚合预测,而不是单一模型的预测。

让我们先看一些集成算法,从 XGBoost 开始。

使用 XGBoost 算法实现梯度提升

XGBoost 于 2014 年推出,是一种集成分类算法,因其基于梯度提升原理而广受欢迎。那么,梯度提升到底是什么呢?本质上,它是一种机器学习技术,涉及顺序构建多个模型,每个新模型都试图纠正前一个模型的错误。这个过程会一直进行,直到误差率显著降低,或添加了预定义数量的模型为止。

在 XGBoost 的背景下,它采用一组相互关联的决策树,并通过梯度下降优化它们的预测结果,梯度下降是一种常用的优化算法,旨在找到一个函数的最小值——在这种情况下是残差误差。简单来说,梯度下降通过不断调整模型来最小化预测值与实际值之间的差异。

XGBoost 的设计使其非常适合分布式计算环境。这种兼容性扩展到 Apache Spark——一个大规模数据处理平台,以及像 Google Cloud 和Amazon Web ServicesAWS)这样的云计算平台。这些平台提供了高效运行 XGBoost 所需的计算资源,特别是在处理更大数据集时。

现在,我们将走过使用 XGBoost 算法实现梯度提升的过程。我们的旅程包括数据准备、模型训练、生成预测和评估模型性能。首先,数据准备是正确使用 XGBoost 算法的关键。原始数据通常包含不一致性、缺失值或可能不适合算法的变量类型。因此,必须对数据进行预处理和清理,按需规范化数值字段并编码分类字段。一旦数据格式化正确,我们可以继续进行模型训练。已经创建了一个 XGBClassifier 实例,我们将用它来拟合我们的模型。让我们看看步骤:

  1. 这个过程使用X_trainy_train数据子集进行训练,分别代表我们的特征和标签:

    from xgboost import XGBClassifier
    classifier = XGBClassifier()
    classifier.fit(X_train, y_train) 
    
    XGBClassifier(base_score=None, booster=None, callbacks=None,
                  colsample_bylevel=None, colsample_bynode=None,
                  colsample_bytree=None, early_stopping_rounds=None,
                  enable_categorical=False, eval_metric=None, feature_types=None,
                  gamma=None, gpu_id=None, grow_policy=None, importance_type=None,
                  interaction_constraints=None, learning_rate=None, max_bin=None,
                  max_cat_threshold=None, max_cat_to_onehot=None,
                  max_delta_step=None, max_depth=None, max_leaves=None,
                  min_child_weight=None, missing=nan, monotone_constraints=None,
                  n_estimators=100, n_jobs=None, num_parallel_tree=None,
                  predictor=None, random_state=None, ...) 
    
  2. 然后,我们将基于新训练的模型生成预测结果:

    y_pred = classifier.predict(X_test)
    cm = metrics.confusion_matrix(y_test, y_pred) 
    
  3. 该过程产生如下输出:

    cm 
    
    array([[64, 4],
           [4, 28]]) 
    
  4. 最后,我们将量化模型的性能:

    accuracy = metrics.accuracy_score(y_test,y_pred)
    recall = metrics.recall_score(y_test,y_pred)
    precision = metrics.precision_score(y_test,y_pred)
    print(accuracy,recall,precision) 
    
  5. 这将给我们以下输出:

    0.92 0.875 0.875 
    

现在,让我们看看随机森林算法。

随机森林算法是一种集成学习方法,通过结合多个决策树的输出,减少偏差和方差,从而实现其效果。在这里,我们深入探讨它是如何训练的,以及它是如何生成预测的。在训练过程中,随机森林算法利用一种称为 bagging(或自助聚合)的技术。它从训练数据集中生成N个子集,每个子集通过从输入数据中随机选择一些行和列来创建。这个选择过程引入了随机性,因此被称为“随机森林”。每个数据子集用于训练一棵独立的决策树,最终得到一组树,表示为 C[1]到 C[m]。这些树可以是任何类型,但通常是二叉树,每个节点根据一个特征来划分数据。

在预测方面,随机森林模型采用民主投票系统。当一个新的数据实例被输入模型进行预测时,森林中的每棵决策树都会生成自己的标签。最终预测由多数投票决定,意味着得到最多票数的标签将成为整体预测。

图 7.12所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_12.png

图 7.12:随机森林

请注意,在图 7.12中,训练了m棵树,表示为C[1]到C[m],即树 = {C[1],…,C[m]*}。

每棵树都会生成一个预测,表示为一个集合:

个体预测 = P= {P[1],…, P[m]}

最终预测由Pf表示。它由大多数个体预测决定。可以使用mode函数找到多数决策(mode是出现次数最多且占多数的数字)。个体预测与最终预测是相关的,如下所示:

Pf = mode (P) 

这种集成技术提供了几个优势。首先,数据选择和决策树构建中引入的随机性降低了过拟合的风险,从而提高了模型的鲁棒性。其次,森林中的每棵树都独立运行,使得随机森林模型高度并行化,因此适用于大数据集。最后,随机森林模型具有多功能性,能够处理回归和分类任务,并有效应对缺失数据或异常数据。

然而,请记住,随机森林模型的有效性在很大程度上取决于它包含的树木数量。树木太少可能导致模型较弱,而树木太多则可能导致不必要的计算。根据应用的具体需求,调整这个参数非常重要。

区分随机森林算法与集成增强方法

随机森林和集成增强法代表了集成学习的两种不同方法,集成学习是机器学习中的一种强大方法,它通过结合多个模型来创建更稳健和准确的预测。

在随机森林算法中,每棵决策树独立运行,不受其他树的性能或结构的影响。每棵树都是从数据的不同子集构建的,并且在做出决策时使用不同的特征子集,这增加了集成的整体多样性。最终输出是通过对所有树的预测进行聚合来确定的,通常通过多数投票。

集成增强法(ensemble boosting)采用一种顺序过程,每个模型都意识到其前一个模型的错误。增强技术生成一系列模型,其中每个后续模型旨在纠正前一个模型的错误。通过为下一模型分配更高的权重给训练集中的错误分类实例来实现这一点。最终的预测是所有模型预测的加权总和,从而有效地赋予更准确的模型更多的影响力。

本质上,虽然随机森林利用独立性和多样性的优势,集成增强法则侧重于纠正错误并从过去的错误中改进。每种方法都有其自身的优势,根据数据的性质和结构的不同,它们可能更有效。

使用随机森林算法进行分类挑战

让我们实例化随机森林算法,并使用训练数据来训练我们的模型。

这里有两个关键的超参数,我们将重点关注:

  • n_estimators

  • max_depth

n_estimators 超参数决定了集成中构建的单个决策树的数量。本质上,它决定了“森林”的大小。更多的树通常会导致更稳健的预测,因为它增加了决策路径的多样性和模型的泛化能力。然而,重要的是要注意,增加更多的树也会增加计算复杂性,超过某个点后,准确性的提升可能变得微不足道。

另一方面,max_depth 超参数指定了每棵树可以达到的最大深度。在决策树的上下文中,“深度”是指从根节点(树顶的起始点)到叶节点(底部的最终决策输出)之间的最长路径。通过限制最大深度,我们实际上控制了学习结构的复杂性,在欠拟合和过拟合之间取得平衡。过于浅的树可能会错过重要的决策规则,而过于深的树可能会对训练数据进行过拟合,捕捉到噪声和异常值。

微调这两个超参数在优化基于决策树的模型性能方面起着至关重要的作用,能够在预测能力和计算效率之间取得正确的平衡。

为了使用随机森林算法训练分类器,我们将执行以下步骤:

classifier = RandomForestClassifier(n_estimators = 10, max_depth = 4,
criterion = 'entropy', random_state = 0)
classifier.fit(X_train, y_train) 
RandomForestClassifier(n_estimators = 10, max_depth = 4,criterion = 'entropy', random_state = 0) 

一旦随机森林模型训练完成,我们就可以用它来进行预测:

y_pred = classifier.predict(X_test)
cm = metrics.confusion_matrix(y_test, y_pred)
cm 

这将给出如下输出:

array ([[64, 4],
        [3, 29]]) 

现在,让我们量化模型的好坏:

accuracy= metrics.accuracy_score(y_test,y_pred)
recall = metrics.recall_score(y_test,y_pred)
precision = metrics.precision_score(y_test,y_pred)
print(accuracy,recall,precision) 

我们将观察到以下输出:

0.93 0.90625 0.8787878787878788 

请注意,随机森林是一种流行且多用途的机器学习方法,既可以用于分类任务,也可以用于回归任务。它因其简单性、鲁棒性和灵活性而著称,使其能够应用于各种不同的场景。

接下来,我们来看一下逻辑回归。

逻辑回归

逻辑回归是一种用于二分类的分类算法。它使用逻辑函数来表述输入特征与标签之间的关系。它是最简单的分类技术之一,用于建模二元因变量。

假设条件

逻辑回归假设如下:

  • 训练数据集没有缺失值。

  • 标签是二元分类变量。

  • 标签是有序的——换句话说,是具有顺序值的分类变量。

  • 所有特征或输入变量相互独立。

建立关系

对于逻辑回归,预测值的计算方法如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_035.png

假设:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_036.png

所以现在:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_016.png

前述关系可以图形化表示如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_13.png

图 7.13:绘制 sigmoid 函数

请注意,如果 z 很大,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_017.png (z) 将等于 1。如果 z 很小或是一个很大的负数,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_017.png (z) 将等于 0。另外,当 z 为 0 时,https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_017.png (z) = 0.5。sigmoid 是一个自然函数,适用于表示概率,因为它的值严格限定在 0 到 1 之间。所谓“自然”是指它由于固有的性质而特别适用或有效。在这种情况下,sigmoid 函数始终输出一个介于 0 和 1 之间的值,这与概率范围一致。这使得它成为逻辑回归中建模概率的一个优秀工具。训练逻辑回归模型的目标是找到 wj 的正确值。

逻辑回归得名于用于表述它的函数,称为逻辑函数sigmoid 函数

损失函数和成本函数

损失函数定义了我们如何为训练数据中的某个特定示例量化误差。成本函数定义了我们如何在整个训练数据集上最小化误差。因此,损失函数用于训练数据集中的单个示例,而成本函数用于量化实际值和预测值之间的总体偏差。它依赖于wh的选择。

在逻辑回归中,用于训练集中特定示例i损失函数如下:

损失(ý^((i)), y^((i))) = - (y^((i)) log ý^((i)) + (1-y^((i)) ) log (1-ý^((i)))

请注意,当y^((i)) = 1 时,损失(ý^((i)), y^((i))) = - logý((i))。最小化损失将导致ý((i))的值变大。作为一个 sigmoid 函数,它的最大值为1

如果y^((i)) = 0,损失(ý^((i)), y^((i))) = - log (1-ý^((i)))

最小化损失将导致ý^((i))尽可能小,这时其值为0

逻辑回归的成本函数如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_020.png

现在,让我们深入了解逻辑回归的细节。

何时使用逻辑回归

逻辑回归在二分类器中表现很好。为了澄清,二分类是指预测两种可能结果之一的过程。例如,如果我们尝试预测一封邮件是否为垃圾邮件,这就是一个二分类问题,因为只有两种可能的结果——‘垃圾邮件’或‘非垃圾邮件’。

然而,逻辑回归存在一些局限性。特别是,当处理质量较差的大型数据集时,它可能会遇到困难。例如,假设有一个数据集,其中包含大量缺失值、异常值或无关特征。在这种情况下,逻辑回归模型可能会发现很难做出准确的预测。

此外,虽然逻辑回归能够有效地处理特征与目标变量之间的线性关系,但在处理复杂的非线性关系时可能会显得力不从心。想象一个数据集,其中预测变量与目标之间的关系不是一条直线,而是曲线;在这种情况下,逻辑回归模型可能会遇到困难。

尽管存在这些局限性,逻辑回归通常仍然是分类任务的一个可靠起点。它提供了一个基准性能,可以用来与更复杂的模型效果进行比较。即使它不能提供最高的准确度,但它提供了可解释性和简单性,在某些情境下是非常有价值的。

使用逻辑回归算法进行分类器挑战

在本节中,我们将看到如何使用逻辑回归算法来进行分类器挑战:

  1. 首先,让我们实例化一个逻辑回归模型并使用训练数据进行训练:

    from sklearn.linear_model import LogisticRegression
    classifier = LogisticRegression(random_state = 0)
    classifier.fit(X_train, y_train) 
    
  2. 让我们预测test数据的值并创建混淆矩阵:

    y_pred = classifier.predict(X_test)
    cm = metrics.confusion_matrix(y_test, y_pred)
    cm 
    
  3. 我们在运行上述代码后得到以下输出:

    array ([[65, 3],
            [6, 26]]) 
    
  4. 现在,让我们来看一下性能指标:

    accuracy= metrics.accuracy_score(y_test,y_pred)
    recall = metrics.recall_score(y_test,y_pred)
    precision = metrics.precision_score(y_test,y_pred)
    print(accuracy,recall,precision) 
    
  5. 我们在运行上述代码后得到以下输出:

    0.91 0.8125 0.8996551724137931 
    

接下来,让我们看看SVM

SVM 算法

SVM分类器是机器学习工具中的一个强大工具,通过识别一个最优决策边界或超平面,来显著区分两类。为了进一步解释,可以将这个“超平面”看作是一条线(二维中),一个面(三维中),或一个流形(高维中),它能最好地分隔特征空间中的不同类别。

区分 SVM 的关键特性是其优化目标——旨在最大化边距,即决策边界与来自任一类的最近数据点(称为“支持向量”)之间的距离。简单来说,SVM 算法不仅仅是找到一条分割类别的线;它还尝试找到一条尽可能远离每个类别最近点的线,从而最大化分隔间隙。

考虑一个基本的二维示例,我们尝试将圆圈与叉号分开。我们使用 SVM 的目标不仅仅是找到一条分开这两种形状的线,而是找到一条线,使它与最靠近它的圆圈和叉号之间的距离最大。

SVM(支持向量机)在处理高维数据、复杂领域,或当类之间无法通过简单的直线分割时,非常有用。它们在逻辑回归可能失败的地方表现尤为出色,例如,在非线性可分的数据场景中。

边距定义为分隔超平面(决策边界)与最接近该超平面的训练样本之间的距离,这些训练样本被称为支持向量。因此,我们从一个非常基本的二维示例开始,X[1]和X[2]。我们希望找到一条线来分开圆圈和叉号。如下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_14.png

图 7.14:SVM 算法

我们画了两条线,两条线都能完美地将叉号和圆圈分开。然而,必须存在一条最优线或决策边界,它能为我们提供最大机会,以正确分类大多数额外的样本。一个合理的选择可能是将这两类之间均匀分隔的线,从而为每个类提供一点缓冲区,如下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_15.png

图 7.15:与 SVM 相关的概念

此外,与逻辑回归不同,SVM 更适合处理较小、更干净的数据集,并且它能够在不需要大量数据的情况下捕捉复杂的关系。然而,这里有一个权衡——可解释性——尽管逻辑回归能够提供易于理解的模型决策过程,SVM 由于其固有的复杂性,解释起来不如逻辑回归直观。

现在,让我们看看如何使用 SVM 来训练一个分类器以应对我们的挑战。

使用 SVM 算法解决分类器挑战

首先,让我们实例化 SVM 分类器,然后使用标记数据的训练部分来训练它。kernel 超参数决定了对输入数据应用何种变换,以使其线性可分:

from sklearn.svm import SVC
classifier = SVC(kernel = 'linear', random_state = 0)
classifier.fit(X_train, y_train) 
  1. 一旦训练完成,让我们生成一些预测并查看混淆矩阵:

    y_pred = classifier.predict(X_test)
    cm = metrics.confusion_matrix(y_test, y_pred)
    cm 
    
  2. 观察以下输出:

    array ([[66, 2],
            [9, 23]]) 
    
  3. 现在,让我们来看一下各种性能指标:

    accuracy= metrics.accuracy_score(y_test,y_pred)
    recall = metrics.recall_score(y_test,y_pred)
    precision = metrics.precision_score(y_test,y_pred)
    print(accuracy,recall,precision) 
    

运行上述代码后,我们得到以下值作为输出:

0.89 0.71875 0.92 

理解朴素贝叶斯算法

基于概率论,朴素贝叶斯是最简单的分类算法之一。如果使用得当,它可以给出准确的预测。朴素贝叶斯算法得名有两个原因:

  • 它基于一个朴素假设,即特征与输入变量之间是独立的。

  • 它基于贝叶斯定理。请注意,贝叶斯定理用于计算在观察到某些特征的情况下,特定类别或结果的概率。

该算法尝试基于先前属性/实例的概率来分类实例,假设属性之间完全独立。

事件有三种类型:

  • 独立事件不会影响另一个事件发生的概率(例如,收到一封提供免费入场技术活动的电子邮件 公司进行重组的事件)。

  • 相关事件会影响另一个事件发生的概率;也就是说,它们在某种程度上是相关的(例如,你准时到达会议的概率可能会受到航空公司员工罢工或航班可能不准时的影响)。

  • 互斥事件不能同时发生(例如,掷骰子时同时出现三和六的概率为 0——这两个结果是互斥的)。

贝叶斯定理

贝叶斯定理用于计算两个独立事件 AB 之间的条件概率。事件 AB 发生的概率分别由 P(A) 和 P(B) 表示。条件概率由 P(B|A) 表示,即在事件 A 发生的情况下,事件 B 发生的条件概率:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_021.png

在应用朴素贝叶斯算法时,特别适用于输入数据维度(特征数量)较高的场景。这使得它非常适合用于文本分类任务,如垃圾邮件检测或情感分析。

它可以处理连续数据和离散数据,并且计算效率高,适用于实时预测。当你拥有有限的计算资源并且需要快速简单的实现时,朴素贝叶斯也是一个不错的选择。但需要注意的是,它的“朴素”假设(即特征独立性)在某些情况下可能成为限制。

计算概率

朴素贝叶斯基于概率基础。单个事件发生的概率(观察概率)是通过计算事件发生的次数,并将其除以可能导致该事件发生的总次数。例如,呼叫中心每天接到超过 100 个支持电话,在一个月内发生了 50 次。你想知道在过去的时间记录下,电话能否在三分钟内得到回应的概率。如果呼叫中心在 27 次情况下达到了这个时间记录,那么 100 个电话在三分钟内得到回应的观察概率如下:

P(3 分钟内接到 100 个支持电话) = (27 / 50) = 0.54(54%)

基于过去 50 次发生的记录,100 个电话可以在大约一半的时间内在三分钟内得到回应。

现在,让我们来看看AND事件的乘法规则。

AND事件的乘法规则

要计算两个或多个事件同时发生的概率,首先要考虑事件是独立的还是依赖的。如果它们是独立的,则使用简单的乘法规则:

P(结果 1 AND 结果 2)= P(结果 1) P(结果 2)*

例如,要计算收到一封包含免费入场券的技术活动电子邮件并且工作场所发生重组的概率,可以使用这个简单的乘法规则。这两个事件是独立的,因为一个事件的发生不会影响另一个事件发生的概率。

如果收到技术活动电子邮件的概率是 31%,而员工重组的概率是 82%,则两者同时发生的概率如下计算:

P(电子邮件 AND 重组) = P(电子邮件) P(重组) = (0.31) * (0.82) = 0.2542(25%)*

一般乘法规则

如果两个或多个事件是依赖的,则使用一般乘法规则。这个公式实际上在独立事件和依赖事件的两种情况下都是有效的:

P(结果 1 AND 结果 2)= P(结果 1) P(结果 2 | 结果 1)*

请注意,P(事件 2 | 事件 1) 是指在事件 1 已经发生的情况下,事件 2 发生的条件概率。这个公式反映了事件之间的依赖性。如果事件是独立的,那么条件概率就不再重要,因为一个事件不会影响另一个事件发生的概率,此时 P(事件 2 | 事件 1) 就简化为 P(事件 2)。请注意,这种情况下的公式变成了简单的乘法规则。

让我们通过一个简单的例子来说明。假设你从一副牌中抽两张牌,并且你想知道先抽到一张 ace 再抽到一张 king 的概率。第一个事件(抽到 ace)会改变第二个事件(抽到 king)的条件,因为我们并没有将 ace 放回牌堆。根据一般的乘法规则,我们可以计算为 P(ace) * P(king | ace),其中 P(king | ace) 是在已抽到 ace 的情况下抽到 king 的概率。

OR 事件的加法规则

在计算事件 A 或事件 B 发生的概率(互斥事件)时,使用以下简单的加法规则:

P(事件 1 或 事件 2) = P(事件 1) + P(事件 2)

例如,掷出 6 或 3 的概率是多少?为了解答这个问题,首先需要注意这两个结果不能同时发生。掷出 6 的概率是 (1 / 6),同样掷出 3 的概率也是 (1 / 6):

P(6 或 3) = (1 / 6) + (1 / 6) = 0.33 (33%)

如果事件不是互斥的且可以同时发生,则使用以下通用加法公式,这在互斥和非互斥的情况下都适用:

P(事件 1 或 事件 2) = P(事件 1) + P(事件 2) - P(事件 1 和 事件 2)

使用朴素贝叶斯算法解决分类器挑战

现在,让我们使用朴素贝叶斯算法来解决分类器挑战:

  1. 首先,我们将导入 GaussianNB() 函数并用它来训练模型:

    # Fitting Decision Tree Classification to the Training set
    from sklearn.naive_bayes import GaussianNB
    classifier = GaussianNB()
    classifier.fit(X_train, y_train) 
    
    GaussianNB() 
    
  2. 现在,让我们使用训练好的模型来预测结果。我们将用它来预测测试数据集的标签,即 X_test

    # Predicting the Test set results
    y_pred = classifier.predict(X_test)
    cm = metrics.confusion_matrix(y_test, y_pred) 
    
  3. 现在,让我们打印混淆矩阵:

    cm 
    
    array([[66, 2],
    [6, 26]]) 
    
  4. 现在,让我们打印性能矩阵,量化我们训练模型的质量:

    accuracy= metrics.accuracy_score(y_test,y_pred)
    recall = metrics.recall_score(y_test,y_pred)
    precision = metrics.precision_score(y_test,y_pred)
    print(accuracy,recall,precision) 
    

结果如下:

0.92 0.8125 0.9285714285714286 

对于分类算法,最终的赢家是…

让我们稍作停顿,比较一下我们讨论过的各种算法的性能指标。不过,请记住,这些指标高度依赖于我们在这些例子中使用的数据,对于不同的数据集,它们可能会有显著差异。

一个模型的性能可能会受到数据特性、数据质量以及模型假设与数据匹配程度等因素的影响。

下面是我们观察结果的总结:

算法准确率召回率精确度
决策树0.940.930.88
XGBoost0.930.900.87
随机森林0.930.900.87
逻辑回归0.910.810.89
SVM0.890.710.92
朴素贝叶斯0.920.810.92

从上表来看,决策树分类器在这个特定的背景下,无论是准确度还是召回率都展现了最高的性能。就精确度而言,我们看到 SVM 和朴素贝叶斯算法并列。

然而,记住这些结果是数据依赖的。例如,SVM 可能在数据线性可分或者通过核变换使其可分的场景中表现出色。另一方面,朴素贝叶斯在特征独立时表现良好。决策树和随机森林在处理复杂的非线性关系时可能更受青睐。逻辑回归是二分类任务的稳健选择,并且可以作为一个不错的基准模型。最后,XGBoost 作为一种集成技术,在处理各种数据类型时非常强大,且在许多任务中通常在模型性能上领先。

因此,在选择模型之前,理解你的数据和任务需求至关重要。这些结果仅仅是一个起点,应该针对每个具体的应用场景进行更深入的探索和验证。

理解回归算法

如果标签是连续变量,则监督式机器学习模型会使用回归算法。在这种情况下,机器学习模型称为回归器。

为了提供更具体的理解,假设我们想基于历史数据预测下周的温度,或者我们希望预测零售店在未来几个月的销售额。

温度和销售额都是连续变量,这意味着它们可以取指定范围内的任何值,与之相对,分类变量具有固定数量的离散类别。在这种情况下,我们会使用回归器而不是分类器。

在本节中,我们将介绍可以用于训练监督式机器学习回归模型的各种算法,简单来说就是回归器。在深入探讨这些算法的细节之前,我们先创建一个挑战,来测试这些算法的性能、能力和有效性。

回归器挑战展示

类似于我们在分类算法中使用的方法,我们将首先提出一个问题,作为所有回归算法的挑战。我们将这个共同的问题称为回归器挑战。然后,我们将使用三种不同的回归算法来解决这一挑战。使用共同挑战来比较不同回归算法的方法有两个好处:

  • 我们可以先准备数据,然后在所有三种回归算法上使用准备好的数据。

  • 我们可以以有意义的方式比较三种回归算法的性能,因为我们将使用它们来解决相同的问题。

让我们看看挑战的题目。

回归算法挑战的任务描述

预测不同车辆的油耗在今天非常重要。一辆高效的车辆对环境有益,也更具成本效益。油耗可以根据发动机的功率和车辆的特性来估算。让我们为回归算法设计一个挑战,训练一个模型,根据车辆的特性预测每加仑的英里数MPG)。

让我们看看将用于训练回归算法的历史数据集。

探索历史数据集

以下是我们拥有的历史数据集中的特征:

名称类型描述
名称类别标识特定的车辆
气缸数连续型气缸的数量(介于四个和八个之间)
排量连续型引擎的排量(单位:立方英寸)
马力连续型发动机的马力
加速度连续型从 0 加速到 60 英里每小时所需的时间(单位:秒)

这个问题的标签是一个连续变量MPG,它指定每辆车的英里每加仑(MPG)值。

首先,我们为这个问题设计数据处理管道。

使用数据处理管道进行特征工程

让我们看看如何设计一个可重用的数据处理管道来应对回归算法的挑战。如前所述,我们将一次性准备好数据,然后在所有回归算法中使用它。让我们按以下步骤进行:

  1. 我们将首先导入数据集,如下所示:

    dataset = pd.read_csv('https://storage.googleapis.com/neurals/data/data/auto.csv') 
    
  2. 现在让我们预览一下数据集:

    dataset.head(5) 
    
  3. 这就是数据集的样子:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_16.png

图 7.16:请在此添加标题

  1. 现在,让我们继续进行特征选择。我们将删除NAME列,因为它只是车辆所需的标识符。数据集中用于标识行的列与训练模型无关。让我们删除这一列。

  2. 让我们转换所有输入变量,并填充所有null值:

    dataset=dataset.drop(columns=['NAME'])
    dataset.head(5)
    dataset= dataset.apply(pd.to_numeric, errors='coerce')
    dataset.fillna(0, inplace=True) 
    

    填充缺失值可以提高数据质量,并为训练模型做好准备。现在,让我们看一下最后一步。

  3. 让我们将数据分为测试集和训练集:

    y=dataset['MPG']
    X=dataset.drop(columns=['MPG'])
    # Splitting the dataset into the Training set and Test set
    from sklearn.model_selection import train_test_split
    from sklearn.cross_validation import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 0) 
    

这已创建以下四个数据结构:

  • X_train:包含训练数据特征的数据结构

  • X_test:包含训练测试特征的数据结构

  • y_train:包含训练数据集标签值的向量

  • y_test:包含测试数据集标签值的向量

现在,让我们使用准备好的数据在三种不同的回归算法上进行测试,以便比较它们的性能。

线性回归

在各种监督学习算法中,线性回归通常被认为是最简单的。最初,我们将探讨简单线性回归,然后逐渐扩展讨论,涵盖多元线性回归。

然而,重要的是要注意,尽管线性回归方法易于实现且易于使用,但并不总是在所有情况下都是‘最佳’选择。每种机器学习算法,包括我们到目前为止讨论过的,都有其独特的优势和局限性,并且它们的效果因数据的类型和结构而异。

例如,决策树和随机森林在处理分类数据和捕捉复杂的非线性关系方面表现出色。支持向量机(SVM)在高维数据中表现良好,并且对异常值具有鲁棒性,而逻辑回归对于二分类问题特别有效。

另一方面,线性回归模型非常适合预测连续结果,并且具有可解释性,这在理解单个特征的影响时非常有价值。

简单线性回归

在最基本的层面上,线性回归建立了两个变量之间的关系,通常表示为一个自变量和一个因变量。线性回归是一种技术,可以帮助我们研究因变量(绘制在 y 轴上)的变化是如何受到自变量(绘制在 x 轴上)变化的影响的。它可以表示如下:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_022.png

这个公式可以这样解释:

线性回归基于以下假设:

  • 线性关系:自变量与因变量之间的关系是线性的。

  • 独立性:观察值之间相互独立。

  • 无多重共线性:自变量之间没有过高的相关性。

单一连续因变量与单一连续自变量之间关系的一些例子如下:

  • 一个人的体重与其卡路里摄入量之间的关系

  • 特定社区中房屋价格与其面积(以平方英尺计)之间的关系

  • 空气中的湿度与降雨概率之间的关系

对于线性回归,输入(自变量)和目标(因变量)必须都是数值型的。通过最小化每个数据点到通过所有点绘制的直线的垂直距离的平方和,来找到最佳的关系。假设自变量与标签之间是线性关系。例如,投入更多资金进行研发,会导致销售额的提高。

让我们来看一个具体的例子。假设我们要建立营销支出与某一产品销售之间的关系。经过研究发现,它们是直接相关的。营销支出和销售额在二维图表上绘制出来,显示为蓝色的菱形点。我们可以通过绘制一条直线来近似表示这种关系,如下图所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_17.png

图 7.17:线性回归

一旦画出线性直线,我们就能看到营销支出与销售额之间的数学关系。

回归模型评估

我们绘制的线性直线是依赖变量和自变量之间关系的近似。即便是最佳拟合线,也会与实际值有所偏差,如下所示:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_18.png

图 7.18:回归模型评估

量化线性回归模型性能的常见方法是使用 均方根误差 (RMSE)。该方法计算训练模型的误差标准差。例如,在训练数据集中,loss 函数的计算如下:

Loss (ý^((i)), y^((i))) = 1/2(ý^((i))- y^((i))

这得出了以下 cost 函数,用于最小化训练集所有样本的损失:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_024.png

让我们试着解释 RMSE。如果我们预测产品价格的示例模型的 RMSE 为 50 美元,这意味着大约 68.2% 的预测结果会落在真实值的 50 美元范围内(即 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_025.png)。这也意味着 95% 的预测结果会落在真实值的 100 美元范围内(即 https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_026.png)。最后,99.7% 的预测结果会落在真实值的 150 美元范围内。

让我们深入了解多元回归。

多元回归

事实上,大多数现实世界的分析都涉及多个自变量。多元回归是简单线性回归的扩展。其关键区别在于,除了原有的回归系数外,还有额外的预测变量系数。在训练模型时,目标是找到那些最小化线性方程误差的回归系数。让我们尝试用数学公式表达依赖变量与一组自变量(特征)之间的关系。

比如,在房地产市场中,房价(依赖变量)可能受多个因素的影响,如房屋的面积、位置、年龄等(自变量)。

类似于简单线性方程,依赖变量 y 可以量化为截距项与每个 i 特征的 x 值乘以回归系数的乘积之和:

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_028.png

误差由https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_029.png表示,表明预测并不完美。

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_027.png系数使每个特征对y值的影响可以单独估算,因为当x[i]增加 1 个单位时,y变化了https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_031.png。此外,截距(https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_032.png)表示当所有自变量为 0 时,y的期望值。

请注意,前面方程中的所有变量都可以表示为一组向量。目标和预测变量现在是带有一行的向量,而回归系数https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_033.png和误差https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_029.png也都是向量。

接下来,让我们看看如何使用线性回归来解决回归者挑战。

使用线性回归算法解决回归者挑战

现在,让我们使用数据集的训练部分来训练模型。请注意,我们将使用之前讨论过的相同数据和数据工程逻辑:

  1. 让我们先导入线性回归包:

    from sklearn.linear_model import LinearRegression 
    
  2. 然后,让我们实例化线性回归模型,并使用训练数据集对其进行训练:

    regressor = LinearRegression()
    regressor.fit(X_train, y_train) 
    
    LinearRegression() 
    
  3. 现在,让我们使用数据集的测试部分来预测结果:

    y_pred = regressor.predict(X_test)
    from sklearn.metrics import mean_squared_error
    sqrt(mean_squared_error(y_test, y_pred)) 
    
  4. 运行前面的代码生成的输出将如下所示:

    19.02827669300187 
    

如前节所述,RMSE 是误差的标准差。它表明 68.2%的预测结果将在标签值的4.36范围内。

让我们看看什么时候可以使用线性回归。

什么时候使用线性回归?

线性回归被用来解决许多现实世界中的问题,包括以下几个:

  • 销售预测

  • 预测最佳产品价格

  • 量化事件与反应之间的因果关系,例如在临床药物试验、工程安全测试或市场研究中。

  • 识别可以用来预测未来行为的模式,前提是已知某些标准——例如,预测保险理赔、自然灾害损失、选举结果和犯罪率。

接下来,让我们看看线性回归的弱点。

线性回归的弱点

线性回归的弱点如下:

  • 它只适用于数值特征。

  • 类别数据需要预处理。

  • 它不适应缺失数据。

  • 它对数据有假设。

回归树算法

类似于用于分类结果的分类树,回归树是决策树的另一个子集,但它们在目标或标签是连续变量而不是类别变量时使用。这一区别影响了树算法如何处理和学习数据。

对于分类树,算法试图识别数据点属于哪个类别。然而,在回归树中,目标是预测一个特定的连续值。这可能是房价、公司未来的股票价格,或者明天的温度。

分类树和回归树之间的这些差异也导致了所使用算法的不同。在分类树中,我们通常使用如基尼不纯度或熵等指标来找到最佳分割点。而回归树则利用均方误差MSE)等度量来最小化实际值和预测值之间的距离。

使用回归树算法解决回归问题挑战

在这一部分,我们将看到如何使用回归树算法来处理回归问题挑战:

  1. 首先,我们使用回归树算法来训练模型:

    from sklearn.tree import DecisionTreeRegressor
    regressor = DecisionTreeRegressor(max_depth=3)
    regressor.fit(X_train, y_train) 
    
    DecisionTreeRegressor(max_depth=3) 
    
  2. 一旦回归树模型训练完成,我们就使用训练好的模型来预测值:

    y_pred = regressor.predict(X_test) 
    
  3. 然后,我们计算 RMSE 来量化模型的性能:

    from sklearn.metrics import mean_squared_error
    from math import sqrt
    sqrt(mean_squared_error(y_test, y_pred)) 
    

我们得到了以下输出:

4.464255966462035 

梯度提升回归算法

现在,让我们把焦点转向梯度提升回归算法,它通过集成多个决策树来提取数据集中的潜在模式。

从本质上讲,梯度提升回归通过创建一个“团队”决策树来运作,每个成员逐步从前一个成员的错误中学习。实际上,序列中的每棵决策树都试图纠正前一棵树所做的预测错误,从而形成一个“集成”模型,基于所有单个树的集体智慧做出最终预测。这个算法真正独特之处在于它能够处理广泛的数据,并且具有抗过拟合的能力。这种多功能性使得它能够在不同的数据集和问题场景中表现出色。

使用梯度提升回归算法解决回归问题挑战

在这一部分,我们将看到如何使用梯度提升回归算法解决回归问题挑战,预测汽车的 MPG(每加仑英里数)评分,这是一个连续变量,因此是一个典型的回归问题。记住,我们的自变量包括像‘CYLINDERS’、‘DISPLACEMENT’、‘HORSEPOWER’、‘WEIGHT’ 和 ‘ACCELERATION’ 等特征。

仔细观察,MPG 并不像看起来那样简单,因为影响因素之间存在多方面的关系。例如,虽然具有更大排量的汽车通常消耗更多燃料,导致较低的 MPG,但这一关系可能会被像重量和马力等因素所抵消。正是这些微妙的交互作用可能会让简单的模型如线性回归或单一决策树无法捕捉到。

这时,梯度提升回归算法可能会派上用场。通过构建一个决策树的集成体,每棵树都从前一个树的错误中学习,模型将旨在辨别数据中的这些复杂模式。每棵树都为数据提供其理解,精炼预测,使其更加准确和可靠。

例如,一个决策树可能会发现,拥有较大‘DISPLACEMENT’值的汽车往往具有较低的MPG。下一个决策树可能会捕捉到一个细微差别,即具有相同‘DISPLACEMENT’值的较轻汽车(‘WEIGHT’)有时可以获得更高的MPG。通过这种迭代学习过程,模型揭示了变量之间复杂的关系层次:

  1. 我们的 Python 脚本的第一步是导入必要的库:

    from sklearn import ensemble 
    
  2. 在这里,我们从sklearn库导入ensemble模块:

    params = {'n_estimators': 500, 'max_depth': 4,          'min_samples_split': 2, 'learning_rate': 0.01,          'loss': 'squared_error'}
    regressor = ensemble.GradientBoostingRegressor(**params)
    regressor.fit(X_train, y_train) 
    
    GradientBoostingRegressor(learning_rate=0.01, max_depth=4, n_estimators=500) 
    
    y_pred = regressor.predict(X_test) 
    
  3. 最后,我们计算 RMSE 以量化模型的表现:

    from sklearn.metrics import mean_squared_error
    from math import sqrt
    sqrt(mean_squared_error(y_test, y_pred)) 
    
  4. 运行这个程序将给出如下输出值:

    4.039759805419003 
    

对于回归算法,优胜者是……

让我们看看在相同数据和完全相同的用例上,我们使用的三种回归算法的表现:

算法RMSE
线性回归4.36214129677179
回归树5.2771702288377
梯度提升回归4.034836373089085

从所有回归算法的表现来看,很明显梯度提升回归的表现最佳,因为它的 RMSE 最低。紧随其后的是线性回归。回归树算法在这个问题中的表现最差。

实际例子——如何预测天气

现在,我们将从理论过渡到应用,运用本章讨论的概念,通过一整年的某城市天气数据来预测明天的降雨情况。这个实际场景旨在强化有监督学习的原理。

有许多算法能够完成这个任务,但选择最合适的算法取决于我们问题和数据的具体特点。每种算法都有独特的优势,在特定情境下表现出色。例如,当存在明显的数值相关性时,线性回归可能是理想选择,而当处理分类变量或非线性关系时,决策树可能更有效。

对于这个预测挑战,我们选择了逻辑回归。这个选择源于我们预测目标的二元性质(即,明天是否会下雨?),这种情况下逻辑回归往往表现优异。该算法提供一个介于 0 和 1 之间的概率分数,使我们能够做出明确的是/否预测,非常适合我们的降雨预测场景。

记住,这个实际例子与之前的例子有所不同。它旨在帮助你理解我们如何选择并应用特定的算法来解决实际问题,提供了算法选择背后的思维过程的更深入理解。

训练此模型的数据位于名为weather.csv的 CSV 文件中:

  1. 让我们将数据导入为 pandas DataFrame:

    import numpy as np
    import pandas as pd
    df = pd.read_csv("weather.csv") 
    
  2. 让我们来看一下 DataFrame 的列:

    df.columns 
    
    Index(['Date', 'MinTemp', 'MaxTemp', 'Rainfall', 
           'Evaporation', 'Sunshine', 'WindGustDir', 
           'WindGustSpeed', 'WindDir9am', 'WindDir3pm', 
           'WindSpeed9am', 'WindSpeed3pm', 'Humidity9am', 
           'Humidity3pm', 'Pressure9am', 'Pressure3pm', 
           'Cloud9am', 'Cloud3pm', 'Temp9am', 'Temp3pm', 
           'RainToday', 'RISK_MM', 'RainTomorrow'],
          dtype='object') 
    
  3. 现在,让我们来看一下weather.csv数据中显示城市典型天气的前 13 列的表头:

    df.iloc[:,0:12].head() 
    

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_19.png

图 7.19:显示城市典型天气的数据

  1. 现在,让我们看看weather.csv数据的最后 10 列:

    df.iloc[:,12:25].head() 
    

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/B18046_07_20.png

图 7.20:weather.csv数据的最后 10 列

  1. 让我们用x来表示输入特征。我们将删除Date字段,因为它在预测中没有用处。我们还将删除RainTomorrow标签:

    x = df.drop(['Date','RainTomorrow'],axis=1) 
    
  2. 让我们用y来表示标签:

    y = df['RainTomorrow'] 
    
  3. 现在,让我们将数据划分为train_test_split

    from sklearn.model_selection import train_test_split
    train_x , train_y ,test_x , test_y = train_test_split(x,y,
    test_size = 0.2,random_state = 2) 
    
  4. 由于标签是一个二元变量,我们将训练一个分类器。因此,逻辑回归在这里是一个不错的选择。首先,让我们实例化逻辑回归模型:

    model = LogisticRegression() 
    
  5. 现在,我们可以使用train_xtest_x来训练模型:

    model.fit(train_x , test_x) 
    
  6. 一旦模型训练完成,我们就可以使用它进行预测:

    predict = model.predict(train_y) 
    
  7. 现在,让我们来看看我们训练模型的准确度:

    predict = model.predict(train_y)
    from sklearn.metrics import accuracy_score
    accuracy_score(predict , test_y) 
    
    0.9696969696969697 
    

现在,这个二元分类器可以用来预测明天是否会下雨。

总结

总结来说,本章是一次深入监督机器学习多面性领域的全面探索。我们重点介绍了分类和回归算法的主要组成部分,剖析了它们的机制和应用。

本章通过实际例子展示了广泛的算法,提供了在现实场景中理解这些工具功能的机会。这一旅程强调了监督学习技术的适应性及其解决各种问题的能力。

通过并列不同算法的表现,我们强调了在选择最佳机器学习策略时,上下文的重要性。数据大小、特征复杂度和预测要求等因素在这个选择过程中起着重要作用。

随着我们进入接下来的章节,从本次探索中获得的知识将作为坚实的基础。这种如何在实际场景中应用监督学习技术的理解,是机器学习广阔领域中的一项关键技能。在我们进一步探索人工智能的迷人世界时,保持这些见解在手,为深入研究神经网络的复杂世界做好准备。

在 Discord 上了解更多

要加入本书的 Discord 社区——你可以在这里分享反馈、向作者提问并了解新版本——请扫描下面的二维码:

packt.link/WHLel

https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/50-algo-evy-prog-shld-know/img/QR_Code1955211820597889031.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值