实验:机器学习之 K-Means 聚类 第一章 K-Means 介绍 第一节 什么是 K 均值聚类 如今人工智能发展迅速,有许多用于“聚类”的模型。在这个课程中,我们将展示被认为是其中最简单的模型之一:K-Means 聚类。尽管“K-Means”很简单,但它仍然在许多数据科学应用中被广泛用。
无监督机器学习是一个过程,即教导计算机运用未标记、未分类的数据,使算法能在无监督情况下对这些数据进行操作。在没有先前数据训练的情况下,机器的任务就是依据相似性、模式和变化来整理未排序的数据。
K 代表聚类,它依据数据点与聚类中心的距离,将数据点分配到 K 个聚类中的一个。它首先会在空间中随机设定簇质心,然后每个数据点根据与簇质心的距离分配到相应的簇。在将每个点分配到某个簇后,会重新设定簇质心。此过程不断迭代,直至找到良好的集群。在分析中,假定簇的数量是预先给定的,且必须将点归入其中一组。
在某些情形下,K 没有明确界定,我们需考虑 K 的最佳数量。K 意味着聚类表现最佳,数据能很好地分离。当数据点重叠时,该聚类不适用。与其他聚类技术相比,K 均值聚类速度更快,提供了数据点之间的强耦合。但 K 均值聚类不能提供明确的聚类质量信息,不同的簇质心初始分配可能导致不同的簇。此外,K 均值算法对噪声敏感,可能陷入局部最小值。
K-Means 应用案例:
客户细分
了解网站访问者试图完成什么
模式识别
机器学习
数据压缩
第二节 k 均值聚类的目标是什么 聚类的目标在于把总体或数据点集划分成多个组,使得每个组内的数据点相互间更具可比性,同时与其他组内的数据点存在差异。其本质就是依据事物间相似程度与不同程度来对事物进行分组。
第三节 k 均值聚类的工作原理 我们获取一组具有特定特征及对应值(如向量形式)的项目数据集,任务是对这些项目进行分类。为此,我们采用 K-means 算法,这是一种无监督学习算法,其中的“K”代表要将项目分类成的组或簇的数量。(可将项目视作 n 维空间中的点来理解)。该算法会把项目分成 k 个相似的组或簇,计算相似度时采用欧几里得距离作为度量标准。
其具体工作流程如下: 首先,随机初始化 k 个点,称之为均值或聚类质心。然后,将每个项目归类到距离其最近的均值所属的类别,并更新均值的坐标,使其成为该簇中已分类项目的平均值。重复该过程特定的迭代次数后,便得到最终的簇。上述所提到的“点”被称作均值,是因为它们是所在分类项目的平均值。对于初始化这些均值,我们有多种选择,一种较为直观的方法是用数据集中随机项目的均值来初始化,另一种方法是在数据集边界范围内的随机值处初始化均值(若对于特征 x,项目的值处于[0,3]区间内,就用[0,3]内的 x 值来初始化均值)。
K-Means 伪代码:
1 2 3 4 5 6 7 8 初始化 k 个均值为随机值 重复以下步骤指定的迭代次数: 遍历所有项目: 计算项目与每个均值的欧几里得距离 确定最接近项目的均值 将项目分配给该均值 对于每个簇: 通过计算簇中项目的平均值来更新该簇的均值
第二章 实验部分 第一节 安装环境 1 pip install seaborn pandas numpy matplotlib
第二节 K-Means 聚类实验(纯代码实现) make_blobs
函数是sklearn.datasets
中的一个函数,主要用于产生聚类数据集,生成一个数据集和相应的标签。以下是make_blobs
函数的参数说明:
n_samples
:表示数据样本点个数,默认值为 100。
n_features
:表示每个样本的特征(或属性)数,也表示数据的维度,默认值为 2。
centers
:表示类别数(标签的种类数),默认值为 3。
cluster_std
:表示每个类别的方差,浮点数或者浮点数序列,默认值为 1.0。例如,若希望生成 2 类数据,其中一类比另一类具有更大的方差,可以将cluster_std
设置为浮点数 1.0~3.0。
center_box
:中心确定之后的数据边界,默认值为(-10.0,10.0)。
shuffle
:将数据进行洗乱,默认值是True
。
random_state
:官网解释是随机生成器的种子,可以固定生成的数据。给定数之后,每次生成的数据集就是固定的。若不给定值,则由于随机性将导致每次运行程序所获得的结果可能有所不同。在使用数据生成器练习机器学习算法练习或 python 练习时建议给定数值。
2.1 数据初始化 1 2 3 4 5 6 7 8 9 10 11 12 import numpy as npimport matplotlib.pyplot as pltfrom sklearn.datasets import make_blobsX,y = make_blobs(n_samples = 500 , n_features = 2 , centers = 3 , random_state = 23 ) fig = plt.figure(0 ) plt.grid(True ) plt.scatter(X[:,0 ], X[:,1 ]) plt.show()
2.2 初始化随机质心 代码为 K-means 聚类初始化三个聚类。它设置一个随机种子并在指定范围内生成随机簇中心,并为每个簇创建一个空的点列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 k = 3 clusters = {} np.random.seed(23 ) for idx in range (k): center = np.random.uniform(low=-2 , high=2 , size=X.shape[1 ]) points = [] clusters[idx] = { 'center' : center, 'points' : [] } clusters
2.3 绘制中心点 该图以网格线显示数据点 $(X[:,0], X[:,1])$ 的散点图。它还标记了 K-means 聚类生成的初始聚类中心(红色星星)。
1 2 3 4 5 6 7 8 9 10 11 12 13 plt.scatter(X[:,0 ], X[:,1 ]) plt.grid(True ) for i in clusters: center = clusters[i]['center' ] plt.scatter(center[0 ],center[1 ],marker = '*' ,c = 'red' ) plt.show()
2.4 定义欧几里得距离 欧几里得距离是一种在欧几里得空间中衡量两点之间距离的方法。 具体来说,对于空间中的两个点 $P(x_1,y_1,z_1,…)$ 和 $Q(x_2,y_2,z_2,…)$,它们之间的欧几里得距离就是各对应坐标分量差值的平方和的平方根,即:
$$ d(P,Q) = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2 + \cdots} $$
它是一种非常基础且广泛应用的距离概念,在许多领域如数学、物理学、计算机科学等中都有重要作用,常用于聚类分析、模式识别等方面。
1 2 3 def distance (p1, p2 ): return np.sqrt(np.sum ((p1 - p2) ** 2 ))
2.5 创建分配和更新集群中心的功能 E 步(Expectation 步)是根据当前的聚类中心,计算每个数据点属于每个聚类的概率。
在 K-Means 中,可以通过计算每个数据点到每个聚类中心的距离来确定概率。距离越近,数据点属于该聚类的概率就越高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 def assign_clusters (X, clusters ): dots_num = X.shape[0 ] for idx in range (dots_num): dist = [] curr_x = X[idx] for i in range (k): dis = distance(curr_x, clusters[i]['center' ]) dist.append(dis) curr_cluster = np.argmin(dist) clusters[curr_cluster]['points' ].append(curr_x) return clusters
所以,E 步实际上找 一个蓝色的点 与 三个红色星星 对比距离,距离短的,就归属对应的红色星星所有。
M 步(Maximization 步)是根据 E 步计算出的概率,重新计算聚类中心。
在 K-Means 中,更新聚类中心的方法是将属于每个聚类的数据点的平均值作为新的聚类中心。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def update_clusters (X, clusters ): for i in range (k): points = np.array(clusters[i]['points' ]) if points.shape[0 ] > 0 : new_center = points.mean(axis =0 ) clusters[i]['center' ] = new_center clusters[i]['points' ] = [] return clusters
所以,M 步事实上,平均 E 步所收集的点(将收集的点的 x 坐标累加在再平均,y 坐标累加再平均)得到一个新的中心点坐标。
2.6 创建用于预测数据点集群的函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def pred_cluster (X, clusters ): pred = [] dots_num = X.shape[0 ] for i in range (dots_num): dist = [] for j in range (k): d = distance(X[i], clusters[j]['center' ]) dist.append(d) pred.append(np.argmin(dist)) return pred
2.7 赋值、更新中心点位置、推理 1 2 3 4 5 6 7 8 clusters = assign_clusters(X, clusters) clusters = update_clusters(X, clusters) pred = pred_cluster(X, clusters)
2.8 显示推理结果 1 2 3 4 5 6 7 8 9 10 11 12 plt.scatter(X[:,0 ], X[:,1 ], c = pred) for i in clusters: center = clusters[i]['center' ] plt.scatter(center[0 ], center[1 ], marker = '^' , c = 'red' ) plt.show()
第三节 K-Means 聚类实验(sklearn 实现) 3.1 抑制警告 这段代码的主要作用是抑制(屏蔽)警告信息。 通过自定义一个名为 warn
的函数,它什么也不做(只是直接通过),然后将 warnings
模块中的 warn
方法替换为这个自定义的函数,这样当有警告产生时,就不会实际输出或显示这些警告信息了。
1 2 3 4 5 def warn (*args, **kwargs ): pass import warningswarnings.warn = warn
3.2 数据初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import randomimport numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeansfrom sklearn.datasets import make_blobs%matplotlib inline np.random.seed(0 ) centers = [ [4 ,4 ], [-2 , -1 ], [2 , -3 ], [1 , 1 ] ] X, y = make_blobs(n_samples=5000 , centers=centers, cluster_std=0.9 ) plt.scatter(X[:, 0 ], X[:, 1 ], marker='.' )
%matplotlib inline 主要的作用:在使用 Jupyter Notebook 等交互式环境时,它使得 matplotlib 绘制的图形可以直接嵌入在当前的 Notebook 页面内显示,而不是弹出单独的窗口来展示图形。这样方便在 Notebook 中直接查看和交互图形,有助于进行数据分析和可视化的过程。
3.3 设置 K-Means 函数 首先,我们进行 KMeans
设置。现在我们有了随机数据。
KMeans
类有很多可使用的参数,这里我们使用以下三个:
init
(初始化质心的方法):值为 "k-means++"
,表示以一种智能的方式选择初始聚类中心来加速收敛。
n_clusters
(要形成的簇的数量以及要生成的质心数量):值为 4,因为我们有 4 个中心。
n_init
定义了要运行算法的次数,默认情况下,n_init
设置为 10,这意味着算法将在 10 个不同的随机初始质心上运行,并返回最佳结果。
然后,使用这些参数初始化 KMeans
,将输出参数命名为 k_means
。
1 2 3 4 5 k_means = KMeans(init="k-means++" , n_clusters=4 , n_init=12 ) k_means.fit(X)
3.4 显示图像 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 fig = plt.figure(figsize=(6 , 4 )) colors = plt.cm.Spectral(np.linspace(0 , 1 , n_clusters)) ax = fig.add_subplot(1 , 1 , 1 ) plt.xlim(-10 , 10 ) plt.ylim(-10 , 10 ) for k, col in zip (range (n_clusters), colors): my_members = (k_means3.labels_ == k) ax.plot(X[my_members, 0 ], X[my_members, 1 ], 'w' , markerfacecolor=col, marker='.' ) cluster_center = k_means3.cluster_centers_[k] ax.plot(cluster_center[0 ], cluster_center[1 ], 'o' , markerfacecolor=col, markeredgecolor='k' , markersize=10 ) plt.show()
3.5 完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeansfrom sklearn.datasets import make_blobs%matplotlib inline np.random.seed(0 ) centers = [ [4 ,4 ], [-2 , -1 ], [2 , -3 ], [1 , 1 ] ] X, y = make_blobs(n_samples=5000 , centers=centers, cluster_std=0.5 ) n_clusters = 4 k_means3 = KMeans(init="k-means++" , n_clusters=n_clusters, n_init=12 ) k_means3.fit(X) fig = plt.figure(figsize=(6 , 4 )) colors = plt.cm.Spectral(np.linspace(0 , 1 , n_clusters)) ax = fig.add_subplot(1 , 1 , 1 ) plt.xlim(-10 , 10 ) plt.ylim(-10 , 10 ) for k, col in zip (range (n_clusters), colors): my_members = (k_means3.labels_ == k) ax.plot(X[my_members, 0 ], X[my_members, 1 ], 'w' , markerfacecolor=col, marker='.' ) cluster_center = k_means3.cluster_centers_[k] ax.plot(cluster_center[0 ], cluster_center[1 ], 'o' , markerfacecolor=col, markeredgecolor='k' , markersize=10 ) plt.show()
第四节 K-Means 聚类鸢尾花分类实验(sklearn 实现)
4.1 加载鸢尾花数据 1 2 3 4 5 6 7 8 9 import pandas as pdimport numpy as npimport seaborn as snsimport matplotlib.pyplot as pltimport matplotlib.cm as cmfrom sklearn.datasets import load_irisfrom sklearn.cluster import KMeansX, y = load_iris(return_X_y=True )
4.2 找到最佳簇数 1 2 3 4 5 6 sse = [] for k in range (1 ,11 ): km = KMeans(n_clusters=k, random_state=2 ) km.fit(X) sse.append(km.inertia_)
4.3 显示簇数与误差的关系 “Sum Squared Error”的意思是“误差平方和”或“平方误差总和”。 对于一组实际值 $y_i$ 和预测值 $\hat{y}_i$($i=1,2,\cdots,n$),误差平方和的公式为:
$$ SSE = \sum_{i=1}^{n}(y_i - \hat{y}_i)^2 $$
它常被用于评估模型预测值与实际值之间的偏差程度。
1 2 3 4 5 6 7 8 sns.set_style("whitegrid" ) g=sns.lineplot(x=range (1 , 11 ), y=sse) g.set (xlabel ="Number of cluster (k)" , ylabel = "Sum Squared Error" , title ='Elbow Method' ) plt.show()
从上图中,我们可以观察到,在 k=2 和 k=3 时,肘状的情况。我们考虑 K=3。
4.4 构建 Kmeans 聚类模型 1 2 kmeans = KMeans(n_clusters=3 , random_state=2 ) kmeans.fit(X)
4.5 找到簇中心
4.6 推理 1 2 pred = kmeans.fit_predict(X) pred
4.7 输出图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 plt.figure(figsize=(12 ,5 )) plt.subplot(1 , 2 , 1 ) plt.scatter(X[:,0 ], X[:,1 ],c=pred, cmap=cm.Accent) plt.grid(True ) for center in kmeans.cluster_centers_: center = center[:2 ] plt.scatter(center[0 ],center[1 ],marker = '^' ,c = 'red' ) plt.xlabel("petal length (cm)" ) plt.ylabel("petal width (cm)" ) plt.subplot(1 , 2 , 2 ) plt.scatter(X[:,2 ], X[:,3 ], c=pred, cmap=cm.Accent) plt.grid(True ) for center in kmeans.cluster_centers_: center = center[2 :4 ] plt.scatter(center[0 ],center[1 ],marker = '^' ,c = 'red' ) plt.xlabel("sepal length (cm)" ) plt.ylabel("sepal width (cm)" ) plt.show()