无监督学习与生成式人工智能(MEAP)(一)(1)https://developer.aliyun.com/article/1522502
第二章:聚类技术
在这第二章中,我们将涵盖以下主题:
- 行业中的聚类技术及其显著用例
- 可用的各种聚类算法
- K 均值、层次聚类和 DBSCAN 聚类
- 在 Python 中实现算法
- 聚类分析案例研究
“简单是最终的精妙” – 列奥纳多·达·芬奇
大自然崇尚简单,教导我们走同样的路。大多数时候,我们的决定都是简单的选择。简单的解决方案更容易理解,耗时更少,更容易维护和思考。机器学习世界也不例外。一个优雅的机器学习解决方案不是最复杂的可用算法,而是解决业务问题的一个。一个强大的机器学习解决方案易于理解,并且实施起来实用。聚类解决方案通常更容易被理解。
在上一章中,我们定义了无监督学习,并讨论了可用的各种无监督算法。随着我们在本书中的工作,我们将涵盖每一种算法;在这第二章中,我们将专注于其中的第一个:聚类算法。
我们将首先定义聚类,然后学习不同类型的聚类技术。我们将检查每种算法的数学基础、准确度测量以及优缺点。我们将使用 Python 代码在数据集上实现其中三种算法,以补充理论知识。本章结束时,我们将探讨聚类技术在实际业务场景中的各种用例,为进入实际业务世界做准备。这种技术在整本书中都在使用,我们首先学习概念,然后实现实际代码以提高 Python 技能,并深入研究实际的业务问题。
本章我们将学习基本的聚类算法,包括 K 均值聚类、层次聚类和 DBSCAN 聚类。这些聚类算法通常是我们研究聚类时的起点。在本书的后面章节中,我们将探讨更复杂的算法,如谱聚类、高斯混合模型、时间序列聚类、模糊聚类等。如果你对 K 均值聚类、层次聚类和 DBSCAN 有很好的理解 - 你可以跳到下一章。但建议你读完本章一次 - 你可能会发现一些有用的东西来更新你的概念!
让我们首先了解一下“聚类”是什么意思。祝你在掌握基于无监督学习的聚类技术的旅程中一切顺利!
2.1 技术工具箱
本章我们将使用 Python 的 3.6+版本。我们期望你对 Python 和代码执行有基本的了解。建议你复习面向对象编程和 Python 概念。
本书始终使用 Jupyter 笔记本执行代码。Jupyter 在执行和调试方面提供了灵活性,因此被广泛使用。它非常用户友好,可以在任何平台或操作系统上使用。所以,无论你使用 Windows、Mac OS 还是 Linux,Jupyter 都可以正常工作。
所有数据集和代码文件都已经检入到 Github 代码库中(github.com/vverdhan/UnsupervisedLearningWithPython/tree/main/Chapter2
)。你需要安装以下 Python 库来执行代码 – numpy
, pandas
, matplotlib
, scipy
, sklearn
。CPU 足够执行,但如果遇到一些计算延迟,并且想加快执行速度,切换到 GPU 或 Google 协作平台(colab)。Google 协作平台为机器学习解决方案提供免费计算。建议你了解更多关于谷歌协作平台以及如何用它来训练机器学习算法的内容。
现在,我们将在下一节开始聚类。
2.2 聚类
想象一下这样的情景。一群孩子被要求将房间中的物品分成不同的部分。每个孩子都可以使用自己的逻辑。有人可能根据重量来分类物品,其他孩子可能使用材料,而另一些人可能同时使用重量、材料和颜色。排列组合很多,取决于用于分组的 参数。在这里,一个孩子根据所选择的逻辑对物品进行分组或 clustering。
正式来说,clustering 用于将具有相似属性的对象分组到同一段中,具有不同属性的对象分到不同段中。结果的聚类在自身之间具有相似性,而它们在彼此之间更加异质。我们可以通过下图(图 2.1)更好地理解它。
图 2.1 聚类是将具有相似属性的对象分组到逻辑段中。分组是基于不同观测共享的相似性特征,因此它们被分组到一组中。在这里,我们使用形状作为聚类的变量。
聚类分析不是单个算法或解决方案,而是用于解决实际业务场景中的问题的机制。它们是无监督学习下的一类算法。这是一个迭代过程,遵循逻辑方法和定性业务输入。它导致对数据的深入理解、其中的逻辑模式、模式发现和信息检索。作为一种无监督方法,聚类不需要目标变量。它通过分析数据集中的潜在模式来进行分段,这些模式通常是多维的,因此难以用传统方法分析。
理想情况下,我们希望聚类算法具有以下特点:
- 输出的聚类应该易于解释和理解,可用且应该具有商业意义。聚类数目不应该太少或太多。例如,如果我们只有 2 个聚类,分割就不够清晰和明确。或者如果我们有 20 个聚类,处理将变得很有挑战性。
- 算法不应过于敏感于异常值、缺失值或数据集中的噪声。通常来说,一个好的解决方案应该能够处理多种数据类型。
- 一个好的解决方案对于用于聚类目的的输入参数需要较少的领域理解。这允许具有较少领域理解的分析师训练聚类算法。
- 算法应该独立于输入参数的顺序。如果顺序很重要,那么聚类就会对顺序产生偏见,因此会给过程增加更多的混乱。
- 随着我们持续生成新的数据集,聚类必须能够适应新的训练示例,并且不应该是一个耗时的过程。
众所周知,聚类输出将取决于用于分组的属性。在下面所示的(图 2.2)中,对于相同的数据集,可以有两个逻辑分组,而且两者都是同样有效的。因此,明智地选择用于聚类的属性或变量通常取决于手头的业务问题。
图 2.2 使用不同的属性进行聚类会得到相同数据集的不同聚类结果。因此,选择正确的属性集定义我们将实现的最终结果。
除了用于聚类的属性之外,所使用的实际技术也会产生很大影响。研究人员已经开发了相当多(事实上超过 80 个)的聚类技术。对于感兴趣的观众,我们在附录中提供了所有聚类算法的列表。我们将在下一节开始学习不同的聚类技术。
2.2.1 聚类技术
聚类可以通过各种算法实现。这些算法使用不同的方法来定义对象之间的相似性。例如,基于密度的聚类,基于中心点的聚类,基于分布的方法等。甚至在衡量对象之间距离时,也有多种技术,如欧氏距离,曼哈顿距离等。选择距离测量方法会导致不同的相似度分数。我们将在后面的部分研究这些相似度测量参数。
在高层次上,我们可以确定两种广义的聚类方法:硬聚类和软聚类(见图 2.3)。当决定非常明确一个对象属于某个类或簇时,称为硬聚类。在硬聚类中,算法非常确定对象的类。另一方面,软聚类为对象被归属于某个特定簇分配了可能性得分。因此,软聚类方法不会将对象放入一个簇中,而是一个对象可以属于多个簇。有时软聚类也被称为模糊聚类。
图 2.3 硬聚类具有明显的簇,而在软聚类的情况下,数据点可以属于多个簇,我们得到数据点属于簇的可能性分数。左边的第一个图是硬聚类,右边的图是软聚类。
我们可以如下图所示广义地将聚类技术进行分类:
表 2.1 聚类方法的分类、简要描述和示例
序号 | 聚类方法 | 方法的简要描述 | 示例 |
1 | 基于质心的聚类 | 到指定质心的距离 | k-means |
2 | 基于密度的模型 | 数据点在向量空间的密集区域内连接 | DBSCAN, OPTICS |
3 | 基于连接性的聚类 | 距离连接性是行动方式 | 分层聚类, BIRCH |
4 | 分布模型 | 建模基于统计分布 | 高斯混合模型 |
5 | 深度学习模型 | 无监督的基于神经网络的 | 自组织映射 |
表 2.1 中描述的方法并不是唯一可用的方法。我们可以有基于图的模型、重叠的聚类、子空间模型等。
通常,工业中使用的六种流行的聚类算法如下:
- K 均值聚类(带有诸如 k-中值、k-中心点之类的变种)
- 凝聚式聚类或者分层聚类
- DBSCAN (基于密度的空间应用聚类)
- 光谱聚类
- 高斯混合模型或 GMM
- BIRCH(平衡迭代减少 & 使用层次聚类)
还有多种其他可用的算法,如 Chinese whisper,canopy 聚类,SUBCLU,FLAME 等。我们在本章中学习前三种算法以及书中后续章节中的一些高级算法。
快速测验-回答这些问题以检查你的理解… 答案在书的末尾
- DBSCAN 聚类是基于质心的聚类技术。TRUE or FALSE.
- 聚类是一种监督学习技术,具有固定的目标变量。TRUE or FALSE.
- 硬聚类和软聚类有什么区别?
在下一节中,我们将开始学习基于质心的聚类方法,其中我们将学习 k-means 聚类。
2.3 基于质心的聚类
基于重心的算法根据对象到聚类重心的距离来衡量它们的相似性。距离是针对聚类的特定数据点到重心的距离来衡量的。距离越小,相似度越高。我们可以通过接下来的图 2.4 来理解这个概念。右侧的图表示了每个聚类组的相应重心。
注意
要更清楚地了解重心和其他数学概念,请参考末尾的附录。
图 2.4 基于重心的聚类方法为各自的聚类创建一个重心,并根据到重心的距离来衡量相似度。在这种情况下,我们有 5 个重心。因此,这里有五个不同的聚类。
在聚类中,距离起着核心作用,因为许多算法将其用作度量相似性的度量。在基于重心的聚类中,距离是在点之间和重心之间进行衡量的。有多种方法来度量距离。最常用的是下面列出的:
- 欧几里德距离:它是最常用的距离度量。它表示空间中两点之间的直线距离,是两点之间的最短路径。如果我们想计算点 P[1]和 P[2]之间的距离,其中 P[1]的坐标为(x[1], y[1]),P[2]的坐标为(x[2], y[2]),那么欧几里德距离由下面的(方程式 2.1)给出。几何表示如图 2.5 所示
(方程式 2.1)
距离 = √(y[2] – y[1])² + (x[2] – x[1])²
如果你想复习几何概念(坐标几何),请参考附录。
- 切比雪夫距离:以俄罗斯数学家帕夫努蒂·切比雪夫的名字命名,它被定义为两点之间的距离,以便它们的差异在任何坐标维度上的最大值。数学上,我们可以在下面的(方程式 2.2)中表示切比雪夫距离,并在(图 2.5)中显示:
(方程式 2.2)
距离[切比雪夫] = max (|y[2] – y[1]|, |x[2] – x[1]|)
- 曼哈顿距离:曼哈顿距离是一个非常简单的概念。它只是计算网格路径上两点之间的距离,因此距离是沿着右角轴测量的。因此,有时它也被称为城市街区距离或出租车度量。数学上,我们可以在(方程式 2.3)中表示曼哈顿距离,并如图 2.5 所示:
(方程式 2.3)
距离[曼哈顿] = (|y[2] – y[1]| + |x[2] – x[1]|)
曼哈顿距离以 L1 范数形式而欧几里德距离以 L2 范数形式。您可以参考附录详细学习 L1 范数和 L2 范数。如果数据集中有大量的维度或变量,曼哈顿距离比欧几里德距离更好。这是由于本书第三章将要学习的维度诅咒。
- 余弦距离:余弦距离用于测量向量空间图中两点之间的相似性。在三角学中,0 度的余弦是 1,90 度的余弦是 0。因此,如果两个点彼此相似,则它们之间的角度将为零,因此余弦将为 1,这意味着这两个点彼此非常相似,反之亦然。从数学上讲,余弦相似性可以表示为(公式 2.4)。如果我们想要测量向量 A 和 B 之间的余弦值,则余弦是
(公式 2.4)
距离 [余弦] = (A . B) / (||A|| ||B||)
如果您想刷新向量分解的概念,请参考附录。
图 2.5 欧氏距离、曼哈顿距离、切比雪夫距离和余弦相似度是主要使用的距离度量。请注意,使用这些度量标准时,两个点之间的距离是不同的。在欧氏距离中,直接距离被测量为左侧第一张图所示的两点之间的距离。
还有其他距离度量标准,如汉明距离、Jaccard 距离等。在我们实际的业务问题中,通常使用欧氏距离,但有时也会使用其他距离度量标准。
注意
上述距离度量标准对其他聚类算法也适用。建议您使用本书中的 Python 代码测试不同的距离度量标准,并比较性能。
现在我们已经了解了各种距离度量标准,我们将继续学习 k-means 聚类,这是最广泛使用的算法。
2.3.1 K-means 聚类
k-means 聚类是一种简单直接的方法。它可以说是最广泛使用的聚类方法,用于分段数据点并创建非重叠聚类。我们必须指定我们希望创建的聚类数“k”作为输入,算法将将每个观察结果关联到 k 个聚类中的一个。
注意
有时人们会将 k-means 聚类与 k 最近邻分类器(knn)混淆。虽然两者之间存在一定的关系,但 knn 用于分类和回归问题。
这是一种相当优雅的方法,它从一些初始的聚类中心开始,然后迭代地将每个观察结果分配给最接近的中心。在这个过程中,中心点被重新计算为聚类中的点的平均值。让我们通过下面的图表(图 2.6)逐步学习所使用的方法。为了简单起见,我们假设数据集中有三个聚类。
步骤 1:让我们假设我们有如下所示的所有数据点。
图 2.6 步骤 1 代表原始数据集。在步骤 2 中,算法初始化三个随机质心,因为我们已经给定了三个聚类的输入数。在步骤 3 中,质心的所有相邻点都被分配到同一个聚类中。
第二步:初始时随机初始化了三个中心,如三个方块所示-蓝色、红色和绿色。这个三是我们希望最后的簇的数量。
第三步:计算所有数据点到中心的距离,并将点分配给最近的中心。注意,由于它们最接近相应的中心,点的颜色变为蓝色、红色和绿色。
第四步:在这一步中,重新调整了三个中心。中心被重新计算为该簇中点的平均值,如图 2.7 所示。我们可以看到,在第四步中,与第三步相比,三个方块的位置发生了变化。
图 2.7 是在第 4 步重新计算质心。在第 5 步中,数据点再次被重新分配新中心。在第 6 步中,根据新的计算结果,质心再次进行调整。
第五步:再次重新计算所有数据点到新中心的距离,并将点重新分配给最近的中心。请注意,在这一步中,两个蓝色数据点变成了红色,而一个红点变成了绿色。
第六步:中心再次进行调整,与第四步类似。
图 2.8 是重新计算质心,并且这个过程一直持续到无法进一步改善聚类为止。然后过程停止,如第 8 步所示
第七步:数据点再次被分配到新的簇中,如前图(图 2.8)所示。
第八步:该过程将继续,直到达到收敛。换句话说,该过程将继续,直到不再重新分配数据点。因此,我们无法进一步改进聚类,已实现最终聚类。
k-means 聚类的目标是确保簇内变化尽可能小,而簇之间的差异尽可能大。换句话说,同一簇的成员彼此最相似,而不同簇的成员则不相似。一旦结果不再改变,我们可以得出结论,已达到局部最优,聚类可以停止。因此,最终的簇在内部是同质的,而在彼此之间是异质的。
有两点需要注意:
- 由于 k-means 聚类是随机初始化中心点,因此它只能找到局部最优解,而非全局最优解。因此,建议多次迭代解决方案,并从所有结果中选择最佳输出。迭代意味着多次重复该过程,因为在每次迭代中,随机选择的质心将不同。
- 我们必须输入我们希望的最终簇数“k”,它会显著改变输出。与数据规模相比,如果 k 值非常小,结果将是多余的簇,因为没有任何用处。换句话说,如果相对于庞大的数据具有非常小的 k 值,具有不同特征的数据点将被混合在几个群中。具有非常高的 k 值将创建微不同的簇。此外,具有非常高数量的簇将难以在长期内管理和更新。让我们通过一个例子来研究。如果一个电信运营商有 100 万订阅者,那么如果我们将簇数设为 2 或 3,得到的簇的规模将非常大。它还可能导致将不同的客户分类到相同的段中。另一方面,如果我们将簇数设为 50 或 60,由于簇数庞大,输出变得难以管理、分析和维护。
使用不同的“k”值,我们会得到不同的结果,因此我们需要了解如何为数据集选择最佳簇数。现在,让我们研究如何测量聚类解决方案的准确性的过程。
2.3.2 测量聚类的准确性
聚类的一个目标是找到最干净的簇。理论上(虽然不理想),如果我们有与观察数量相同的簇数,结果将完全准确。换句话说,如果有 100 万客户,最纯净的聚类将有 100 万个簇 - 每个客户在一个单独的簇中。但这并不是最佳方法,也不是一种实用的解决方案。聚类旨在在一个簇中创建相似观察结果的组,并且我们使用相同的原理来衡量解决方案的准确性。
- 簇内平方和(WCSS)或聚合:这个指标衡量数据点相对于它们距离簇质心的距离的变异性。这个指标是每个数据点距离簇的质心的平均距离,对每个数据点重复。如果值太大,表明数据扩散很大,而较小的值表示数据点非常相似和均匀,因此簇是紧凑的。
有时,这个簇内距离也被称为该簇的惯性。它简单地是所有距离的总和。惯性值越低,簇就越好。
图 2.9 簇内距离与簇间距离 - 两者都用于衡量最终簇的纯度和聚类解决方案的性能
- 聚类间平方和:此度量用于衡量所有聚类质心之间的距离。为了得到它,我们测量所有聚类的质心之间的距离,并将其除以聚类的数量以获得平均值。它越大,聚类越好,表明聚类是异质的,并且彼此可以区分,正如我们在(图 2.9)中所表示的那样。
- 轮廓系数是衡量聚类成功的指标之一。它的取值范围从-1 到+1,数值越高越好。它衡量了数据点与其所属聚类中其他数据点相似程度,与其他聚类相比。作为第一步,对于每个观察值——我们计算与同一聚类中所有数据点的平均距离,我们称之为 x[i]。然后我们计算与最近聚类中所有数据点的平均距离,我们称之为 y[i]。然后我们通过下面的方程(方程 2.5)计算系数
(方程 2.5)
轮廓系数 = (y[i] - x[i])/ max(y[i],x[i])
如果系数的值为-1,则意味着观察点位于错误的聚类中。
如果为 0,则该观察点与相邻聚类非常接近。
如果系数的值为+1,则意味着该观察点与相邻聚类之间存在距离。
因此,我们期望获得系数的最高值以获得良好的聚类解决方案。
- Dunn 指数也可用于衡量聚类的效果。它使用了点 2 和点 3 中定义的聚类间距离和聚类内距离测量,并由下面的方程(方程 2.6)给出
(方程 2.6)
Dunn 指数 = min(聚类间距离)/max(聚类内距离)
显然,我们会努力最大化 Dunn 指数的值。为了实现这一点,分子应尽可能大,意味着聚类之间相距较远,而分母应尽可能低,表明聚类非常健壮且紧密排列。
现在我们已经检验了衡量算法性能的方法。我们现在将转向寻找 k-means 聚类的最佳“k”值。
2.3.3 寻找最佳的“k”值
选择最优的聚类数量并不容易。正如我们之前所说,最好的聚类是当聚类数量等于观察数量时——但是正如我们在上一节中所学到的,这在实际中是不可能的。但是我们必须将聚类数量“k”作为算法的输入提供。
图 2.10 肘方法寻找最优聚类数量。红色圆圈表示转折点。但最终的聚类数量取决于业务逻辑,通常根据业务知识合并/拆分聚类。维护聚类的便利性在其中也扮演了至关重要的角色
或许找到“k”的最优值最广泛使用的方法是Elbow Method。在这种方法中,我们计算不同“k”值的簇内平方和或 WCSS。该过程与上一节中讨论的相同。然后,将 WCSS 绘制在图表上,针对不同的“k”值。在我们观察到一个弯曲或肘部的地方,如(图 2.10)所示,它就是数据集的最优簇数。注意(图 2.10)中描绘的锋利边缘。
测验 - 回答这些问题来检查你的理解。书的结尾有答案。
- K-means 聚类不需要作为输入的簇数- 真 或 假
- Knn 和 k-means 聚类是一回事 - 真 或 假
- 描述一种可能的找到最优“k”值的过程
但这并不意味着这是我们建议用于业务问题的最终簇数。根据落入每个簇的观测数量,几个簇可能被合并或分解成子簇。我们还考虑创建簇所需的计算成本。簇的数量越多,计算成本和所需时间就越大。
我们也可以使用前面讨论过的 Silhouette Coefficient 找到最优的簇数。
注意:
探讨合并几个簇或拆分几个簇的业务逻辑是至关重要的。最终,解决方案必须在实际的业务场景中实施。
通过这个,我们已经研究了 k-means 聚类的点点滴滴 - 数学概念和过程,各种距离度量以及确定最佳 k 值。现在我们将研究 k-means 算法为我们提供的优势。
2.3.4 k-means 聚类的优缺点
k-means 算法是一个非常流行和广泛实施的聚类解决方案。该解决方案提供以下优势:
- 与其他算法相比,它简单易懂且相对容易实现。距离测量计算使得即使是非统计背景的用户也很容易理解。
- 如果维度的数量很大,k-means 算法比其他聚类算法更快,创建的簇更紧凑。因此,如果维度的数量相当大,则更倾向于使用它。
- 它很快适应新的观察结果,并且能够非常好地概括各种形状和大小的簇。
- 解决方案通过一系列重新计算的迭代产生结果。大多数情况下使用欧氏距离度量,这使得计算成本较低。它还确保算法一定会收敛并产生结果。
K-means 在现实生活中的业务问题中被广泛使用。尽管 k-means 聚类有明显的优点,但我们确实面临着算法的某些挑战:
- 选择最佳聚类数目并不容易。我们必须将其作为输入提供。使用不同的“k”值,结果会完全不同。选择最佳“k”值的过程在上一节中已经探讨过。
- 解决方案取决于质心的初始值。由于质心是随机初始化的,因此每次迭代的输出都将不同。因此,建议运行多个解决方案的版本并选择最佳的一个。
- 该算法对异常值非常敏感。它们可能会破坏最终结果,因此在开始聚类之前,我们必须处理异常值。我们还可以实现 k 均值算法的其他变体,如k-modes聚类,以应对异常值的问题。我们将在后续章节讨论处理异常值的方法。
- 由于 k 均值聚类的基本原理是计算距离,因此该解决方案不直接适用于分类变量。换句话说,我们不能直接使用分类变量,因为我们可以计算数字值之间的距离,但不能对分类变量进行数学计算。为了解决这个问题,我们可以使用独热编码将分类变量转换为数字变量,这是我们在本章末尾讨论的内容之一。
尽管存在这些问题,k 均值聚类是最常用的聚类解决方案之一,因其简单性和易于实现。还有一些不同版本的 k 均值算法,如 k-medoids、k-中位数等,有时用于解决所面临的问题。
- 如其名,k-中位数聚类是基于数据集中位数而不是 k 均值中心的。这增加了计算时间,因为只有在数据排序之后才能找到中位数。但与此同时,k 均值对异常值很敏感,而 k 中位数对它们的影响较小。
- 接下来,我们有k-medoids 聚类作为 k 均值算法的变体之一。Medoids 与均值类似,只是它们始终来自同一数据集,并且在难以获得均值的情况下实施,比如图像。Medoid 可以被认为是簇中最核心的点,与簇中的所有其他成员最不相似。K-medoids 选择实际观测值作为中心,而不是 k 均值,其中质心甚至可能不是数据的一部分。与 k 均值聚类算法相比,它对异常值的敏感性较低。
还有其他版本,如 kmeans++、小批量 k 均值等。一般来说,在工业界,大多数聚类解决方案都使用 k 均值。如果结果不理想或计算时间太长,您可以探索其他选项,如 kmeans++、小批量 k 均值等。此外,使用不同的距离测量指标可能会为 k 均值算法产生不同的结果。
本节结束了我们对 k-means 聚类算法的讨论。是时候进入实验室,开发真正的 Python 代码了!
2.3.5 使用 Python 实现 k-means 聚类
我们现在将为 k-means 聚类创建一个 Python 解决方案。在这种情况下,我们使用了链接中的数据集
github.com/vverdhan/UnsupervisedLearningWithPython/tree/main/Chapter2
这个数据集包含了四种车型的特征信息。基于车辆的特征,我们将把它们分成不同的群。
第 1 步: 将库和数据集导入到一个数据框中。在这里,vehicles.csv 是输入数据文件。如果数据文件不在与 Jupyter 笔记本相同的文件夹中,您需要提供文件的完整路径。Dropna 用于删除可能存在的缺失值。
import pandas as pd vehicle_df = pd.read_csv('vehicle.csv').dropna()
第 2 步: 对数据进行一些初始检查,比如形状、信息、前五行、类别分布等。这是为了确保我们已经加载了完整的数据集,并且在加载数据集时没有损坏。Shape
命令将给出数据中的行数和列数,info
将描述所有变量及其类型,head
将显示前 5 行。value_counts
显示class
变量的分布。换句话说,value_counts
返回唯一值的计数。
vehicle_df.shape vehicle_df.info() vehicle_df.head() pd.value_counts(vehicle_df['class'])
第 3 步: 让我们为变量“class”
生成两个图表。数据集中的汽车示例更多,而对于公共汽车和货车,数据是平衡的。我们使用 matplotlib 库来绘制这些图表。图表的输出如下所示。
import matplotlib.pyplot as plt %matplotlib inline pd.value_counts(vehicle_df["class"]).plot(kind='bar') pd.value_counts(vehicle_df['class']).hist(bins=300)
第 4 步: 现在我们将检查数据集中是否有任何缺失的数据点。我们的数据集中没有缺失的数据点,因为我们已经处理过了。
vehicle_df.isna().sum()
注意
我们将在后面的章节中讨论处理缺失值的方法,因为删除缺失值通常不是最好的方法。
第 5 步: 现在我们将对数据集进行标准化。对于聚类来说,标准化数据集是一个很好的实践。这是很重要的,因为不同的维度可能处于不同的尺度,如果某个维度的值自然上比其他维度的值要大得多,那么一个维度可能会在距离计算中占据主导地位。下面使用zscore
和StandardScaler()
函数来实现。请参考书的附录,了解zscore
和StandardScaler()
函数之间的区别。
vehicle_df_1 = vehicle_df.drop('class', axis=1) from scipy.stats import zscore vehicle_df_1_z = vehicle_df_1.apply(zscore) from sklearn.preprocessing import StandardScaler import numpy as np sc = StandardScaler() X_standard = sc.fit_transform(vehicle_df_1)
第 6 步: 我们现在要快速查看数据集,生成一个散点图。该图显示了我们在上一步中创建的X_standard
的所有数据点的分布。
plt.scatter(X_standard[:,0], X_standard[:,1]) plt.show()
步骤 7: 我们现在将执行 k-means 聚类。首先,我们必须使用肘部法选择最佳聚类数。从 sklearn 库中,我们导入 KMeans。在一个 for 循环中,我们对聚类值从 1 到 10 进行迭代。换句话说,算法将创建 1、2、3、4 到 10 个聚类,然后生成结果供我们选择最优 k 值。
在下面的代码片段中,模型对象包含了在上一步生成的 X_standard 上适配的 KMeans 算法的输出。这里,欧几里得距离被用作距离度量。
from sklearn.cluster import KMeans from scipy.spatial.distance import cdist clusters=range(1,10) meanDistortions=[] for k in clusters: model=KMeans(n_clusters=k) model.fit(X_standard) prediction=model.predict(X_standard) meanDistortions.append(sum(np.min(cdist(X_standard, model.cluster_centers_, 'euclidean'), axis=1)) / X_standard .shape[0]) plt.plot(clusters, meanDistortions, 'bx-') plt.xlabel('k') plt.ylabel('Average distortion') plt.title('Selecting k with the Elbow Method')
步骤 8: 正如我们所观察到的,最佳聚类数为 3. 这是一个尖锐转折点,在图表中清晰可见。我们将使用 3 个聚类数进行 k-means 聚类。尽管这里的数字 3 没有特别之处,但它最适合这个数据集,我们也可以使用 4 或 5 个聚类数。random_state
是一个用于确定质心初始化的参数。我们将其设置为一个值以使随机性变得确定性。
kmeans = KMeans(n_clusters=3, n_init = 15, random_state=2345) kmeans.fit(X_standard)
步骤 9: 获取聚类的质心
centroids = kmeans.cluster_centers_ centroids
步骤 10: 现在我们将使用质心,以便它们可以按列进行分析。
centroid_df = pd.DataFrame(centroids, columns = list(X_standard) )
步骤 11: 我们现在将创建一个仅用于创建标签的 dataframe
,然后将其转换为分类变量。
dataframe_labels = pd.DataFrame(kmeans.labels_ , columns = list(['labels'])) dataframe_labels['labels'] = dataframe_labels['labels'].astype('category')
步骤 12: 在这一步中,我们将两个 dataframes
进行连接
dataframe_labeled = vehicle_df_1.join(dataframe_labels)
步骤 13: 执行 group by 操作以创建用于分析的数据框
dataframe_analysis = (dataframe_labeled.groupby(['labels'] , axis=0)).head(1234) dataframe_labeled['labels'].value_counts()
步骤 14: 现在,我们将为我们定义的聚类创建可视化。这是使用 mpl_toolkits
库完成的。逻辑很容易理解。数据点根据相应的标签着色。其余的步骤与调整标签、标题、刻度等有关的细节。由于在绘图中不可能绘制所有的 18 个变量,我们选择了 3 个变量来显示在图中。
from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize=(8, 6)) ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=20, azim=60) kmeans.fit(vehicle_df_1_z) labels = kmeans.labels_ ax.scatter(vehicle_df_1_z.iloc[:, 0], vehicle_df_1_z.iloc[:, 1], vehicle_df_1_z.iloc[:, 3],c=labels.astype(np.float), edgecolor='k') ax.w_xaxis.set_ticklabels([]) ax.w_yaxis.set_ticklabels([]) ax.w_zaxis.set_ticklabels([]) ax.set_xlabel('Length') ax.set_ylabel('Height') ax.set_zlabel('Weight') ax.set_title('3D plot of KMeans Clustering on vehicles dataset')
我们也可以用多个其他值对上述代码进行测试。我们已经创建了使用不同值的代码。出于空间考虑,我们将不同值的测试代码放在了 github 位置。
在上面的例子中,我们首先对数据集进行了小型的探索性分析。
注意
探索性数据分析(EDA)是实现稳健机器学习解决方案和成功项目的关键。在随后的章节中,我们将为数据集创建详细的 EDA。
接下来是确定最佳聚类数,本例中为三。然后我们实施了 k-means 聚类。您应该迭代不同的初始化 k-means 解决方案并比较结果,迭代不同的 k 值,并可视化以分析数据点的移动。在本章后面,我们将使用 Python 创建层次聚类的相同数据集。
基于质心的聚类是最推荐的解决方案之一,因为其逻辑较少,易于实现,灵活性高且易于维护。每当我们需要聚类作为解决方案时,大多数情况下我们都会从创建一个作为基准的 k 均值聚类解决方案开始。该算法非常受欢迎,通常是聚类的首选解决方案之一。然后我们会测试并迭代其他算法。
这标志着对基于质心的聚类算法的讨论结束。我们现在将继续前进,讨论连接性解决方案,并在下一节中讨论层次聚类。
2.4 基于连接性的聚类
“物以类聚”是连接性聚类中遵循的原则。核心概念是-彼此连接的对象相似。因此,根据这些对象之间的连接性,它们被组合成簇。图 2.11 中展示了这种表示的一个示例,我们可以迭代地对观察进行分组。例如,我们从所有事物开始,分成生物和非生物等等。这种表示最好使用右侧的图表展示,称为Dendrogram。
图 2.11 层次聚类利用迭代地对相似对象进行分组。右侧是聚类的可视化表示,称为树状图。
由于存在树状结构,连接性聚类有时被称为层次聚类。
层次聚类很好地符合人类直觉,因此我们容易理解它。与 k 均值聚类不同,在层次聚类中,我们不必输入最终聚类的数量,但该方法确实需要一个终止条件,即聚类应何时停止。同时,层次聚类不建议聚类的最佳数量。从生成的层次结构/树状图中,我们必须自己选择最佳的聚类数。在接下来的部分中,当我们为其创建 Python 代码时,我们将更多地探讨它。
层次聚类可以通过图 2.12 来理解,如下所示。这里第一个节点是根节点,然后迭代地分裂成节点和子节点。每当一个节点不能再进一步分裂时,它被称为终端节点或叶子。
图 2.12 层次聚类具有一个根节点,分裂为节点和子节点。不能再进一步分裂的节点称为叶子。在自底向上的方法中,将合并叶子节点
由于将观察结果合并成簇的过程或逻辑不止一个,我们可以生成大量的树状图,如下所示的(Equation 2.7):
(Equation 2.7)
树状图数量 = (2n-3)!/[2^((n-2)) (n-2)!]
其中 n 是观察数或叶子数。所以如果我们只有 2 个观察,我们只能有 1 个树状图。如果有 5 个观察,我们可以有 105 个树状图。因此,根据观察数,我们可以生成大量的树状图。
根据用于创建观察值分组的过程,层次聚类可以进一步分类,我们将在下面探讨这一点。
2.4.1 层次聚类的类型
基于分组策略,层次聚类可以细分为两种类型:聚合聚类和分裂聚类。
序号 | 聚合方法 | 分裂方法 |
1 | 自底向上方法 | 自顶向下方法 |
2 | 每次观察都会创建自己的群集,然后随着算法的进行而进行合并 | 我们从一个群集开始,然后观察被迭代地分割以创建类似树状的结构 |
3 | 采用贪婪方法进行合并(下面描述了贪婪方法) | 采用贪婪方法进行分割 |
4 | 观察会找到最佳配对进行合并,当所有观察都与彼此合并时,流程完成 | 一开始会取全部观察,然后根据分裂条件进行分割,直到所有观察都被用完或达到终止条件 |
图 2.13 中层次聚类的步骤。从左到右是聚合聚类(节点的分裂),从右到左是分裂聚类(节点的合并)
让我们先探讨贪婪方法的含义。贪婪方法或贪婪算法是指在每一步都会做出最佳选择,而不考虑对未来状态的影响。换句话说,我们活在当下,并从当下可用的选择中选择最佳选项。当前选择与未来选择无关,算法会在以后解决子问题。贪婪方法可能不会提供最优解,但通常在合理时间内会提供接近最优解的局部最优解。层次聚类在合并或分割节点时遵循这种贪婪方法。
我们现在将审查层次聚类方法中的步骤:
第一步: 如上图(图 2.13)所示,假设我们的数据集中有五个观察值– 1, 2, 3, 4 和 5。
第二步: 在这一步中,观察 1 和 2 被分为一组,4 和 5 被合并成一组。3 没有被合并到任何一组中。
第三步: 现在,在这一步中,我们将前一步中 4,5 的输出和观察 3 分为一个群集。
第四步: 步骤 3 的输出与 1,2 的输出结合成为一个单一的群集。
在这种方法中,从左到右,我们有一种自下而上的方法,而从右到左则是一种自上而下的方法。在自下而上的方法中,我们正在合并观测结果,而在自上而下的方法中,我们正在分割观测结果。我们可以同时使用自下而上或自上而下的方法进行层次聚类。分割性聚类是一种详尽的方法,有时可能比其他方法花费更多时间。
与 k-means 聚类类似,在这里用于测量距离的距离度量标准起着重要作用。我们知道并了解如何测量数据点之间的距离,但是有多种方法来定义该距离,我们现在正在研究这些方法。
无监督学习与生成式人工智能(MEAP)(一)(3)https://developer.aliyun.com/article/1522505