数据挖掘算法之K-Means算法演示
来源:互联网 发布:知乎a tender feeling 编辑:程序博客网 时间:2024/05/29 09:25
目录
- 目录
- 算法描述
- 算法流程
- 代码实现
- 实验结果
算法描述
K-Means算法是数据挖掘中较为常用的算法之一,同时也是聚类算法中较为简单的一种。K-Means是一种简单的迭代型的聚类算法,它将给定的数据集分为用户指定的k个聚簇。其中k一般是使用者根据一定的先验知识预先设定,Mean也就是英文中平均值的意思。因此,该方法从字面意思也能猜出该方法是通过计算平均值来找到数据的K个聚类中心。
首先,我们需要明白K-Means的目标是什么。k-means要做的就是根据一群没有标签数据的特征,将这些数据分成我们预先设定的k个种类。k-means算法的输入对象是d维向量空间中的一些点。因此,它是对一个d维向量空间的点集
其次,K-Means算法是根据“紧密度”或“相似度”等概念对点集进行分组,通常而言会选择欧几里得距离作为度量方式。K-Means算法实质是要最小化下面的非负的代价函数:
其中,
算法流程
输入:数据集D,聚簇数k
- 在数据集中随机选择k个值作为聚簇中心;
Repeat - 将每个数据点分配到当前与之最近的聚簇中心;
- 根据步骤(2)重新设定均值,更新聚簇中心
C .
until 目标函数Cost=∑i=1N(argminj∥∥xi−cj∥∥22) 收敛
输出:聚簇代表集合C,每个数据点所属聚簇的标签
代码实现
该代码是采用c++完成,是对一副图像进行聚类的,如果有什么不对的地方,请大家见谅。
Kmeans.h
#pragma once#include <cmath>#include <vector>#include <algorithm>#include <functional>#include <string>typedef int Width;typedef int Height;typedef unsigned char uint8;typedef int Status;typedef int Cluster;#define OK 1#define ERROR 0#define Alpha 0.0005class Kmeans{public: std::string Imagepath; //图像地址 int numClusters; //聚类的数目 // 聚类中心的结构体 struct CenterCluster { uint8 R; uint8 G; uint8 B; }; //图像表示的结构体 struct Image{ Width w; Height h; uint8 *r,*g,*b; int *cluster; //存放每个像素的聚类类别 int step; int depth; int nChannel; }; std::vector<CenterCluster> CCluster; //存放聚类中心的vector std::vector<CenterCluster> *dataCluster; //分别存放不同类别的图像像素点 std::vector<CenterCluster> oriCCluster; //存放原始的聚类中心 Kmeans(int k,std::string path); virtual ~Kmeans(); Status ImageKmeans(); Status readImage(const char* imgName,Image *Img); Status InitCluster(Image *Img,int k); Status startCluster(Image *Img); double CalEuclideanDistance(CenterCluster *pixel,CenterCluster *Center); Status ReCalClusterCenter(); double DisClusterToCenter(std::vector<CenterCluster> CC); Status newImageShow(Image *Img);private:};
Kmeans.cpp
// K-means.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "Kmeans.h"#include <opencv2\highgui\highgui.hpp>using namespace std;using namespace cv;Kmeans::Kmeans(int k,string path){ numClusters=k; Imagepath=path; dataCluster=new vector<CenterCluster>[k]; if (!CCluster.empty()) { CCluster.clear(); }}Kmeans::~Kmeans(){ if (dataCluster!=NULL) { delete[] dataCluster; dataCluster=NULL; }}//对图像进行Kmeans操作Status Kmeans::ImageKmeans(){ if (numClusters<=0 || Imagepath.empty()) { return ERROR; } Image Img; Status rResult=readImage(Imagepath.c_str(),&Img); if(!rResult) { printf("读取图片失败!\n"); return ERROR; } Status initResult=InitCluster(&Img,numClusters); if (!initResult) { printf("聚类点初始化失败!\n"); return ERROR; } Status startClusterResult=startCluster(&Img); if (!startClusterResult) { printf("聚类失败!\n"); } double newDist=DisClusterToCenter(CCluster); double oriDist=1000000.0; while (abs(newDist-oriDist)>Alpha) { oriCCluster.assign(CCluster.begin(),CCluster.end()); oriDist=newDist; Status rCalResult=ReCalClusterCenter(); if (!rCalResult) { printf("更新中心错误!\n"); return ERROR; } Status cResult=startCluster(&Img); if (!cResult) { printf("聚类失败!\n"); return ERROR; } newDist=DisClusterToCenter(CCluster); } printf("图像聚类结束!\n"); int ShowResult=newImageShow(&Img); if (!ShowResult) { return ERROR; } if (Img.r!=NULL) { delete[](Img.r); Img.r=NULL; } if (Img.g!=NULL) { delete[] Img.g; Img.g=NULL; } if (Img.b!=NULL) { delete[] Img.b; Img.b=NULL; } if (Img.cluster!=NULL) { delete[] Img.cluster; Img.cluster=NULL; } return OK;}//利用opencv 读取图像Status Kmeans::readImage(const char* Imagepath,Image *Img){ IplImage *pImage=cvLoadImage(Imagepath); //建立新的图像 /*CvSize size; size.height=pImage->height; size.width=pImage->width; IplImage *newImg=cvCreateImage(size,pImage->depth,3);*/ if(pImage==NULL) return ERROR; Img->w=pImage->width; Img->h=pImage->height; int nchanels=pImage->nChannels; Img->nChannel=nchanels; int step=pImage->widthStep/sizeof(uint8); Img->step=step/nchanels; Img->depth=pImage->depth; Img->r=new uint8[Img->h*Img->w]; Img->g=new uint8[Img->h*Img->w]; Img->b=new uint8[Img->h*Img->w]; Img->cluster=new int[Img->h*Img->w]; for (int i=0;i<Img->h;i++) for (int j=0;j<Img->w;j++){ Img->b[i*step/nchanels+j]=pImage->imageData[i*step+j*nchanels+0]; Img->g[i*step/nchanels+j]=pImage->imageData[i*step+j*nchanels+1]; Img->r[i*step/nchanels+j]=pImage->imageData[i*step+j*nchanels+2]; } return OK;}//初始化聚类中心,利用随机生成k个聚类中心Status Kmeans::InitCluster(Image *Img, int k){ if(Img==NULL||k==0) return ERROR; int w=Img->w; int h=Img->h; int step=Img->step; //CenterCluster cc; for (int i=0;i<k;i++) { int wRand=rand()% w; int hRand=rand()% h; uint8 b=Img->b[hRand*step+wRand]; uint8 g=Img->g[hRand*step+wRand]; uint8 r=Img->r[hRand*step+wRand]; CenterCluster cc; cc.B=b; cc.G=g; cc.R=r; CCluster.push_back(cc); } if (CCluster.empty()) return ERROR; return OK;}//开始进行聚类,利用像素点到聚类中心的欧几里得距离Status Kmeans::startCluster(Image *Img){ Width width=Img->w; Height height=Img->h; int step=Img->step; for (int i=0;i<height;i++) { for (int j=0;j<width;j++) { CenterCluster pixel; double MinDis=9999999999; int flag=0; pixel.B=Img->b[i*step+j]; pixel.G=Img->g[i*step+j]; pixel.R=Img->r[i*step+j]; for (int k=0;k<numClusters;k++) { double Dis=CalEuclideanDistance(&pixel,&CCluster[k]); //计算欧几里得距离 if (Dis<MinDis) { MinDis=Dis; flag=k; } } Img->cluster[i*step+j]=flag; dataCluster[flag].push_back(pixel); } } return OK;}double Kmeans::CalEuclideanDistance(CenterCluster *pixel,CenterCluster *Center){ double dist; uint8 Pr,Pg,Pb,Cr,Cg,Cb; Pr=pixel->R; Pg=pixel->G; Pb=pixel->B; Cr=Center->R; Cg=Center->G; Cb=Center->B; dist=sqrt(pow((double)abs(Pr-Cr),2)+pow((double)abs(Pg-Cg),2)+pow((double)abs(Pb-Cb),2)); return dist;}//重新计算聚类中心Status Kmeans::ReCalClusterCenter(){ int r0,g0,b0; for (int k=0;k<numClusters;k++) { vector<CenterCluster>:: size_type i=(dataCluster[k]).size(); r0=0;g0=0; b0=0; for (vector<CenterCluster>::size_type j=0;j<i;j++) { r0+=dataCluster[k][j].R; g0+=dataCluster[k][j].G; b0+=dataCluster[k][j].B; } r0=(uint8)(r0/i); g0=(uint8)(g0/i); b0=(uint8)(b0/i); CCluster[k].R=r0; CCluster[k].G=g0; CCluster[k].B=b0; (dataCluster[k]).clear(); } return OK;}//计算像素点到聚类中心的距离平方和,目标函数为最小化对象到其簇质心的距离的平方和,收敛条件是前后两次迭代距离平方和小于设定的某一阈值double Kmeans::DisClusterToCenter(vector<CenterCluster> CC){ double Dis=0.0; vector<CenterCluster>::size_type num; double count=0; for (int i=0;i<numClusters;i++) { uint8 r0=CC[i].R; uint8 g0=CC[i].G; uint8 b0=CC[i].B; for (num=0;num<dataCluster[i].size();num++) { count++; uint8 r1=dataCluster[i][num].R; uint8 g1=dataCluster[i][num].G; uint8 b1=dataCluster[i][num].B; Dis+=pow((double)(r0-r1),2)+pow((double)(g0-g1),2)+pow((double)(b0-b1),2); } } return Dis/count;}//生成新的聚类图像Status Kmeans::newImageShow(Image *Img){ Width w=Img->w; Height h=Img->h; int step=Img->step; int nchannel=Img->nChannel; CvSize size; size.height=h; size.width=w; IplImage *newImage=cvCreateImage(size,Img->depth,nchannel); if (!newImage) return ERROR; for (int i=0;i<h;i++) { for (int j=0;j<w;j++) { int flag=Img->cluster[i*step+j]; if (flag<0||flag>=numClusters) return ERROR; newImage->imageData[i*step*nchannel+j*nchannel+0]=CCluster[flag].B; newImage->imageData[i*step*nchannel+j*nchannel+1]=CCluster[flag].G; newImage->imageData[i*step*nchannel+j*nchannel+2]=CCluster[flag].R; } } cvNamedWindow("kmeans",CV_WINDOW_AUTOSIZE); //cvNamedWindow("kmeans",0); cvShowImage("kmeans",newImage); cvWaitKey(0); cvReleaseImage(&newImage); cvDestroyWindow("kmeans"); return OK;}
包含头文件 Cluster.h
#pragma once#include "Kmeans.h"
主程序入口,设置k值 Cluster.cpp
#include "stdafx.h"#include <iostream>#include "Cluster.h"using namespace std;int _tmain(int argc, _TCHAR* argv[]){ string Imgpath="F:\\kobe.jpg"; int k; printf("Please key in one integer for clustering\n"); cin>>k; Kmeans k_means(k,Imgpath); k_means.ImageKmeans(); return 1;}
实验结果
实验原图
K=2聚类结果
K=3聚类结果
0 0
- 数据挖掘算法之K-Means算法演示
- 数据挖掘之K-means算法
- 数据挖掘实战之 K-means算法
- 数据挖掘算法之 k-means
- 数据挖掘-K-means算法
- 数据挖掘算法-k-means
- 十大经典数据挖掘算法之K-Means算法
- 大数据挖掘算法篇之K-Means实例
- 数据挖掘十大经典算法之二:K-means
- 数据挖掘之聚类算法K-Means总结
- 数据挖掘之聚类算法K-Means总结_0
- 数据挖掘之聚类算法K-Means总结
- (2)数据挖掘算法之k-means
- 数据挖掘之k-means算法的Python实现
- 【数据挖掘】k-means聚类算法
- 数据挖掘-k-means算法实现
- 数据挖掘 K-Means++聚类算法
- 数据挖掘K-means算法必记。
- iOS中音频
- hdu1561(树形dp)
- DAO,如何实现模糊查询
- Bulb Switcher---319
- Spring框架中CharacterEncodingFilter的作用真的没那么大
- 数据挖掘算法之K-Means算法演示
- Linux&Unix 分类下的文章
- hdu2639(01背包的第K大)
- bat修改文件内容
- sqldeveloper使用
- 正则表达之----验证数字的正则表达式集
- 根据给定日期判断当天是星期几
- android studio 签名,打包,混淆,多渠道打包
- XMPP的简单了解及使用(1)