聚类之K-Means算法(一)

423 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

之前讲的除了降维之外的算法,都是基于有监督的学习。也就是我们需要训练的数据集,也需要含有的标签的数据集。我们通过训练集和标签集训练出来模型后,还需要测试集和标签最后进行判断模型的是否够好。

而实际上我们绝大数可用的数据可能没有标签,而接下来我们需要了解一下如何在只有输入特征XX,而没有标签yy的无监督学习是怎么工作的。首先是介绍其中一个任务方向-聚类

聚类

那么什么是聚类:能够识别相似的实例并将其分配给相似实例的集群或组。

上面的话似乎有点拗口,简单的举个例子,还记得之前讲过的鸢尾花的案例吗,其中鸢尾花有三个变种:山鸢尾、变色鸢尾和维吉尼亚鸢尾。现在我们现在要做的不是将每个实例分出是哪种具体的花,而是只需要将分散的实例能够分辨出是有三个不同的集群就行。大概如下图1所示:

图1 分类(左)和聚类(右)

聚类没有了标签,但是需要能够将右图中根据一定的算法将其准确分出三类来。这时候我们就开始引入聚类中比较常用的算法之一-K-Means。

K-Means算法

K-Means是一种简单的算法,能够非常快速的、高效的对上面类似的数据进行聚类,通常只需要几次迭代即可。 接下来,让我们先使用Scikit-Learn中的make_blobs()函数生成一个聚类的数据集

blob_centers=np.array([[ 0.2,  2.3],[-1.5 ,  2.3], [-2.8,  1.8],[-2.8,  2.8],[-2.8,  1.3]])
blob_std = np.array([0.4, 0.3, 0.1, 0.1, 0.1])
make_blobs(n_samples=2000, centers=blob_centers,cluster_std=blob_std, random_state=42)
  • centers- 用于设置聚类的中心点,也就是聚类有哪几类
  • cluster_std-类别内部的方差,也就是每个类生成群体实例之间的范围
  • 在上面生成的数据集中,请读者需要注意一下blob_centers的五个点,这是聚类的分类,表示会有五类 让我们看一下他会生成怎么样的数据集:

图2 聚类数据集

能够很直观的看出有五大坨,但是他们没有任何的标记。

OK,接下来让我们来训练一个K-Means聚类器,看看他能不能在这个数据集上工作的很好。

from sklearn.cluster import KMeans
# n_clusters 指定要找的集群数k=5
kmeans = KMeans(n_clusters=5, random_state=42)
y_pred = kmeans.fit_predict(X)

我们在初始化K-Means算法时,都必须指定一个需要找到的集群数k。

这里我们因为在上帝视角和根据一个很中规中矩的散点图做出的判断,所以我们能直接看出集群数k=5,但是实际上我们可能面对其他的数据集是很复杂的,并不能很准绝很明白的找出集群数,后面我也会介绍该如何判断我们选择的集群数好不好。

同时我们要记得,这时候y_pred对于每一个实例返回的判断,不再是某一个标签了,而是标签的索引,因为我们并没有准绝的定义每个标签,只能知道应该有五个不同的标签。

y_pred #输出:array([4, 0, 2, ..., 0, 4, 1])

同时我们可以通过kmeans.cluster_centers_看一下预测的中心点:

# blob_centers->[[ 0.2,  2.3],[-1.5 ,  2.3], [-2.8,  1.8],[-2.8,  2.8],[-2.8,  1.3]]
array([[ 0.20925539,  2.30351618], [-2.81545596,  1.84556821], [-1.44440462,  2.3227444 ], 
        [-2.78675499,  1.31605644], [-2.79811398,  2.79621268]])

如果加上这些中心的边界,我们就能够得到一个Voronoi图,如下所示:

图3 K-Means决策边界(Voronoi图)

可能看出,每个集群之间的直径约不相同,那么K-Means的算法将会越好。因为他在分配实例给某个集群时,考虑的只有实例与其中心点的距离。

我们可以直接分配给某一个单独的集群,这样的结果梆硬(硬聚类),因此效果也会悦差一点。当然我们也有柔软的方法,可以给一个实例分配每个集群一种分数(软聚类),这样的话更有效。

  • 分数的设置有多种:可以是实例与中心点的距离啊,可以是相似性分数,如之间讲过的高斯径向基函数(RBF)

我们只需要执行一下kmeans.transform(X)就能获得如下输出:

array([[3.08735242, 1.22420294, 1.53149796, 1.75325445, 0.27358854],
       [0.91581972, 3.89680397, 2.45199888, 4.03747055, 3.75115842],
       [2.19999762, 0.89300187, 0.73147033, 0.97437505, 1.34571428],
       ...,
       [0.71203604, 3.27763858, 2.02035925, 3.25788029, 3.45443173],
       [3.19300158, 1.03856204, 1.58743649, 1.56826014, 0.1564388 ],
       [3.06477378, 0.08763554, 1.47452112, 0.44276721, 1.03787112]])

这一下似乎又打开了新的大陆,我们好像可以通过这个将高维的数据讲到k维,没错这也是一种不错的非线性降维技术。

ok,本编简单的介绍了一下K-Means,后面文章还会继续深入K-Means的工作机制。