无监督学习算法K-means算法总结与c++编程实现
来源:互联网 发布:罗其胜网络班教程 编辑:程序博客网 时间:2024/06/08 14:12
Figure 1: K-means algorithm. Training examples are shown as dots, and cluster centroids are shown as crosses. (a) Original dataset. (b) Random initial cluster centroids (in this instance, not chosen to be equal to twotrainingexamples). (c-f) Illustration of running two iterations of k-means. In each iteration, we assign each training example to the closest cluster centroid(shown by “painting” the training examples the same color as the cluster centroid to which is assigned); then we move each cluster centroid to the mean of the points assigned to it. (Best viewed in color.) Images courtesy Michael Jordan.
注意:
问题1:K值选取问题
K的选取通常是我们的目标,也就是说,我们要将这队数据分为几类。因此,是相对明确的。
问题2:初始值的选取问题
初始值的选取对于迭代的结果有较大的影响,选取不当,会出现所有点都归为一类的情况。一个通常的解决方案是:随机选取多组初始值进行分类,选取损失函数最小的分类结果。
编程举例:
将如下三维空间的点进行k-means分类:
[input.txt]
1.0 , 5.7 , 2.8
4.5 , 5.2 , -0.3
-0.9 , 8.1 , 1.4
0.5 , 6.6 , 2.3
3.5 , 4.7 , 0.2
4.7 , 5.9 , -1
5.1 , 8.2 , 0.9
2.1 , 7.4 , 3.0
0.6 , 6.5 , 3.8
在三维空间的图及k-means分类的结果
K-means算法的c++实现代码:
#include <iostream>#include <fstream>#include <cmath>#include <vector>#define dim 3//输入特征x的维数#define K 2//分类的个数#define MAX_NUM 100//读取输入数据的最大数目using namespace std;//定义类,封装输入向量x,及它要分到哪个类别class InputFeature{public:double x[dim];//保存输入数据的向量坐标int cluster;//标志分类的哪一类(0,1,...,K)InputFeature(double x[dim])//构造函数{ memcpy(this->x,x,sizeof(double)*dim); cluster=-1;}//输入函数void print(){cout<<"cluster="<<cluster<<" x=[";for (int i=0;i<dim;i++){cout<<x[i]<<" ";}cout<<"]"<<endl;}//计算该特征向量与输入向量的2范数(欧式距离)double distanceOf(double u[dim]){ double distance=0;for (int i=0;i<dim;i++){distance+=(u[i]-x[i])*(u[i]-x[i]);}distance=sqrt(distance);return distance;}};vector<InputFeature> InputVector;//保存输入特征向量void inputData()//从文件中读取数据{ifstream ifile("input.txt"); if(!ifile) { cout<<"input.txt cannot be opened!"<<endl; return; } char ch; int i; for (i=0;i<MAX_NUM;i++)//读取数目 { string s_X1,s_X2,s_X3; if(!ifile.get(ch)) { return; } while (ch!=',')//读取第一个数据 { if (ch==' ')//跳过空格 { if(!ifile.get(ch)) {return; } continue; } s_X1+=ch; if(!ifile.get(ch)) {return; } } if(!ifile.get(ch)) { return; } while (ch!=',')//读取第二个数据 { if (ch==' ') { if(!ifile.get(ch)) {return; } //跳过空格 continue; } s_X2+=ch; if(!ifile.get(ch)) {return; } } if(!ifile.get(ch)) { return; } while(ch!='\n')//读取第三个数据 { if (ch==' ') { if(!ifile.get(ch)) {return; } continue; } s_X3+=ch; if(!ifile.get(ch)) {cout<<"文件已经读完!"<<endl; return; } }double xt[dim];//将读入的字符串转化为小数xt[0]=atof(s_X1.c_str());xt[1]=atof(s_X2.c_str());xt[2]=atof(s_X3.c_str());InputFeature t1(xt);//生成特征向量 InputVector.push_back(t1);//保存到数组 } ifile.close();}//计算在误差err内,2次迭代的结果是否一样bool CompareU(double U0[K][dim],double U[K][dim],double err){for (int k=0;k<K;k++){for (int d=0;d<dim;d++){if(abs(U0[k][d]-U[k][d])>err){return false;}} }return true;}//k-means算法核心void k_means(double U[K][dim]){ double U0[K][dim]; memcpy(U0,U,sizeof(double)*K*dim); while (true) { //第一步 标定集合中的点,离哪个U点最近,即将其cluster修改为对应的分类 int j; vector<InputFeature>::iterator it; for(it = InputVector.begin(); it != InputVector.end(); ++it) { double dist[K]; for (j=0;j<K;j++)//计算该向量到各个标定向量的欧式距离 { dist[j]=it->distanceOf(U[j]); } double minDist=dist[0];//初始化最小距离 it->cluster=0;//初始化分类 for (j=1;j<K;j++) { if (dist[j]<minDist)//如果发现离第j个更近,则更新分类 { minDist=dist[j]; it->cluster=j; } } } //第二步 更新重心U double sum[K][dim],num[K]; memset(&sum,0,sizeof(double)*K*dim); memset(&num,0,sizeof(double)*K); for(it = InputVector.begin(); it != InputVector.end(); ++it) { for (int d=0;d<dim;d++)//计算所有相同分类的坐标重心 { sum[it->cluster][d]+=it->x[d]; } num[it->cluster]++;//计算相同分类的数目 } for (j=0;j<K;j++)//更新标定向量的重心 { for (int d=0;d<dim;d++) { if (num[j]!=0) U[j][d]=(sum[j][d])/(num[j]); } } //判断是否收敛, U==U0时收敛 if (CompareU(U0,U,1.0e-5)) { break; } memcpy(U0,U,sizeof(double)*K*dim);//将本次迭代的结果保存 } //输出聚类的结果 cout<<"k-means聚类的中心点坐标为:"<<endl; for (int k=0;k<K;k++) { cout<<"U"<<k<<"="; for (int d=0;d<dim;d++) { cout<<U[k][d]<<" "; } cout<<endl; } //此时每个元素的分类情况为: cout<<"输入向量的聚类情况及坐标点:"<<endl; vector<InputFeature>::iterator it; for(it = InputVector.begin(); it != InputVector.end(); ++it) { it->print(); } //计算误差函数 double J=0; for(it = InputVector.begin(); it != InputVector.end(); ++it) { J+=it->distanceOf(U[it->cluster]); } cout<<"误差函数J(c,u)="<<J<<endl;}/* *@author:郑海波 zhb931706659@126.com *http://blog.csdn.net/nuptboyzhb/ */int main(){cout<<"@author:郑海波 zhb931706659@126.com"<<endl;inputData();//读入输入的数据//初始化K=2个标定向量double U[K][dim]={{4.5,8.1,2.8},{-0.9,4.7,-0.3}};k_means(U);//进行k-means聚类return 0;}
实验结果:
matlab plot code
% *@author:郑海波 zhb931706659@126.com% *http://blog.csdn.net/nuptboyzhb/clear all;clc;close all;x=[1.0 , 5.7 , 2.84.5 , 5.2 , -0.3-0.9 , 8.1 , 1.40.5 , 6.6 , 2.33.5 , 4.7 , 0.24.7 , 5.9 , -15.1 , 6.2 , 0.92.1 , 7.4 , 3.0];subplot(1,2,1);plot3(x(:,1),x(:,2),x(:,3),'o');hold on;U1=[4.5,8.1,2.8];U2=[-0.9,4.7,-0.3];plot3(U1(1),U1(2),U1(3),'+');hold on;plot3(U2(1),U2(2),U2(3),'r+');hold on;title('o--原始数据,+初始的迭代点');x1=[1.0 , 5.7 , 2.8-0.9 , 8.1 , 1.40.5 , 6.6 , 2.32.1 , 7.4 , 3.0];x2=[4.5 , 5.2 , -0.33.5 , 4.7 , 0.24.7 , 5.9 , -15.1 , 6.2 , 0.9];subplot(1,2,2);plot3(x1(:,1),x1(:,2),x1(:,3),'o');hold on;plot3(x2(:,1),x2(:,2),x2(:,3),'ro');hold on;U1=[4.45 5.5 -0.05];U2=[0.675 6.95 2.125];plot3(U1(1),U1(2),U1(3),'r*');hold on;plot3(U2(1),U2(2),U2(3),'*');hold on;title('迭代结果,*为聚类点的中心');
未经允许不得用于商业目的
- 无监督学习算法K-means算法总结与c++编程实现
- 无监督学习之K-means算法
- K-means聚类算法(无监督学习算法)
- 机器学习--K-means算法(聚类,无监督学习)
- 机器学习算法---无监督学习,k-means聚类
- 机器学习笔记:K-Means无监督聚类算法
- 【机器学习算法-python实现】K-means无监督学习实现分类
- 无监督学习k-means简单实现
- 机器学习笔记11——无监督学习之k-means聚类算法
- 非监督学习算法K均值(K-Means)探讨
- c实现K-means算法
- 无监督学习之K-均值算法分析与MATLAB代码实现
- SPMF源码学习与总结——k-means算法
- 无监督K-maeans算法
- 无监督学习算法
- K-means算法实现
- K-means算法实现
- c语言实现k-means算法
- 设计模式-策略模式
- ARM MPlayer移植过程
- MTK 智能机平台系统文件夹文件详解
- android电话流程(打电话,接电话,挂断电话)
- Xcode ARC需要什么版本的环境支持
- 无监督学习算法K-means算法总结与c++编程实现
- 基于ATmega32的SD卡上FAT32文件系统数据读取
- Android通信模块代码流程
- Change Has Come to America
- 交叉编译jrtplib 问题
- 2012年12月21日 工作总结
- MTK Android 紧急号码修改
- 图像处理与计算机视觉:基础,经典以及最近发展(1)序
- 通过libxml2更新XML的属性值和元素值