简单的聚类方法,如k-means,可能不像当代神经网络或其他最近的高级非线性分类器那样性感,但它们肯定有其效用,知道如何正确地处理一个无监督学习问题是你所拥有的一项伟大的技能。
这将是一系列文章中的第一篇,探讨k-means聚类管道的不同方面。在这第一篇文章中,我们将讨论中心点初始化:它是什么,它能实现什么,以及一些不同的方法。我们将假设对机器学习、Python编程和聚类的一般概念比较熟悉。
k-means聚类
k-means是一种简单但通常有效的聚类方法。传统上,从一个给定的数据集中随机选择k个数据点作为聚类中心或中心点,所有的训练实例被绘制出来并添加到最近的聚类中。在所有实例被添加到聚类中后,代表每个聚类中实例平均值的中心点被重新计算,这些重新计算的中心点成为各自聚类的新中心。
在这一点上,所有的集群成员资格都被重置,训练集的所有实例都被重新绘制,并被重新添加到它们最近的,可能是重新定中心的集群中。这个反复的过程一直持续到中心点或其成员资格没有变化为止,并且集群被认为已经稳定。
一旦重新计算的中心点与上一次迭代的中心点相匹配,或者在某个预设的范围内,就实现了收敛。在K-means中,距离的测量通常是欧几里得,给定2个点的形式为(x,y),可以表示为。
%5E2%20+%20(y_%7B1%7D%20-%20y_%7B2%7D)%5E2%7D%7D%7D)
在技术上,特别是在并行计算的时代,k-means中的迭代聚类在本质上是串行的;但是,一个迭代中的距离计算不需要。因此,对于相当大的集合,距离计算是k-means聚类算法中值得并行化的目标。
中心点初始化方法
由于k-means聚类的目的是通过连续的迭代收敛于一组最优的聚类中心(中心点)和基于与这些中心点的距离的聚类成员,因此直观地看,这些初始中心点的定位越理想,k-means聚类算法收敛所需的迭代次数就越少。这表明,对这些初始中心点的初始化进行一些战略性的考虑会被证明是有用的。
有哪些中心点初始化的方法?虽然有很多初始化策略,但我们还是要关注以下几个方面。
- 随机数据点。在这种方法中,如上文 "传统 "案例所述,从数据集中选择k个随机数据点作为初始中心点,这种方法显然是高度不稳定的,并提供了一种情况,即所选中心点在整个数据空间中的位置并不理想。
- k-means++。由于分散初始中心点被认为是一个值得追求的目标,*k-means++*通过将第一个中心点分配给一个随机选择的数据点的位置,然后根据与给定点最近的现有中心点的距离的平方成比例的概率,从剩余的数据点中选择后续的中心点。其效果是试图将中心点推到尽可能远的地方,从初始化开始就尽可能多地覆盖所占用的数据空间。*K-means++*的原始论文,来自2006年,可以在这里阅读。
- naive sharding。这种不太知名的(未知的?)中心点初始化方法是我自己一些研究生研究的主题。它主要依赖于反映一个实例的所有属性值的复合求和值的计算。一旦这个综合值被计算出来,它就被用来对数据集的实例进行排序。然后,数据集被横向分割成k块,或称碎片。最后,每个碎片的原始属性被独立求和,它们的平均值被计算出来,碎片属性平均值的结果行集合成为用于初始化的中心点集合。我们的期望是,作为一个确定性的方法,它应该比随机性的方法执行得更快,并通过综合求和值接近初始中心点在数据空间的分布。如果感兴趣,你可以在这里阅读更多关于它的信息。
其中任何一种变体都是可能的:你可以从数据空间的任何地方随机选择,而不仅仅是包含现有数据点的空间;你可以尝试首先找到最中心的数据点,而不是随机选择,然后从那里开始进行k-means++;你可以将求和后的平均操作换成天真分片中的另一种。
另外,可以用一种分层聚类的形式(通常是Ward的方法)作为寻找初始聚类中心的方法,然后可以将其交给k-means来完成实际的数据聚类任务。这可能是有效的,但由于这意味着还要讨论分层聚类的问题,我们将把这个问题留到以后的文章中讨论。
中心点初始化和Scikit-learn
由于我们将使用Scikit-learn来进行聚类,让我们看一下它的 KMeans
模块,在那里我们可以看到以下关于可用中心点初始化方法的文字。
**init{‘k-means++’, ‘random’, ndarray, callable}, default=’k-means++’**
初始化的方法。
'
k-means++
':以一种聪明的方式为k-mean聚类选择初始簇中心,以加快收敛速度。详见k_init中的注释部分。'
random
': 从数据中随机选择n_clusters
观测值(行)作为初始中心点。如果传入一个ndarray,它应该是(n_clusters, n_features)的形状,并给出初始中心。
如果传递的是一个可调用程序,它应该接受参数X、n_clusters和一个随机状态并返回一个初始化。
考虑到这一点,并且由于我们希望能够比较和检查初始化的中心点--这一点我们无法用Scikit-learn的实现来做--我们将使用上面讨论的3种方法的自己的实现,即随机中心点初始化、k-means++和naive sharding。然后我们可以使用独立于Scikit-learn的实现来创建我们的中心点,并在聚类时将它们作为ndarray传入。我们也可以使用可调用选项,而不是ndarray选项,并将中心点初始化整合到Scikit-learn的k-means执行中,但这又使我们回到了原点,无法在聚类前检查和比较这些中心点。
说到这里,centroid_initialization.py
文件包含了我们的中心点初始化的实现。
将这个文件放在我启动Jupyter笔记本的同一目录下,我可以从导入开始,进行如下操作。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import centroid_initialization as cent_init
%matplotlib inline
创建一些数据
显然,我们将需要一些数据。我们将创建一个小型的合成数据集,以便控制明确划分我们的集群(见图1)。
from sklearn.datasets import make_blobs
n_samples = 250
n_features = 2
n_clusters = 4
random_state = 42
max_iter = 100
X, y = make_blobs(n_samples=n_samples,
n_features=n_features,
centers=n_clusters,
random_state=random_state)
fig=plt.figure(figsize=(8,8), dpi=80, facecolor='w', edgecolor='k')
plt.scatter(X[:, 0], X[:, 1]);
图1:我们的合成数据集
初始化中心点
让我们使用上面的实现来初始化一些中心点。
随机初始化
random_centroids = cent_init.random(X, n_clusters)
print(random_centroids)
[[-7.09730839 -5.78133274]
[ 4.56277713 2.31432166]
[ 4.9976662 2.53395421]
[ 4.16493353 1.31984045]]
k-means++初始化
plus_centroids = cent_init.plus_plus(X, n_clusters)
print(plus_centroids)
[[-1.59379551 9.34303724]
[-6.63466062 -7.38705277]
[-7.31520368 7.86243296]
[ 5.1549141 2.48695563]]
Naive Sharding初始化
naive_centroids = cent_init.naive_sharding(X, n_clusters)
print(naive_centroids)
[[-9.09917527 -7.00640594]
[-6.48108313 2.12605542]
[-2.66275228 7.07500918]
[ 4.66778007 9.47156226]]
正如你所看到的,这些初始化的中心点集合彼此不同。
中心点初始化的可视化
让我们看看我们的中心点是如何相互比较的,以及与数据点的直观比较。我们将多次调用下面的绘图函数进行比较。图2是上面创建的3组中心点与数据点的对比图。
def centroid_plots(X, rand, plus, naive):
fig=plt.figure(figsize=(8,8), dpi=80, facecolor='w', edgecolor='k')
plt.scatter(X[:, 0], X[:, 1],
s=50,
marker='o',
label='cluster 1')
plt.scatter(rand[:, 0],
rand[:, 1],
s=200, c='yellow',
marker='p')
plt.scatter(plus[:, 0],
plus[:, 1],
s=200, c='red',
marker='P')
plt.scatter(naive[:, 0],
naive[:, 1],
s=100, c='green',
marker='D');
centroid_plots(X, random_centroids, plus_centroids, naive_centroids)
图2.中心点与数据点的关系图:随机(黄色)、K-means++(红色)、天真分片(绿色)。
值得注意的是,幸运的是,随机初始化的中心点中有3个位于最右边的聚类中;*k-means++*初始化的中心点位于每个聚类中的一个;而天真分片的中心点最终以某种弧形方式向上和向右分布在数据空间中,向数据严重聚类的地方偏移。
请记住,我们创建了一个二维的数据空间,所以这些中心点的可视化是准确的;我们并没有剥离一些维度,人为地将数据绘制在一个剥离的空间中,这可能是有问题的。这意味着在这种情况下,中心点的比较是尽可能的准确。
你可能会问,这种初始化有什么受到随机播种的影响?随机初始化和和*k-means++*都是随机的方法,所以它们会受到随机种子的影响。如果我们将一些不同的种子传递给初始化算法的额外运行,其结果可在图3中看到。
图3.用随机种子进行中心点初始化的实验,从左上角顺时针方向:123, 249, 127, 13
一些观察结果。
-
随机初始化是(恰当地)到处都是的
-
k-means++在聚类内相对一致,选择作为初始中心点的具体数据点略有不同
-
天真分片是确定的,所以不受播种的影响。
这让我们想到一些问题,这些问题直接关系到聚类的下一步。
-
中心点放置对所产生的聚类任务有什么影响?
-
中心点放置对产生的聚类任务的速度有什么影响?
-
中心点放置对所产生的聚类任务的准确性有什么影响?
-
什么时候我们会使用这些初始化方法中的某些方法而不是其他方法?
-
如果从优化放置的角度来看,随机似乎是一个糟糕的初始化方法,那么为什么要使用它?
-
确定性的初始化方法,如天真的分片,是否有任何优势?
我们将在下一次开始寻找这些问题的答案。
Matthew Mayo(@mattmayo13)是一位数据科学家,也是KDnuggets的主编,KDnuggets是开创性的在线数据科学和机器学习资源。他的兴趣在于自然语言处理、算法设计和优化、无监督学习、神经网络和机器学习的自动化方法。马修拥有计算机科学的硕士学位和数据挖掘的研究生文凭。他的联系方式是editor1 at kdnuggets[dot]com。