基本Kmeans算法介绍及其实现
1.基本Kmeans算法[1]
选择K个点作为初始质心
repeat将每个点指派到最近的质心,形成K个簇重新计算每个簇的质心
until 簇不发生变化或达到最大迭代次数
时间复杂度:O(tKmn),其中,t为迭代次数,K为簇的数目,m为记录数,n为维数
空间复杂度:O((m+K)n),其中,K为簇的数目,m为记录数,n为维数
2.注意问题
(1)K如何确定
kmenas算法首先选择K个初始质心,其中K是用户指定的参数,即所期望的簇的个数。这样做的前提是我们已经知道数据集中包含多少个簇,但很多情况下,我们并不知道数据的分布情况,实际上聚类就是我们发现数据分布的一种手段,这就陷入了鸡和蛋的矛盾。如何有效的确定K值,这里大致提供几种方法,若各位有更好的方法,欢迎探讨。
1.与层次聚类结合[2]
经常会产生较好的聚类结果的一个有趣策略是,首先采用层次凝聚算法决定结果粗的数目,并找到一个初始聚类,然后用迭代重定位来改进该聚类。
2.稳定性方法[3]
稳定性方法对一个数据集进行2次重采样产生2个数据子集,再用相同的聚类算法对2个数据子集进行聚类,产生2个具有k个聚类的聚类结果,计算2个聚类结果的相似度的分布情况。2个聚类结果具有高的相似度说明k个聚类反映了稳定的聚类结构,其相似度可以用来估计聚类个数。采用次方法试探多个k,找到合适的k值。
3.系统演化方法[3]
系统演化方法将一个数据集视为伪热力学系统,当数据集被划分为K个聚类时称系统处于状态K。系统由初始状态K=1出发,经过分裂过程和合并过程,系统将演化到它的稳定平衡状态Ki,其所对应的聚类结构决定了最优类数Ki。系统演化方法能提供关于所有聚类之间的相对边界距离或可分程度,它适用于明显分离的聚类结构和轻微重叠的聚类结构。
4.使用canopy算法进行初始划分[4]
基于Canopy Method的聚类算法将聚类过程分为两个阶段
Stage1、聚类最耗费计算的地方是计算对象相似性的时候,Canopy Method在第一阶段选择简单、计算代价较低的方法计算对象相似性,将相似的对象放在一个子集中,这个子集被叫做Canopy ,通过一系列计算得到若干Canopy,Canopy之间可以是重叠的,但不会存在某个对象不属于任何Canopy的情况,可以把这一阶段看做数据预处理;
Stage2、在各个Canopy 内使用传统的聚类方法(如K-means),不属于同一Canopy 的对象之间不进行相似性计算。
从这个方法起码可以看出两点好处:首先,Canopy 不要太大且Canopy 之间重叠的不要太多的话会大大减少后续需要计算相似性的对象的个数;其次,类似于K-means这样的聚类方法是需要人为指出K的值的,通过Stage1得到的Canopy 个数完全可以作为这个K值,一定程度上减少了选择K的盲目性。
其他方法如贝叶斯信息准则方法(BIC)可参看文献[5]。
(2)初始质心的选取
(3)距离的度量
(4)质心的计算
(5)算法停止条件
(6)空聚类的处理
3.适用范围及缺陷
4.实现
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <math.h>
#include <stdlib.h>
#define k 3//簇的数目
using namespace std;
//存放元组的属性信息
typedef vector<double> Tuple;//存储每条数据记录int dataNum;//数据集中数据记录数目
int dimNum;//每条记录的维数//计算两个元组间的欧几里距离
double getDistXY(const Tuple& t1, const Tuple& t2)
{double sum = 0;for(int i=1; i<=dimNum; ++i){sum += (t1[i]-t2[i]) * (t1[i]-t2[i]);}return sqrt(sum);
}//根据质心,决定当前元组属于哪个簇
int clusterOfTuple(Tuple means[],const Tuple& tuple){double dist=getDistXY(means[0],tuple);double tmp;int label=0;//标示属于哪一个簇for(int i=1;i<k;i++){tmp=getDistXY(means[i],tuple);if(tmp<dist) {dist=tmp;label=i;}}return label;
}
//获得给定簇集的平方误差
double getVar(vector<Tuple> clusters[],Tuple means[]){double var = 0;for (int i = 0; i < k; i++){vector<Tuple> t = clusters[i];for (int j = 0; j< t.size(); j++){var += getDistXY(t[j],means[i]);}}//cout<<"sum:"<<sum<<endl;return var;}
//获得当前簇的均值(质心)
Tuple getMeans(const vector<Tuple>& cluster){int num = cluster.size();Tuple t(dimNum+1, 0);for (int i = 0; i < num; i++){for(int j=1; j<=dimNum; ++j){t[j] += cluster[i][j];}}for(int j=1; j<=dimNum; ++j)t[j] /= num;return t;//cout<<"sum:"<<sum<<endl;
}void print(const vector<Tuple> clusters[])
{for(int lable=0; lable<k; lable++){cout<<"第"<<lable+1<<"个簇:"<<endl;vector<Tuple> t = clusters[lable];for(int i=0; i<t.size(); i++){cout<<i+1<<".(";for(int j=0; j<=dimNum; ++j){cout<<t[i][j]<<", ";}cout<<")\n";}}
}void KMeans(vector<Tuple>& tuples){vector<Tuple> clusters[k];//k个簇Tuple means[k];//k个中心点int i=0;//一开始随机选取k条记录的值作为k个簇的质心(均值)srand((unsigned int)time(NULL));for(i=0;i<k;){int iToSelect = rand()%tuples.size();if(means[iToSelect].size() == 0){for(int j=0; j<=dimNum; ++j){means[i].push_back(tuples[iToSelect][j]);}++i;}}int lable=0;//根据默认的质心给簇赋值for(i=0;i!=tuples.size();++i){lable=clusterOfTuple(means,tuples[i]);clusters[lable].push_back(tuples[i]);}double oldVar=-1;double newVar=getVar(clusters,means);cout<<"初始的的整体误差平方和为:"<<newVar<<endl; int t = 0;while(abs(newVar - oldVar) >= 1) //当新旧函数值相差不到1即准则函数值不发生明显变化时,算法终止{cout<<"第 "<<++t<<" 次迭代开始:"<<endl;for (i = 0; i < k; i++) //更新每个簇的中心点{means[i] = getMeans(clusters[i]);}oldVar = newVar;newVar = getVar(clusters,means); //计算新的准则函数值for (i = 0; i < k; i++) //清空每个簇{clusters[i].clear();}//根据新的质心获得新的簇for(i=0; i!=tuples.size(); ++i){lable=clusterOfTuple(means,tuples[i]);clusters[lable].push_back(tuples[i]);}cout<<"此次迭代之后的整体误差平方和为:"<<newVar<<endl; }cout<<"The result is:\n";print(clusters);
}
int main(){char fname[256];cout<<"请输入存放数据的文件名: ";cin>>fname;cout<<endl<<" 请依次输入: 维数 样本数目"<<endl;cout<<endl<<" 维数dimNum: ";cin>>dimNum;cout<<endl<<" 样本数目dataNum: ";cin>>dataNum;ifstream infile(fname);if(!infile){cout<<"不能打开输入的文件"<<fname<<endl;return 0;}vector<Tuple> tuples;//从文件流中读入数据for(int i=0; i<dataNum && !infile.eof(); ++i){string str;getline(infile, str);istringstream istr(str);Tuple tuple(dimNum+1, 0);//第一个位置存放记录编号,第2到dimNum+1个位置存放实际元素tuple[0] = i+1;for(int j=1; j<=dimNum; ++j){istr>>tuple[j];}tuples.push_back(tuple);}cout<<endl<<"开始聚类"<<endl;KMeans(tuples);return 0;
}
这里是随机选取的初始质心,以鸢尾花的数据集为例,原数据集中1-50为一个簇,51-100为第二个簇,101到150为第三个簇:
第二次运行结果 SSE=98.1404
。。。
第五次运行结果 SSE=123.397
由于初始质心是随机选取的,前两次还算正常,运行到第五次时,第一个簇基本包括了后51-150个记录,第二个簇和第三个簇包含了第1-50个记录,可能的原因就是随机选择初始点时,有两个初始点都选在了1-50个记录中。
参考:
[1]Pang-Ning Tan等著,《数据挖掘导论》,2011
[2]Jiawei Han等著,《数据挖掘概念与技术》,2008
[3]聚类分析中类数估计方法的实验比较
[4]http://www.cnblogs.com/vivounicorn/archive/2011/09/23/2186483.html
[5]一种基于贝叶斯信息准则的k均值聚类方法
[6]http://www.zhihu.com/question/19640394?nr=1¬i_id=8736954
基本Kmeans算法介绍及其实现相关推荐
- K-means算法介绍
K-means算法介绍 K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大.该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独 ...
- 【阿旭机器学习实战】【16】KMeans算法介绍及实战:利用KMeans进行足球队分类
[阿旭机器学习实战]系列文章主要介绍机器学习的各种算法模型及其实战案例,欢迎点赞,关注共同学习交流. 本文对机器学习中的KMeans算法原理进行了简单介绍,并且通过实战案例:足球队分类详细介绍了其使用 ...
- Kmeans算法介绍及其实现
KMeans算法的基本思想是初始随机给定K个簇中心,按照最邻近原则把待分类样本点分到各个簇.然后按平均法重新计算各个簇的质心,从而确定新的簇心.一直迭代,直到簇心的移动距离小于某个给定的值. K- ...
- 【机器学习】k-means算法介绍及简单实现
文章目录 前言 一.什么是k-means算法 二.k-means的算法过程 三.k-means算法的优缺点 3.1 优点 3.2 缺点 四.k-means算法简单案例 4.1 手写实现k-means算 ...
- 【OpenCV学习笔记 020】K-Means聚类算法介绍及实现
一.K-Means算法介绍 在数据挖掘中,K-Means算法是一种cluster analysis的算法,其主要是来计算数据聚集的算法,主要通过不断地取离种子点最近均值的算法. 问题 K-Means算 ...
- 机器学习算法-k-means聚类算法介绍
聚类算法介绍: 在"无监督学习"(unsupervised learning)中,训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律,为进一步的 ...
- c均值聚类matlab程序_聚类算法之kmeans算法
一.k-means算法介绍 k-means最早是由James MacQueen在1967年提出的,这一观点能够追溯到1957年Hugo Steinhaus所提出的想法.1957年,斯图亚特最先提出这一 ...
- K-means算法的Java实现 聚类分析681个三国武将(1)
k-means算法接受输入量 k :然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高:而不同聚类中的对象相似度较小.聚类相似度是利用各聚类中对象的均值所获得一个& ...
- MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别
应一个小伙伴的要求介绍了一下K均值聚类算法.本人也不是很专业,这是之前自学的,如果有错,大家可以提出来,共同进步嘛. 文章目录 一.k-means算法(k-均值) 1.k-means算法介绍 2.k- ...
最新文章
- 打造属于自己的underscore系列 ( 一 )
- ecshop入门第一步,替换ecshop模板的显示图片
- SVN安装部署方案(一)
- 队列的C语言实现(通过内核链表)
- 从exe4j生成的exe中抽取jar文件
- 使用SAP云平台portal service之前,需要做好哪些准备
- 外观模式(Façade Pattern)
- 蓝桥杯 左baby右兄弟
- ubuntu 下groovy 安装配置
- Git -- 分支管理简介
- 超市管理系统软件测试用例图,超市管理系统用户管理模块测试用例集.doc
- 机器视觉技术的应用领域
- acu风格是什么意思_acu 是什么意思,是一个品牌吗???
- 计算机安全模式无法启动,电脑无法启动,电脑安全模式进不去解决方法大全?...
- SVG中的text文字高度ascentbaselinedescent(资料及测试)
- php微信支付分取消订单,PHP实现微信支付和退款
- BJTU 懒羊羊吃青草
- 缺少配色灵感,整理超全超赞的配色工具帮你
- 动态规划(Dynamic Programing)背包——01背包
- 一个主机可以接几个php,一台主机如何连接两个显示器
热门文章
- Linux内核下载(包括历史版本)
- 总谐波失真加噪声(THD+N)
- 最详细的Java入门完整教程,学Java先收藏了
- 计算机usb端口没反应,电脑usb接口没反应怎么解决
- centos 6.5升级内核到3.10
- jspdf-html2canvas 自动分页 网页导出pdf 自动根据dom子节点的高度进行分页,避免dom的内容在分页的时候被截断
- NoSql的四大类型
- 正则 环视 oracle,环视正则 - travler的个人空间 - OSCHINA - 中文开源技术交流社区...
- 冰箱中的爱马仕没跑了,用COLMO来开启你的美好新春吧
- Java爬虫之jsoup的使用