聚类算法属于无监督学习,其中最常见的是均值聚类,scikit-learn
中,有两种常用的均值聚类算法:
一种是有名的K-means
(也就是K-均值)聚类算法,这个算法几乎是学习聚类必会提到的算法;
另一个是均值偏移聚类,它与K-means
各有千秋,只是针对的应用场景不太一样,但是知名度远不如K-Means
。
本篇介绍如何在scikit-learn
中使用这两种算法。
K-means
算法起源于1967年,由James MacQueen和J. B. Hartigan提出。
它的基本原理是是将n个点划分为K个集群,使得每个点都属于离其最近的均值(中心点)对应的集群。
K-Means算法主要包含2个部分:
\(d(X_i, C_j) = ||X_i - C_j||^2\) 其中,\(X_i\)是数据点,\(C_j\)是质心。
\(J = \sum_{j=1}^k \sum_{i=1}^{N_j} ||X_i - C_j||^2\) 其中,\(N_j\)表示第\(j\)个簇中的样本数量。
均值漂移算法最早是由Fukunaga等人在1975年提出的。
它的基本原理是对每一个数据点,算法都会估算其周围点的密度梯度,然后沿着密度上升的方向移动该点,直至达到密度峰值。
均值漂移算法主要有3个步骤:
\(K(x) = \exp(-||x||^2 / (2h^2))\) 其中,\(h\)为带宽参数,控制核的宽度。
利用scikit-learn
中的样本生成器,创建一些用于聚类的数据。
import matplotlib.pyplot as plt from sklearn.datasets import make_blobs X, y = make_blobs(n_samples=1000, centers=5) plt.scatter(X[:, 0], X[:, 1], marker="o", c=y, s=25) plt.show()
生成了包含5个类别的1000条样本数据。
首先,划分训练集和测试集。
from sklearn.model_selection import train_test_split # 分割训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
按照8:2
的比例划分了训练集和测试集。
对于K-Means
算法来说,需要指定聚类的数目,通过观察数据,我们指定聚类的数目5
。
这里的样本数据比较简单,能够一下看出来,实际情况下并不会如此容易的知道道聚类的数目是多少,
常常需要多次的尝试,才能得到一个比较好的聚类数目,也就是K
的值。
基于上面的数据,我们设置5个簇,看看聚类之后的质心在训练集和测试集上的表现。
from sklearn.cluster import KMeans # 定义 reg = KMeans(n_clusters=5, n_init="auto") # 训练模型 reg.fit(X_train, y_train) # 绘制质心 _, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 4)) markers = ["x", "o", "^", "s", "*"] centers = reg.cluster_centers_ axes[0].scatter(X_train[:, 0], X_train[:, 1], marker="o", c=y_train, s=25) axes[0].set_title("【训练集】的质心位置") axes[1].scatter(X_test[:, 0], X_test[:, 1], marker="o", c=y_test, s=25) axes[1].set_title("【测试集】的质心位置") for idx, c in enumerate(centers): axes[0].plot(c[0], c[1], markers[idx], markersize=10) axes[1].plot(c[0], c[1], markers[idx], markersize=10) plt.show()
均值漂移聚类,事先是不用指定聚类的数目的,通过调整它的bandwidth
参数,
可以训练出拥有不同数目质心的模型。
下面,设置了bandwidth=5
,训练之后得到了拥有3个质心的模型。
from sklearn.cluster import MeanShift # 定义 reg = MeanShift(cluster_all=False, bandwidth=5) # 训练模型 reg.fit(X, y) # 绘制质心 _, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 4)) markers = ["x", "o", "^", "s", "*"] centers = reg.cluster_centers_ print(len(centers)) axes[0].scatter(X_train[:, 0], X_train[:, 1], marker="o", c=y_train, s=25) axes[0].set_title("【训练集】的质心位置") axes[1].scatter(X_test[:, 0], X_test[:, 1], marker="o", c=y_test, s=25) axes[1].set_title("【测试集】的质心位置") for idx, c in enumerate(centers): axes[0].plot(c[0], c[1], markers[idx], markersize=10) axes[1].plot(c[0], c[1], markers[idx], markersize=10) plt.show()
它把左下角的3类比较接近的样本数据点算作一类。
通过调整 bandwidth
参数,也可以得到和 K-Means 一样的结果,
有兴趣的话可以试试,大概设置 bandwidth=2
左右的时候,可以得到5个质心,与上面的K-Means算法的结果类似。
K-Means
和均值漂移聚类都是强大的聚类工具,各有其优缺点。
K-Means
的优势是简单、快速且易于实现,当数据集是密集的,且类别之间有明显的分离时,效果非常好;
不过,它需要预先设定簇的数量k
,且对初始质心的选择敏感,所以,对于不是凸形状或者大小差别很大的簇,效果并不好。
而均值漂移聚类的优势在于不需要预先知道簇的数量,可以自适应地找到数据的“模式”,对噪声和异常值也有很好的鲁棒性。
不过,与K-Means
相比,它需要选择合适的带宽参数,对高维数据可能不太有效,且计算复杂度较高。
最后,对于这两种均值聚类算法来说,选择哪种取决于数据的性质和应用的需求。