数据挖掘算法之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维向量空间的点集D={xi|i=1,...N} 进行聚类,其中xiRd 表示第i个对象。此外,在K-Means中,每个聚簇都用Rd 的一个点来表示,可将这些聚簇表示成集合C={cj|j=1,...k},这k个聚簇有时也称为聚簇中心或聚簇均值。
  其次,K-Means算法是根据“紧密度”或“相似度”等概念对点集进行分组,通常而言会选择欧几里得距离作为度量方式。K-Means算法实质是要最小化下面的非负的代价函数:

Cost=i=1N(argminjxicj22)

  其中,xi 只属于距离它欧几里得距离最近的聚簇中心cj, cjC
  

算法流程

输入:数据集D,聚簇数k

  1. 在数据集中随机选择k个值作为聚簇中心;
    Repeat
  2. 将每个数据点分配到当前与之最近的聚簇中心;
  3. 根据步骤(2)重新设定均值,更新聚簇中心 C.
    until 目标函数Cost=i=1N(argminjxicj22) 收敛

输出:聚簇代表集合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;}

实验结果

gutianle
                     实验原图
                     
k=2

                                    K=2聚类结果

k=3

                                K=3聚类结果
0 0
原创粉丝点击