K-means聚类算法原理和C++实现

给定训练集,想把这些样本分成不同的子集,即聚类,,但是这是个无标签数据集,也就是说我们再聚类的时候不能利用标签信息,所以这是一个无监督学习问题。

k-means聚类算法的流程如下:

1. 随机初始化聚类中心

2. a. 对与每一个聚类中心,计算所有样本到该聚类中心的距离,然后选出距离该聚类中心最近的几个样本作为一类;

  

  这个公式的意思是,某个样本 i 属于哪一类,取决于该样本距离哪一个聚类中心最近,步骤a就是利用这个规则实现。

  b. 对上面分成的k类,根据类里面的样本,重新估计该类的中心:

    

  对于新的聚类中心,重复a,这里1{...}是一个真值判断,例如1{3=2}=0,1{3=3}=1.

  

  c. 重复a和b直至收敛

但是k-means真的能保证收敛吗?k-means的目的是选出聚类中心和每一类的样本,定义失真函数:

这个函数衡量的是某个聚类的中心与该类中所有样本距离的平方和,根据上面k-means的算法,可以看出,a 是固定聚类中心,选择该类的样本,b 是样本固定,调整聚类中心,即每次都是固定一个变量,调整另一个变量,所以k-means完全是在针对失真函数 J 坐标上升,这样,J 必然是单调递减,所以J的值必然收敛。在理论上,这种方法可能会使得k-means在一些聚类结果之间产生震荡,即几组不同的 c 和 μ 有着相同的失真函数值,但是这种情况在实际情况中很少出现。

由于失真函数是一个非凸函数,所以坐标上升不能保证该函数全局收敛,即失真函数容易陷入局部收敛。但是大多数情况下,k-means都可以产生不错的结果,如果担心陷入局部收敛,可以多运行几次k-means(采用不同的随机初始聚类中心),然后从多次结果中选出失真函数最小的聚类结果。

下面是一个简单k-means的C++代码,对{1, 2, 3, 11, 12, 13, 21, 22, 23}这9个样本值聚类:

  1 #include<iostream>
  2 #include<cmath>
  3 #include<vector>
  4 #include<ctime>
  5 using namespace std;
  6 typedef unsigned int uint;
  7
  8 struct Cluster
  9 {
 10     vector<double> centroid;
 11     vector<uint> samples;
 12 };
 13 double cal_distance(vector<double> a, vector<double> b)
 14 {
 15     uint da = a.size();
 16     uint db = b.size();
 17     if (da != db) cerr << "Dimensions of two vectors must be same!!\n";
 18     double val = 0.0;
 19     for (uint i = 0; i < da; i++)
 20     {
 21         val += pow((a[i] - b[i]), 2);
 22     }
 23     return pow(val, 0.5);
 24 }
 25 vector<Cluster> k_means(vector<vector<double> > trainX, uint k, uint maxepoches)
 26 {
 27     const uint row_num = trainX.size();
 28     const uint col_num = trainX[0].size();
 29
 30     /*初始化聚类中心*/
 31     vector<Cluster> clusters(k);
 32     uint seed = (uint)time(NULL); 33     for (uint i = 0; i < k; i++)
 34     {
 35         srand(seed);
 36         int c = rand() % row_num;
 37         clusters[i].centroid = trainX[c];
 38         seed = rand();
 39     }
 40
 41     /*多次迭代直至收敛,本次试验迭代100次*/
 42     for (uint it = 0; it < maxepoches; it++)
 43     {
 44         /*每一次重新计算样本点所属类别之前,清空原来样本点信息*/
 45         for (uint i = 0; i < k; i++)
 46         {
 47             clusters[i].samples.clear();
 48         }
 49         /*求出每个样本点距应该属于哪一个聚类*/
 50         for (uint j = 0; j < row_num; j++)
 51         {
 52             /*都初始化属于第0个聚类*/
 53             uint c = 0;
 54             double min_distance = cal_distance(trainX[j],clusters[c].centroid);
 55             for (uint i = 1; i < k; i++)
 56             {
 57                 double distance = cal_distance(trainX[j], clusters[i].centroid);
 58                 if (distance < min_distance)
 59                 {
 60                     min_distance = distance;
 61                     c = i;
 62                 }
 63             }
 64             clusters[c].samples.push_back(j);
 65         }
 66
 67         /*更新聚类中心*/
 68         for (uint i = 0; i < k; i++)
 69         {
 70             vector<double> val(col_num, 0.0);
 71             for (uint j = 0; j < clusters[i].samples.size(); j++)
 72             {
 73                 uint sample = clusters[i].samples[j];
 74                 for (uint d = 0; d < col_num; d++)
 75                 {
 76                     val[d] += trainX[sample][d];
 77                     if (j == clusters[i].samples.size() - 1)
 78                         clusters[i].centroid[d] = val[d] / clusters[i].samples.size();
 79                 }
 80             }
 81         }
 82     }
 83     return clusters;
 84 }
 85
 86 int main()
 87 {
 88     vector<vector<double> > trainX(9,vector<double>(1,0));
 89     //对9个数据{1 2 3 11 12 13 21 22 23}聚类
 90     double data = 1.0;
 91     for (uint i = 0; i < 9; i++)
 92     {
 93         trainX[i][0] = data;
 94         if ((i+1) % 3 == 0) data += 8;
 95         else data++;
 96     }
 97
 98     /*k-means聚类*/
 99     vector<Cluster> clusters_out = k_means(trainX, 3, 100);
100
101     /*输出分类结果*/
102     for (uint i = 0; i < clusters_out.size(); i++)
103     {
104         cout << "Cluster " << i << " :" << endl;
105
106         /*子类中心*/
107         cout << "\t" << "Centroid: " << "\n\t\t[ ";
108         for (uint j = 0; j < clusters_out[i].centroid.size(); j++)
109         {
110             cout << clusters_out[i].centroid[j] << " ";
111         }
112         cout << "]" << endl;
113
114         /*子类样本点*/
115         cout << "\t" << "Samples:\n";
116         for (uint k = 0; k < clusters_out[i].samples.size(); k++)
117         {
118             uint c = clusters_out[i].samples[k];
119             cout << "\t\t[ ";
120             for (uint m = 0; m < trainX[0].size(); m++)
121             {
122                 cout << trainX[c][m] << " ";
123             }
124             cout << "]\n";
125         }
126     }
127     return 0;
128 }

下面是4次运行结果:

由于数据简单,容易看出第一次和第是三次结果是理想的,而第二次和第四次都是较差出的聚类结果,即上面说的失真函数陷入了局部最优,所以在实践中多次运行,取出较好的聚类结果。

时间: 2024-08-09 02:02:13

K-means聚类算法原理和C++实现的相关文章

k-均值聚类算法;二分k均值聚类算法

根据<机器学习实战>一书第十章学习k均值聚类算法和二分k均值聚类算法,自己把代码边敲边理解了一下,修正了一些原书中代码的细微差错.目前代码有时会出现如下4种报错信息,这有待继续探究和完善. 报错信息: Warning (from warnings module): File "F:\Python2.7.6\lib\site-packages\numpy\core\_methods.py", line 55 warnings.warn("Mean of empty

机器学习实战笔记-利用K均值聚类算法对未标注数据分组

聚类是一种无监督的学习,它将相似的对象归到同一个簇中.它有点像全自动分类.聚类方法几乎可以应用于所有对象,簇内的对象越相似,聚类的效果越好 簇识别给出聚类结果的含义.假定有一些数据,现在将相似数据归到一起,簇识别会告诉我们这些簇到底都是些什么.聚类与分类的最大不同在于,分类的目标事先巳知,而聚类则不一样.因为其产生的结果与分类相同,而只是类别没有预先定义,聚类有时也被称为无监督分类(unsupervised classification ). 聚类分析试图将相似对象归人同一簇,将不相似对象归到不

K均值聚类算法

k均值聚类算法(k-means clustering algorithm)是一种迭代求解的聚类分析算法,其步骤是随机选取K个对象作为初始的聚类中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心.聚类中心以及分配给它们的对象就代表一个聚类.每分配一个样本,聚类的聚类中心会根据聚类中现有的对象被重新计算.这个过程将不断重复直到满足某个终止条件.终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小.

基于改进人工蜂群算法的K均值聚类算法(附MATLAB版源代码)

其实一直以来也没有准备在园子里发这样的文章,相对来说,算法改进放在园子里还是会稍稍显得格格不入.但是最近邮箱收到的几封邮件让我觉得有必要通过我的博客把过去做过的东西分享出去更给更多需要的人.从论文刊登后,陆陆续续收到本科生.研究生还有博士生的来信和短信微信等,表示了对论文的兴趣以及寻求算法的效果和实现细节,所以,我也就通过邮件或者短信微信来回信,但是有时候也会忘记回复. 另外一个原因也是时间久了,我对于论文以及改进的算法的记忆也越来越模糊,或者那天无意间把代码遗失在哪个角落,真的很难想象我还会全

K均值聚类算法的MATLAB实现

1.K-均值聚类法的概述 之前在参加数学建模的过程中用到过这种聚类方法,但是当时只是简单知道了在matlab中如何调用工具箱进行聚类,并不是特别清楚它的原理.最近因为在学模式识别,又重新接触了这种聚类算法,所以便仔细地研究了一下它的原理.弄懂了之后就自己手工用matlab编程实现了,最后的结果还不错,嘿嘿~~~ 简单来说,K-均值聚类就是在给定了一组样本(x1, x2, ...xn) (xi, i = 1, 2, ... n均是向量) 之后,假设要将其聚为 m(<n) 类,可以按照如下的步骤实现

k means聚类过程

k-means是一种非监督 (从下图0 当中我们可以看到训练数据并没有标签标注类别)的聚类算法 0.initial 1.select centroids randomly 2.assign points 3.update centroids 4.reassign points 5.update centroids 6.reassign points 7.iteration reference: https://www.naftaliharris.com/blog/visualizing-k-me

机器学习之感知器算法原理和Python实现

(1)感知器模型 感知器模型包含多个输入节点:X0-Xn,权重矩阵W0-Wn(其中X0和W0代表的偏置因子,一般X0=1,图中X0处应该是Xn)一个输出节点O,激活函数是sign函数. (2)感知器学习规则 输入训练样本X和初始权重向量W,将其进行向量的点乘,然后将点乘求和的结果作用于激活函数sign(),得到预测输出O,根据预测输出值和目标值之间的差距error,来调整初始化权重向量W.如此反复,直到W调整到合适的结果为止. (3)算法的原始形式 (4)Python代码实现 1 import

聚类之K均值聚类和EM算法

这篇博客整理K均值聚类的内容,包括: 1.K均值聚类的原理: 2.初始类中心的选择和类别数K的确定: 3.K均值聚类和EM算法.高斯混合模型的关系. 一.K均值聚类的原理 K均值聚类(K-means)是一种基于中心的聚类算法,通过迭代,将样本分到K个类中,使得每个样本与其所属类的中心或均值的距离之和最小. 1.定义损失函数 假设我们有一个数据集{x1, x2,..., xN},每个样本的特征维度是m维,我们的目标是将数据集划分为K个类别.假定K的值已经给定,那么第k个类别的中心定义为μk,k=1

BIRCH聚类算法原理

在K-Means聚类算法原理中,我们讲到了K-Means和Mini Batch K-Means的聚类原理.这里我们再来看看另外一种常见的聚类算法BIRCH.BIRCH算法比较适合于数据量大,类别数K也比较多的情况.它运行速度很快,只需要单遍扫描数据集就能进行聚类,当然需要用到一些技巧,下面我们就对BIRCH算法做一个总结. 1. BIRCH概述 BIRCH的全称是利用层次方法的平衡迭代规约和聚类(Balanced Iterative Reducing and Clustering Using H