博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
k均值聚类算法(k-means)
阅读量:6290 次
发布时间:2019-06-22

本文共 3713 字,大约阅读时间需要 12 分钟。

前言

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

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

K-means

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

  1. 分类

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

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

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

实现

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

代码实现步骤如下:

  1. 初始化一定数量的二维坐标点(x,y),点数量可自定义,所有坐标点的初始类别都为0
  2. 然后根据自定义的类别数,比如说我需要把数据聚类成三类,则从上述坐标点中,随机取三个点。作为类别的中心点
  3. 迭代所有坐标点,分别与三个中心点计算平方距离(就是Δx^2+Δy^2),这一平方距离可以认为是与标准类别样本的差异度,取平方距离最小的类别作为该坐标点的类别
  4. 根据步骤三,可以得到每种分类的样本,然后,再从每种分类的样本中,重新计算该分类的类别中心。具体来说,比较简单的一种做法就是该类别所有样本的x与y,分别求和,然后除与该类别的样本数
  5. 迭代步骤3、4,直到每个类别的中心点不变或变化很小为止。

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

另外平方距离也只是计算差异度的一种比较方便的方法。
重新计算类别中心,亦即簇中心的方法也有很多,文中也仅仅是提及一种实现比较方便的方式

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

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

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

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

clipboard.png

下面是具体的代码:

#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);}

转载地址:http://cgkta.baihongyu.com/

你可能感兴趣的文章
Django 文件下载功能
查看>>
走红日本 阿里云如何能够赢得海外荣耀
查看>>
磁盘空间满引起的mysql启动失败:ERROR! MySQL server PID file could not be found!
查看>>
点播转码相关常见问题及排查方式
查看>>
[arm驱动]linux设备地址映射到用户空间
查看>>
弗洛伊德算法
查看>>
【算法之美】求解两个有序数组的中位数 — leetcode 4. Median of Two Sorted Arrays
查看>>
精度 Precision
查看>>
Android——4.2 - 3G移植之路之 APN (五)
查看>>
Linux_DHCP服务搭建
查看>>
[SilverLight]DataGrid实现批量输入(like Excel)(补充)
查看>>
秋式广告杀手:广告拦截原理与杀手组织
查看>>
翻译 | 摆脱浏览器限制的JavaScript
查看>>
闲扯下午引爆乌云社区“盗窃”乌云币事件
查看>>
02@在类的头文件中尽量少引入其他头文件
查看>>
JAVA IO BIO NIO AIO
查看>>
input checkbox 复选框大小修改
查看>>
BOOT.INI文件参数
查看>>
vmstat详解
查看>>
新年第一镖
查看>>