【机器学习】KMeans 聚类算法原理与实现

K-Means算法是无监督的聚类算法,它实现起来比较简单,聚类效果也不错,因此应用很广泛。K-Means算法有大量的变体,比如最传统的K-Means算法,在其基础上优化变体方法:包括初始化优化K-Means++, 距离计算优化elkan K-Means算法和大数据情况下的优化Mini Batch K-Means算法。

1、K-Means原理

K-Means算法的基本思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。

如果用数据表达式表示,假设簇划分为(C1,C2,...Ck),则我们的目标是最小化平方误差E:

\[E = \sum\limits_{i=1}^k\sum\limits_{x \in C_i} ||x-\mu_i||_2^2 \]

其中μi是簇Ci的均值向量,有时也称为质心,表达式为:

\[\mu_i = \frac{1}{|C_i|}\sum\limits_{x \in C_i}x \]

如果我们想直接求上式的最小值并不容易,这是一个NP难的问题,因此只能采用启发式的迭代方法。

如图,我们可以直观的理解k-means的启发式算法。

上图a表达了初始的数据集,假设k=2。在图b中,我们随机选择了两个k类所对应的类别质心,即图中的红色质心和蓝色质心,然后分别求样本中所有点到这两个质心的距离,并标记每个样本的类别为和该样本距离最小的质心的类别,如图c所示,经过计算样本和红色质心和蓝色质心的距离,我们得到了所有样本点的第一轮迭代后的类别。此时我们对我们当前标记为红色和蓝色的点分别求其新的质心,如图4所示,新的红色质心和蓝色质心的位置已经发生了变动。图e和图f重复了我们在图c和图d的过程,即将所有点的类别标记为距离最近的质心的类别并求新的质心。最终我们得到的两个类别如图f。

当然在实际K-Mean算法中,我们一般会多次运行图c和图d,才能达到最终的比较优的类别。

2、K-Means算法流程

首先我们看看K-Means算法的一些要点。
1)对于K-Means算法,首先要注意的是k值的选择,一般来说,我们会根据对数据的先验经验选择一个合适的k值,如果没有什么先验知识,则可以通过交叉验证选择一个合适的k值。

2)在确定了k的个数后,我们需要选择k个初始化的质心,就像上图b中的随机质心。由于我们是启发式方法,k个初始化的质心的位置选择对最后的聚类结果和运行时间都有很大的影响,因此需要选择合适的k个质心,最好这些质心不能太近。

下面总结下传统K-means算法的基本流程:

3、初始化优化K-Means++

在上节我们提到,k个初始化的质心的位置选择对最后的聚类结果和运行时间都有很大的影响,因此需要选择合适的k个质心。如果仅仅是完全随机的选择,有可能导致算法收敛很慢。K-Means++算法就是对K-Means随机初始化质心的方法的优化。

K-Means++的对于初始化质心的优化策略也很简单,如下:

a) 从输入的数据点集合中随机选择一个点作为第一个聚类中心μ1
b) 对于数据集中的每一个点xi,计算它与已选择的聚类中心中最近聚类中心的距离\(D(x_i) = arg\;min||x_i- \mu_r||_2^2\;\;r=1,2,...k_{selected}\)
c) 选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大
d) 重复b和c直到选择出k个聚类质心
e) 利用这k个质心来作为初始化质心去运行标准的K-Means算法

4、距离计算优化elkan K-Means

在传统的K-Means算法中,我们在每轮迭代时,要计算所有的样本点到所有的质心的距离,这样会比较的耗时。
elkan K-Means算法就是从这块入手加以改进,利用了两边之和大于等于第三边,以及两边之差小于第三边的三角形性质,来减少不必要的距离的计算。
主要有以下两种规律:

利用上边的两个规律,elkan K-Means比起传统的K-Means迭代速度有很大的提高。但是如果我们的样本的特征是稀疏的,有缺失值的话,这个方法就不使用了,此时某些距离无法计算,则不能使用该算法。

5、大样本优化Mini Batch K-Means

在统的K-Means算法中,要计算所有的样本点到所有的质心的距离。如果样本量非常大,即使加上elkan K-Means优化也依旧。因此Mini Batch K-Means应运而生。

顾名思义,Mini Batch,也就是用样本集中的一部分的样本来做传统的K-Means,这样可以避免样本量太大时的计算难题,算法收敛速度大大加快。当然此时的代价就是我们的聚类的精确度也会有一些降低。一般来说这个降低的幅度在可以接受的范围之内。

在Mini Batch K-Means中,我们会选择一个合适的批样本大小batch size,batch size个样本一般是通过无放回的随机采样得到的。

为了增加算法的准确性,我们一般会多跑几次Mini Batch K-Means算法,用得到不同的随机采样集来得到聚类簇,选择其中最优的聚类簇。

6、K-Means算法的优缺点

K-Means的主要优点有:

    1)原理比较简单,实现也是很容易,收敛速度快。

    2)聚类效果较优。

    3)算法的可解释度比较强。

    4)主要需要调参的参数仅仅是簇数k。

K-Means的主要缺点有:

    1)K值的选取不好把握

    2)对于不是凸的数据集比较难收敛

    3)如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。

    4) 采用迭代的启发式算法,得到的结果只是局部最优。

    5) 对噪音和异常点比较的敏感。

7、K-Means算法和KNN算法的联系与区别

8、K值得评估标准

不像监督学习的分类问题和回归问题,我们的无监督聚类没有样本输出,也就没有比较直接的聚类评估方法。但是我们可以从簇内的稠密程度和簇间的离散程度来评估聚类的效果。常见的方法有轮廓系数Silhouette Coefficient和CH系数Calinski-Harabasz Index。

(1) 轮廓系数

轮廓系数(Silhouette Coefficient),是聚类效果好坏的一种评价方式。

轮廓系数的计算综合考虑了内聚度和分离度两种因素。

1)计算 簇内不相似度a(i) :i向量到同簇内其他点不相似程度(距离)的平均值,体现凝聚度

2)计算 簇间不相似度b(i) :i向量到其他簇的平均不相似程度(距离)的最小值,bi =min{bi1, bi2, ..., bik},体现分离度

那么第i个对象的轮廓系数就为:

所有样本的s i 的均值称为聚类结果的轮廓系数,定义为S,是该聚类是否合理、有效的度量。
聚类结果的轮廓系数的取值在【-1,1】之间,最佳值为1,最差值为-1。接近0的值表示重叠的群集。负值通常表示样本已分配给错误的聚类,因为不同的聚类更为相似。

(2)Calinski-Harabasz

Calinski-Harabasz Index,这个计算简单直接,得到的Calinski-Harabasz分数值s越大则聚类效果越好。

Calinski-Harabasz分数值s的数学计算公式是:

\[s(k) = \frac{tr(B_k)}{tr(W_k)} \frac{m-k}{k-1} \]

其中m为训练集样本数,k为类别数。Bk为类别之间的协方差矩阵,Wk为类别内部数据的协方差矩阵。tr为矩阵的迹。

在scikit-learn中,对应得方法如下:

9、代码实现

首先我们随机创建一些二维数据作为训练集,先用传统得Kmeans算法看看聚类情况。

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets.samples_generator import make_blobs
from sklearn.cluster import KMeans
# X为样本特征,Y为样本簇类别, 共1000个样本,每个样本2个特征,共4个簇,簇中心在[-1,-1], [0,0],[1,1], [2,2], 簇方差分别为[0.4, 0.2, 0.2]
X, y = make_blobs(n_samples=1000, n_features=2, centers=[[-1,-1], [0,0], [1,1], [2,2]], cluster_std=[0.4, 0.2, 0.2, 0.2], 
                  random_state =9)
y_pred = KMeans(n_clusters=4, random_state=9).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()

用Calinski-Harabasz Index评估的k=4时候聚类分数

from sklearn import metrics
# metrics.silhouette_score(X, y_pred) # 轮廓系数
metrics.calinski_harabaz_score(X, y_pred)   #CH系数
5924.050613480169

现在我们再看看用MiniBatchKMeans的效果,我们将batch size设置为200. 由于我们的4个簇都是凸的,所以其实batch size的值只要不是非常的小,对聚类的效果影响不大。

from sklearn.cluster import MiniBatchKMeans
for index, k in enumerate((2,3,4,5)):
    plt.subplot(2,2,index+1)
    y_pred = MiniBatchKMeans(n_clusters=k, batch_size = 200, random_state=9).fit_predict(X)
    score= metrics.calinski_harabaz_score(X, y_pred)  
    plt.scatter(X[:, 0], X[:, 1], c=y_pred)
    plt.text(.99, .01, ('k=%d, score: %.2f' % (k,score)),
                 transform=plt.gca().transAxes, size=10,
                 horizontalalignment='right')
plt.show()

参考资料:
https://www.cnblogs.com/pinard/p/6164214.html
https://www.cnblogs.com/pinard/p/6169370.html
https://www.cnblogs.com/lliuye/p/9144312.html

posted @ 2021-10-19 13:54  亚北薯条  阅读(1075)  评论(0编辑  收藏  举报