使用java做k均值聚类,k均值聚类算法(k-means)

前言

在机器学习的各类算法中,分为两类:监督学习算法以及无监督学习算法,一个月前写的ID3决策树算法就是典型的监督学习算法。两者的区别就在于给定的样本是否已经明确具有类别。

今天,在这篇文章里,要给自己备忘一下聚类算法里面,简单但是却应用广泛的算法:k均值聚类算法。

K-means

首先,我们需要来明确一下分类与聚类两者的概念。

分类

在我看来,所谓分类,是根据给定类别的特征,把样本分到最为符合的类别里面,如垃圾分类,就是典型的分类,根据每种垃圾的特征,分为可回收、不可回收、厨余垃圾等等。这一概念里面隐含着一个特定前提,那就是类别已经事先给定了,从机器学习的角度来说,即是给出的训练样本里面包含的明确的类别。也就是说,倘若要让计算机学习分类垃圾,那么训练样本要包括每种垃圾的特征以及该垃圾具体属于哪一类。

聚类

而聚类,则是给定的样本没有事先确定类别,根据自己需要,确定类别数量,再把样本归到不同的类别里面。也就是说,同样是垃圾分类的例子,你给一堆垃圾,我可以根据可回收、不可回收分为聚类为两堆;也可以根据可回收、不可回收、厨余垃圾聚类为三堆。而其中聚类为同一堆的条件,我们可以理解为垃圾间的相似程度。

可以看到,从机器学习的角度来讲,分类是明显的监督学习的例子,聚类则是无监督学习的例子。

实现

那给我们一堆数据,如何实现聚类呢? 我们刚刚说到,判断是否聚为同一类的条件是样本间的相似性,那么,我们肯定需要该类别的标准。就拿代码来讲。

代码实现步骤如下:

初始化一定数量的二维坐标点(x,y),点数量可自定义,所有坐标点的初始类别都为0

然后根据自定义的类别数,比如说我需要把数据聚类成三类,则从上述坐标点中,随机取三个点。作为类别的中心点

迭代所有坐标点,分别与三个中心点计算平方距离(就是Δx^2+Δy^2),这一平方距离可以认为是与标准类别样本的差异度,取平方距离最小的类别作为该坐标点的类别

根据步骤三,可以得到每种分类的样本,然后,再从每种分类的样本中,重新计算该分类的类别中心。具体来说,比较简单的一种做法就是该类别所有样本的x与y,分别求和,然后除与该类别的样本数

迭代步骤3、4,直到每个类别的中心点不变或变化很小为止。

通常,把聚类中的类别叫做簇。

另外平方距离也只是计算差异度的一种比较方便的方法。

重新计算类别中心,亦即簇中心的方法也有很多,文中也仅仅是提及一种实现比较方便的方式

更详细的计算差异度的方法可以参考这篇文章:

更详细的计算类别中心的方法可以参考这篇文章:

代码的具体结果以用图像表示就是这样:

可以看到坐标点被聚类成四类

bV2GXj?w=450&h=469

下面是具体的代码:

#include

#include

#include

#include

#include

#define max 100000000;

typedef struct

{

double x;

double y;

int centroid;//坐标点的类别

} Position;

//随机生成二维数据点

//len:坐标点个数

//range:坐标点取值范围

Position *randomPosition(int len, int range)

{

srand((unsigned)time(NULL));

Position *allPos = malloc(len * sizeof(Position));

int tempX;

int tempY;

for (int i = 0; i < len; ++i)

{

allPos[i].x = (double)rand() / 2147483647 * range;

allPos[i].y = (double)rand() / 2147483647 * range;

allPos[i].centroid = 0;

}

return allPos;

}

//centroidNum:类别(簇)数量

//len:坐标点数量

void kMeans(Position *allPos, int centroidNum, int len)

{

//随机生成簇中心点

Position *centroidPos = malloc(centroidNum * sizeof(Position));

for (int i = 0; i < centroidNum; ++i)

{

centroidPos[i] = allPos[rand() % len];

}

for (int t = 0; t < 500; ++t)

{

double sum = 0;

//对每个数据点进行分类

for (int i = 0; i < len; ++i)

{

int dis = max;

int newDis;

for (int x = 0; x < centroidNum; ++x)

{

//遍历所有簇中心取最近的簇

newDis = pow((allPos[i].x - centroidPos[x].x), 2) + pow((allPos[i].y - centroidPos[x].y), 2);

if (newDis < dis)

{

dis = newDis;

allPos[i].centroid = x;

}

}

}

//重新计算簇中心

//每个簇的计数器

int *clusterS_num = malloc(centroidNum * sizeof(int));

double *clusterS_sumX = malloc(centroidNum * sizeof(double));

double *clusterS_sumY = malloc(centroidNum * sizeof(double));

for (int i = 0; i < centroidNum; ++i)

{

//初始化计数器

clusterS_num[i] = clusterS_sumX[i] = clusterS_sumY[i] = 0;

}

for (int i = 0; i < len; i++)

{

clusterS_num[allPos[i].centroid]++;

clusterS_sumX[allPos[i].centroid] += allPos[i].x;

clusterS_sumY[allPos[i].centroid] += allPos[i].y;

}

//重新计算簇中心

for (int i = 0; i < centroidNum; ++i)

{

centroidPos[i].x = clusterS_sumX[i] / clusterS_num[i];

centroidPos[i].y = clusterS_sumY[i] / clusterS_num[i];

}

for (int i = 0; i < len; ++i)

{

sum = sum + pow((allPos[i].x - centroidPos[allPos[i].centroid].x), 2) + pow((allPos[i].y - centroidPos[allPos[i].centroid].y), 2);

}

free(clusterS_num);

free(clusterS_sumX);

free(clusterS_sumY);

}

}

int main()

{

Position *allPos = randomPosition(500, 1000);

kMeans(allPos, 4, 500);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值