k-means聚类分析
什么是聚类分析
k-means聚类分析(k-means clustering)源于信号处理中的一种向量量化方法,现在则更多地作为一种聚类分析方法流行于数据挖掘领域。来自维基百科对聚类分析的目的是:把n个点(可以是样本的一次观察或一个实例)瓜分到k个聚类中,使得每个点都属于离他最近的均值(此即聚类中心)对应的聚类,以之作为聚类的标准。
早期的聚类分析是在考古分类和昆虫分类研究中发展起来的,目的是找到隐藏于数据中客观存在的“自然小类”,“自然小类”具有类内结构相似、类键结构差异显著的特点。
基本原理
k-means这是一个迭代算法:
- 假定我们要对N个样本观测做聚类,要求聚为K类,首先选择K个点作为初始中心点;
- 接下来,按照距离初始中心点最小的原则,把所有观测分到各中心点所在的类中;
每类中有若干个观测,计算K个类中所有样本点的均值,作为第二次迭代的K个中心点; - 然后根据这个中心重复第2、3步,直到收敛(中心点不再改变或达到指定的迭代次数),聚类过程结束。
举个栗子
k=2时的迭代算法:
-
现在我们要将a中的n个绿色点聚为2类,先随机选择蓝叉和红叉分别作为初始中心点;
-
分别计算所有点到初始蓝叉和初始红叉的距离,距离蓝叉更近就涂为蓝色,距离红叉更近就涂为红色,遍历所有点,直到全部都染色完成,如图(b);
-
现在我们不管初始蓝叉和初始红叉了,对于已染色的红色点计算其红色中心,蓝色点亦然,得到第二次迭代的中心,如图(c );
-
重复第2、3步,直到收敛,聚类过程结束。
如何确定初始中心点
k-means算法的本质目标是实现同一个簇中的样本差异小,即最小化SSE。在分析中,有两个地方降低了SSE(误差项平方和):
把样本点分到最近邻的簇中,这样会降低SSE的值;重新优化聚类中心点,进一步的减小了SSE。
这样的重复迭代、不断优化,会找到局部最优解(局部最小的SSE),如果想要找到全局最优解需要找到合理的初始聚类中心。
那合理的初始中心怎么选?
方法有很多,譬如先随便选个点作为第1个初始中心C1,接下来计算所有样本点与C1的距离,距离最大的被选为下一个中心C2,直到选完K个中心。
上面这个算法叫做k-means++,是k-means的进阶版,能有效地解决初始中心的选取问题,但无法解决离群点问题。
总而言之,多设置几个不同的初始点,从中选最优,也就是具有最小SSE值的那组作为最终聚类。
k值的确定
理论上来说,K设置得越大,样本划分得就越细,每个簇的聚合程度就越高,误差平方和SSE自然就越小。但是K值如果无限大,会使集团数量过多,分析起来更为复杂,无法进行实际应用,违背了聚类分析的初衷。
确定K值的一个主流方法叫“手肘法”。
如果我们拿到的样本,客观存在J个“自然小类”,这些真实存在的小类是隐藏于数据中的。三维以下的数据我们还能画图肉眼分辨一下J的大概数目,更高维的就不能直观地看到了,我们只能从一个比较小的K,譬如K=2开始尝试,去逼近这个真实值J。
-
当K小于样本真实簇数J时,K每增大一个单位,就会大幅增加每个簇的聚合程度,这时SSE的下降幅度会很大;
-
当K接近J时,再增加K所得到的聚合程度回报会迅速变小,SSE的下降幅度也会减小;
-
随着K的继续增大,SSE的变化会趋于平缓。
代码(C语言)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define N 11
#define K 3
typedef struct
{
float x;
float y;
}Point;
int center[N]; /// 判断每个点属于哪个簇
Point point[N] = {
{
2.0, 10.0},
{
2.0, 5.0},
{
8.0, 4.0},
{
5.0, 8.0},
{
7.0, 5.0},
{
6.0, 4.0},
{
1.0, 2.0},
{
4.0, 9.0},
{
7.0,