无监督学习与生成式人工智能(MEAP)(二)

原文:zh.annas-archive.org/md5/ef4ba0ae2a37f1fc608875bd4cfb7156

译者:飞龙

协议:CC BY-NC-SA 4.0

第四章:关联规则

本章内容包括

  • 关联规则学习

  • 不同类型的关联规则算法

  • 不同关联规则算法的实现

  • 使用 SPADE 进行序列学习

  • 案例研究

“关联的力量比美的力量更强大;因此关联的力量就是美的力量– 约翰·罗斯金”

恭喜你完成了本书的第一部分!你探索了无监督学习的基础知识以及 k-means 聚类、层次聚类、DBSCAN、主成分分析等算法。预计你已经掌握了第一部分的数学概念,并创建了解决每章末尾练习的 Python 代码。

欢迎来到本书的第二部分,在这一部分中,我们将利用第一部分学到的概念,并探索稍微复杂一些的主题。我们将从本书的第四章开始学习关联规则。祝你好运!

下次你去附近的杂货店时,看看店内各种物品的摆放。你会发现架子上摆放着牛奶、鸡蛋、面包、糖、洗衣粉、肥皂、水果、蔬菜、饼干和其他各种物品。你有没有想过这种摆放的逻辑是什么,以及这些物品是如何摆放的?为什么某些产品放在一起,而另一些则相距很远?显然,这种摆放不可能是随意的,背后必须有科学的原理。或者你是否想知道,Netflix 是如何根据你的观影历史为你推荐电影的?我们将在本章中找到这些问题的答案。和往常一样,我们将先学习概念。我们将通过数学逻辑来讨论不同算法的优缺点,并使用 Python 进行实际实现。本章末尾提供了一个商业案例研究来补充知识。

欢迎来到第四章,祝一切顺利!

4.1 技术工具包

我们将继续使用迄今为止使用的相同版本的 Python 和 Jupyter 笔记本。本章使用的代码和数据集已经存储在此位置。

本章需要安装几个 Python 库 – apyori、pyECLAT、fpgrowth_py 和 pyspade。除此之外,我们还需要 numpy 和 pandas。使用库,我们可以很快地实现这些算法。否则,编写这些算法将是一项耗时且痛苦的任务。

让我们开始关联规则的学习。

4.2 关联规则学习

你可能听说过著名的“啤酒和尿布故事”。根据这个轶事,超市的顾客(主要是年轻男子)购买尿布的同时也购买啤酒在同一张发票上。换句话说,购买尿布给他们的宝宝的年轻男子在同一笔交易中购买啤酒的概率相当高。我们不评论这个故事的真实性,但是可以归因于此故事的逻辑是关联规则学习

正式来说 - 关联规则可以用来发现数据集中存在的变量之间的有力关系。我们可以使用关联规则来测量数据集中变量之间的相关性和共同出现。在上述示例中(假设故事是真实的),人们可以分析每日的顾客交易。如果啤酒和尿布之间存在关系,这对超市来说是非常强大的洞察力,可以让他们定制啤酒和尿布的摆放位置,或者调整营销策略,甚至改变价格。

我们可以通过超市中的另一个例子来理解。考虑下面的例子。假设通过分析在超市生成的五张发票,我们得到了如下表 4.1 所示的数据。在这个例子中,发票编号 1001 购买了牛奶,因此它的值为 1,而奶酪没有购买,因此它是 0。

表 4.1 超市生成的发票示例。第一个发票编号是 1001,在该发票中购买了牛奶。因此,在牛奶前面有 1。而奶酪在 1001 中没有购买,因此,在奶酪前面有 0。
发票编号牛奶鸡蛋面包奶酪
10011110
10020001
10031110
10040101
10051101

因此,在发票编号 1001 中,购买了牛奶、鸡蛋和面包,而在发票编号 1002 中只购买了奶酪。在这里,我们可以看到每当牛奶和鸡蛋一起购买时,面包总是在同一张发票中购买。这确实是一个重要的发现。

现在将这种理解扩展到一天内数千笔交易。这将导致非常强大的关系,人眼通常忽视,但关联规则算法可以为我们揭示它们。它可以导致更好的产品摆放、产品更好的价格和更优化的营销支出。这些模式将提升客户体验,并且被证明对于改善整体客户满意度非常有用。

我们可以将关联规则可视化,如图 4.1 所示。在这里,有一些表示为节点 1、2、3、4 等的传入变量。这些节点之间存在关联,如箭头所示。它们之间的这种关系导致了规则 A 和 B 的产生。如果我们回顾一下本节开头提到的啤酒/尿布故事,规则 A 可能是,当年轻男性顾客购买尿布时,他们也经常购买啤酒;而规则 B 可能是,当购买牛奶和鸡蛋时,经常也会购买面包。

图 4.1 关联规则可以被视为数据集中各种变量之间的关系。这些变量之间相互关联,并建立了重要的关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面讨论的超市示例有时被称为市场篮子分析。但是关联规则不仅适用于杂货零售。它们在生物信息学、医疗行业、入侵检测等其他领域的效用已被证明。Netflix 或 Spotify 可以利用它们分析历史用户行为,然后推荐用户最有可能喜欢的内容。Web 开发人员可以分析客户在其网站上的历史点击和使用情况。通过识别模式,他们可以找出用户倾向于点击的内容和哪些功能将最大化他们的参与度。医生可以使用关联规则更好地诊断患者。医生可以比较症状与其他症状之间的关系的概率,并提供更准确的诊断。用例跨越多个业务领域和业务功能。

我们现在将了解关联规则的构建模块。

4.3 关联规则的构建模块

我们在上一节中介绍了关联规则的定义。

现在让我们了解关联规则背后的数学概念。假设我们在零售店有以下数据集-

  1. 让 X = {x[, x[2], x[3], x[4], x[5] …., x[n]} 代表零售店中可用的n种商品。例如,它们可以是牛奶、鸡蛋、面包、奶酪、苹果等等。

  2. 让 Y = {y[, y[2], y[3], y[4], y[5] …., y[m]} 代表在该零售店生成的m笔交易。每笔交易可能包含来自零售店的全部或部分商品。

显然,交易中的每个商品都只能从零售店购买。换句话说,在集合 Y 中的交易中的每个商品都是集合 X 中商品的子集。同时,每个商品都会附带一个唯一的标识符,每个交易都会附带一个唯一的发票号码。

现在,我们有兴趣分析模式并发现关系。这将用于生成任何规则或见解。因此,让我们首先定义规则的含义。

  1. 假设我们发现一条规则,即每当购买列表 P 中的商品时,列表 Q 中的商品也会被购买。这条规则可以写成如下形式:

  2. 规则是P -> Q。这意味着当购买了 P 中定义的物品时,也会购买 Q。

  3. P 中的物品将是 X 的子集或P Í X。

  4. 同样,Q 中的物品将是 X 的子集或Q Í X。

  5. P 和 Q 不能有任何共同的元素,即P Ç Q = 0

现在,让我们通过一个实际案例来理解这些数学概念。

假设 X = {牛奶,香蕉,鸡蛋,奶酪,苹果,面包,盐,糖,饼干,黄油,冷饮料,水}。这是零售店中所有可用的物品。

Y = {1001, 1002, 1003, 1004, 1005}。这是在该零售店生成的五张发票。每张发票中购买的相应物品如表 4.2 所示。

表 4.2 零售店生成的五张发票示例。请注意,对于每张发票,我们对每个物品都有 0 和 1 相关联。这些发票仅用于说明目的。实际发票中物品的数量可以更多。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用这个数据集,让我们假设我们创建了两个规则:{牛奶,香蕉} -> {鸡蛋}和{牛奶,香蕉} -> {面包}。

第一个规则意味着每当购买牛奶和香蕉,鸡蛋也会在同一次交易中购买。第二个规则意味着每当购买牛奶和香蕉,面包也会在同一次交易中购买。通过分析上述数据集,我们可以清楚地看到规则 1 始终为真,而规则 2 不是。

左边的物品称为前项或 LHS,右边的物品称为后项或 RHS。

在现实世界中,对于任何这样的规则来说,同样的模式必须在数百甚至数千个交易中重复出现。只有在这种情况下,我们才会得出这个规则确实是真实存在的,并且可以推广到整个数据库。

与此同时,可能会有很多这样的规则。在一个每天生成数千份发票的零售店中,可能会有数百个这样的规则。我们如何发现哪些规则是重要的,哪些是不重要的呢?这可以通过我们将在下一节学习的支持度、置信度和提升度的概念来理解。

4.3.1 支持度、置信度、提升度和确信度

在上一节中,我们确定了关联规则中规则的含义。我们也理解,基于事务数据集,可能会有数百条规则。在本节中,我们将探讨如何测量这些规则的有效性,并筛选出最有趣的规则。这可以通过支持度、置信度、提升度和确信度的概念来实现。

回顾上一节我们讨论了规则的泛化。支持度、置信度、提升度和确信度可以帮助我们衡量泛化程度。简而言之,使用这四个参数,我们可以确定规则在我们实际的商业中有多么有用。毕竟,如果一个规则不实用或者不够强大,就没有必要实施。支持度、置信度、提升度和确信度是检查规则效力的参数。我们现在将详细介绍这些概念。

我们将使用表 4.3 中的以下数据集来理解支持度、置信度和提升度的概念。

表 4.3 我们将用来理解支持度、置信度和提升度概念的数据集。第一张发票 1001 包含牛奶、鸡蛋和面包,而奶酪没有购买。同样,为了这个例子,我们总共只考虑了 4 个项目。
发票号码牛奶鸡蛋面包奶酪
10011110
10020111
10031110
10040101
10050110

在这里,对于一个发票,1 表示该发票中是否有购买该商品,而 0 则表示该发票中没有购买该商品。例如,发票编号 1001 包含牛奶、鸡蛋和面包,而 1002 包含鸡蛋、面包和奶酪。

现在让我们研究支持度。

支持

支持度测量数据集中项目的频率百分比。换句话说,它测量了项目在数据集中出现的交易百分比。

支持度可以表示如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

参考表 4.3,如果我们对规则 {牛奶, 鸡蛋} -> {面包} 感兴趣。在这种情况下,有两笔交易中同时包含这三种项目(牛奶、鸡蛋和面包)。总交易数为五笔。因此,该规则的支持度为 2 / 5,即 0.4 或 40%。

如果我们对规则 {面包, 鸡蛋} -> {奶酪} 感兴趣。在这种情况下,只有一笔交易中同时包含这三种商品。总交易数为五笔。因此,该规则的支持度为 1 / 5,即 0.2 或 20%。

支持度越高,规则越好。通常,我们设定一个最低阈值来获取支持度。最低阈值通常是与业务利益相关者协商确定的。

现在我们将研究规则的置信度。

置信度

置信度测量规则的真实频率。换句话说,它测量了包含前提的交易中也包含结果的百分比。

因此,如果我们想要测量规则 A->B 的置信度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里,分子在交易中同时存在 A 和 B 时为支持度,而分母则指的是仅 A 的支持度。

表 4.3 我们将用于理解支持度、确信度和提升度概念的数据集。第一张发票 1001 有牛奶、鸡蛋和面包,而奶酪没有购买。同样,为了本例,我们只考虑了总共 4 个物品。
发票号码牛奶鸡蛋面包奶酪
10011110
10020111
10031110
10040101
10050110

参考表 4.3,如果我们对规则 {牛奶,鸡蛋} -> {面包}感兴趣。在这种情况下,有两个交易中同时存在牛奶和鸡蛋。因此,支持度为 2/5 = 0.4。这是分母。有两个交易中同时存在三个物品(牛奶,鸡蛋,面包)。因此,支持度为 2/5 = 0.4,这是分子。放入上面的方程,规则 {牛奶,鸡蛋} -> {面包}的确信度为 0.4/0.4 = 1。

如果我们对规则 {鸡蛋,面包} -> {奶酪}感兴趣。在这种情况下,有三个交易中存在 (鸡蛋,面包)。总交易数为五。所以,这意味着支持度为 3/5,即 0.6。只有一个交易中同时存在三个物品(鸡蛋,面包,奶酪)。所以,支持度为 1/5 = 0.2。因此,规则 {鸡蛋,面包} -> {奶酪}的确信度为 0.2/0.6 = 0.33。

对于规则的信心越高,它就越好。像支持一样,我们对确信度设置了最低阈值。

有时,它也被称为 A 在 B 上的条件概率。它可以理解为在 A 已经发生的情况下 B 发生的概率,并且可以写成 P(A|B)。因此,在上面引用的例子中,提前购买了鸡蛋、面包的情况下购买奶酪的概率为 33%,而购买了牛奶、鸡蛋的情况下购买面包的概率为 100%。

到目前为止,我们已经涵盖了信心和支持。现在我们将研究提升度和确信度,这是评估规则的真正标准。

提升度和确信度

提升度是一种非常重要的规则测量标准。规则 A-> B 的提升度可以定义为

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里,分子在交易中同时存在 A 和 B 时得到支持,而分母则是指 A 的支持乘以 B 的支持。

表 4.3 我们将用于理解支持度、确信度和提升度概念的数据集。第一张发票 1001 有牛奶、鸡蛋和面包,而奶酪没有购买。同样,为了本例,我们只考虑了总共 4 个物品。
发票号码牛奶鸡蛋面包奶酪
10011110
10020111
10031110
10040101
10050110

参考表 4.3,如果我们对规则{牛奶,鸡蛋} -> {面包}感兴趣。在这种情况下,有两个交易中都存在所有三个(牛奶,鸡蛋,面包)。因此,支持度再次为 2/5 = 0.4,这是分子。有两个交易中只有(牛奶,鸡蛋)存在,所以支持度为 2/5 = 0.4。有四个交易中存在面包,因此支持度为 4/5 = 0.8。将其代入上述方程中,规则{牛奶,鸡蛋} -> {面包}的提升度为 0.4/(0.4x0.8) = 1.25。

如果我们对规则{鸡蛋,面包} -> {奶酪}感兴趣。在这种情况下,只有一个交易中存在(鸡蛋,面包,奶酪)。总交易数为五。因此,支持度为 1 / 5,即 0.2。有两个交易中存在(奶酪)。因此,支持度为 2/5 = 0.4。有四个交易中存在(鸡蛋,面包),因此支持度为 4/5 = 0.8。将其代入上述方程中,规则{鸡蛋,面包} -> {奶酪}的提升度为 0.2/(0.4x0.8) = 0.625。

如果提升度的值等于 1,则意味着前项和后项彼此独立,不能从中得出任何规则。

如果提升度的值大于 1,则意味着前项和后项是相互依赖的。此规则可用于预测未来交易中的前项。这是我们想要从数据集中得出的见解。

提升度的值小于 1,意味着前项和后项是彼此替代的。其中一个的存在可能对另一个产生负面影响。这也是业务团队进行战略规划的重要洞察。

在评估任何规则时,使用提升度时,必须将领域知识应用于其中。例如,如果我们评估规则{鸡蛋,面包} -> {奶酪},它表明鸡蛋、面包可以替代奶酪。我们知道这在现实生活中是不正确的。因此,在这种情况下,我们不能对此规则做出任何决定。我们必须借助领域知识来对该规则进行任何结论。

同时,规则{牛奶,鸡蛋} -> {面包}可能是一个可以多次成立的规则。对于许多客户,当他们一起购买牛奶和鸡蛋时,面包很可能也会在同一交易中购买。因此,对于这样的客户来说,这个规则更有意义。目标是有一个强有力的业务逻辑来支持或否定使用算法识别的规则。

信心是一个重要的参数,其公式如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例如,如果我们对规则 {鸡蛋,面包} -> {奶酪} 感兴趣。在这种情况下,只有一个交易中存在 (奶酪)。总交易数为五。所以,支持度是 1/5,即 0.2,并将用于分子。我们已经计算出置信度为 0.625。放回公式中,我们可以计算说服力为 (1-0.2)/(1-0.625) = 2.13

我们可以将说服力解释为 - 规则 {鸡蛋,面包} -> {奶酪} 如果 {鸡蛋,面包,奶酪} 之间的关联纯粹是随机选择的,那么它的错误率将是 2.13 倍。

在大多数业务场景中,提升度是使用的测量标准。还有其他测量参数,如杠杆、集体强度等。但是大多数情况下,置信度、支持度和提升度被用来衡量任何规则的有效性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 小测验 - 回答这些问题来检查你的理解… 答案在书的末尾

  1. 支持度测量规则在数据集中出现的频率。是或否。

  2. 如果提升度大于 1,则表示两个项目彼此独立。是或否。

  3. 降低置信度值,规则就越好。是或否。

在分析数据集时,我们评估任何规则时,大多数情况下都会为置信度、支持度和提升度设置阈值。这使我们能够减少规则数量并过滤掉不相关的规则。换句话说,我们只关注那些非常频繁的规则。当我们为数据集创建 Python 解决方案时,我们将更详细地研究它。

现在我们将研究关联规则中使用的各种算法。第一个这样的算法是 Apriori 算法,这是下一个主题。

4.4 Apriori 算法

Apriori 算法是用于关联规则的最流行算法之一。它由 Agrawal 和 Shrikant 在 1994 年提出。本章末尾给出了论文链接。

Apriori 用于理解和分析交易数据库中的频繁项。它利用“自下而上”的方法,首先根据子集的频率生成候选项。让我们通过一个示例来理解整个过程。

我们将使用我们之前讨论过的相同数据集。

表 4.3 我们将用来理解支持度、置信度和提升度概念的数据集。第一个发票 1001 有牛奶、鸡蛋和面包,而奶酪没有购买。
发票编号牛奶鸡蛋面包奶酪
10011110
10020111
10031110
10040101
10050110

Apriori 算法中使用的过程看起来像图 4.2 中下面的过程。

图 4.2 Apriori 算法过程可以如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

假设我们想要分析面包与数据集中所有其他项目的关系。在这种情况下,级别 1 是面包,我们找到其出现的频率。

然后我们转移到下一层,即第 2 层。现在我们找到面包与其他每个物品 - 牛奶、鸡蛋和奶酪(在第 2 层)的关系。在这里,我们再次找到所有可能组合的出现频率,即 {面包,牛奶},{面包,鸡蛋} 和 {面包,奶酪}。可以在图 4.3 中显示。

图 4.3 我们在 1 级有面包,而其他物品(牛奶、鸡蛋和奶酪)都放在 2 级。面包放在 1 级,因为我们希望分析面包与所有其他物品的关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分析完第 2 层后,我们转移到第三层、第四层等。这个过程会一直持续,直到我们达到最后一层,其中所有的物品都被用完。

通过这个过程,我们可以计算所有可能组合的支持度。例如,我们可以得到

{面包} -> {牛奶},

{面包} -> {鸡蛋} 和

{面包} -> {奶酪}。

对于下一层,我们还会得到支持

{面包,牛奶} -> {鸡蛋},

{面包,鸡蛋} -> {牛奶},

{面包,牛奶} -> {奶酪},

{面包,奶酪} -> {牛奶},

{面包,奶酪} -> {鸡蛋} 和

{面包,鸡蛋} -> {奶酪}。

现在,使用同样的过程,计算下一层的所有可能组合。例如,{面包,鸡蛋,牛奶} -> {奶酪},{面包,鸡蛋,奶酪} -> {牛奶} 等。

当所有的项集都用完时,这个过程将停止。完整的架构可以看起来像图 4.4。

图 4.4 Apriori 算法的完整架构。在这里,我们将计算所有可能组合的支持度。探索所有项目之间的关系,由于整个数据库的扫描,Apriori 的速度受到影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,我们可以轻松理解可能的组合数量相当高,这是 Apriori 的挑战之一。Apriori 算法还有一些其他缺点,我们将在后面学习。但现在是用 Python 实现 Apriori 的时候了。

4.4.1 Python 实现

现在我们将继续使用 Python 实现 Apriori 算法。数据集和 Python Jupyter 笔记本已经上传到 GitHub 代码库。

你可能需要安装 apyori。

安装这些库很简单,只需按照以下步骤进行。

import sys
!{sys.executable} -m pip install apyori

步骤 1: 导入用例所需的库。我们导入了 numpy、pandas。为了实现 Apriori,我们还有一个叫做 apyori 的库也被导入了。

import numpy as np
import pandas as pd
from apyori import apriori

步骤 2: 现在,我们导入数据集 store_data.csv 文件。

store_dataset = pd.read_csv('store_data.csv')

你还建议查看打开 .csv 文件的数据集。它会像下面的截图一样。截图显示了前 25 行。每一行代表一个发票。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 3: 让我们通过 .info.head 命令对数据进行一些基本检查。

store_dataset.info()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

store_dataset.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 4 步: 在这里我们可以看到,代码已将第一个交易作为标题考虑。因此,我们将重新导入数据,但这次他会指定标题等于 None。

store_dataset = pd.read_csv('store_data.csv', header=None)

第 5 步: 让我们再次看一下头部。这次看起来是正确的。

store_dataset.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 6 步: 我们用于代码的库接受数据集作为列表的列表。整个数据集必须是一个大列表,而每个交易是大列表中的内部列表。因此,为了实现它,我们首先将我们的 store_dataset 数据框转换为列表。

all_records = []
for i in range(0, 7501):
    all_records.append([str(store_dataset.values[i,j]) for j in range(0, 20)])

第 7 步: 接下来,我们实施 Apriori 算法。

对于算法,我们正在处理我们在第 6 步创建的 all_records 列表。指定的最小支持度为 0.5 或 50%,最小置信度为 25%,最小提升为 4,规则的最小长度为 2。

此步骤的输出是 apriori_rules 类对象。然后将此对象转换为我们可以理解的列表。最后,我们打印此列表。

apriori_rules = apriori(all_records, min_support=0.5, min_confidence=0.25, min_lift=4, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))

代码的输出将为 0。这意味着不存在满足我们设置的规则条件的规则。

我们再次尝试执行相同的代码,尽管将最小支持度减少到 25%。

apriori_rules = apriori(all_records, min_support=0.25, min_confidence=0.25, min_lift=4, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))

再次,没有生成任何规则,输出为零。即使将最小支持度降低到 10%,也不会产生任何规则。

apriori_rules = apriori(all_records, min_support=0.1, min_confidence=0.25, min_lift=4, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))

现在,我们将最小提升减少到 2。这次我们得到的输出是 200。这意味着有 200 个满足条件的规则。

apriori_rules = apriori(all_records, min_support=0.25, min_confidence=0.25, min_lift=2, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))

第 8 步: 让我们看一下第一条规则。

print(apriori_rules[0])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

规则解释了杏仁和汉堡之间的关系。支持率为 0.005,置信度为 0.25。提升为 2.92 表明此规则本身相当强大。

第 9 步: 我们现在将详细查看所有规则。为此,循环遍历规则并从每次迭代中提取信息。每个规则都有构成规则的项目以及支持、置信度和提升的相应值。我们在第 8 步中展示了一个示例。现在在第 9 步中,我们只是使用 for 循环从所有规则中提取该信息。

for rule in apriori_rules:
    item_pair = rule[0] 
    items = [x for x in item_pair]
    print("The apriori rule is: " + items[0] + " -> " + items[1])
    print("The support for the rule is: " + str(rule[1]))
    print("The confidence for the rule is: " + str(rule[2][0][2]))
    print("The lift for the rule is: " + str(rule[2][0][3]))
    print("************************")

此步骤的输出如下所示。在这里,我们可以观察到每个规则及其支持、置信度和提升的相应值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以轻松解释这些规则。例如,规则杏仁->汉堡包的提升为 2.92,置信度为 25.49%,支持率为 0.51%。这完成了我们使用 Python 的实现。此示例可以扩展到任何其他真实业务数据集。

并非所有生成的规则都适合使用。在本章的最后一节中,我们将探讨如何从生成的所有规则中获取最佳规则。

Apriori 算法是一种稳健且非常有见地的算法。但像任何其他解决方案一样,它也有一些缺点,我们现在正在讨论。

4.4.2 Apriori 算法的挑战

我们在前面的部分已经讨论过 Apriori 算法生成的子集数量相当大。

图 4.5 数据集的完全扫描被多次进行,因此速度显著下降。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

生成候选项集非常繁琐,因此分析数据集变得相当繁重。Apriori 多次扫描整个数据集,因此需要将数据库加载到内存中。我们可以肯定地得出结论,它需要大量时间才能进行计算。当处理非常大的数据集时,这个问题就会被放大。事实上,在现实世界的问题中,会产生数百万次交易,会产生相当多的候选项集,因此在整个数据集上使用 Apriori 是非常耗时的。

因此,一般情况下,会设定一个最小支持值来减少可能的规则数量。在上面给出的例子中,我们可以根据表 4.4 中所示计算级别 1 组合的支持,如果我们将最小支持值设为 0.5,只有一个规则会被列入候选名单。

表 4.4 对每种商品的组合都进行了支持计算。例如,对于牛奶和面包-交易次数为 2,而总交易次数为 5。所以,支持率为 2/5,即 0.4。
组合交易次数总交易次数支持率
牛奶,鸡蛋250.4
牛奶,面包250.4
牛奶,奶酪050
鸡蛋,面包450.8
鸡蛋,奶酪250.4
面包,奶酪150.2

设定一个最小支持值是一个明智的策略,可以使规则更易管理。这样可以减少时间,生成更有意义的规则。毕竟,从分析生成的规则应该足够通用,以至于可以应用于整个数据库。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 快速测验 - 回答这些问题来检查你的理解。书末有答案。

  1. Apriori 算法只扫描数据库一次。是真还是假?

  2. 如果香蕉在 12 次交易中出现了 5 次,那么香蕉的支持率就是 5/12。正确还是错误?

但 Apriori 算法是一种开创性的解决方案。它仍然非常受欢迎,通常是讨论关联规则时的第一个算法。

数据准备是关键步骤之一,也是一个挑战,我们将在本章最后的案例研究中探讨这一挑战。

我们接下来会学习下一个算法,也就是 ECLAT 算法。

4.5 等价类聚类和自底向上格遍历(ECLAT)

接下来我们会在这一节学习等价类聚类和自底向上格遍历算法,或者称之为 ECLAT,有人认为这个算法在速度和实现的方便性方面比 Apriori 更好。

ECLAT 使用深度优先搜索方法。这意味着 ECLAT 沿着数据集以纵向方式进行搜索。它从根节点开始。然后进入更深的一层,并继续直到达到第一个终端注释。假设终端节点在 X 级。一旦到达终端节点,算法然后返回一步,并到达级别(X-1),并继续直到再次找到终端节点。让我们通过表 4.6 中显示的树状图来理解这个过程。

图 4.6 ECLAT 算法过程的树状图。 它从 1 开始,直到 16 结束。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ECLAT 将采取以下步骤:

  1. 算法从根节点 1 开始。

  2. 然后它向根节点 2 深入一层。

  3. 然后它会继续向更深的一层,直到达到终端节点 11。

  4. 一旦它到达终端注释 11,然后退回一步,到达节点 5。

  5. 然后算法搜索是否有可用的节点。在节点 5 处,我们可以看到没有可用的节点。

  6. 因此,算法再次退回一步,它达到了节点 2。

  7. 在节点 2 处,算法再次探索。它发现可以到达注释 6。

  8. 因此,算法进入节点 6,并开始探索,直到达到终端节点 12。

  9. 这个过程会一直持续,直到所有组合耗尽。

显然,计算速度取决于数据集中存在的不同项目的总数。这是因为不同项目的数量定义了树的宽度。每笔交易中购买的商品将定义每个节点之间的关系。

在执行 ECLAT 时,将分析每个商品(单独或成对)。让我们使用我们已经用于 Apriori 的相同示例来更好地理解 ECLAT,如表 4.5 所示。

表 4.5 我们将用来理解 ECLAT 的数据集。 第一张发票 1001 有牛奶、鸡蛋和面包,而奶酪没有购买。
发票号牛奶鸡蛋面包奶酪
10011110
10020111
10031110
10040101
10050110

ECLAT 将经历以下步骤来分析数据集:

  1. 在第一次运行中,ECLAT 将找到所有单个商品的发票号。换句话说,它会找到所有商品的个别发票号。可以在下面的表 4.6 中显示,其中牛奶在发票号 1001 和 1003 中出现,而鸡蛋出现在所有发票中。
表 4.6 每个商品所在的相应发票。牛奶出现在 1001 和 1003 号发票中,而鸡蛋出现在五张发票中。
商品发票号
Milk1001,1003
Eggs1001, 1002, 1003, 1004, 1005
Bread1001, 1002, 1003, 1005
Cheese1002, 1004
  1. 现在在下一步中,所有两个项目数据集都将如下所示地进行探索,如表 4.7 所示。例如,牛奶和鸡蛋出现在发票号码 1001 和 1003 中,而牛奶和奶酪没有出现在任何发票中。
表 4.7 现在探索了两个项目数据集。牛奶和鸡蛋出现在发票号码 1001 和 1003 中,而没有牛奶和奶酪的发票。
项目发票号码
牛奶,鸡蛋1001, 1003
牛奶,面包1001, 1003
牛奶,奶酪-
鸡蛋,面包1001, 1002, 1003, 1005
鸡蛋,奶酪1002, 1004
面包,奶酪1002
  1. 在接下来的步骤中,所有三个项目数据集都将如表 4.8 所示进行探索。
表 4.8 在这一步中分析了三个项目数据集。我们只有两个组合。
项目发票号码
牛奶,鸡蛋,面包1001, 1003
鸡蛋,面包,奶酪1002
  1. 在我们的数据集中没有包含四个项目的发票。

  2. 现在根据我们设置的支持计数值的阈值,我们可以选择规则。因此,如果我们希望使规则为真的最小交易次数等于三,那么只有一个规则符合条件,即{鸡蛋,面包}。如果我们将最小交易次数的阈值设定为两,则诸如{牛奶,鸡蛋,面包},{牛奶,鸡蛋},{牛奶,面包},{鸡蛋,面包}和{鸡蛋,奶酪}等规则都符合条件。

现在我们将为 ECLAT 创建一个 Python 解决方案。

4.5.1 Python 实现

现在我们将使用 Python 进行 ECLAT 的执行。我们在这里使用 pyECLAT 库。数据集如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 1: 我们将在这里导入库。

import numpy as np
import pandas as pd
from pyECLAT import ECLAT

步骤 2: 现在导入数据集

data_frame = pd.read_csv('Data_ECLAT.csv', header = None)

步骤 3: 这里我们正在生成一个 ECLAT 实例。

eclat = ECLAT(data=data_frame)

在上一步生成的 ECLAT 实例 eclat 中有一些属性,如 eclat.df_bin 是一个二进制数据框,eclat.uniq_ 是所有唯一项目的列表。

步骤 4: 现在我们将适配模型。我们在这里给出了最小支持度为 0.02。之后我们将打印支持度。

get_ECLAT_indexes, get_ECLAT_supports = eclat.fit(min_support=0.02,
                                                           min_combination=1,
                                                           max_combination=3,
                                                           separator=' & ')
get_ECLAT_supports

输出是

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以根据支持度提供的结果进行解释。对于每个项目和项目组合,我们都得到了支持度的值。例如,对于炸薯条和鸡蛋,支持度的值为 3.43%。

ECLAT 相对于 Apriori 算法具有一些优势。由于它使用深度搜索方法,因此比 Apriori 更快,计算所需的内存更少。它不会迭代地扫描数据集,因此使其比 Apriori 更快。在我们学习了最后一个算法之后,我们将再次比较这些算法。

现在我们将转向第三个算法:F-P 生长算法。

4.6 频繁模式生长算法(F-P 算法)

F-P 算法或频繁模式增长算法是本章要讨论的第三个算法。它是对 Apriori 算法的改进。回想一下,在 Apriori 中,我们面临计算耗时和昂贵的挑战。FP 通过将数据库表示为一种名为频繁模式树或 FP 树的树来解决这些问题。因为这种频繁模式,所以没有必要像 Apriori 算法那样生成候选项。现在让我们详细讨论一下 FP。

FP 树或频繁模式树是一个树形结构,它挖掘数据集中最频繁出现的项。见图 4.7。

图 4.7 FP 算法可以用树状图结构表示。我们将逐步创建这个树。每个节点代表一个唯一的项。根节点为空。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个节点表示数据集中的唯一项。树的根节点通常保持为空。树中的其他节点是数据集中的项。如果它们在同一发票中,则节点彼此连接。我们将逐步学习整个过程。

假设我们使用如表 4.9 所示的数据集。因此,我们有唯一的项:苹果,牛奶,鸡蛋,奶酪和面包。总共有 9 个交易,每个交易中的相应项如表 4.9 所示。

表 4.9 我们将使用的数据集,以了解 FP 算法的概念。这里有九个交易,例如在 T1 中我们有苹果,牛奶和鸡蛋。
交易项集
T1苹果,牛奶,鸡蛋
T2牛奶,奶酪
T3牛奶,面包
T4苹果,牛奶,奶酪
T5苹果,面包
T6牛奶,面包
T7苹果,面包
T8苹果,牛奶,面包,鸡蛋
T9苹果,牛奶,面包

现在让我们将 FP 算法应用于该数据集。

步骤 1: 就像 Apriori 算法一样,我们首先扫描整个数据集。记录每个项出现的次数并生成频率计数。结果如表 4.10 所示。我们按照整个数据集中各项的频率或对应的支持计数从大到小排列。

表 4.10 各项集的相应频率。例如,苹果已被购买了六次。
频率或支持计数
牛奶7
苹果6
面包6
奶酪2
鸡蛋2

如果两个项的频率完全相同,则任何一个都可以排在前面。在上面的例子中,面包和苹果的频率相同。因此,我们可以将面包或苹果作为第一个。

步骤 2: 让我们开始构建 FP 树。我们从根节点开始创建,通常是图 4.8 中的空节点。

图 4.8 FP 树的根节点通常保持为空。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 3 步: 现在分析第一个交易 T1。在这里,我们有苹果、牛奶和鸡蛋在第一笔交易中。其中牛奶有最高的支持计数,为 7。因此,从根节点延伸到牛奶的连接,并用 Milk:1 表示。我们在图 4.9 中展示了。

图 4.9 从根节点到牛奶的连接。牛奶有最高的支持,因此我们选择了牛奶。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 4 步: 现在我们来看看 T1 中的其他项目。苹果的支持计数为 6,鸡蛋的支持计数为 2。所以,我们将从牛奶到苹果延伸连接,并命名为 Apple:1,然后从苹果到鸡蛋并称之为 Eggs:1。我们在图 4.10 中展示了。

图 4.10 过程的第 4 步,我们已经完成了 T1 中的所有项目。所有的项目牛奶、苹果和鸡蛋现在都彼此连接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 5 步: 现在让我们看看 T2。它有牛奶和奶酪。牛奶已经连接到根节点。所以,牛奶的计数变成 2,变成了 Milk:2。我们接下来会从牛奶到奶酪创建一个分支,并称之为 Cheese:1。增加的部分显示在图 4.11 中。

图 4.11 过程的第 5 步,我们开始分析 T2。牛奶已经连接,所以它的计数增加了 2,同时奶酪被添加到树中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 6 步: 现在轮到 T3。T3 有牛奶和面包。所以,类似于第 5 步,牛奶的计数是 3,变成了 Milk: 3。与第 5 步类似,我们从牛奶到面包添加另一个连接,称为 Bread:1。更新后的树显示在图 4.12 中。

图 4.12 在第 6 步,现在分析 T3。牛奶的计数增加了一个,变成了 3,而面包被添加为一个新的连接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 7 步: 在 T4 中,我们有苹果、牛奶和奶酪。牛奶的计数现在变成了 4,苹果现在是 2。然后我们创建了一个从苹果到奶酪的分支,称之为 Cheese:1。我们在图 4.13 中展示。

图 4.13 在过程的第 7 步中,正在分析 T4。牛奶的计数变成了 4,苹果的计数增加到了 2,并添加了一个新的从苹果到奶酪的分支。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 8 步: 我们可以在 T5 中发现,我们有苹果和面包。两者都不直接连接到根节点,频率相等为 6。因此,我们可以任选其一连接到根节点。图更新为图 4.14。

图 4.14 在分析完 T5 之后,图示如下所示发生了变化。我们有苹果和面包被添加到树中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 9 步: 这个过程会继续,直到我们耗尽所有的交易,最终的图形如图 4.15 所示。

图 4.15 一旦我们耗尽了所有可能的组合,最终的树就是这样。但是在此之后还有更多的步骤。到目前为止,我们只创建了树。现在我们需要生成数据集,如表 4.11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

到目前为止,做得很棒!但是,过程还没有结束。我们刚刚建立了数据集中项目之间的连接。现在我们需要填写一个看起来像表 4.11 的表。

表 4.11 FP 算法要完成的表。这是我们希望生成的输出。
项目条件模式基条件 FP 树频繁模式生成
奶酪
面包
鸡蛋
苹果

也许你会想为什么只列出了 4 个项目。由于牛奶直接源自根节点,没有其他到达它的方式,我们不需要为牛奶单独设置一行。

第 10 步: 在继续之前,我们将最小支持计数固定为 2,以便接受任何规则。由于数据集相当小,我们这样做是为了简单起见。

对于现实生活中的业务问题,建议您尝试多个甚至更高的支持计数值,否则生成的规则数量可能会非常多。

让我们从奶酪作为第一个项目开始。我们可以通过{NULL-牛奶-奶酪}和{NULL-牛奶-苹果-奶酪}到达奶酪。对于这两条路径,奶酪的计数为 1。因此,(如果我们忽略 NULL)我们的条件模式基为{牛奶-奶酪}或{牛奶:1}和{牛奶-苹果-奶酪}或{牛奶-苹果:1}。完整的条件模式基变为{{牛奶:1},{牛奶-苹果:1}}。这些信息添加到表 4.12 的第二列。

表 4.12 过程的第 10 步,我们已经填写了奶酪的第一个单元格。我们填写了奶酪的第一个单元格。
项目条件模式基条件 FP 树频繁模式生成
奶酪{{牛奶:1},{牛奶-苹果:1}}
面包
鸡蛋
苹果

第 11 步: 现在,如果我们在条件模式基中添加两个值,我们会得到牛奶为 2,苹果为 1。由于我们已经为频率计数设置了 2 的阈值,我们将忽略苹果的计数。条件 FP 树的值,即表中的第三列,变为{牛奶:2}。现在我们只需将原始项目添加到此项中,变为频繁模式生成或第 4 列。现在表是 4-13。

表 4.13 过程的第 11 步,在这一步中我们完成了奶酪项目的详细信息。同样,所有其他项目都将被分析并添加到表中。
项目条件模式基条件 FP 树频繁模式生成
奶酪{{牛奶:1},{牛奶-苹果:1}}{牛奶:2}{牛奶-奶酪:2}
面包
鸡蛋
苹果

第 12 步: 以类似的方式填写表中的所有其他单元格,最终表为表 4.14。

我们分析了所有项目的组合后,表 4.14 是最终表。
项目条件模式基条件 FP 树频繁模式生成
奶酪{{牛奶:1},{牛奶-苹果:1}}{牛奶:2}{牛奶-奶酪:2}
面包{{牛奶-苹果:2}, {牛奶:2}, {苹果:2}}{{牛奶:4, 苹果:2}, {苹果:2}}{牛奶-面包:4}, {苹果-面包:4}, {牛奶-苹果-面包:2}
鸡蛋{{牛奶-苹果:1},{牛奶-苹果-面包:1}}{牛奶:2, 苹果:2}{牛奶-鸡蛋:2},{牛奶-苹果:2},{牛奶-苹果:2}
苹果{牛奶:4}{牛奶:4}{牛奶-苹果:4}

这确实是一个复杂的过程。但是一旦步骤清晰,就会变得非常简单。

通过这个练习,我们已经得到了最终的规则集,如最后一列频繁模式生成所示。

请注意,所有的规则都彼此不同。

我们将使用最后一列“频繁模式生成”作为我们数据集的规则。

使用 FP 增长算法的 Python 实现非常简单,并且使用库进行计算很容易。出于空间考虑,我们已将 Jupyter 笔记本上传到了本章的 GitHub 存储库中。

我们现在将探讨另一个有趣的主题,即序列规则挖掘。这是一个非常强大的解决方案,使企业能够根据客户的需求量身定制他们的营销策略和产品推荐。

4.7 序列规则挖掘

想象一下。Netflix 会有一个包含顾客随时间订购的所有电影的交易数据库。如果他们分析并发现 65%的购买了战争电影 X 的客户在接下来的一个月也购买了浪漫喜剧 Y,这是非常有见地和可操作的信息。这将使他们能够向客户推荐他们的产品,并且他们可以定制他们的营销策略。不是吗?

到目前为止,在这一章中,我们已经涵盖了三种关联规则算法。但是所有的数据点都限于相同的数据集,并且没有涉及到序列。序列模式挖掘允许我们分析一组事件发生的数据集。通过分析数据集,我们可以找到统计上相关的模式,这使我们能够解读整个事件序列。显然,事件序列是按照特定顺序排列的,这是序列规则挖掘过程中非常重要的一个属性。

序列规则挖掘不同于时间序列分析。要了解更多关于时间序列分析的信息,请参阅附录。

序列规则挖掘被应用于多个领域和功能。它可以在生物学中用于在 DNA 测序期间提取信息,也可以用于了解用户的在线搜索模式。序列规则挖掘将帮助我们了解用户接下来要搜索什么。在讨论关联规则时,我们使用了购买牛奶、面包、鸡蛋的同一交易中的交易。序列规则挖掘是对此的扩展,其中我们分析连续的交易,并试图解读是否存在序列。

在研究 SPADE 算法时,我们将学习构成该算法基础的数学概念。这些概念有点复杂,可能需要多次阅读才能掌握。

4.7.1 SPADE

我们现在正在使用等价类进行顺序模式挖掘(Sequential Pattern Discovery using Equivalence classes)或 SPADE 探索序列规则挖掘。这是由穆罕默德·J·扎基(Mohammed J. Zaki)提出的,文章链接在本章末尾。

我们了解到,我们希望分析事件的顺序。例如,顾客购买了手机和充电器。一周后买了耳机,两周后买了手机壳和手机屏幕保护壳。因此,在每次交易中都购买了项目。并且每次交易都可以称为一个事件。让我们更详细地了解一下。

假设我们有讨论所需的所有项目的完整列表。I 将包含像 i[1]、i[2]、i[3]、i[4]、i[5] 等项目。因此,我们可以写成

I = {i[1], i[2], i[3], i[4], i[5]………, i[n]},其中我们总共有 n 个不同的项目。

项目可以是任何东西。如果我们考虑杂货店的例子,项目可以是牛奶、鸡蛋、奶酪、面包等等。

一个事件将是在同一交易中的项目集合。一个事件可以包含像(i[1], i[5], i[4], i[8)这样的项目。例如,一个事件可以包含在同一交易中购买的项目(牛奶, 糖, 奶酪, 面包)。我们将用 ⍺ 表示一个事件。

接下来让我们了解一下序列。序列只是按顺序的事件。换句话说,⍺[1] -> ⍺[2] ->⍺[3] ->⍺[4] 可以被称为事件序列。例如,(牛奶, 奶酪)->(面包, 鸡蛋)->(奶酪, 面包, 糖)->(牛奶, 面包)是一系列交易。这意味着在第一次交易中购买了牛奶和奶酪。在接下来的交易中,购买了面包和鸡蛋,依此类推。

包含 k 个项目的序列是一个 k 项目序列。例如,序列(牛奶, 面包)->(鸡蛋)包含 3 个项目。

我们现在将逐步了解 SPADE 算法。

假设我们生成了以下序列。在第一个序列 1001 的交易中,第一个交易中购买了牛奶。第二个交易中购买了牛奶、鸡蛋和面包。紧随其后购买了牛奶和面包。在第四个序列中只购买了糖。在序列 1001 的第五个和最后一个交易中,购买了面包和苹果。并且这适用于所有相应的序列。

表 4.15 序列挖掘的数据集。在序列 ID 1001 中,我们有多个事件。在第一次购买中,购买了牛奶。然后购买了(牛奶, 鸡蛋, 面包)等等。
序列 ID序列
1001<(牛奶) (牛奶, 鸡蛋, 面包) (牛奶, 面包) (糖)(面包, 苹果)>
1002<(牛奶, 糖) (面包) (鸡蛋, 面包) (牛奶, 奶酪)>
1003<(奶酪, 苹果) (牛奶, 鸡蛋) (糖, 苹果) (面包) (鸡蛋)>
1004<(奶酪, 香蕉)(牛奶, 苹果)(面包)(鸡蛋)(面包)>

这个(表 4.15 )可以转换成垂直数据格式,如表 4.16 所示。在这一步中,我们计算单个项目的频率,即仅包含一个项目的序列。这仅需要进行一次数据库扫描。

表 4.16 表 4.15 的垂直格式。我们只是得到了每个项目的序列 ID 和项目 ID,并在此处表示它。
序列 ID元素 ID项目
10011牛奶
10012牛奶, 鸡蛋, 面包
10013牛奶, 面包
10014
10015面包, 苹果
10021牛奶, 糖
10022面包
10023鸡蛋, 面包
10024牛奶, 奶酪
10031奶酪, 苹果
10032牛奶, 鸡蛋
10033糖, 苹果
10034面包
10035鸡蛋
10041奶酪, 香蕉
10042牛奶, 苹果
10043面包
10044鸡蛋
10045面包

表 4.16 只是表 4.15 的垂直制表符号表示。例如,在序列 ID 1001 中,元素 ID 1 是牛奶。对于序列 ID 1001,元素 ID 2 是牛奶,鸡蛋,面包等。

为了解释的目的,我们只考虑两个项目 0 牛奶和鸡蛋以及支持阈值为 2。

然后,在下一步中,我们将对每个项目进行分解。例如,牛奶出现在序列 ID 1001 和元素 ID 1,序列 ID 1001 和元素 ID 2,序列 ID 1001 和元素 ID 3,序列 ID 1002 和元素 ID 1 等中。它会产生类似表 4.17 的表格,我们在其中显示了牛奶和鸡蛋。它需要应用到数据集中的所有项目。

表 4.17 中相应的牛奶和鸡蛋序列 ID。同样的方法可以应用到所有项目和序列中。
牛奶鸡蛋
序列 ID元素 ID
10011
10012
10013
10021
10024
10032
10043

现在,我们希望计算 2-序列或具有 2 项序列。我们可以有两个序列 - 要么是牛奶 -> 鸡蛋,要么是鸡蛋 -> 牛奶。让我们先来看看牛奶-> 鸡蛋。

对于牛奶 -> 鸡蛋,我们需要在鸡蛋前面放牛奶。对于相同的序列 ID,如果牛奶的元素 ID 小于鸡蛋的元素 ID,则它是一个合格的序列。在上面的示例中,对于序列 ID 1002,牛奶的元素 ID 是 1,而鸡蛋的元素 ID 是 2。因此,我们可以将其添加为第一个合格对,如下表 4.18 的第一行所示。对于序列 ID 1002 也是如此。在表 4.17 中,第 4 行我们有序列 ID 1002。牛奶的元素 ID 是 1,而第 2 行的鸡蛋的元素 ID 是 3。同样,牛奶的元素 ID 小于鸡蛋的元素 ID,因此它变为第二个条目。进程继续。

表 4.18 序列牛奶鸡蛋可以写在这里。关键点是在比较牛奶和鸡蛋的相应元素 ID 时具有相同的序列 ID。
牛奶 鸡蛋
序列 ID
1001
1002
1003
1004

使用相同的逻辑,我们可以创建表格,例如鸡蛋 -> 牛奶,如下所示在下表 4.19 中显示。

表 4.19:序列鸡蛋牛奶可以在此处写下。关键在于比较牛奶和鸡蛋的各自元素 ID 时保持相同的序列 ID。
美纪牛奶
序列 ID
1001
1002

可以对所有可能的组合都进行此操作。现在我们将转向创建 3 项序列,我们将创建 Milk,Eggs -> Milk。为此,我们必须合并这两个表。

表 4.20:结合序列,即牛奶->鸡蛋和鸡蛋->牛奶,以合并表格。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结合的逻辑是匹配序列 ID 和元素 ID。我们已经用红色和绿色分别突出显示匹配的部分。对于序列 ID 1001,左表中鸡蛋的元素 ID 和右表中鸡蛋的元素 ID 匹配,这成为表 4.21 的第一条目。同样对于序列 ID 1002,元素 ID 3 匹配。这导致了表 4.21 的生成。

表 4.21:在分析了所有物品的组合后的最终表。
牛奶,鸡蛋 -> 牛奶
序列 ID
1001
1002

这个过程将继续进行。当找不到频繁序列时算法停止。

我们将使用 Python 在数据集上实现 SPADE。我们使用pyspade库,因此我们必须加载数据集并调用函数。它会为我们生成结果。这里支持率设置为 0.6,然后我们打印结果。

from pycspade.helpers import spade, print_result
spade_result = spade(filename='SPADE_dataset.txt', support=0.6, parse=True)
print_result(spade_result)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这结束了我们在本章中要讨论的四种算法。现在我们将转向案例研究,为您提供真实的体验。

4.8 关联规则案例研究

关联规则挖掘是一个非常有用和强大的解决方案。我们将使用关联规则解决一个实际案例研究。

回想一下,在本章的开头,我们建议研究杂货店的模式。这种店铺摆设的逻辑是什么呢?

想象一下:你在像沃尔玛、乐购、Spar 或 Marks & Spencer 等杂货零售商工作。他们必须规划新店的视觉布局。显然,零售商要明智地利用店内空间,充分利用最大的容量。与此同时,至关重要的是不妨碍顾客的活动。顾客应该可以接触到所有展示的物品,并能够轻松地导航。你可能会有一些经历过一些店铺让我们感到窒息和陈列品充斥,而另一些则整齐摆放的情况。

我们如何解决这个问题呢?

这个问题可能有多种解决方案。一些零售商可能希望根据商品类别对物品进行分组。他们可能希望将所有烘焙产品都放在一个架子上,或者根据任何其他条件使用。我们在这里学习的是机器学习的例子。

使用购物篮分析,我们可以生成规则,指示各种商品之间的相关关系。我们可以预测哪些商品经常一起购买,并且可以将它们放在店里的一起。例如,如果我们知道牛奶和面包经常一起购买,那么面包可以放在牛奶柜台附近。购买牛奶的顾客可以轻松找到面包并继续购买。

但事情并不像听起来那么简单。让我们逐步解决这个案例。

  1. 业务问题定义:第一步是明确定义我们清楚的业务问题。我们希望发现各种商品之间的关系,以便可以更好地安排商店内的布局。在这里,陈列计划 变得很重要。陈列计划帮助零售商以明智的方式规划商店内的空间利用,使顾客也可以轻松导航和访问产品。它可以被视为商店的视觉布局。示例如图 4.16 所示。
图 4.16 显示了一个陈列计划的示例。陈列计划对于视觉营销非常有用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在图中,我们可以看到每个商品类别都有特定的区域。关联规则非常有洞察力,可以帮助生成陈列计划的方向。

  1. 数据发现:下一步是数据发现,其中历史交易被搜索并加载到数据库中。通常,一笔交易看起来像表 4.22。
表 4.22 实际零售店生成的发票示例。将这种数据格式转换为可以被关联规则算法消费的格式是相当具有挑战性的。
发票编号日期商品金额
100101-Jun-21牛奶,鸡蛋,奶酪,面包$10
100201-Jun-21面包,香蕉,苹果,黄油$15
100301-Jun-21黄油,胡萝卜,奶酪,鸡蛋,面包,牛奶,香蕉$19
100401-Jun-21牛奶$1
100501-Jun-21面包$0.80
  1. 数据准备:这一步也许是最困难的一步。正如你可能意识到的那样,关联规则模型的创建是一项非常简单的任务。我们有可以为我们处理繁重工作的库。但是它们期望的数据集是以特定格式存在的。这是一项繁琐的任务,非常耗时,并且需要大量的数据预处理技能。

    在准备数据集时,有几个注意事项你必须记住,它们是:

  2. 有时在数据准备阶段我们会得到NULL空值。数据集中的缺失值可能会导致计算时出现问题。在其他机器学习解决方案中,我们建议处理缺失值。在关联规则的情况下,我们建议忽略相应的交易,并且不在最终数据集中考虑它。

  3. 很多时候,我们在数据中会得到垃圾值。数据集中会发现像!@%^&*()_ 这样的特殊字符。这可能是由系统中不正确的输入导致的。因此,需要进行数据清洗。

  4. 我们在书的附录中非常详细地介绍了数据预处理步骤,在这里我们处理 NULL 值和垃圾值。

  5. 将表格转换为关联规则学习算法可以理解和使用的格式是一项必不可少但又艰巨的步骤。深入了解 SQL 数据透视的概念以更好地理解这个概念。否则,你可能需要某人(一位数据工程师)为你创建数据集。

  6. 模型准备: 或许最容易的步骤之一就是建模。我们已经为不同的算法解决了 Python 解决方案。所以,你应该对此相当熟悉。

  7. 模型解释: 创建模型可能很容易,但解释规则却不是。大多数时候,你可以得到如下规则:

  8. #NA -> (牛奶,奶酪) - 这样的规则显然是不可用的,也没有任何意义。它表明数据准备不正确,数据集中仍然存在一些垃圾值。

  9. (某些物品)->(包装材料)- 或许是最明显的规则,但同样不可用。这个规则表明,无论何时购物,都会购买包装材料,相当明显对吧?

  10. (土豆,番茄)->(洋葱):这种规则看起来可能是正确的,但这是零售商已经知道的常识。显然,大多数购买蔬菜的顾客都会一起购买土豆、番茄和洋葱。这样的规则可能对业务价值增加不多。

  11. 支持、置信度和提升阈值允许过滤出最重要的规则。我们可以按提升的降序排序规则,然后移除最明显的规则。

  12. 业务主题专家: 业务利益相关者和主题专家参与每一个步骤是至关重要的。在这个案例研究中,运营团队、视觉营销团队、产品团队和营销团队是关键参与者,他们应该在每一个步骤都密切配合。

  13. 一旦规则生成并得到接受,我们就可以使用它们来改善零售空间的陈列方案。零售商可以利用它们来改善营销策略并改进产品促销活动。例如,如果接受了像(A,B)->(C)这样的规则,零售商可能希望将产品捆绑在一起并作为一个单一实体出售。这将增加同一交易中购买的平均物品数量。

  14. 这个案例研究可以扩展到任何其他领域或业务功能。例如,如果我们希望检查用户在网页上的移动,可以使用相同的步骤。网页开发人员可以分析客户在其网站上的历史点击和使用情况。通过识别模式,他们可以找出用户倾向于点击什么以及哪些功能会最大化他们的参与度。医生可以使用关联规则更好地诊断患者。医生可以比较与其他症状的概率相关的症状,并提供更准确的诊断。

现在,我们将研究这些算法的局限性以及关联规则和序列规则的其他可用解决方案。

4.9 总结思考

在我们研究的关联规则和序列规则中,存在一些假设和限制。

  • 在生成规则时,忽略了物品的各自重要性。例如,如果一个顾客在一次交易中购买了 5 罐牛奶和一公斤苹果,那么它被类似地对待于一张发票,其中购买了一罐牛奶和五公斤苹果。因此,我们必须记住物品的各自重要性没有被考虑。

    • 商品的成本反映了产品的感知价值。一些昂贵的产品更为重要,因此,如果顾客购买了它们,就可以产生更多的收入。在分析发票时,我们忽略了物品的成本。

    • 在分析序列时,我们没有考虑两个交易之间的各自时间段。例如,在 T1 和 T2 之间有 10 天,而在 T2 和 T3 之间有 40 天 - 这两个时间段被视为相同。

    • 在所有的分析中,我们将不同类别视为相同。易腐烂商品和不易腐烂商品被以类似方式处理。例如,保质期为 2-3 天的新鲜牛奶被类似对待于保质期无限的洗衣粉。

    • 许多时候,我们在分析后得到了不感兴趣的规则。这些结果来自常识(土豆,西红柿)->(洋葱)。这样的规则并不太有用。我们很多时候都面临这样的问题。

    • 虽然不感兴趣的规则是一个挑战,但发现的大量规则又是问题之一。我们得到了成百上千的规则,要理解和分析每一个都变得困难。在这里,阈值变得很有用。

    • 计算所需的时间和内存需求是巨大的。这些算法需要多次扫描数据集,因此是非常耗时的练习。

  • 生成的规则取决于用于分析的数据集。例如,如果我们仅在夏季生成的数据集上进行分析,我们就不能将规则应用于冬季,因为消费者的偏好会随着不同季节而变化。此外,随着时间的流逝,宏观和微观经济因素也会发生变化,因此算法也应随之更新。

还有一些其他算法也很有趣。对于关联规则,我们可以有多关系关联规则、k-最佳模式发现、近似频繁数据集、广义关联规则、高阶模式发现等。对于序列挖掘,我们有广义序列模式、FreeSpan、PrefixSpan、挖掘相关模式等。这些算法非常有趣,可以用于增强知识。

关联规则和序列挖掘是非常有趣的主题。各个商业领域和功能越来越多地使用关联规则来理解事件的模式。这些见解使团队能够做出明智而科学的决策,以改善客户体验和整体参与度。本章是本书第二部分的第一章。我们在本章中探讨了关联规则和序列挖掘。这些是使用 Apriori、FP 和 ECLAT 算法进行研究的,而序列挖掘则使用了 SPADE。

在下一章中,我们将研究高级聚类算法。敬请关注!

您现在可以继续问题部分。

实际的下一步和建议的阅读
  1. 请阅读以下关联规则算法的研究论文

  2. 关联规则的快速发现(www.cs.bme.hu/~marti/adatbanya/apriori_hashtree.pdf

  3. 用于挖掘关联规则的快速算法(rakesh.agrawal-family.com/papers/vldb94apriori.pdf

  4. 模式和关联规则挖掘方法的高效分析(arxiv.org/pdf/1402.2892.pdf

  5. 关于其保护隐私能力的关联规则挖掘技术综述(www.ripublication.com/ijaer17/ijaerv12n24_216.pdf

  6. 对于序列挖掘,请阅读以下研究论文:

  7. SPADE:用于挖掘频繁序列的高效算法(link.springer.com/content/pdf/10.1023/A:1007652502315.pdf

  8. 顺序挖掘:模式与算法分析(arxiv.org/pdf/1311.0350.pdf

  9. 基于趣味性的顺序模式挖掘算法(ieeexplore.ieee.org/document/8567170

  10. 一种解决顺序模式挖掘问题的新方法(link.springer.com/chapter/10.1007/978-3-642-34630-9_6

4.10 总结

  • 我们研究了关联规则,这些规则可用于发现数据集中存在的变量之间的引人注目的关系

    • 我们介绍了支持度、置信度、提升度和确信度的概念,用于衡量生成的规则的效果。

    • 然后我们转向了 apriori 算法,该算法利用了“自下而上”的方法,首先根据子集的频率生成候选项。Apriori 算法会迭代地扫描整个数据,因此需要很长时间。

    • 我们讨论了 ECLAT,这是一种深度优先搜索方法。它在整个数据集上以垂直方式执行搜索。由于它不会迭代地扫描数据集,因此比 apriori 更快。

    • 我们还介绍了频繁模式增长算法,该算法通过将数据库表示为称为频繁模式树或 FP 树的树来工作。由于这种频繁模式,无需像 Apriori 算法中那样生成候选项,因此它所需的时间较少。

    • 我们接着介绍了一种名为 SPADE 的基于序列的学习技术,其中我们也考虑了各种事件发生的具体顺序。

  • 最后,我们使用 apyori、pyECLAT、fpgrowth_py 和 pyspade 实现了 Python 技术。

第五章:聚类 (高级)

“在复杂中寻找简单- 爱因斯坦”

有时生活很简单,有时我们会遇到相当复杂的情况。我们在两种情况下都能应对,并根据情况调整我们的方法。

在本书的第一部分中,我们涵盖了更简单、更简单的主题。这使您为前进的旅程做好了准备。我们目前处于第二部分,比第一部分稍微复杂一些。第三部分比前两部分更加高级。因此,随着每一章的进行,难度将稍微增加,期望也会增加。

我们在本书的第一部分中学习了聚类算法。我们了解到聚类是一种无监督学习技术,我们希望通过发现数据集中的有趣模式来将数据点分组。我们深入研究了聚类解决方案的含义、聚类算法的不同类别以及最后的案例研究。在那一章中,我们深入探讨了 kmeans 聚类、层次聚类和 DBSCAN 聚类的数学背景、过程、Python 实现以及优缺点。在开始本章之前,建议您复习第二章。

你可能会遇到许多次不符合简单形式和形状的数据集。此外,我们在选择最终要实现的算法之前必须找到最佳匹配项。在这里,我们可能需要更复杂的聚类算法的帮助;这就是本章的主题。在本章中,我们将再次研究三种这样的复杂聚类算法 - 谱聚类、高斯混合模型 (GMM) 聚类和模糊聚类。一如既往,Python 实现将遵循数学和理论概念。本章在数学概念上稍微有些复杂。您不需要成为数学博士,但有时了解算法如何在后台工作是很重要的。与此同时,您会惊讶地发现这些算法的 Python 实现并不乏味。本章没有任何案例研究。

在本书的第五章中,我们将涵盖以下主题:

  1. 谱聚类

  2. 模糊聚类

  3. 高斯混合模型 (GMM) 聚类

  4. 总结

欢迎来到第五章,祝你一切顺利!

5.1 技术工具包

我们将继续使用到目前为止所使用的相同版本的 Python 和 Jupyter 笔记本。本章中使用的代码和数据集已经上传到 GitHub (github.com/vverdhan/UnsupervisedLearningWithPython/tree/main/Chapter%205)。

我们将继续使用到目前为止所使用的常规 Python 库 - numpy、pandas、sklearn、seaborn、matplotlib 等等。在本章中,您需要安装几个 Python 库,它们是 - skfuzzy 和 network 。使用库,我们可以非常快速地实现算法。否则,编写这些算法是相当耗时且繁琐的任务。

让我们开始重新了解一下聚类!

5.2 聚类

回顾第二章,聚类用于将相似的对象或数据点分组。它是一种无监督学习技术,我们的目的是在数据中找到自然分组,如图 5-1 所示。

图 5-1 将对象结果聚类为自然分组。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里,我们可以观察到,在左侧我们有未分组的数据,在右侧数据点已经被分组成逻辑组。我们还可以观察到,可以有两种方法来进行分组或聚类,而且两者都会产生不同的聚类。聚类作为一种技术,在业务解决方案中被广泛应用,如客户细分、市场细分等。

我们在第二章中理解了 kmeans、层次和 DBSCAN 聚类。我们还涵盖了各种距离测量技术和用于衡量聚类算法性能的指标。建议您重新查看这些概念。

在本章中,我们将专注于高级聚类方法。我们将在下一节开始使用谱聚类。

5.3 谱聚类

谱聚类是独特的聚类算法之一。在这个领域进行了一些高质量的研究。像杨安东教授、迈克尔·乔丹教授、雅尔·韦斯教授、施坚博教授、杰特恩德拉·马利克教授等著名的研究人员。我们在本章的最后一节引用了一些论文。

让我们首先定义谱聚类。谱聚类基于数据点的相似性而不是绝对位置进行聚类。因此,在数据处于复杂形状的任何地方,谱聚类都是答案。我们在图 5-2 中展示了一些谱聚类可以提供合理解决方案的示例。

图 5-2 各种复杂数据形状的示例,可以使用谱聚类进行聚类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于图 5-2,我们也可以使用其他算法,如 k 均值聚类。但它们可能无法充分反映数据的复杂形状。正式地说,像 kmeans 聚类这样的算法利用数据点的紧密程度。换句话说,点之间的接近程度和向群集中心的紧密程度驱动 kmeans 中的聚类。另一方面,在谱聚类中,连通性是驱动逻辑。在连通性中,数据点要么彼此紧密相邻,要么以某种方式连接。这种基于连接性的聚类示例已在图 5-2 中描述。

观察图 5-3(i),数据点呈现出环形图案。可能有数据点遵循这种环形图案。这是一个复杂的模式,我们需要对这些数据点进行聚类。想象一下,通过使用聚类方法,红色圆圈被划分为同一簇,如图 5-3(ii)所示。毕竟,它们彼此靠近。但是如果我们仔细观察,这些点是呈圆形排列的,存在一种模式,因此实际的簇应该如图 5-3(iii)所示。

图 5-3 (i) 我们可以有这样一种复杂的数据点表示需要进行聚类。观察环形(ii)一个非常简单的解释可能导致将红点视为同一簇的一部分,但显然,它们并不属于同一簇(iii)我们这里有两个圆。内圆中的点属于一个簇,而外部的点属于另一个簇。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-3 中显示的示例旨在描述谱聚类的优势。

正如我们之前所说的,谱聚类利用连接方法进行聚类。在谱聚类中,立即相邻的数据点在图中被识别出来。这些数据点有时被称为节点。然后,这些数据点或节点被映射到低维空间。低维空间实际上就是在这个过程中,谱聚类使用从数据集派生的特征值、亲和力矩阵、拉普拉斯矩阵和度矩阵。然后,低维空间可以被分成多个簇。

谱聚类利用连接方法进行聚类,它依赖于图论,在这里我们根据连接它们的边来识别节点的簇。

我们将详细研究这个过程。但在检查这个过程之前,有一些重要的数学概念构成了谱聚类的基础,我们现在将进行介绍。

5.3.1 谱聚类的构建模块

我们知道聚类的目标是将相似的数据点分组到一个簇中,而不相似的数据点分组到另一个簇中。我们应该了解一些数学概念。我们将从相似性图的概念开始,这是数据点的一种相当本质的表示。

相似性图

图是表示数据点的一种最简单和直观的方法之一。在图 5-4(i)中,我们展示了一个图的示例,它只是数据点之间的连接,用边来表示。现在,如果两个数据点之间的相似性是正的,或者它高于某个阈值,那么它们就会连接起来,如图 5-4(ii)所示。我们可以使用相似性的权重而不是绝对值。因此,在图 5-4(ii)中,由于点 1 和 2 相对于点 1 和 3 更相似,因此点 1 和 2 之间的连接权重高于点 1 和 3 之间的连接权重。

图 5-4(i) 图是数据点的简单表示。如果它们非常相似,则点或节点彼此连接(ii)如果数据点之间的相似性高,则权重较高,否则对于不相似的数据点,权重较低。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以,我们可以得出结论——使用相似性图,我们希望对数据点进行聚类,使得

  • 数据点的边具有较高的权重值,因此彼此相似,因此它们在同一簇中。

  • 数据点的边具有较低的权重值,因此彼此不相似,因此它们在不同的簇中。

除了相似性图之外,我们还应该了解特征值和特征向量的概念,我们已经在前一章中详细介绍了。建议您进行复习。我们现在将转向邻接矩阵的概念。

邻接矩阵

仔细看图 5-5。我们可以看到从 1 到 5 的各个点彼此连接。然后我们在矩阵中表示连接。该矩阵称为邻接矩阵

正式地说,在邻接矩阵中,行和列分别是相应的节点。矩阵内部的值表示连接——如果值为 0,则表示没有连接,如果值为 1,则表示存在连接。

图 5-5 邻接矩阵表示各个节点之间的连接,如果值为 1,则表示行和列中的相应节点连接。如果值为 0,则表示它们不连接。例如,节点 1 和节点 5 之间存在连接,因此该值为 1,而节点 1 和节点 4 之间没有连接,因此相应的值为 0。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以对于邻接矩阵,我们只关心两个数据点之间是否存在连接。如果我们扩展邻接矩阵的概念,我们得到度矩阵,这是我们的下一个概念。

度矩阵

正式地说,度矩阵是一个对角矩阵,其中沿对角线的节点的度数是连接到它的边的数量。如果我们使用上述相同的示例,我们可以将度矩阵表示为图 5-6 所示。节点 3 和 5 各自有三个连接,它们在对角线上得到 3 作为值,而其他节点各自只有两个连接,因此它们在对角线上得到 2 作为值。

图 5-6 虽然邻接矩阵表示各个节点之间的连接,但度矩阵是每个节点的连接数。例如,节点 5 有三个连接,因此其前面有 3,而节点 1 只有两个连接,所以它有 2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可能会想为什么我们要使用矩阵?矩阵提供了数据的优雅表示,并且可以清楚地描述两个点之间的关系。

现在我们已经涵盖了邻接矩阵和度矩阵,我们可以转向拉普拉斯矩阵。

拉普拉斯矩阵

拉普拉斯矩阵有很多变体,但如果我们采用最简单的形式,即拉普拉斯矩阵是度矩阵减去邻接矩阵。换句话说,L = D – A。我们可以在图 5-7 中展示它。

图 5-7 拉普拉斯矩阵相当容易理解。要获得拉普拉斯矩阵,我们只需将邻接矩阵从度矩阵中减去,如上例所示。这里,D 表示度矩阵,A 是邻接矩阵,L 是拉普拉斯矩阵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拉普拉斯矩阵是非常重要的,我们使用 L 的特征值来开发谱聚类。一旦我们获得了特征值和特征向量,我们可以定义另外两个值 – 谱间隙和 Fielder 值。第一个非零特征值是 谱间隙,它定义了图的密度。Fielder 值 是第二个特征值,它提供了将图分割成两个组件所需的最小切割的近似值。Fielder 值的相应向量称为 Fielder 矢量

Fielder 矢量具有正负组件,它们的结果总和为零。

我们将在下一节详细学习谱聚类的过程时使用这个概念。在转向谱聚类的过程之前,我们现在将介绍亲和力矩阵的另一个概念。

亲和力矩阵

在邻接矩阵中,如果我们用权重的相似性替换连接的数量,我们将得到亲和力矩阵。如果点完全不相似,则亲和力为 0,否则如果它们完全相似,则亲和力为 1。矩阵中的值表示数据点之间不同水平的相似性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 快速测验 – 回答这些问题以检查你的理解。答案在书的末尾

  1. 度矩阵是通过计算连接数量创建的。真还是假。

  2. 拉普拉斯矩阵是否是度和邻接矩阵之间的转置的除法。真还是假。

  3. 在纸上写下一个矩阵,然后推导出它的邻接和度矩阵。

我们现在已经掌握了谱聚类的所有构建模块。我们现在可以转向谱聚类的过程。

5.3.2 谱聚类的过程

现在我们已经掌握了谱聚类的所有构建模块。在高层次上,各个步骤可以总结如下:

  1. 我们获得数据集并计算其度矩阵和邻接矩阵。

  2. 使用它们,我们得到拉普拉斯矩阵。

  3. 然后我们计算拉普拉斯矩阵的前 k 个特征向量。k 个特征向量实际上就是对应于 k 个最小特征值的向量。

  4. 这样形成的矩阵用于在 k 维空间中对数据点进行聚类。

现在我们将介绍使用示例来覆盖谱聚类的过程,如图 5-8 所示。这些步骤通常在实际实现中不会遵循,因为我们有包和库来实现它。这里涵盖了这些步骤,以便让您了解如何从零开始开发算法的想法。对于 Python 实现,我们将仅使用库和包。虽然可能从头开始开发实现,但重新发明轮子不够高效。

图 5-8 考虑所示的示例,其中我们有一些数据点,它们彼此连接。我们将对这些数据执行谱聚类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在当我们希望对这些数据进行谱聚类时。

  1. 我们将把创建邻接矩阵和度矩阵的工作留给您。

  2. 下一步是创建拉普拉斯矩阵。我们在图 5-9 中分享了输出的拉普拉斯矩阵。

图 5-9 展示了数据的拉普拉斯矩阵。建议您创建度矩阵和邻接矩阵并检查输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 现在,Fielder 向量如图 5-10 所示,用于上述拉普拉斯矩阵。我们创建 Fielder 向量,如上一节所述。观察矩阵的和为零。
图 5-10 Fielder 向量是拉普拉斯矩阵的输出,在这里观察到矩阵的和为零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 我们可以看到有一些正值和一些负值,根据这些值,我们可以创建两个不同的簇。这是一个非常简单的示例,用来说明谱聚类的过程。
图 5-11 识别了两个簇。这是一个非常简单的示例,用来说明谱聚类的过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述过程是谱聚类的一个非常简单的表示。谱聚类对图像分割、语音分析、文本分析、实体解析等非常有用。这是一种非常简单直观的方法,不对数据的形状做出任何假设。像 kmeans 这样的方法假设点在聚类中心周围呈球形分布,而在谱聚类中没有这样的强假设。

另一个重要的区别是,在谱聚类中,与其他方法相比,数据点不需要具有凸边界,其中紧凑性驱动聚类。谱聚类有时会很慢,因为需要计算特征值、拉普拉斯等。随着数据集的增大,复杂性增加,因此谱聚类可能会变慢,但是当我们有一个稀疏的数据集时,它是一种快速的方法。

我们现在将进行谱聚类算法的 Python 实现。

5.2.1 谱聚类的 Python 实现

到目前为止,我们已经涵盖了谱聚类的理论细节,现在是时候进入代码了。为此,我们将创建一个数据集并运行 k-means 算法,然后使用谱聚类来比较结果。

第一步:首先导入所有必要的库。这些库是标准库,除了我们将要介绍的几个。sklearn 是最著名和最受欢迎的库之一,我们从中导入 SpectralClusteringmake_blobsmake_circles

from sklearn.cluster import SpectralClustering
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles
from numpy import random
import numpy as np
from sklearn.cluster import SpectralClustering, KMeans
from sklearn.metrics import pairwise_distances
from matplotlib import pyplot as plt
import networkx as nx
import seaborn as sns

第二步:我们现在将创建一个数据集。我们使用的是 make_circles 方法。在这里,我们取 2000 个样本,并将它们表示成一个圆。结果如下所示。

data, clusters = make_circles(n_samples=2000, noise=.01, factor=.3, random_state=5)
plt.scatter(data[:,0], data[:,1]) 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三步:我们现在将使用 kmeans 聚类测试这个数据集。两种颜色显示两个不同的重叠的簇。

kmeans = KMeans(init='k-means++', n_clusters=2)
km_clustering = kmeans.fit(data)
plt.scatter(data[:,0], data[:,1], c=km_clustering.labels_, cmap='prism', alpha=0.5, edgecolors='g')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第四步:我们现在用谱聚类运行相同的数据,发现两个簇在这里被单独处理。

spectral = SpectralClustering(n_clusters=2, affinity='nearest_neighbors', random_state=5)
sc_clustering = spectral.fit(data)
plt.scatter(data[:,0], data[:,1], c=sc_clustering.labels_, cmap='prism', alpha=0.5, edgecolors='g')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以观察到同一数据集在两个算法中的处理方式不同。谱聚类在处理分离的圆圈时表现更好,分离的圆圈被独立的表示出来。

第五步:你可以尝试改变数据集中的值并运行算法,模拟不同的情况,进而比较结果。

第一部分的算法已经讲解完成。下一部分我们会讲解模糊聚类。

5.3 模糊聚类

到目前为止,我们已经涵盖了相当多的聚类算法。你是否想过为什么一个数据点只能属于一个聚类?为什么一个数据点不能属于多个聚类?看一下图 5-12。

图 5-12 左侧的图表示所有数据点。红点可以属于多个聚类。实际上,我们可以给每个点分配多个聚类。可以给一个点赋予属于特定聚类的概率分数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们知道聚类是根据对象之间的相似度将其分为同一个组的方法。相似的项在同一簇中,而不同的项在不同的簇中。聚类的思想是确保同一簇中的项应该尽可能相似。当对象只能在一个簇中时,称为硬聚类。K-means 聚类是硬聚类的经典例子。但是,如果回顾图 5-12,我们可以观察到一个对象可以属于多个簇。这也被称为软聚类

创建模糊边界比创建硬聚类更经济。

在模糊聚类中,一个项目可以被分配给多个聚类。靠近聚类中心的项目可能比靠近聚类边缘的项目更多地属于该聚类。这被称为成员关系。它采用最小二乘解决方案找到一个物品的最佳位置。这个最佳位置可能是两个或多个聚类之间的概率空间。我们将在详细介绍模糊聚类过程时详细讨论这个概念,现在我们将转向模糊聚类算法的类型。

5.3.3 模糊聚类的类型

模糊聚类可以进一步分为经典模糊算法和基于形状的模糊算法,我们通过图 5-13 来展示。

图 5-13 模糊算法可以分为经典模糊算法和基于形状的模糊算法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们将在这里详细介绍模糊 c 均值算法。其他算法我们将简要介绍。

  1. 高斯塔夫松-凯塞尔算法,有时称为 GK 算法,通过将一个项目与一个聚类和一个矩阵相关联。通过使用协方差矩阵,GL 会生成椭圆形聚类,并根据数据集中的各种结构进行修改。它允许算法捕捉聚类的椭圆形属性。GK 可以导致更窄的聚类,并且在项目数较多的地方,这些区域可能更加薄。

  2. Gath-Geva 算法不基于目标函数。聚类可以导致任何形状,因为它是统计估计值的模糊化。

  3. 基于形状的聚类算法根据其名称自解释。圆形模糊聚类算法将导致圆形的聚类,依此类推。

  4. 模糊 c 均值算法或 FCM 算法是最流行的模糊聚类算法。它最初由 J.C. Dunn 于 1973 年开发,之后进行了多次改进。它与 k 均值聚类非常相似。这里有一个成员关系的概念,我们将在下面介绍。

参考图 5-14。在第一幅图中,我们有一些项目或数据点。这些数据点可以是聚类数据集的一部分,如客户交易等。在第二幅图中,我们为这些数据点创建了一个聚类。在创建该聚类时,为每个数据点分配成员关系成绩。这些成员关系成绩表明数据点属于聚类的程度或级别。我们将很快介绍计算这些值的数学函数。

我们不应该混淆程度和概率。如果我们对这些程度进行求和,可能得不到 1,因为这些值在 0 和 1 之间对所有项目进行了归一化。

在第三幅图中,我们可以观察和比较点 1 靠近聚类中心,因此比点 2 更多地属于该聚类的程度,而点 2 靠近聚类的边界或边缘。

图 5-14 (i) 我们这里有一些数据点,可以被聚类 (ii) 数据点可以被分成两个簇。对于第一个簇,聚类中心用加号表示。(iii) 我们可以观察到这里点 1 相对于点 2 更接近聚类中心。因此,我们可以得出结论,点 1 属于这个聚类的程度高于聚类 2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们将深入探讨算法的技术细节。尽管可能有点数学重,但本节可以作为可选内容。

假设我们有一组 n 个项目

X = {x[1], x[2], x[3], x[4], x[5]…. x[n]}

我们将 FCM 算法应用于这些项。这些 n 项根据某些标准被聚类成 c 个模糊聚类。假设我们从算法中获得了一个 c 个聚类中心的列表,表示为 C = {c[1], c[2], c[3], c[4], c[5]…. c[c]}

算法还返回一个可以定义为下述的划分矩阵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里,每个元素 w[i,j] 表示元素 X 中每个元素属于聚类 c[j] 的程度。这是划分矩阵的目的。

数学上,我们可以得到 w[i,j] 如方程式 5-1 所示。方程的证明超出了本书的范围。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该算法还为聚类生成聚类中心。聚类的中心是该聚类中所有点的平均值,平均值由它们各自的属于该聚类的程度加权得到。如果我们用数学表示,我们可以写成方程式 5-2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在方程式 5-1 和 5-2 中,有一个非常重要的术语“m”。m 是用于控制聚类模糊性的超参数。m 的值 ≥ 1,通常可以保持为 2。

m 值越高,我们将获得更模糊的聚类。

现在我们将逐步检查 FCM 算法的过程:

  1. 首先,我们像 k-means 聚类一样开始。我们选择我们希望在输出中拥有的聚类数量。

  2. 然后,将系数随机分配给每个数据点。

  3. 现在我们希望迭代直到算法收敛。回想一下 k-means 算法如何收敛,我们通过随机分配聚类数量来启动该过程。然后,我们迭代地计算每个聚类的中心。这就是 kmeans 如何收敛的。对于 FCM,我们将使用类似的过程,尽管有细微的差异。我们增加了一个成员值 w[i,j] 和 m。

  4. 对于 FCM,为了使算法收敛,我们根据方程式 5-2 计算每个聚类的中心。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 对于每个数据点,我们还计算其在特定聚类中的系数。我们将使用方程式 5-1。

  2. 现在我们必须迭代直到 FCM 算法收敛。我们希望最小化的成本函数由 () 给出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦这个函数被最小化,我们就可以得出结论,即 FCM 算法已经收敛。换句话说,我们可以停止进程,因为算法已经完成处理。

现在是与 k 均值算法进行比较的好时机了。在 k 均值中,我们有一个严格的目标函数,它只允许一个聚类成员身份,而对于 FCM 聚类,我们可以根据概率分数得到不同的聚类成员身份。

FCM 在边界不清晰且严格的业务案例中非常有用。考虑在生物信息学领域中,一个基因可以属于多个聚类。或者如果我们有重叠的数据集,比如在营销分析、图像分割等领域。与 k 均值相比,FCM 可以给出相对更稳健的结果。

我们现在将在下一节继续进行 FCM 聚类的 Python 实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 小测验 - 回答这些问题以检查你的理解。答案在本书末尾。

  1. 模糊聚类允许我们创建重叠的聚类。真或假。

  2. 一个数据点只能属于一个聚类。真或假。

  3. 如果“m”的值较低,则我们会得到更清晰的聚类。真或假。

5.3.4 FCM 的 Python 实现

我们在上一节已经介绍了 FCM 的过程。本节我们将着手讨论 FCM 的 Python 实现。

步骤 1:导入必要的库。

import skfuzzy as fuzz
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

步骤 2:我们现在将声明一个颜色调色板,稍后将用于对聚类进行颜色编码。

color_pallete = ['r','m','y','c', 'brown', 'orange','m','k', 'gray','purple','seagreen']

步骤 3:我们将定义聚类中心。

cluster_centers = [[1, 1],
           [2, 4],
           [5, 8]]

步骤 4:

sigmas = [[0.5, 0.6],
          [0.4, 0.5],
          [0.1, 0.6]]

步骤 5:

np.random.seed(5)  

xpts = np.zeros(1)
ypts = np.zeros(1)
labels = np.zeros(1)
for i, ((xmu, ymu), (xsigma, ysigma)) in enumerate(zip(cluster_centers, sigmas)):
    xpts = np.hstack((xpts, np.random.standard_normal(500) * xsigma + xmu))
    ypts = np.hstack((ypts, np.random.standard_normal(500) * ysigma + ymu))
    labels = np.hstack((labels, np.ones(500) * i))

步骤 6:

fig0, ax0 = plt.subplots()
for label in range(5):
    ax0.plot(xpts[labels == label], ypts[labels == label], '.')
ax0.set_title('Data set having 500 points.')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 7:

fig1, axes1 = plt.subplots(3, 3, figsize=(10, 10))
alldata = np.vstack((xpts, ypts))
fpcs = []

for ncenters, ax in enumerate(axes1.reshape(-1), 2):
    cntr, u, u0, d, jm, p, fpc = fuzz.cluster.cmeans(
        alldata, ncenters, 2, error=0.005, maxiter=1000, init=None)

    # Store fpc values for later
    fpcs.append(fpc)

    # Plot assigned clusters, for each data point in training set
    cluster_membership = np.argmax(u, axis=0)
    for j in range(ncenters):
        ax.plot(xpts[cluster_membership == j],
                ypts[cluster_membership == j], '.', color=colors[j])

    # Mark the center of each fuzzy cluster
    for pt in cntr:
        ax.plot(pt[0], pt[1], 'rs')

    ax.set_title('cluster_centers = {0}; FPC = {1:.2f}'.format(ncenters, fpc), size=12)
    ax.axis('off')

fig1.tight_layout()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过这个我们结束了模糊聚类,我们可以在下一节转到高斯混合模型。

5.4 高斯混合模型

我们将继续我们在上一节中关于软聚类的讨论。回想一下,我们在那里介绍了高斯混合模型。现在我们将对其进行详细说明。我们将研究这个概念,并对其进行 Python 实现。

首先让我们重新理解高斯分布,有时也被称为正态分布。你可能听说过钟形曲线,它指的是同一件事情。

在图 5-15 中,观察µ(平均值)为 0,σ²(标准差)为 1 的分布。这是一个完美的正态分布曲线。比较这里不同曲线上的分布。

(图片来源 - 维基百科)

图 5-15 高斯分布是最著名的分布之一。观察均值和标准差值的变化及其对应曲线的影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

高斯分布的数学表达式为

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的方程也被称为概率密度函数 (pdf)。在图 5-15 中,观察到当 µ 为 0,σ² 为 1 时的分布。这是一个完美的正态分布曲线。通过改变均值和标准差的值,我们在图 5-15 中比较不同曲线中的分布,从而得到不同的图形。

你可能想知道为什么我们在这里使用高斯分布。这里有一个非常著名的统计定理叫做中心极限定理。我们在这里简要解释该定理。根据该定理,我们收集的数据越多,分布就越趋向于高斯分布。这种正态分布可以在化学、物理、数学、生物学或任何其他学科中观察到。这就是高斯分布的美妙之处。

在图 5-15 中显示的图是一维的。我们也可以有多维高斯分布。在多维高斯分布的情况下,我们将得到图 5-16 中显示的 3D 图。我们的输入是一维的标量。现在,我们的输入不再是标量,而是一个向量,均值也是一个向量,代表数据的中心。因此,均值的维度与输入数据相同。方差现在是协方差矩阵 ∑。该矩阵不仅告诉我们输入的方差,还评论了不同变量之间的关系。换句话说,如果 x 的值发生变化,y 的值会受到影响。请看下面的图 5-16。我们可以理解这里的 x 和 y 变量之间的关系。

(图片来源 – 维基百科)

图 5-16 展示了高斯分布的三维表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

协方差在这里起着重要作用。K-means 不考虑数据集的协方差,而在 GMM 模型中使用。

让我们来研究 GMM 聚类的过程。想象我们有一个包含 n 个项目的数据集。当我们使用 GMM 聚类时,我们不是使用质心方法来找到聚类,而是对手头的数据集拟合一组 k 个高斯分布。换句话说,我们有 k 个聚类。我们必须确定每个高斯分布的参数,包括聚类的均值、方差和权重。一旦确定了每个分布的参数,我们就可以找到每个项目属于 k 个聚类的相应概率。

从数学上讲,我们可以根据方程 5-5 计算概率。该方程用于告诉我们一个特定点 x 是 k 个高斯分布的线性组合。Φ[j] 术语用于表示高斯的强度,并且可以看到第二个方程中这种强度的总和等于 1。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于谱聚类,我们必须确定 Φ、∑ 和 µ 的值。正如你所想象的,获取这些参数的值可能是一项棘手的任务。确实有一种稍微复杂的叫做期望最大化技术或 EM 技术,我们现在将介绍它。这一部分涉及的数学概念比较深,是可选的。

5.4.1 期望最大化(EM)技术

EM 是确定模型正确参数的统计和数学解决方案。有很多流行的技术,也许最著名的是最大似然估计。但同时,最大似然估计也可能存在一些挑战。数据集可能有缺失值,或者换句话说,数据集是不完整的。或者数据集中的一个点可能是由两个不同的高斯分布生成的。因此,确定生成数据点的分布将非常困难。在这里,EM 可以提供帮助。

k-means 只使用平均值,而 GMM 利用数据的平均值和方差。

用于生成数据点的过程被称为潜在变量。由于我们不知道这些潜在变量的确切值,EM 首先使用当前数据估计这些潜在变量的最佳值。一旦完成了这一步,然后估计模型参数。使用这些模型参数,再次确定潜在变量。并且使用这些新的潜在变量,推导出新的模型参数。这个过程持续进行,直到获得足够好的潜在值和模型参数,使其很好地适应数据。现在让我们更详细地研究一下。

我们将使用上一节中的相同示例。

想象一下我们有一个包含 n 个项的数据集。当我们使用 GMM 聚类时,我们不是使用质心方法找到簇,而是将一组 k 个高斯分布拟合到手头的数据集。换句话说,我们有 k 个簇。我们必须确定每个高斯分布的参数,即簇的平均值、方差和权重。假设均值为 µ[1]、µ[2]、µ[3]、µ[4]…. µ[k],协方差为 ∑[1]、∑[2]、∑[3]、∑[4]…. ∑[k]。我们还可以有一个参数来表示分布的密度或强度,可以用符号 Φ 表示。

现在我们将从期望步骤或 E 步开始。在这一步中,每个数据点都以概率分配到一个簇。因此,对于每个点,我们计算其属于一个簇的概率,如果这个值很高,则该点在正确的簇中,否则该点在错误的簇中。换句话说,我们正在计算每个数据点由每个 k 个高斯分布生成的概率。

由于我们正在计算概率,因此这些被称为软分配。

概率是使用方程式 5-6 计算的。如果我们仔细观察,分子是概率,然后我们通过分母进行归一化。分子与我们在方程式 5-5 中看到的相同。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在上面的期望步骤中,对于数据点 x[i,j],其中 i 是行,j 是列,我们得到一个矩阵,其中行由数据点表示,列由它们对应的高斯值表示。

现在期望步骤已经完成,我们将执行最大化或 M 步骤。在这一步中,我们将使用方程式 5-7 下面的公式更新 µ、∑ 和 Φ 的值。回想一下,在 k-means 聚类中,我们只是取数据点的平均值并继续前进。我们在这里做了类似的事情,尽管使用了我们在上一步中计算的概率或期望。

三个值可以使用下面的方程式计算。方程式 5-7 是协方差 ∑[j] 的计算,在其中我们计算所有点的协方差,然后以该点由高斯 j 生成的概率加权。数学证明超出了本书的范围。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

均值 µ[j] 由方程式 5-8 确定。在这里,我们确定所有点的均值,以该点由高斯 j 生成的概率加权。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

类似地,密度或强度是由方程式 5-9 计算的,其中我们将每个点生成为高斯 j 的概率相加,然后除以总点数 N。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据这些值,推导出新的 ∑、µ 和 Φ 的值,并且该过程继续直到模型收敛。当我们能够最大化对数似然函数时,我们停止。

这是一个复杂的数学过程。我们已经涵盖了它,以便让您深入了解统计算法背后发生的事情。Python 实现比我们现在将要介绍的数学概念要简单得多。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 POP QUIZ – 回答这些问题以检查您的理解。答案在书的末尾。

  1. 高斯分布的均值等于 1,标准差等于 0。是或否。

  2. GMM 模型不考虑数据的协方差。是或否。

5.4.2 GMM 的 Python 实现

我们首先导入数据,然后将使用 kmeans 和 GMM 进行结果比较。

步骤 1:我们将导入所有的库并导入数据集。

import pandas as pd
data = pd.read_csv('vehicle.csv')
import matplotlib.pyplot as plt

步骤 2:我们现在将从数据集中删除任何 NA。

data = data.dropna()

步骤 3:我们现在将拟合一个 kmeans 算法。我们将聚类数保持为 5。请注意,我们并不是说它们是理想的聚类数。这些聚类数仅用于说明目的。我们声明一个变量 kmeans,然后使用五个聚类,然后是下一个拟合数据集。

from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=5)
kmeans.fit(data)

步骤 4:我们现在将绘制聚类。首先,在数据集上进行预测,然后将值添加到数据框作为新列。然后,用不同颜色表示不同的聚类绘制数据。

输出显示在下图中。

pred = kmeans.predict(data)
frame = pd.DataFrame(data)
frame['cluster'] = pred

color=['red','blue','orange', 'brown', 'green']
for k in range(0,5):
    data = frame[frame["cluster"]==k]
    plt.scatter(data["compactness"],data["circularity"],c=color[k])
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 4:我们现在将拟合一个 GMM 模型。请注意,代码与 kmeans 算法相同,只是算法的名称从 kmeans 更改为 GaussianMixture。

from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=5)
gmm.fit(data)

#predictions from gmm
labels = gmm.predict(data)
frame = pd.DataFrame(data)
frame['cluster'] = labels

步骤 5:我们现在将绘制结果。输出如下所示。

color=['red','blue','orange', 'brown', 'green']
for k in range(0,5):
    data = frame[frame["cluster"]==k]
    plt.scatter(data["compactness"],data["circularity"],c=color[k])
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 6:建议您使用不同的聚类值运行代码以观察差异。下面的图中,左边是具有两个聚类的 kmeans,右边是具有两个聚类的 GMM。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

高斯分布是最广泛使用的数据分布之一。如果我们比较 kmeans 和 GMM 模型,我们会发现 kmeans 并不考虑数据的正态分布。kmeans 也不考虑各个数据点之间的关系。

Kmeans 是基于距离的算法,GMM 是基于分布的算法。

简而言之,使用 GMM 模型创建聚类尤其有利,特别是当我们有重叠的数据集时。这是一个在金融和价格建模、基于 NLP 的解决方案等方面非常有用的技术。

通过这个,我们已经涵盖了本章的所有算法。我们现在可以转到总结。

5.5 总结

在本章中,我们探讨了三种复杂的聚类算法。你可能觉得数学概念有点沉重。它们确实很沉重,但能更深入地理解过程。并不是说这些算法对每个问题都是最好的。在现实世界的业务问题中,理想情况下,我们应该首先使用经典的聚类算法 - kmeans、层次聚类和 DBSCAN。如果我们得不到可接受的结果,那么我们可以尝试复杂的算法。

许多时候,将数据科学问题等同于算法的选择,其实并不是这样。算法当然是整个解决方案的重要组成部分,但不是唯一的部分。在现实世界的数据集中,有很多变量,数据量也相当大。数据有很多噪音。当我们筛选算法时,我们必须考虑所有这些因素。算法的维护和更新也是我们心中的主要问题之一。所有这些细节在书的最后一章中都有详细介绍。

我们将在下一章介绍复杂的降维技术。你现在可以转到问题。

实际下一步和建议阅读
  1. 在第二章中,我们使用了各种技术进行聚类。使用那里的数据集,并执行谱聚类、GMM 和 FCM 聚类以比较结果。

  2. 第二章末尾提供了数据集,可以用于聚类。

  3. 从此 Kaggle 链接获取用于聚类的信用卡数据集(www.kaggle.com/vipulgandhi/spectral-clustering-detailed-explanation),以及我们之前也使用过的著名的 IRIS 数据集。

  4. 有一本由亨利·赫克斯莫尔(Henry Hexmoor)撰写的《计算网络科学》是研究数学概念的好书。

  5. 从下面的链接获取光谱聚类论文并学习它们:

  6. 关于光谱聚类:分析与算法proceedings.neurips.cc/paper/2001/file/801272ee79cfde7fa5960571fee36b9b-Paper.pdf

  7. 具有特定特征值选择的光谱聚类www.eecs.qmul.ac.uk/~sgg/papers/XiangGong-PR08.pdf

  8. 光谱聚类背后的数学及其与主成分分析的等价性arxiv.org/pdf/2103.00733v1.pdf

  9. 从以下链接获取 GMM 论文并探索它们:

  10. 用于聚类的特定高斯混合模型citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.79.7057&rep=rep1&type=pdf

  11. 在数据流中应用复合高斯混合模型ieeexplore.ieee.org/document/5620507

  12. 从以下链接获取 FCM 论文并学习它们:

  13. FCM:模糊 c 均值聚类算法www.sciencedirect.com/science/article/pii/0098300484900207

  14. 对模糊 c 均值聚类技术进行调查www.ijedr.org/papers/IJEDR1704186.pdf

  15. 模糊 c 均值和可能性 c 均值聚类算法的实现,聚类倾向分析和聚类验证arxiv.org/pdf/1809.08417.pdf

第六章:维度降低(高级)

“生活很简单,但我们坚持把它变复杂了 - 孔子”

简单是一种美德,无论是在生活中还是在数据科学中都是如此。到目前为止,我们已经讨论了很多算法 - 其中一些足够简单,而另一些则有些复杂。在本书的第一部分,我们学习了更简单的聚类算法,在最后一章中,我们考察了高级聚类算法。同样,在第三章中,我们学习了几个维度算法,如 PCA。在同样的基础上,我们将在本章中学习两种高级降维技术。

本书本部分和下一部分涵盖的高级主题旨在为您准备复杂问题。虽然您可以应用这些高级解决方案,但始终建议从经典解决方案(如 PCA 进行维度降低)开始。如果获得的解决方案不够优秀,则可以尝试高级方案。

在我们有很多变量时,降维是最受追捧的解决方案之一。请回忆一下第三章中讨论过的“维度诅咒”。在继续之前,建议您回顾第三章。我们将在本章中涵盖 t 分布随机邻居嵌入(t-SNE)和多维缩放(MDS)。本章将涉及一些创建我们将讨论的高级技术基础的数学概念。像往常一样,我们将有概念讨论,接着是 Python 实现。本章末尾将有一个简短的案例研究。此外,在本章中,我们也将使用图像数据集开发解决方案!

您的脑海中可能会有一个困惑。需要什么水平的数学知识,深入的统计知识是先决条件吗?答案既是肯定也是否定。虽然,具备数学理解能让您更深入地理解算法并欣赏过程;同时,对于实际业务实现,有时可能想跳过数学直接转向 Python 实现。我们建议您至少具有超过基本的数学理解以充分把握概念。在本书中,我们提供这种数学支持水平而不过度深入 - 实践世界和数学概念的最佳混合。

在本书的第六章中,我们将介绍以下主题:

  1. t 分布随机邻居嵌入(t-SNE)

  2. 多维缩放(MDS)

  3. 算法的 Python 实现

  4. 案例研究

欢迎来到第六章,并祝你好运!

6.1 技术工具箱

我们将继续使用与迄今为止相同版本的 Python 和 Jupyter 笔记本。本章中使用的代码和数据集已在github.com/vverdhan/UnsupervisedLearningWithPython/tree/main/Chapter%206位置检查过。

在本章中,您需要安装 Keras 作为额外的 Python 库。除此之外,我们还需要常规的库 - numpy、pandas、matplotlib、seaborn、sklearn。使用库,我们可以非常快速地实现算法。否则,编写这些算法是非常耗时和痛苦的任务。

让我们开始学习本书的第六章吧!

6.2 多维缩放(MDS)

我喜欢旅行。不幸的是,由于 COVID 大流行,旅行受到了打击。正如您所知,地图在旅行中非常方便。现在,想象一下你被分配了一个任务。你收到了世界各地一些城市之间的距离。例如,伦敦和纽约之间,伦敦和巴黎之间,巴黎和新德里之间等等。然后我们要求您重新创建生成这些距离的地图。如果我们必须重新创建那个二维地图,那将通过试错,我们将做出一些假设并继续进行该过程。这肯定是一项令人疲惫的练习,容易出错,而且确实非常耗时。MDS 可以轻松地为我们完成这项任务。

在考虑上述例子时,请忽略地球不是平的这一事实。并假设距离测量度量是恒定的。例如,英里或公里之间没有混淆。

作为例证,考虑图 6-1。

图 6-1 表示城市之间的距离以及它们在地图上的表示。该图仅用于帮助理解,并不代表实际结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正式地说,如果我们有 x 个数据点,多维缩放(MDS)可以帮助我们将这些 x 点之间的成对距离的信息转换为笛卡尔空间中点的配置。或者简单地说,MDS 将一个大的维度数据集转换为一个较低维度的数据集,并在这个过程中保持点之间的距离或相似度不变。

为了简化,考虑下面的图片。这里有三个点 - A、B 和 C。我们将这些点表示在一个三维空间中。然后我们在二维空间中表示这三个点,最后它们在一维空间中表示。在下图 6-2 中,图中的点之间的距离不是按比例的。图 6-2 中的示例表示降低维度数量的含义。

图 6-2 表示三个点 - 首先我们展示三个点在三维空间中。然后它们被表示在二维空间中,最后在一维空间中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,在 MDS 中,多维数据被降低到较低维度。

我们可以有三种类型的 MDS 算法

  1. 经典 MDS,

  2. 度量多维缩放和

  3. 非度量多维缩放。

我们将在书中详细讨论度量 MDS 过程,同时我们将简要介绍经典的和非度量的方法。

想象一下我们有两个点 – i 和 j。假设两点之间的原始距离是 d[ij],而低维空间中的相应距离是 d[ij]。

在经典 MDS 中,点之间的距离被视为欧几里得距离,原始距离和拟合距离在相同的度量中表示。这意味着,如果在高维空间中使用欧几里得方法计算原始距离,那么在低维空间中计算的拟合距离也是使用欧几里得距离计算的。我们已经知道如何计算欧几里得距离了。例如,我们要找到点 i 和 j 之间的距离,假设距离为 d[ij]。距离可以用方程式 6-1 给出的欧几里得距离公式给出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在早些章节中,我们已经讨论过其他距离函数,如曼哈顿距离、汉明距离等。建议你进行复习。

现在我们将来到非度量 MDS。刚才我们注意到欧几里得距离可以用来计算两点之间的距离。有时候无法采用距离的实际值,比如当 d[ij] 是一个实验的结果,主观评价被做出时。换句话说,各个数据参数被分配了一个排名。例如,如果点 2 和 5 之间的距离在原始数据中排名第 4,那么在这种情况下,使用 d[ij] 的绝对值就不明智了,因此必须使用相对值或排名值。这就是非度量 MDS 的过程。例如,想象一下我们有四个点 – A、B、C 和 D。我们希望排列这四个点之间的相应距离。点的相应组合可以是 – A 和 B、A 和 C、A 和 D、B 和 C、B 和 D,最后是 C 和 D。它们的距离可以按照表 6-1 中所示的排名进行排列。

表 6-1 代表着四个点之间的相应距离以及距离的排名
点对距离相应距离的排名
A 和 B1003
A 和 C1054
A 和 D952
B 和 C2056
B 和 D1505
C 和 D551

因此,在非度量 MDS 方法中,我们不是使用实际距离,而是使用距离的相应排名。现在我们将转向度量 MDS 方法。

我们知道在经典的 MDS 中,原始距离和拟合距离在相同的度量中表示。在度量 MDS中,假设通过在数据集上应用一些参数变换,d[ij]的值可以转换为欧几里得距离。在一些文章中,你可能会发现经典 MDS 和度量 MDS 被互换使用。

在 MDS 中,作为第一步,计算点之间的相应距离。一旦计算出相应的距离,MDS 将尝试将更高维的数据点表示为更低维的空间。为了执行此操作,必须进行优化过程,以便选择最合适的结果维数。因此,必须优化一个损失函数或成本函数。

如果你不知道什么是成本函数,请看下面这一节。

成本函数

我们使用算法来预测变量的值。例如,我们可能会使用某种算法来预测下一年产品的预期需求。我们希望算法尽可能准确地预测。成本函数是检查算法性能的一种简单方法。

成本函数是衡量我们算法效果的一种简单技术。它是衡量预测模型性能的最常见方法。它比较算法预测的原始值和预测值,并计算模型在预测中的错误程度。

正如你所想象的,在理想解决方案中,我们希望预测值与实际值相同,这是非常难以实现的。如果预测值与实际值相差很大,成本函数的输出就会更高。如果预测值接近实际值,则成本函数的值较低。一个健壮的解决方案是具有较低成本函数值的解决方案。因此,优化任何算法的目标将是最小化成本函数的值。成本函数也称为损失函数,这两个术语可以互换使用。

在度量 MDS 中,我们也可以将成本函数称为应力。应力的公式由下面给出的方程式 6-2 给出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在让我们来了解方程式:

  1. 术语 Stress[D] 是 MDS 函数必须最小化的值。

  2. 在较低维空间中具有新坐标集的数据点由 x[, x[2], x[3]…. x[N].] 表示。

  3. 术语 ||x[i] – x[j]|| 是它们在较低维空间中的两点之间的距离。

  4. 术语 d[ij] 是原始多维空间中两点之间的原始距离。

通过观察方程式,我们可以清楚地理解,如果||x[i] – x[j]||和 d[ij]的值彼此接近,结果应力的值将较小。

最小化应力值是损失函数的目标。

为了优化这个损失函数,可以使用多种方法。其中最著名的方法之一是使用梯度下降法,最初由 Kruskal 和 Wish 于 1978 年提出。梯度下降法非常简单易懂,并可以用一个简单的类比来解释。

想象一下你站在山顶上,想要下来。在这样做的同时,你想选择最快的路径,因为你想尽快下来(不,你不能跳!)。所以,为了迈出第一步,你会四处看看,无论哪条路最陡峭,你都可以朝那个方向迈出一步,然后你会到达一个新的点。然后再次,你会朝最陡峭的方向迈出一步。我们在图 6-3(i) 中展示了这个过程。

图 6-3 (i) 第一幅图是一个站在山顶上并试图下来的人。梯度下降过程遵循这种方法 (ii) 梯度下降过程中成本函数的实际优化过程。注意,在收敛点,成本函数的值是最小的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,如果一个算法要达到类似的效果,过程可以表示为图 6-3 (ii),其中损失函数从一个点开始,最终到达收敛点。在这个收敛点,成本函数是最小的。

MDS 与其他降维技术不同。与诸如 PCA 等技术相比,MDS 不对数据集做任何假设,因此可以用于更大类型的数据集。此外,MDS 允许使用任何距离测量度量。与 PCA 不同,MDS 不是一种特征值-特征向量技术。回想一下,在 PCA 中,第一轴捕获最大的方差,第二轴具有下一个最佳方差,依此类推。在 MDS 中,没有这样的条件。MDS 中的轴可以根据需要翻转或旋转。接下来,在大多数其他使用的降维方法中,算法确实计算了许多轴,但无法查看。在 MDS 中,开始时明确选择较小数量的维度。因此,解决方案中的歧义较少。此外,在其他解决方案中通常只有一个唯一的解决方案,而 MDS 尝试迭代地找到可接受的解决方案。这意味着在 MDS 中,对于相同的数据集可能会有多个解决方案。

但与此同时,MDS 所需的计算时间对于更大的数据集要求更高。在用于优化的梯度下降方法中存在一个陷阱。参考图 6-4。让我们参考上一节中提到的山的例子。想象一下,当你从山顶下来时。起点是 A,山的底部在 C 点。当你下来时,你到达了 B 点。正如图 6-4(i) 中所示,B 点周围有一点高度。在 B 点,你可能会错误地得出结论,认为自己已经到达了山底。换句话说,你会认为自己完成了任务。这就是局部最小值的确切问题。

有可能损失函数会陷入局部极小值而不是全局极小值。算法可能会认为已经达到收敛点,而实际上并没有完全收敛,我们仍然处于局部极小值。

图 6-4 虽然第一幅图是收敛点并且代表梯度下降法,但请注意第二幅图中全局极小值在其他地方,而算法可能会陷入局部极小值。该算法可能认为它已经优化了成本函数并达到了全局极小值,而实际上它只达到了局部极小值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关于 MDS 解决方案的有效性仍有个问题有待解答。我们如何衡量解决方案的有效性?在原始论文中,Kruskal 推荐使用应力值来衡量解决方案的拟合度,这些值显示在表 6-1 中。这些建议大多基于 Kruskal 的经验经验。这些应力值基于 Kruskal 的经验。

应力值拟合度
0.200较差
0.100中等
0.050良好
0.025优秀
0.000完美

下一个逻辑问题是 - 我们应该选择多少个最终维度?Scree 图提供了答案,如第 6-5 图所示。回想一下,在第二章,我们使用了类似的肘部法选择了 kmeans 聚类中的最优聚类数。对于 MDS,我们也可以使用肘部法来确定代表数据的最优组件数。

图 6-5 Scree 图来找出最优组件数。与我们在前几章讨论过的 kmeans 解决方案类似;我们需要在图表中找到肘部。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就结束了我们对 MDS 的讨论。现在我们将转向算法的 Python 实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 答题 - 回答这些问题来检查你的理解。答案在书的末尾处

  1. 度量和非度量 MDS 算法有什么区别?

  2. 梯度下降法用于最大化成本。是 True 还是 False。

  3. 使用一个简单的例子解释梯度下降法。

6.2.1 MDS 的 Python 实现

现在我们将采用 Python 实现 MDS 方法。我们将使用我们之前使用过的著名的鸢尾花数据集。由于 scikit learn 包中提供的库,算法的实现相当简单。

由于库提供了重要的工作,因此实现通常很简单。

步骤 1:我们首先加载库。通常会用到sklearnmatplotlibnumpy,并且还从sklearn加载MDS

import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.manifold import MDS
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
import warnings
warnings.filterwarnings("ignore")

步骤 2:现在加载数据集。鸢尾花数据集在sklearn库中可用,因此我们无需在此处导入 excel 或.csv 文件。

raw_data = load_iris()
dataset = raw_data.data

步骤 3:MDS 的要求是在实际可视化之前对数据集进行缩放。我们使用MixMaxScalar()函数来实现相同的效果。 MinMax 缩放只是使用以下公式对数据进行缩放:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

d_scaler = MinMaxScaler()
dataset_scaled = d_scaler.fit_transform(dataset)

作为此步骤的输出,数据已经被缩放,准备进行下一步的建模。

步骤 4:我们现在从 sklearn 库中调用 MDS 方法。 random_state 值允许我们重现结果。我们已经决定示例中的组件数量为 3。

mds_output = MDS(3,random_state=5)

步骤 5:我们现在将使用 MDS 模型拟合之前创建的缩放数据。

data_3d = mds_output.fit_transform(dataset_scaled)

步骤 6:我们现在声明我们希望用于可视化的颜色。接下来,数据点在散点图中可视化。

mds_colors = ['purple','blue', 'yellow']
for i in np.unique(raw_data.target):
  d_subset = data_3d[raw_data.target == i]

  x = [row[0] for row in d_subset]
  y = [row[1] for row in d_subset]
  plt.scatter(x,y,c=mds_colors[i],label=raw_data.target_names[i])
plt.legend()
plt.show()

上述代码的输出如下图 6-6 所示:

图 6-6 IRIS 数据的输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述 Python 实现示例是 IRIS 数据的可视化。这是一个相当简单的例子,但不涉及压力和组件数量的优化。我们现在将在一个精心策划的数据集上实施 MDS。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们假设我们有五个城市,它们之间的距离分别在表 6-2 中给出。

步骤 1:我们已经在上一个代码中导入了库。

import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.manifold import MDS
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
import warnings
warnings.filterwarnings("ignore")

步骤 2:现在让我们创建数据集。我们在这里创建数据集,但在实际业务场景中,它将仅以距离的形式存在。

data_dummy_cities = {'A':[0,40,50,30,40],
          'B':[40,0,40,50,20],
          'C':[50,40,0,20,50],
          'D':[30,50,20,0,20],
          'E':[40,20,50,20,0],
          }
cities_dataframe = pd.DataFrame(data_dummy_cities, index =['A','B','C','D','E'])
cities_dataframe

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 3:我们现在将使用 MinMaxScalar()函数来缩放数据集,就像我们在上一个编码练习中所做的那样。

scaler = MinMaxScaler()
df_scaled = scaler.fit_transform(cities_dataframe) 

步骤 4:现在,让我们朝着找到最优组件数量的方向前进。我们将迭代不同组件数量的值。对于每个组件数量的值,我们将获得压力的值。当观察到一个拐点时,那就是最优的组件数量。

作为第一步,我们将声明一个空数据框,用于存储组件数量和相应压力值的值。然后,我们在 for 循环中从 1 到 10 进行迭代。最后,对于每个组件值(1 到 10),我们获取相应的压力值。

MDS_stress = []
for i in range(1, 10):
    mds = MDS(n_components=i)
    pts = mds.fit_transform(df_scaled)
    MDS_stress.append(mds.stress_)

步骤 5:我们已经得到了压力值。我们现在将这些值绘制在图表中。每个轴的相应标签也给出了。观察值为 2 和 3 的拐点。这可以是最优组件数量的值。

plt.plot(range(1, 10), MDS_stress)
plt.xticks(range(1, 5, 2))
plt.title('Plot of stress')
plt.xlabel('Number of components')
plt.ylabel('Stress values')
plt.show()
图 6-7 屏幕图以选择优化的组件数量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 6:我们现在将运行组件数量= 3 的解决方案。如果我们查看压力值,组件数量= 3,它将生成压力值的最小值为 0.00665。

mds = MDS(n_components=3)
x = mds.fit_transform(df_scaled)
cities = ['A','B','C','D','E']

plt.figure(figsize=(5,5))
plt.scatter(x[:,0],x[:,1])
plt.title('MDS with Sklearn')
for label, x, y in zip(cities, x[:, 0], x[:, 1]):
    plt.annotate(
        label,
        xy = (x, y), 
        xytext = (-10, 10),
        textcoords = 'offset points'
    )
plt.show()
print(mds.stress_)
图 6-8 MDS 数据集的输出,在绘图中表示了 5 个城市

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就结束了我们关于 MDS 算法的部分。我们讨论了基础和概念,优缺点,算法评估以及 MDS 的 Python 实现。它是可视化和降维的一个很好的解决方案。它是非线性降维方法之一。

我们现在将转向 t-SNE,在本章中的第二个降维方法。

6.3 t-分布随机邻居嵌入(t-SNE)

如果数据集真的是高维的,分析就变得很麻烦。可视化甚至更加混乱。我们在第二章的维度诅咒部分中对此进行了详细的讨论。建议您在继续之前重新查看这个概念。

这样一个真正高维的数据集可以是图像数据。我们发现很难理解这种真正高维的数据。

您可能在智能手机上使用过人脸识别软件。对于这样的解决方案,必须分析面部图像并训练机器学习模型。看一下下面的图片,图 6-9 中我们有一个人脸,一辆自行车,一台吸尘器和一个游戏的屏幕截图。

图 6-9 图像对算法来说相当复杂,很难解释。图像可以是任何形式的,可以是一个人,或者一台设备,甚至是任何游戏屏幕。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像是一个复杂的数据点。每个图像由像素组成,每个像素可以由 RGB(红色,绿色,蓝色)值组成。而每个红色、绿色、蓝色的值都可以从 0 到 255。结果数据集将是一个非常高维的数据集。

现在,回顾一下我们在第三章学习的主成分分析(PCA)。PCA 是一种线性算法。作为一种线性算法,它的能力受到限制,无法解决非线性和复杂多项式函数。此外,当一个高维数据集必须表示在一个低维空间时,算法应该将类似的数据点保持在彼此附近,这对于线性算法来说可能是一个挑战。PCA 是一种线性降维技术,它试图将不同的数据点分开得尽可能远,因为 PCA 正在尝试最大化数据点之间的方差。结果分析不够稳健,可能不适合进一步使用和可视化。因此,我们有非线性算法如 t-SNE 来帮助。

形式上来说,t-SNE 是一种非线性降维技术,非常适用于高维数据。它基于由 Sam Roweis 和 Geoffrey Hinton 开发的随机邻居嵌入。t-分布变体是由 Lauren van der Maaten 提出的。因此,t-SNE 是对 SNE 算法的改进。

在高水平上,SNE 测量高维空间和低维空间中实例对之间的相似性。一个好的解决方案是这些相似度测量之间的差异最小,因此 SNE 通过成本函数优化这些相似度测量。

我们现在将逐步检验 t-SNE 的过程。下面描述的过程在数学上有些繁重。

  1. 假设我们有一个高维空间,在这个高维空间中有一些点。

  2. 现在我们将度量各个点之间的相似性。对于一个点 x[i],我们将创建一个以该点为中心的高斯分布。我们在书的前几章已经学习了高斯或正态分布。高斯分布如图 6-10 所示。

图 6-10 高斯或正态分布,我们之前已经学过。图片来源:维基百科。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 现在,我们将测量在高斯分布下落在该点(比如 x[j])的点的密度,然后重新归一化它们以获得相应的条件概率(p[j|i])。对于附近和相似的点,这个条件概率会很高,而对于远离和不相似的点,条件概率(p[j|i])的值会非常小。这些概率值是在高维空间中的。对于好奇的人,这个条件概率的数学公式是等式 6-3:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中σ是以 x[i]为中心的高斯分布的方差。这个条件概率的数学证明超出了本书的范围。

  1. 现在我们将在低维空间测量另一组概率。对于这组测量,我们使用柯西分布
柯西分布
  1. 柯西分布,属于连续概率分布家族。虽然与正态分布有些相似,正如我们在图 6-11 中表示的,柯西分布的峰值较窄,扩散速度较慢。这意味着,与正态分布相比,远离峰值的值的概率更高。有时,柯西分布也被称为洛伦兹分布。有趣的是,柯西分布没有定义良好的均值,但中位数是对称中心。
图 6-11 高斯分布与柯西分布的比较。(图片来源:Quora)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 假设我们得到 y[i]和 y[j]作为高维数据点 x[i]和 x[j]的低维对应物。因此,我们可以像上一步那样计算概率分数。使用柯西分布,我们也可以得到第二组概率 q[j|i]。数学公式如下所示,见等式 6-4。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 到目前为止,我们已经计算了两组概率(p[j|i])和(q[j|i])。在这一步中,我们比较这两个分布并测量两者之间的差异。换句话说,当计算(p[j|i])时,我们在高维空间中测量了相似性的概率,而对于(q[j|i]),我们在低维空间中进行了相同的操作。理想情况下,如果两个空间的映射相似,那么(p[j|i])和(q[j|i])之间就不应该有任何差异。因此,SNE 算法试图最小化条件概率(p[j|i])和(q[j|i])之间的差异。

  2. 通过使用 Kullback-Liebler 散度或 KL 散度来衡量两个概率分布之间的差异,我们将在这里探讨。

KL 散度

KL 散度或相对熵用于衡量两个概率分布之间的差异 - 通常一个概率分布是数据或测量得分。第二个概率分布是原始概率分布的近似值或预测值。例如,如果原始概率分布是 X,近似概率分布是 Y。KL 散度可用于测量 X 和 Y 概率分布之间的差异。绝对来说,如果值为 0,则意味着两个分布相似。KL 散度适用于神经科学、统计学和流体力学。

  1. 为了最小化 KL 成本函数,我们使用梯度下降方法。我们已经在讨论 MDS 算法的部分中讨论过梯度下降方法。

  2. 在我们研究 t-SNE 时,还有一点非常重要,即一个重要的超参数称为困惑度。困惑度是一个超参数,允许我们控制和优化每个数据点的邻近数量。

根据官方文件,困惑度的典型值在 5 和 50 之间。

  1. 还可能存在一个额外的细微差别 - t-SNE 算法的输出在连续运行中可能永远不会相同。我们必须优化超参数的值以获得最佳输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 流行测验 - 回答这些问题以检查您的理解… 答案在本书的结尾

  1. 解释自己的话语中的柯西分布。

  2. PCA 是非线性算法。是还是不是。

  3. KL 散度用于衡量两个概率分布之间的差异。是还是不是

现在我们将继续讨论算法的 Python 实现。

6.3.1 t-SNE 的 Python 实现

在这个例子中,我们将使用两个数据集。第一个是已知的 IRIS 数据集,在本书中我们已经多次使用过。第二个数据集非常有趣。它是 MNIST 数据集,这是一个手写数字的数据库。它是用来训练图像处理解决方案的最著名的数据集之一,通常被认为是图像检测解决方案的“Hello World”程序。下面显示了图像表示()。

图 6-12 MNIST 数据集-这是一组手写数字的图像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 1 步:我们将首先导入必要的库。请注意,我们已从 keras 库导入 MNIST 数据集。

from sklearn.manifold import TSNE
from keras.datasets import mnist
from sklearn.datasets import load_iris
from numpy import reshape
import seaborn as sns
import pandas as pd

第 2 步:首先我们将使用 IRIS 数据集。我们将加载 IRIS 数据集。数据集包括两部分 - 一部分是“数据”,另一部分是相应的标签或“目标”。这意味着“数据”是数据的描述,“目标”是 IRIS 的类型。我们使用一小段代码打印特征和标签。

iris = load_iris()
iris_data = iris.data
iris_target = iris.target
iris.feature_names
iris.target_names

第 3 步:下一步是调用 tSNE 算法。我们使用的组件数量为 2,随机状态为 5,以重现结果。然后使用算法拟合数据。

tsne = TSNE(n_components=2, verbose=1, random_state=5)
fitted_data = tsne.fit_transform(iris_data)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第 4 步:现在我们将绘制数据。此步骤允许我们可视化算法在上一步中拟合的数据。

首先,我们将初始化一个空的数据框架。我们将逐一添加三列。我们将从 iris_target 开始,然后是 tSNE_first_component 和 tSNE_second_component。tSNE_first_component 是 fitted_data 数据框架的第一列,因此索引为 0。tSNE_second_component 是 fitted_data 数据框架的第二列,因此索引为 1。最后,我们将数据表示为散点图。

iris_df = pd.DataFrame()
iris_df["iris_target"] = iris_target
iris_df["tSNE_first_component"] = fitted_data[:,0]
iris_df["tSNE_second_component"] = fitted_data[:,1]

sns.scatterplot(x="tSNE_first_component", y="tSNE_second_component", hue=iris_df.iris_target.tolist(),
                palette=sns.color_palette("hls", 3),
                data=iris_df).set(title="Iris data tSNE projection")
图 6-13 IRIS 数据集的 tSNE 投影。请注意,我们为数据集中的三个类别得到了三个单独的聚类

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们将为 MNIST 数据集实现算法。

第 1 步:库已经在上一个代码示例中加载。然后我们加载数据集。数据集需要进行 reshape,这在这里完成

(digit, digit_label), (_ , _) = mnist.load_data()
digit = reshape(digit, [digit.shape[0], digit.shape[1]*digit.shape[2]])
Step 2: the subsequent steps are exactly same to the last example we used. 
tsne_MNIST = TSNE(n_components=2, verbose=1, random_state=5)
fitted_data = tsne_MNIST.fit_transform(digit)

mnist_df = pd.DataFrame()
mnist_df["digit_label"] = digit_label
mnist_df["tSNE_first_component"] = fitted_data[:,0]
mnist_df["tSNE_second_component"] = fitted_data[:,1]

sns.scatterplot(x="tSNE_first_component", y="tSNE_second_component", hue=mnist_df.digit_label.tolist(),
                palette=sns.color_palette("hls", 10),
                data=mnist_df).set(title="MNIST data T-SNE projection")
图 6-14 tSNE 对表示为不同颜色的 10 类数字的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在运行 tSNE 时,请记住以下几个重要点:

  1. 在最终确定解决方案之前,用不同的超参数值运行算法。

  2. 理想情况下,perplexity 应该在 5 和 50 之间,对于优化的解决方案,perplexity 的值应该小于数据点的数量。

  3. tSNE 猜测每个点的近邻数。因此,密集的数据集将需要更高的 perplexity 值。

  4. 特别需要注意的是 perplexity 是一个超参数,它平衡了对数据的局部和全局方面的关注。

tSNE 是广泛流行的算法之一。它用于研究区域的拓扑,但单个 tSNE 不能用于做出最终评估。相反,应创建多个 tSNE 图来做出任何最终推荐。有时会有人抱怨 tSNE 是一个黑箱算法。在某种程度上这可能是真的。使 tSNE 的采用变得更加困难的是,在连续迭代中它不会生成相同的结果。因此,你可能只会发现 tSNE 被推荐用于探索性分析。

这就结束了我们关于 tSNE 的讨论。现在我们将转向案例研究。

6.4 案例研究

请回忆第三章,我们探讨了运用降维技术在电信业的案例研究。在本章中,我们将研究一个小案例,应用 tSNE 或 MDS 进行降维。

你听说过高光谱图像吗?如你所知,我们人类主要看到可见光的颜色有长波长、中波长和短波长三个波段。长波长被感知为红色,中波长为绿色,短波长为蓝色。而频谱成像则将光谱分成许多更多的波段,这种技术可以扩展到可见光以外的波段,因此在生物学、物理学、地球科学、天文学、农业等许多领域都有用途。

高光谱成像收集并处理来自整个电磁波谱的信息。它获取图像中每个像素的光谱。

图 6-15《糖端》土豆条的高光谱图像显示了看不见的缺陷(图片来源:维基百科)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个这样的数据集可能是 Pavia 大学数据集。这个数据集是由 ROSIS 传感器在意大利北部的 Pavia 获取的。数据集的详细信息如下,并且可以从(www.ehu.eus/ccwintco/uploads/e/ee/PaviaU.mat www.ehu.eus/ccwintco/uploads/5/50/PaviaU_gt.mat)下载。

在此数据集中,光谱波段为 103,HIS 尺寸为 610*340 像素,包含 9 类。这种类型的数据可以用于作物分析、矿物检查和勘探等。由于此数据包含有关地质图案的信息,因此对于科学目的非常有用。在开发任何图像识别解决方案之前,我们必须减少此数据集的维度数量。此外,如果维度数量较大,计算成本将会更高。因此,显然应该有较少的代表性维度数量。以下显示了一些示例波段。建议您下载数据集(也已在 git Hub repo 中检入)并在数据集上使用各种降维技术以减少维度数量。

图 6-16 数据集中波段的例子。这只是随机的例子,建议您加载数据集并运行降维算法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

tSNE 和 MDS 可以在许多其他图像数据集和复杂的商业问题中发挥实际作用。下一步列出了一些此类数据集。

6.5 总结

维度缩减是一种非常有趣且有用的解决方案。它使得机器学习变得更加经济高效。想象一下,你有一个包含成千上万个属性或特征的数据集。你对数据了解不多;业务理解很少,同时你需要在数据集中找到模式。你甚至不确定这些变量是否都是相关的,还是只是随机噪声。在这样一个时刻,当我们需要快速降低数据集的维度,使其更加易于处理并减少时间时,维度缩减就是解决方案。

我们在书中较早地介绍了维度缩减技术。本章涵盖了两种高级技术——tSNE 和 MDS。这两种技术不应被视为我们讨论的其他更简单技术的替代品。相反,如果我们没有得到有意义的结果,可以尝试使用这两种技术。建议先使用 PCA,然后再尝试 tSNE 或 MDS。

我们正在增加书中的复杂性。这一章以图像开始——我们只是初探了一下。在下一章中,我们将处理文本数据,也许你会觉得非常有趣和有用。

实际下一步和建议阅读
  1. 使用第二章中使用的车辆数据集进行聚类,并在其上实施 MDS。比较在实施 MDS 之前和之后的聚类性能。

  2. 获取第二章中用于 Python 示例的数据集,并将其用于实施 MDS。

  3. 对于 MDS,您可以参考以下研究论文:

  4. Lauren van der Maaten、Eric Postma 和 H. Japp Van Den Herik 撰写的《维度缩减:一项比较性回顾》维度缩减:一项比较性回顾

  5. Satish V. Ukkusuri 和 Jian Lu 提出了一种基于多维缩放的数据维度缩减方法,用于城市道路网络的短期交通流预测。多维缩放方法在城市道路网络短期交通流预测中的应用

  6. 从下面的链接获取 tSNE 研究论文并研究它们。

  7. Laurens van der Maaten 和 Geoffrey Hinton 撰写的《使用 t-SNE 可视化数据》使用 t-SNE 可视化数据

  8. 《单细胞转录组学中使用 t-SNE 的艺术》单细胞转录组学中使用 t-SNE 的艺术

  9. 还有一篇可能感兴趣的论文-《使用 KNN、SNN 和 SVM 分类器对 t-SNE 和 MDS 维度缩减技术的性能评估》t-SNE 和 MDS 维度缩减技术的性能评估

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值