聚类 - MACHINE-LEARNING教程

无监督学习 - 聚类

想象一下,你走进一个巨大的图书馆,里面所有的书都杂乱无章地堆在地上,你的任务不是去读每一本书(那太费时间了),而是根据书的主题,比如:科幻小说、历史传记、烹饪食谱,将它们分成几堆。在这个过程中,你并没有一个现成的分类清单告诉你哪本书属于哪一类,你完全是依靠书的内容、封面、厚度等特征, 自发地 发现了这些群体。

在机器学习中, 聚类 要做的正是这样的事情。它是一种 无监督学习 方法,目标是在没有预先标注答案(即没有"标签")的数据中,发现其内在的结构和分组。


什么是无监督学习与聚类?

在开始之前,我们先快速区分一下机器学习的两大范式:

  • 监督学习 :就像有老师指导的学习。我们给算法提供大量问题(特征数据)和对应的标准答案(标签),让它学习从问题到答案的映射关系。例如,给算法看很多猫和狗的图片(特征),并告诉它每张图是猫还是狗(标签),训练后它就能识别新的图片。
  • 无监督学习 :就像让机器自己去探索和发现。我们只提供问题(特征数据), 不提供答案(标签) 。算法的任务是自行从数据中找出模式、结构或关系。聚类就是其中最核心的技术之一。

聚类的核心思想 :将数据集中的样本划分成若干个 互不相交 的子集(称为簇或类),使得同一个簇内的样本彼此 相似 ,而不同簇中的样本彼此 不相似

这里的相似通常通过数学上的 距离 来衡量(如欧氏距离)。

距离越近,相似度越高。


经典聚类算法:K-Means

K-Means 是最著名、最常用的聚类算法之一,其思想直观,实现相对简单。

算法原理与步骤

我们可以把 K-Means 的过程想象成竞选代表并重新划区:

  1. 确定簇的数量 K :首先,你需要决定想把数据分成几类。这个 K 值需要预先指定,这是 K-Means 的一个关键参数。
  2. 初始化代表(质心) :随机在数据空间中选取 K 个点,作为每个簇的初始"中心点",我们称之为 质心
  3. 分配居民(样本) :计算数据集中每一个样本点到这 K 个质心的距离。遵循"近者归其类"的原则,将每个样本分配给距离它 最近 的那个质心所在的簇。这样,所有样本就被划分到了 K 个簇中。
  4. 改选新代表更新质心) :现在,每个簇里都有了一批样本。重新计算每个簇的质心,新的质心就是该簇内所有样本点的 平均值 (均值点)。
  5. 重复与收敛 :重复步骤 3(分配)和步骤 4(更新),直到质心的位置不再发生显著变化(即算法收敛)。此时,每个样本的所属簇也不再变化。

代码示例与实践

让我们用 Python 的 scikit-learn 库和一个简单的数据集来演示 K-Means。

示例代码
# 导入必要的库importnumpyasnpimportmatplotlib.pyplotaspltfromsklearn.datasetsimportmake_blobsfromsklearn.clusterimportKMeans# -------------------------- 设置中文字体 start --------------------------plt.rcParams['font.sans-serif']=[# Windows 优先'SimHei','Microsoft YaHei',# macOS 优先'PingFang SC','Heiti TC',# Linux 优先'WenQuanYi Micro Hei','DejaVu Sans']# 修复负号显示为方块的问题plt.rcParams['axes.unicode_minus']=False# -------------------------- 设置中文字体 end --------------------------# 1. 创建一个人工数据集# 我们生成 300 个样本点,它们天然地围绕 4 个中心分布(方便我们观察)X,y_true=make_blobs(n_samples=300,centers=4,cluster_std=0.60,random_state=0)# X 是特征数据,y_true 是真实的类别标签(仅用于最后对比,聚类算法不会用到它)# 2. 可视化原始数据plt.scatter(X[:,0],X[:,1],s=50)# s 是点的大小plt.title("原始未标记数据")plt.show()# 3. 应用 K-Means 聚类# 指定要聚成 4 类kmeans=KMeans(n_clusters=4,random_state=0,n_init='auto')# 拟合模型并预测每个样本的簇标签y_kmeans=kmeans.fit_predict(X)# 4. 获取质心坐标centroids=kmeans.cluster_centers_# 5. 可视化聚类结果plt.scatter(X[:,0],X[:,1],c=y_kmeans,s=50,cmap='viridis')# 用不同颜色标注不同簇的样本点plt.scatter(centroids[:,0],centroids[:,1],c='red',s=200,alpha=0.8,marker='X')# 用红色大叉标出质心位置,alpha 是透明度plt.title("K-Means 聚类结果 (K=4)")plt.show()# 打印前10个样本的预测簇标签print("前10个样本的簇标签:",y_kmeans[:10])# 打印质心坐标print("四个簇的质心坐标:\n",centroids)

代码解析

  • make_blobs :生成用于聚类的模拟数据集, centers=4 表示数据围绕 4 个中心点生成。
  • KMeans(n_clusters=4) :创建 K-Means 模型实例,指定簇数 K 为 4。 n_init='auto' 是运行算法的次数,取最佳结果。
  • fit_predict(X) :核心方法,在数据 X 上拟合模型并返回每个样本的簇索引(0, 1, 2, 3)。
  • cluster_centers_ :属性,存储训练后得到的 K 个质心的坐标。

运行这段代码,你会看到两张图。第一张是杂乱无章的点,第二张则清晰地分成了四个颜色的组,并且中心有红色的 X 标记。这就是 K-Means 的魔力!

前10个样本的簇标签: [1 2 0 2 1 1 3 0 2 2]

四个簇的质心坐标:

 [[ 0.94973532  4.41906906]

 [ 1.98258281  0.86771314]

 [-1.37324398  7.75368871]

 [-1.58438467  2.83081263]]

原始未标记数据:

K-Means 聚类结果:


如何选择最佳的 K 值?

在上面的例子中,因为我们知道数据是围绕 4 个中心生成的,所以轻松地设置了 K=4 。但在现实世界中,我们往往不知道数据应该分成几类。如何选择 K 呢?

一个常用的方法是 "肘部法则" 。其思想是:随着簇数量 K 的增加,样本点到其所属簇质心的平均距离(称为 畸变程度 inertia )会下降。当 K 小于真实簇数时,增加 K 会大幅降低这个距离;当 K 达到真实簇数后,再增加 K,距离的下降幅度会骤减。这个拐点就像手肘的关节,对应的 K 值就是较好的选择。

示例代码
# 肘部法则示例:计算不同K值下的 inertiainertias=[]K_range=range(1,11)# 测试 K 从 1 到 10forkinK_range:kmeans=KMeans(n_clusters=k,random_state=0,n_init='auto')kmeans.fit(X)inertias.append(kmeans.inertia_)# inertia_ 属性即 SSE# 绘制肘部曲线plt.plot(K_range,inertias,'bo-')plt.xlabel('簇的数量 K')plt.ylabel('Inertia (SSE)')plt.title('肘部法则寻找最佳 K 值')plt.axvline(x=4,color='r',linestyle='--',alpha=0.5)# 标记我们已知的 K=4plt.show()

观察生成的曲线,你会发现在 K=4 附近,曲线下降速度明显变缓,形成一个"肘部",这提示我们 K=4 是一个合理的选择。


聚类的应用场景

聚类是一种强大的探索性数据分析工具,应用极其广泛:

  1. 客户细分 :在电商或营销中,根据客户的购买行为、 demographics(人口统计特征)进行聚类,划分出"高价值客户"、"价格敏感客户"等群体,以便实施精准营销。
  2. 图像分割 :将图像中的像素根据颜色、纹理进行聚类,可以用于简化图像、识别前景和背景。
  3. 异常检测 :正常的数据点通常会形成密集的簇,而异常点则远离任何簇的中心。通过聚类可以发现这些离群点。
  4. 文档归类 :对新闻文章或研究论文进行聚类,自动发现热点话题或研究领域。
  5. 社交网络分析 :在社交网络中,通过对用户的关系、互动进行聚类,可以发现社区或圈子。

实践练习与总结

练习 1:尝试不同的 K 值 修改上面 K-Means 示例代码中的 n_clusters 参数,分别设置为 2, 3, 5, 8。观察聚类结果图,感受 K 值选择对结果的影响。

练习 2:使用真实数据集 尝试使用 scikit-learn 自带的 iris (鸢尾花)数据集进行聚类。虽然这个数据集通常用于分类,但你可以忽略它的标签,只用特征数据(花萼和花瓣的长度宽度)进行 K-Means 聚类,然后将聚类结果与真实标签对比,看看效果如何。

示例代码
fromsklearnimportdatasetsiris=datasets.load_iris()X_iris=iris.data# 只使用特征数据