给定训练集$\{x^{(1)},...,x^{(m)}\}$,想把这些样本分成不同的子集,即聚类,$x^{(i)}\in\mathbb{R^{n}}$,但是这是个无标签数据集,也就是说我们再聚类的时候不能利用标签信息,所以这是一个无监督学习问题。

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

1. 随机初始化聚类中心$\mu_{1},\mu_{2},...,\mu_{k}\in\mathbb{R}^{n}$

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

  $c^{(i)}:=\arg\min_{j}||x^{(i)}-\mu_{j}||^{2}$

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

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

   $\mu_{j}:=\frac{\sum_{i=1}^{m}1\{c^{(i)}=j\}x^{(j)}}{\sum_{i=1}^{m}1\{c^{(i)}=j\}}$

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

  

  c. 重复a和b直至收敛

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

$J(c,\mu)=\sum_{i=1}^{m}||x^{(i)}-\mu_{c^{(i)}}||^{2}$

这个函数衡量的是某个聚类的中心与该类中所有样本距离的平方和,根据上面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个样本值聚类:

 #include<iostream>
#include<cmath>
#include<vector>
#include<ctime>
using namespace std;
typedef unsigned int uint; struct Cluster
{
vector<double> centroid;
vector<uint> samples;
};
double cal_distance(vector<double> a, vector<double> b)
{
uint da = a.size();
uint db = b.size();
if (da != db) cerr << "Dimensions of two vectors must be same!!\n";
double val = 0.0;
for (uint i = ; i < da; i++)
{
val += pow((a[i] - b[i]), );
}
return pow(val, 0.5);
}
vector<Cluster> k_means(vector<vector<double> > trainX, uint k, uint maxepoches)
{
const uint row_num = trainX.size();
const uint col_num = trainX[].size(); /*初始化聚类中心*/
vector<Cluster> clusters(k);
uint seed = (uint)time(NULL);
33 for (uint i = ; i < k; i++)
{
srand(seed);
int c = rand() % row_num;
clusters[i].centroid = trainX[c];
seed = rand();
} /*多次迭代直至收敛,本次试验迭代100次*/
for (uint it = ; it < maxepoches; it++)
{
/*每一次重新计算样本点所属类别之前,清空原来样本点信息*/
for (uint i = ; i < k; i++)
{
clusters[i].samples.clear();
}
/*求出每个样本点距应该属于哪一个聚类*/
for (uint j = ; j < row_num; j++)
{
/*都初始化属于第0个聚类*/
uint c = ;
double min_distance = cal_distance(trainX[j],clusters[c].centroid);
for (uint i = ; i < k; i++)
{
double distance = cal_distance(trainX[j], clusters[i].centroid);
if (distance < min_distance)
{
min_distance = distance;
c = i;
}
}
clusters[c].samples.push_back(j);
} /*更新聚类中心*/
for (uint i = ; i < k; i++)
{
vector<double> val(col_num, 0.0);
for (uint j = ; j < clusters[i].samples.size(); j++)
{
uint sample = clusters[i].samples[j];
for (uint d = ; d < col_num; d++)
{
val[d] += trainX[sample][d];
if (j == clusters[i].samples.size() - )
clusters[i].centroid[d] = val[d] / clusters[i].samples.size();
}
}
}
}
return clusters;
} int main()
{
vector<vector<double> > trainX(,vector<double>(,));
//对9个数据{1 2 3 11 12 13 21 22 23}聚类
double data = 1.0;
for (uint i = ; i < ; i++)
{
trainX[i][] = data;
if ((i+) % == ) data += ;
else data++;
} /*k-means聚类*/
vector<Cluster> clusters_out = k_means(trainX, , ); /*输出分类结果*/
for (uint i = ; i < clusters_out.size(); i++)
{
cout << "Cluster " << i << " :" << endl; /*子类中心*/
cout << "\t" << "Centroid: " << "\n\t\t[ ";
for (uint j = ; j < clusters_out[i].centroid.size(); j++)
{
cout << clusters_out[i].centroid[j] << " ";
}
cout << "]" << endl; /*子类样本点*/
cout << "\t" << "Samples:\n";
for (uint k = ; k < clusters_out[i].samples.size(); k++)
{
uint c = clusters_out[i].samples[k];
cout << "\t\t[ ";
for (uint m = ; m < trainX[].size(); m++)
{
cout << trainX[c][m] << " ";
}
cout << "]\n";
}
}
return ;
}

下面是4次运行结果:

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

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

  1. k均值聚类算法原理和(TensorFlow)实现

    顾名思义,k均值聚类是一种对数据进行聚类的技术,即将数据分割成指定数量的几个类,揭示数据的内在性质及规律. 我们知道,在机器学习中,有三种不同的学习模式:监督学习.无监督学习和强化学习: 监督学习,也 ...

  2. 机器学习实战---K均值聚类算法

    一:一般K均值聚类算法实现 (一)导入数据 import numpy as np import matplotlib.pyplot as plt def loadDataSet(filename): ...

  3. K均值聚类算法

    k均值聚类算法(k-means clustering algorithm)是一种迭代求解的聚类分析算法,其步骤是随机选取K个对象作为初始的聚类中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个 ...

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

    其实一直以来也没有准备在园子里发这样的文章,相对来说,算法改进放在园子里还是会稍稍显得格格不入.但是最近邮箱收到的几封邮件让我觉得有必要通过我的博客把过去做过的东西分享出去更给更多需要的人.从论文刊登 ...

  5. K均值聚类算法的MATLAB实现

    1.K-均值聚类法的概述    之前在参加数学建模的过程中用到过这种聚类方法,但是当时只是简单知道了在matlab中如何调用工具箱进行聚类,并不是特别清楚它的原理.最近因为在学模式识别,又重新接触了这 ...

  6. URL短网址生成算法原理和php实现案例

    短网址(Short URL),顾名思义就是在形式上比较短的网址. 短链接的好处:1.内容需要:2.用户友好:3.便于管理为什么要这样做的,原因我想有这样几点:微博限制字数为140字一条,那么如果我们需 ...

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

    (1)感知器模型 感知器模型包含多个输入节点:X0-Xn,权重矩阵W0-Wn(其中X0和W0代表的偏置因子,一般X0=1,图中X0处应该是Xn)一个输出节点O,激活函数是sign函数. (2)感知器学 ...

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

    这篇博客整理K均值聚类的内容,包括: 1.K均值聚类的原理: 2.初始类中心的选择和类别数K的确定: 3.K均值聚类和EM算法.高斯混合模型的关系. 一.K均值聚类的原理 K均值聚类(K-means) ...

  9. BIRCH聚类算法原理

    在K-Means聚类算法原理中,我们讲到了K-Means和Mini Batch K-Means的聚类原理.这里我们再来看看另外一种常见的聚类算法BIRCH.BIRCH算法比较适合于数据量大,类别数K也 ...

随机推荐

  1. ARC062 - F. Painting Graphs with AtCoDeer (Polya+点双联通分量)

    似乎好久都没写博客了....赶快来补一篇 题意 给你一个 \(n\) 个点 , 没有重边和自环的图 . 有 \(m\) 条边 , 每条边可以染 \(1 \to k\) 中的一种颜色 . 对于任意一个简 ...

  2. 51nod1236 序列求和 V3 【数学】

    题目链接 51nod1236 题解 用特征方程求得斐波那契通项: \[f(n) = \frac{(\frac{1 + \sqrt{5}}{2})^{n} - (\frac{1 - \sqrt{5}}{ ...

  3. 洛谷 P1078 文化之旅 解题报告

    P1078 文化之旅 题目描述 有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家).不同的国家可能 ...

  4. luogu1970 花匠(dp)

    设f1[i]表示以1..i中某个合法序列的长度,而且最后一位是较大的 f2[i]表示以1..i中某个合法序列的长度,而且最后一位是较小的 那么就有$f1[i]=max\{f2[j]+1\},(j< ...

  5. 1: Myeclipse10 优化设置

    一.myeclipse字体设置 Window->Preferences->General->Appearance->Colors and Fonts 在右侧找到”Aa Test ...

  6. JAVA Swing使用JFreeChart实现折线图绘制

    效果如下: 实现步骤: 1.导入JAR包 jfreechart官网下载的zip文件中包含这两个jar包 2.代码编写 import org.jfree.chart.ChartFactory; impo ...

  7. 牛客练习赛29 F 算式子

    https://www.nowcoder.com/acm/contest/211/F 经典题. 1.分区间 2.向下取整的值变化 & 合并相同值 #include <bits/stdc+ ...

  8. docker存储与网络

    目录 Docker存储 挂载主机目录 创建一个数据卷 挂载一个宿主机目录作为数据卷 数据卷容器 创建一个数据卷容器 利用数据卷容器迁移数据 删除数据盘 Docker网络 简介 bridge网络 bri ...

  9. 配置DHCP服务

    配置DHCP服务 一.DHCP的简介 1.DHCP是Dynamic Host Configuration Protocol(动态主机配置协议)的缩写: 2.DHCP是从BOOTP(Bootstrap ...

  10. Spring触发器触发2次问题【转】

    触发2遍任务对大多数程序而言,都会造成 数据重复 和 资源浪费. 我们在写spring触发器的timetrigger.xml配置文件的时候, 千万不要在Spring的总配置文件applicationC ...