K-均值聚类算法

时间:2022-05-01 20:30:49
       在此之前子丰发表的机器学习算法的博文都是监督学习算法。如果大家感兴趣的话,可以查看相关博文,如:k-近邻算法、决策树、朴素贝叶斯、Logistic回归、AdaBoost元算法、回归和树回归等。

       接下来子丰会浅谈机器学习算法的另一种类型——无监督学习算法。在无监督学习中,类似监督学习中的目标变量事先并不存在,即事先并不知道要寻找的内容。监督学习讲的是“对于输入的数据X,预测目标Y是什么”;而无监督学习讲的是“从数据X中能发现什么”。

       在k-近邻算法中,子丰提到过开发机器学习应用程序的流程主要有6个步骤:收集数据、准备数据、分析输入数据、训练算法、测试算法、使用算法,但并不是每个具体的算法必须都要执行这6个步骤,如:k-近邻算法就不要执行第4步:训练算法。对于无监督学习算法,它们都不需要执行第4步:训练算法,即无监督学习没有训练过程。

       聚类是一种无监督的学习,它把相似的对象归到同一个簇中。聚类方法几乎可以应用于所有对象,簇内的对象越相似,聚类的效果越好。我们可以发现聚类和分类有点相似,但是又很不同。它们最大的不同之处是分类的目标事先已知,而聚类则没有目标。可以说聚类产生的结果和分类相同,只是类别没有预先定义,所以聚类有时也被称为无监督分类。

       K-均值聚类算法是一种聚类算法。之所以被称为K-均值是因为它可以找到k个不同的簇,且每个簇的中心取值都是簇中所含值的均值。簇的中心被称为该簇的质心。

       K-均值聚类算法中簇的个数k由用户给定,每个簇通过其质心来描述。它的工作原理:首先,随机确定k个初始点作为质心;接着,将数据集中的每个点分配到一个簇中,即为每个点找到距离其最近的质心,并将其分配给该质心所对应的簇;然后,每个簇的质心更新为该簇所有点的平均值。再次重新分配数据集中所有的点,如果所有的点被分配的簇和之前一样,即簇的质心不会再改变,则此时的k个簇就是我们所需要的;如果某个点被分配的簇改变了,则分配完所有的点之后重新更新每个簇的质心,重复分配、更新操作直到所有簇的质心不再改变。

       下面就具体地看看K-均值聚类算法的工作流程。

       聚类是把相似的对象归到同一个簇中,这里的“相似”是通过距离来刻画的。我们知道计算距离的方式有很多种,所以必须要根据实际的情况选择合适的距离公式。最常见的距离公式就是欧式距离公式:

K-均值聚类算法

如果我们需要对坐标轴中的点进行聚类,欧式距离公式无疑是一个很好的选择。

       在算法的刚开始时需要初始化k个质心,那么要如何初始化呢?是不是随便k个点就行?答案是否定的。初始化的k个质心需要遵循一定的约束条件。质心的第j个分量对应数据的第j个特征,所有第j个分量的取值必须在数据集中第j个特征的取值的最小值和最大值之间,至于具体是多少就需要随机生成了。比如,所有数据点的第j个特征的最小值为min,最大值为max。随机生成一个0~1的数r,质心的第j个分量为min+r*(max-min)。按照此方法初始化k个质心。

       K-均值聚类算法的主体部分也不是很难。K-均值聚类算法的工作原理指出只有所有簇的质心不再改变时算法才能结束。而判断簇的质心是否改变的依据可以通过比较更新质心后样本点被分配的簇是否改变来判断。因此,在最外层需要通过一个循环来控制算法是否结束,初始化一个布尔变量change,如果某个点被分配的簇改变了就赋值True,即重复操作;如果所有点被分配的簇都不变就赋值False,即算法结束。该循环的内部还需要嵌套两层循环。第一层循环是对数据集中的点的个数的循环,因为需要把每个样本点分配到一个簇中,有多少样本点就循环多少次。第二层循环是对簇的个数的循环,因为需要通过比较才能知道某个样本点和哪个质心距离最近,有多少质心就循环多少次(质心的个数,即簇的个数k由用户给定)。每分配完一个点就必须要通过比较来判断它被分配的簇是否改变。因为第一次分配之前,所有的点都不属于任何一个簇,所以初始化所有的点在刚开始都被分配到第一个簇中。在记录样本点属于哪个簇的同时记录下该样本点与该簇的质心的距离。最终,我们可以得到k个质心,以及一个m*2的数组(m为数据集中点的个数,数组的第m行第1列记录第m个点所属的簇,第m行第2列记录第m个点距离该簇的质心的距离)。

K-均值聚类算法

不同簇的点使用不同的形状表示,每个簇的中心使用十字表示。


       可能大家会疑惑为什么要记录样本点与它所属的簇的质心的距离。尽管此时看来记录这个值并没有什么用处,但是接下来子丰会讲到另一个聚类算法二分K-均值算法。它是K-均值算法的一种变形,在二分K-均值算法中就会使用到这一数值。

       尽管K-均值聚类算法收敛,但聚类的效果较差,因为K-均值聚类算法可能会收敛到局部最小值,而非全局最小值。用于度量聚类效果好坏的指标为误差平方和(SSE),它的值就是上面提到的所有点到其所属簇的质心的距离的平方总和。SSE值越小表示数据点越接近于它们的质心,聚类效果也就越好。一种肯定能降低SSE值的方法是增加簇的个数,但这违背了聚类的目标——在保持簇数目不变的情况下提高簇的质量。

下图是一个使用K-均值聚类算法聚类后的结果:

K-均值聚类算法

不同簇的点使用不同的形状表示,每个簇的中心使用十字表示。

       我们看到上述结果的聚类效果并不是很好,那么如何进行改进呢?一种方法是将具有最大SSE值的簇划分成两个簇。为了保持簇的总数不变,将某两个簇进行合并。合并的方式有两种:合并最近的两个质心,或者合并两个使得SSE增幅最小的质心。第一种方式通过计算所有质心之间的距离,然后合并距离最近的两个簇。第二种方式需要假设合并某两个簇再计算合并后的SEE值,求得合并后与合并前的增幅,通过组合的方式,求出所有情况的增幅,然后合并增幅最小的两个簇。

       那么如果不是通过改进K-均值聚类算法的结果来优化,而是直接在聚类的过程中得到最优结果,要怎么做呢?为了克服K-均值聚类算法收敛到局部最小值的缺陷,提出了二分K-均值算法。该算法首先将所有点归为一个簇中,然后将簇一分为二,之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE的值,不断重复划分过程,直到簇的个数达到用户指定的值k。

       二分K-均值算法中会把一个簇一分为二,划分簇的方法其实使用的就是K-均值聚类算法。只需要把该簇中所有的点作为输入的数据集,把K-均值聚类算法的k值设为2,因为k值在K-均值聚类算法中是控制数据集划分后的簇的个数。

       二分K-均值算法初始化质心的个数,即簇的个数,为1;初始化该质心取值为所有点的均值。在最外层同样需要通过一个循环来控制算法的结束,循环是对簇的个数的循环,因为刚开始簇的个数只有一个,所以需要通过循环来增加簇的个数从而在达到用户指定的个数的时候结束算法。该循环的内部嵌套一个循环用来判断划分当前已有簇中的哪个簇,选择的依据就是划分后的SSE最小的那个簇。求得每个簇被划分后的SSE值,选择最小的SSE值对应的划分的簇就是我们需要的划分的簇。划分该簇后,更新质点的个数,质点的取值,以及之前提到的那个m*2的数组。

上述使得K-均值聚类算法陷入局部最小值的数据集,在二分K-均值算法的聚类结果是比较完美的。

K-均值聚类算法


       因此,二分K-均值算法的聚类效果要好于K-均值聚类算法。