计算智能——K-均值算法
K-均值(K-means算法)算法学习笔记
——慕课-商务数据分析(复旦大学 -赵卫东/董亮) 学习笔记
文章目录
- K-均值(K-means算法)算法学习笔记
- 一.聚类分析简介
- 二.基于划分的聚类方法
- 四.K-均值(K-means)算法过程
- 五.K值的选取
- 六.K-均值(K-means)具体实现
- 七.K-均值(K-means)算法的优缺点
一.聚类分析简介
聚类分析是一种典型的无监督学习,用于对未知类别的样本进行划分,将它们按照一定的规则划分成若干个类族,把相似(距高相近)的样本聚在同一个类簇中,把不相似的样本分为不同的类簇,从而揭示样本之间内在的性质以及相互之间的联系规律。
聚类算法在银行,零售,保险,医学,军事等诸多领域有着广泛的应用。
二.基于划分的聚类方法
基于划分的方法是简单、常用的一种聚类方法。通过将对象划分为互斥的簇进行聚类,每个对象仅属于一个簇。划分结果旨在使簇之间的相似性低,簇内的相似度高,基于划分的常用算法有K均值(K-means算法)、k-medoids、k-prototype等。
三.K-均值(K-means)算法简介
K-均值(K-means)聚类是基于划分的聚类算法,是一种迭代算法。计算样本点与类簇质心的距离,与类簇质心相近的样本点划分为同一类簇。K-均值通过样本点间的距离来衡量他们之间的相似度,两个样本距离越远,则相似度越低,否则相速度越高。K-均值(K-means)算法不适用于非凸面形状(非球形)的数据集,例如图中的例子,聚类结果与初始目标有非常大的差别。
K-均值算法聚类步骤如下:
1.首先选取K个类簇(K需要用户进行指定)的质心,通常是随机选取。
2.对剩余的每个样本点,计算它们到各个质心的欧式距离,并将其归入到相互间距离最小的质心所在的簇。计算各个新簇的质心。
3.在所有样本点都划分完毕后,根据划分情况重新计算各个簇的质心所在的位置,然后迭代计算各个样本点到各簇质心的距离,对所有样本点进行重新划分。
4.重复第2和第3步,直到迭代计算后,所有样本点的划分情况保持不变,此时说明k-均值算法已经得到了最优解,将运行结果返回。
四.K-均值(K-means)算法过程
K-means工作流程:
K-means算法计算步骤基本上可以概括为两个部分:
1.计算每一个对象到类簇中心的距离;
2.根据类簇内的对象计算新的簇类中心。
创建k个点作为起始质心(多是随机选择)
repeat
对数据集中的每个数据点
repeat
对每个质心
计算质心与数据点之间的距离
将数据点分配到距离其最近的类(或簇)
对每一个类(簇),计算类(簇)中所有点的均值并将其作为质心。
K-means开发流程:
收集数据:使用任意方法
准备数据:需要数值型数据类计算距离, 也可以将标称型数据映射为二值型数据再用于距离计算。
分析数据:使用任意方法
训练算法:无监督学习不需要训练步骤
测试算法:应用聚类算法、观察结果.可以使用量化的误差指标如误差平方和(后面会介绍)来评价算法的结果.
使用算法:可以用于所希望的任何应用.通常情况下, 簇质心可以代表整个簇的数据来做出决策.
五.K值的选取
1.与层次类聚算法结合,先通过层次类聚算法得出大致的类聚数目,并且获得一个初始聚类结果,然后再通过K-均值法改进聚类结果。
2.基于系统演化的方法,将数据集视为伪热力学系统,在分裂和合并的过程中,将系统演化到稳定平衡状态而确定K值。
另外在网络上看到了手肘法和轮廓系数法
https://blog.csdn.net/qq_15738501/article/details/79036255
六.K-均值(K-means)具体实现
慕课上使用python语言实现的我这里使用c++
这里用C++代码完成iris数据测试:
测试数据:iris测试数据
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <math.h>
#include <stdlib.h>
#include <time.h>using namespace std;#define K 3 //簇的数目
#define DIM_NUM 5
#define DATA_NUM 140//存放元组的属性信息
typedef vector<double> Tuple;//存储一条数据记录 //输入两个元组,计算两个元组间的欧几里距离
double getDistXY(const Tuple& t1, const Tuple& t2)
{double sum = 0;for (int i = 1; i <= DIM_NUM; ++i){sum += (t1[i] - t2[i]) * (t1[i] - t2[i]);}return sqrt(sum);
}//输入k个质心和1个元组,根据质心,决定当前元组属于哪个簇
int clusterOfTuple(const 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) //选择距离最小的质心,用label标示{ 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 (unsigned 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(DIM_NUM + 1, 0); //初始化dimNum + 1个0填充数组//第一个位置存放记录编号,第2到dimNum+1个位置存放实际元素 for (int i = 0; i < num; i++) {for (int j = 1; j <= DIM_NUM; ++j){t[j] += cluster[i][j];}}for (int j = 1; j <= DIM_NUM; ++j){t[j] /= num;}return t; //返回簇集中所有元组平均值作为质心//cout<<"sum:"<<sum<<endl;
}void print_Means(const vector<Tuple> clusters[])
{for (int lable = 0; lable < K; lable++){cout << clusters[lable].size() << " " ;Tuple temp = getMeans(clusters[lable]);cout << "(";for (int j = 0; j <= DIM_NUM; ++j){cout << temp[j] << "\t";}cout << ")\n";}
}void print(const vector<Tuple> clusters[])
{for (int lable = 0; lable<K; lable++){cout << "第" << lable + 1 << "个簇:" << endl;vector<Tuple> t = clusters[lable];for (unsigned int i = 0; i<t.size(); i++){cout << i + 1 << ".(";for (int j = 0; j <= DIM_NUM; ++j){cout << t[i][j] << ", ";}cout << ")\n";}}}vector<Tuple>* 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 <= DIM_NUM; ++j){means[i].push_back(tuples[iToSelect][j]); //初始化个质心}++i;}}int lable = 0;//根据默认的质心,将输入的tuples分配给各个簇 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;//当新旧函数值相差不到1即准则函数值不发生明显变化时,算法终止while (abs(newVar - oldVar) >= 0.01) {cout << "第 " << ++t << " 次迭代开始:" << endl;for (i = 0; i < K; i++) //更新每个簇的质心 {means[i] = getMeans(clusters[i]);}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]);}//更新误差判断值 oldVar = newVar;newVar = getVar(clusters, means);cout << "此次迭代之后的整体误差平方和为:" << newVar << endl;}cout << "The result is:\n";print(clusters);print_Means(clusters);return clusters;
}int main()
{char fname[256] = "iris_train.txt";ifstream infile(fname);if (!infile){cout << "不能打开输入的文件" << fname << endl;return 0;}vector<Tuple> tuples;//从文件流中读入数据 for (int i = 0; i<DATA_NUM && !infile.eof(); ++i){string str;getline(infile, str);istringstream istr(str);Tuple tuple(DIM_NUM + 1, 0);//第一个位置存放记录编号,第2到dimNum+1个位置存放实际元素 tuple[0] = i + 1;for (int j = 1; j <= DIM_NUM; ++j){istr >> tuple[j];}tuples.push_back(tuple);}cout << endl << "开始聚类" << endl;KMeans(tuples);system("pause");return 0;
}
运行截图:
结论:
可以多运行几次以上代码,可以看出由于初始点事随机选取的每次运行得到的结果有所差异。这也是基本K-means算法的一个缺点,随着众多改进算法的提出K-means算法的这一问题也得到改善,深入了解的朋友可以查阅相关论文。
想要测试数据的朋友也可以私我。
七.K-均值(K-means)算法的优缺点
优点:
1.聚类效果较优。
2.原理简单,实现容易,收敛速度快。
3.需要调整的参数较少,通常只需要调整簇数K。
缺点:
1.在 K-means 算法中 k 需要事先确定,这个 k 值的选定有时候是比较难确定。
2.在 K-means 算法中,首先需要初始k个聚类中心,然后以此来确定一个初始划分,然后对初始划分进行优化。这个初始聚类中心的选择对聚类结果有较大的影响,一旦初始值选择的不好,可能无法得到有效的聚类结果。多设置一些不同的初值,对比最后的运算结果,一直到结果趋于稳定结束。
3.该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。
4.对离群点很敏感。
5.从数据表示角度来说,在 K-means 中,我们用单个点来对 cluster 进行建模,这实际上是一种最简化的数据建模形式。这种用点来对 cluster 进行建模实际上就已经假设了各 cluster的数据是呈圆形(或者高维球形)或者方形等分布的。不能发现非凸形状的簇。但在实际生活中,很少能有这种情况。所以在 GMM 中,使用了一种更加一般的数据表示,也就是高斯分布。
6.从数据先验的角度来说,在 K-means 中,我们假设各个 cluster 的先验概率是一样的,但是各个 cluster 的数据量可能是不均匀的。举个例子,cluster A 中包含了10000个样本,cluster B 中只包含了100个。那么对于一个新的样本,在不考虑其与A cluster、 B cluster 相似度的情况,其属于 cluster A 的概率肯定是要大于 cluster B的。
7.在 K-means 中,通常采用欧氏距离来衡量样本与各个 cluster 的相似度。这种距离实际上假设了数据的各个维度对于相似度的衡量作用是一样的。但在 GMM 中,相似度的衡量使用的是后验概率 αcG(x|μc,∑c)αcG(x|μc,∑c) ,通过引入协方差矩阵,我们就可以对各维度数据的不同重要性进行建模。
8.在 K-means 中,各个样本点只属于与其相似度最高的那个 cluster ,这实际上是一种 hard clustering 。
针对K-means算法的缺点,很多前辈提出了一些改进的算法。例如 K-modes 算法,实现对离散数据的快速聚类,保留了K-means算法的效率同时将K-means的应用范围扩大到离散数据。还有K-Prototype算法,可以对离散与数值属性两种混合的数据进行聚类,在K-prototype中定义了一个对数值与离散属性都计算的相异性度量标准。当然还有其它的一些算法,这里我 就不一一列举了。
K-means 与 GMM 更像是一种 top-down 的思想,它们首先要解决的问题是,确定 cluster 数量,也就是 k 的取值。在确定了 k 后,再来进行数据的聚类。而 hierarchical clustering 则是一种 bottom-up 的形式,先有数据,然后通过不断选取最相似的数据进行聚类。
感谢浏览,小白一枚,请多包涵。
参考网站:
[1]: https://www.cnblogs.com/baiboy/p/pybnc6.html
[2]: https://blog.csdn.net/u013719780/article/details/78413770
计算智能——K-均值算法相关推荐
- 一句话总结K均值算法
一句话总结K均值算法 核心:把样本分配到离它最近的类中心所属的类,类中心由属于这个类的所有样本确定. k均值算法是一种无监督的聚类算法.算法将每个样本分配到离它最近的那个类中心所代表的类,而类中心的确 ...
- spark Bisecting k-means(二分K均值算法)
Bisecting k-means(二分K均值算法) 二分k均值(bisecting k-means)是一种层次聚类方法,算法的主要思想是:首先将所有点作为一个簇,然后将该簇一分为二.之后选择能最大程 ...
- k均值算法原理详细讲解以及matlab代码实现
有研究生物电信号处理和机器学习的欢迎加我qq429500506共同交流学习进步. 最近更新文章的频率太低了,主要原因是不想为了发文章而发文章,想潜心研究,写好文章,顺便想说一句开源万岁,最近一个月虽然 ...
- k-modes算法mysql_第十一章 K-Means(K均值)算法模型实现(中)
python2 代码实现: from numpy import * import numpy def loadDataSet(fileName): #general function to parse ...
- k均值算法 二分k均值算法_如何获得K均值算法面试问题
k均值算法 二分k均值算法 数据科学访谈 (Data Science Interviews) KMeans is one of the most common and important cluste ...
- k均值算法 二分k均值算法_使用K均值对加勒比珊瑚礁进行分类
k均值算法 二分k均值算法 Have you ever seen a Caribbean reef? Well if you haven't, prepare yourself. 您见过加勒比礁吗? ...
- python实现k均值算法_python实现kMeans算法
聚类是一种无监督的学习,将相似的对象放到同一簇中,有点像是全自动分类,簇内的对象越相似,簇间的对象差别越大,则聚类效果越好. 1.k均值聚类算法 k均值聚类将数据分为k个簇,每个簇通过其质心,即簇中所 ...
- k均值算法的优化目标和随机初始化
这里我们来学习k均值算法的优化目标(代价函数): 优化目标的值等于每一个训练集数据到该所属的聚类中心距离的平方的平均值 其实在我们进行之前所循环进行的聚类分簇和移动聚类中心的操作,都是在不断的最小化J ...
- LBG算法、Lloyd算法和K均值算法
---------------------------------LBG算法.Lloyd算法和K-Means算法--------------------------------- LBG算法是一种矢量 ...
- K均值算法(继续优化中)
K均值算法代码 #include<iostream> #include<math.h> using namespace std; //distance函数通过欧几里得计算两点间 ...
最新文章
- 五.激光SLAM框架学习之A-LOAM框架---项目工程代码介绍---3.laserOdometry.cpp--前端雷达里程计和位姿粗估计
- 非结构化数据上下文中的GraphQL
- 创翼软件linux版本,创翼pc版下载-创翼电脑版下载v5.2.22.5226-软件爱好者
- post url 后面跟参数_都2019年了,还问GET和POST的区别
- Java实现读取文件夹下(包括子目录)所有文件的文件名
- 无需代码,图文并茂详解EEGLAB中的ERPs数据分析方法
- java udp socket编程_JavaSocket编程UDP
- Windows XP优化指南
- 正大国际期货:水泥价格罕见急跌!两个月跌超100元/吨 会不会影响房价?
- pg_bigm 处理中间模糊匹配 like ‘%xxoo%‘
- 分数排名 mysql_mysql 分数排名
- mdf文件和ldf文件ndf是什么,怎么用?如何给SQL server添加数据文件?分离和附加数据库的操作
- BI规划落地的正确姿势,五步教你搭建企业级BI项目
- 项目上线后,接口报错:SELECT command denied to user ‘用户名’ @ localhost‘ for table ‘ ‘
- 残差(residual)
- 强大的多媒体播放器:射手影音播放器SPlayer for Mac
- java hgetall_详解Java使用Pipeline对Redis批量读写(hmsethgetall)
- 【Linux】Linux的共享内存
- 鸿蒙系统用什么用,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可
!【手机吧】_百度贴吧...
- 搜狗 workflow异步调度框架--1.基本介绍篇