You are currently viewing 5种常用的聚类算法~Python

5种常用的聚类算法~Python

聚类算法是一种无监督学习算法。因此聚类算法不需要为数据打标签,聚类算法在不清楚数据标签的情况能自动对数据进行聚类(确实,知道了标签后也就不需要聚类了呀)。

聚类算法通常被用于从数据集中找到数据的规律。能够帮助我们更好地了解数据的特点,挖掘藏在数据中的规律(pattern)和知识(knowledge)。比如根据用户的年龄层,性别,购买商品喜好,购买频率等,对用户进行分组,从而弄清什么样的客户有购买什么商品的倾向。另外聚类还能用在比如将正常数据与异常值或异常值分开等问题。

就下来我们会使用scikit-learn机器学习库实际看看5种常用的聚类算法。

本次使用的数据集

本文中,我们将使用wine数据集。数据集包含了178条数据。13个特征向量。每条数据其实都是带有标签的,我们认为标签就是cluster。但在实际数据中数据是不带标签的,而是通过聚类,分类等算法给数据分配标签。因此我们先不读入数据的标签。

用下面的代码读入源数据

from sklearn.datasets import load_wine

X = load_wine()
print(X[:5]) 
[[1.423e+01 1.710e+00 2.430e+00 1.560e+01 1.270e+02 2.800e+00 3.060e+00
  2.800e-01 2.290e+00 5.640e+00 1.040e+00 3.920e+00 1.065e+03]
 [1.320e+01 1.780e+00 2.140e+00 1.120e+01 1.000e+02 2.650e+00 2.760e+00
  2.600e-01 1.280e+00 4.380e+00 1.050e+00 3.400e+00 1.050e+03]
 [1.316e+01 2.360e+00 2.670e+00 1.860e+01 1.010e+02 2.800e+00 3.240e+00
  3.000e-01 2.810e+00 5.680e+00 1.030e+00 3.170e+00 1.185e+03]
 [1.437e+01 1.950e+00 2.500e+00 1.680e+01 1.130e+02 3.850e+00 3.490e+00
  2.400e-01 2.180e+00 7.800e+00 8.600e-01 3.450e+00 1.480e+03]
 [1.324e+01 2.590e+00 2.870e+00 2.100e+01 1.180e+02 2.800e+00 2.690e+00
  3.900e-01 1.820e+00 4.320e+00 1.040e+00 2.930e+00 7.350e+02]]

我们可以看到,数据共有13列,代表13个特征向量。而最后一列的数值较其他特征尺度较大,可以考虑缩放数据,但是本文中不做数据缩放,只关心聚类算法。

缩放数据:
从算法角度看,许多算法使用相似性或距离来归类数据点,以发现密集的观测区域。缩放后的数据更易于聚类分析。 从数据角度看,当我们的特征空间包含多个特征向量,且特征向量之间的尺度完全不同时,将特征向量缩放至同一个尺度也能够方便我们聚类。 因此,使用聚类算法之前缩放数据有时是一个好方法。

常见聚类算法

scikit-learn库提供了一组不同的聚类算法供您选择。这里我们只讲常用的5个。

  • K-Means
  • DBSCAN
  • Mean Shift
  • Gaussian Mixture Model
  • Agglomerative Clustering(Hierarchical clustering)

我不会深入每个算法背后的理论和算法实现。对理论感兴趣的朋友们可以参考以下链接。

为了将数据和聚类后的cluster可视化出来,我们需要将13个特征向量压缩为2个特征向量,显示在一个二维坐标平面上。我们会使用t-sne算法进行降维。对于降维算法我会在其他文章中详细解释。你也可以从13个特征向量中选择2个能够解释问题的代表来可视化。显示数据时,我会给相同cluster的数据相同的颜色。这样我们能够用直接通过眼睛判断聚类算法的效果是否可靠。

下面的代码使用t-sne对数据进行了降维。注意降维后的数据仅用于可视化,不在聚类算法中使用。另外定义了plot_clusters函数可视化数据和各个cluster。

一些聚类算法要求指定需要在数据中发现的cluster个数,而其他聚类算法则需要指定观察值之间的最小距离,对于本次使用的数据我们假定cluster数量为3。随后我们再解释如何确定cluster个数。

from matplotlib import pyplot
from numpy import where
from sklearn.manifold import TSNE

# tsne is only used to decrease dimension for visualization
tsne = TSNE(n_components=2, init='pca', random_state=0)
x = tsne.fit_transform(X)

def plot_clusters(X, cluster_ids):
    for class_value in range(3):
        row_ix = where(cluster_ids == class_value)
        pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
    pyplot.show() 

K-Means

K-Means可能是最有名的聚类算法了。K-Means通过最小化各个Cluster之间的方差(简单理解为距离)来决定把哪些数据放到哪个Cluster中。

主要参数:

  • n_cluster:cluster个数

另外,关于如何决定n_cluster可以参考文章的附录。希望算法收敛更快速的,可以考虑Mini-Batch K-Means。

使用K-Means对wine数据集进行聚类的代码和结果如下:

from sklearn.cluster import KMeans
kmeans_model = KMeans(n_clusters=3)
kmeans_model.fit(X)
yhat = kmeans_model.predict(X)
plot_clusters(x, yhat) 

K-Means算法清晰地把wine数据分成了3个cluster。

DBSCAN

DBSCAN(Density-Based Spatial Clustering of Applications with Noise的略称)是一种基于密度的聚类算法,DBSCAN通过寻找高密度区域并在特征空间里扩展这些高密度区域来确定各个cluster。

主要参数:

  • eps:参数eps如何合适取值非常关键。给定一个距离ε,任何和质心的距离小于ε的数据点都是它的相邻点。如果ε太小,大部分数据会被标注为噪声,ε太大可能会导致数据都被聚类到同一个cluster中。

使用DBSCAN对wine数据集进行聚类的代码和结果如下:

from sklearn.cluster import DBSCAN
model = DBSCAN(eps=20)
yhat = model.fit_predict(X)
plot_clusters(x, yhat) 
eps=10
eps=40

我们看到eps=10时几乎没有数据可视化出来,因为刚才提到eps太小时大部分数据会被标注为-1。但是将eps增大后效果也不是很理想,大部分数据被标注为同一cluster。

Mean Shift

Mean Shift聚类是一种基于质心的算法。通过数据的密度来查找和调整质心。实际使用Mean Shift算法时不需要考虑cluster个数,算法会为我们决定。

主要参数:无

使用Mean Shift对wine数据集进行聚类的代码和结果如下:

from sklearn.cluster import MeanShift
model = MeanShift()
yhat = model.fit_predict(X)
plot_clusters(x, yhat) 

Mean Shift 的结果似乎相对于K-Means较模糊。

Gaussian Mixture Model

高斯混合模型(GMM)包含了一个多变量概率密度函数,是多个高斯分布函数的线性组合。高斯混合模型(GMM)通常在数据集包含多个不同的分布时比K-Means算法更有优势。因此,当你知道数据包含了不同的分布时果断使用高斯混合模型(GMM)来聚类。另外,高斯混合模型(GMM)能够给我们每个数据点从属于各个cluster的概率。

主要参数:

  • n_cluster:Cluster的个数

使用高斯混合模型(GMM)对wine数据集进行聚类的代码和结果如下:

from sklearn.mixture import GaussianMixture
model = GaussianMixture(n_components=3)
model.fit(X)
yhat = model.predict(X)
plot_clusters(x, yhat) 

wine数据集中似乎没有多个分布存在。高斯混合模型(GMM)的聚类结果不尽人意。

Agglomerative Clustering

Agglomerative Clustering是一种层次聚类算法。Agglomerative Clustering最开始认为每个数据为一个cluster,通过不断合并cluster最终达到算法收敛。聚类的层次可以用树形图表示(dendrogram)。Agglomerative Clustering与K-Means等算法不同,可以不指定cluster的数量。该算法对参数不敏感,一般都能得到最好的结果。但是层次聚类一般使用较少,除非你清楚数据集中含有层次关系,并且你的目的就是为数据集划分层次。

主要参数:

  • n_cluster:Cluster的个数

使用Agglomerative Clustering对wine数据集进行聚类的代码和结果如下:

from sklearn.cluster import AgglomerativeClustering
model = AgglomerativeClustering(n_clusters=3)
yhat = model.fit_predict(X)
plot_clusters(x, yhat) 

Agglomerative Clustering的聚类结果与K-means的结果非常相似。wine数据中可能存在着某种层次关系。

不存在一个算法是万能的。对于不同的数据,我们需要采用不同的聚类算法。关于更多聚类算法可以参阅scikit-learn的官方文档

补充1:关于如何选择n_cluster参数

我们看到很多算法在使用前需要决定cluster的个数。决定cluster个数通常是根据经验的。在不熟悉数据的情况下决定cluster个数是一件困难的事情。通常是将聚类结果反馈到n_cluster参数中,是一个人为的不断尝试迭代的过程。当然我们也可以用一些统计学的方法。一个比较简单的方法是使用Elbow函数。Elbow函数的横坐标是n_cluster,纵坐标是扭曲(distortions)。distortions计算从每个点到其分配的质心的距离的平方和。我们来实际看一下。

import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist

distortions = []
K = range(1,10)
for k in K:
    kmeanModel = KMeans(n_clusters=k).fit(X)
    kmeanModel.fit(X)
    distortions.append(sum(np.min(cdist(X, kmeanModel.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])

# Plot the elbow
pyplot.plot(K, distortions, 'bx-')
pyplot.xlabel('n_cluster')
pyplot.ylabel('Distortion')
pyplot.title('The Elbow Method showing the optimal k')
pyplot.show() 

我们看到n_cluster为3的时候函数转折较大,所以认为wine数据集的cluster个数为3。

另外可能你已经发现wine数据集本身是有标签的。那么我们看一下原始标签是什么样的。

from sklearn.datasets import load_wine

X, y = load_wine(return_X_y=True)
print(y) 
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]

对于wine的178条数据,共有3种标签,0/1/2。所以我们得知cluster个数实际为3,因此通过Elbow函数计算得到的cluster个数是正确的。

补充2:关于聚类的评估方法

关于聚类结果的评估方法。尽管确实存在许多科学的分析手法,但是都是学术性的比较,在实际案例中的评估效果通常不理想。因此通常认为聚类的评估是主观的,需要数据集所在领域的专家,领域专家对领域比较权威会有很多见解,这对我们评估聚类的正确性非常有帮助。聚类的学术评估方法可以参考scikit-learn的官方文档。

另外在实际案例中,我们会遇到非常奇怪的聚类,这让我们即使在领域专家的协助下仍然无法解释聚类结果。这个时候通常有两种情况:

  • 数据中含有大量无效数据或数据的分布很不对称。对于这种情况比较棘手但是处理方法也比较多。比如进行异常值去除,寻找更多数据或更多的特征等。
  • 使用的算法不正确。可以多换几种算法试试。我们有非常多的聚类算法,但是并没有一个算法是万能的。一般我们在不清楚数据集该使用何种算法是会先尝试K-Means。如果不知道cluster数量的话使用Mean-Shift。