zoukankan      html  css  js  c++  java
  • K-means算法研究

                                     

    摘  要

                                   

    本文设计了一个以聚类算法为基础,用K-means算法实现的C++程序。通过对数据量较多的颜色进行聚类分析,最终得出每种颜色的中心分布点,从而实现把相似的东西聚合在一起的“聚类算法”。因为聚类算法只需要计算数据的相似性,而不用关心某一类是什么,本文只讨论了如何实现相似颜色的聚类。

    关键词: K-means聚类算法


                 

                             目    录     

                                                       

         I

    1章  绪 论  1

    1.1  聚类算法的介绍 1

    1.2  聚类算法的要求 1

    1.3  K-means算法的介绍 1

    2章  实验设计 2

    2.1实验设计 2

             2.2 K-means的源码实现 2

     


                

    第1章  绪 论

                               

    1.1 聚类算法的介绍

    聚类分析又称群分析,它是研究(样品或指标)分类问题的一种统计分析方法,同时也是数据挖掘的一个重要算法。

    聚类(Cluster)分析是由若干模式(Pattern)组成的,通常,模式是一个度量(Measurement)的向量,或者是多维空间中的一个点。

    聚类分析以相似性为基础,在一个聚类中的模式之间比不在同一聚类中的模式之间具有更多的相似性。 

    聚类分析起源于分类学,在古老的分类学中,人们主要依靠经验和专业知识来实现分类,很少利用数学工具进行定量的分类。随着人类科学技术的发展,对分类的要求越来越高,以致有时仅凭经验和专业知识难以确切地进行分类,于是人们逐渐地把数学工具引用到了分类学中,形成了数值分类学,之后又将多元分析的技术引入到数值分类学形成了聚类分析。聚类分析内容非常丰富,有系统聚类法、有序样品聚类法、动态聚类法、模糊聚类法、图论聚类法、聚类预报法等。

    1.2  聚类算法的要求       

    可伸缩性

    许多聚类算法在小于 200 个数据对象的小数据集合上工作得很好;但是,一个大规模数据库可能包含几百万个对象,在这样的大数据集合样本上进行聚类可能会导致有偏的结果。

    我们需要具有高度可伸缩性的聚类算法。

    不同属性

    许多算法被设计用来聚类数值类型的数据。但是,应用可能要求聚类其他类型的数据,如二元类型(binary),分类/标称类型(categorical/nominal),序数型(ordinal)数据,或者这些数据类型的混合。

    任意形状

    许多聚类算法基于欧几里得或者曼哈顿距离度量来决定聚类。基于这样的距离度量的算法趋向于发现具有相近尺度和密度的球状簇。但是,一个簇可能是任意形状的。提出能发现任意形状簇的算法是很重要的。

    领域最小化

    许多聚类算法在聚类分析中要求用户输入一定的参数,例如希望产生的簇的数目。聚类结果对于输入参数十分敏感。参数通常很难确定,特别是对于包含高维对象的数据集来说。这样不仅加重了用户的负担,也使得聚类的质量难以控制。

    处理“噪声”

    绝大多数现实中的数据库都包含了孤立点,缺失,或者错误的数据。一些聚类算法对于这样的数据敏感,可能导致低质量的聚类结果。

    记录顺序

    一些聚类算法对于输入数据的顺序是敏感的。例如,同一个数据集合,当以不同的顺序交给同一个算法时,可能生成差别很大的聚类结果。开发对数据输入顺序不敏感的算法具有重要的意义。

    高维度(high dimensionality)

    一个数据库或者数据仓库可能包含若干维或者属性。许多聚类算法擅长处理低维的数据,可能只涉及两到三维。人类的眼睛在最多三维的情况下能够很好地判断聚类的质量。在高维空间中聚类数据对象是非常有挑战性的,特别是考虑到这样的数据可能分布非常稀疏,而且高度偏斜。

    基于约束

    现实世界的应用可能需要在各种约束条件下进行聚类。假设你的工作是在一个城市中为给定数目的自动提款机选择安放位置,为了作出决定,你可以对住宅区进行聚类,同时考虑如城市的河流和公路网,每个地区的客户要求等情况。要找到既满足特定的约束,又具有良好聚类特性的数据分组是一项具有挑战性的任务。

    解释性-可用性

    用户希望聚类结果是可解释的,可理解的,和可用的。也就是说,聚类可能需要和特定的语义解释和应用相联系。应用目标如何影响聚类方法的选择也是一个重要的研究课题。

    记住这些约束,我们对聚类分析的学习将按如下的步骤进行。首先,学习不同类型的数据,以及它们对聚类方法的影响。接着,给出了一个聚类方法的一般分类。然后我们详细地讨论了各种聚类方法,包括划分方法,层次方法,基于密度的方法,基于网格的方法,以及基于模型的方法。最后我们探讨在高维空间中的聚类和孤立点分析(outlier analysis)。     

    1.3  K-means算法的介绍    

     

    k-means 算法接受输入量 k ;然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。

    k-means 算法的工作过程说明如下:

    首先从n个数据对象任意选择 k 个对象作为初始聚类中心;而对于所剩下其它对象,则根据它们与这些聚类中心的相似度(距离),分别将它们分配给与其最相似的(聚类中心所代表的)聚类;

    然后再计算每个所获新聚类的聚类中心(该聚类中所有对象的均值);不断重复这一过程直到标准测度函数开始收敛为止。

    一般都采用均方差作为标准测度函数. k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。

       

    第2章  实验设计

       

    2.1 实验设计

    假设我们提取到原始数据的集合为(x1, x2, …, xn),并且每个xi为d维的向量,K-means聚类的目的就是在给定分类组数k(k ≤ n)值的条件下将原始数据分成k类 S = {S1, S2, …, Sk},在数值模型上,即对以下表达式求最小值:

    这里μi 表示分类Si 的平均值。

    其算法步骤一般如下:

    1、从D中随机取k个元素,作为k个簇的各自的中心。

    2、分别计算剩下的元素到k个簇中心的相异度,将这些元素分别划归到相异度最低的簇。

    3、根据聚类结果,重新计算k个簇各自的中心,计算方法是取簇中所有元素各自维度的算术平均数。

    4、将D中全部元素按照新的中心重新聚类。

    5、重复第4步,直到聚类结果不再变化。

    6、将结果输出。

    用数学表达式来说,

    设我们一共有 N 个数据点需要分为 K 个 cluster ,k-means 要做的就是最小化

    这个函数,其中  在数据点 n 被归类到 cluster k 的时候为 1 ,否则为 0 。直接寻找  和  来最小化  并不容易,不过我们可以采取迭代的办法:先固定  ,选择最优的  ,很容易看出,只要将数据点归类到离他最近的那个中心就能保证  最小。下一步则固定 ,再求最优的 。将  对  求导并令导数等于零,很容易得到  最小的时候  应该满足:

    亦即  的值应当是所有 cluster k 中的数据点的平均值。由于每一次迭代都是取到  的最小值,因此  只会不断地减小(或者不变),而不会增加,这保证了 k-means 最终会到达一个极小值。虽然 k-means 并不能保证总是能得到全局最优解,但是对于这样的问题,像 k-means 这种复杂度的算法,这样的结果已经是很不错的了。

    首先 3 个中心点被随机初始化,所有的数据点都还没有进行聚类,默认全部都标记为红色,如下图所示:

    然后进入第一次迭代:按照初始的中心点位置为每个数据点着上颜色,重新计算 3 个中心点,结果如下图所示:

    可以看到,由于初始的中心点是随机选的,这样得出来的结果并不是很好,接下来是下一次迭代的结果:

    可以看到大致形状已经出来了。再经过两次迭代之后,基本上就收敛了,最终结果如下:

    2.2 K-means的源码实现

    一般情况下,我们通过C++/Matlab/Python等语言进行实现K-means算法,结合我比较熟悉的C++,先从C++实现谈起,C++里面我们一般采用的是OpenCV库中写好的K-means函数,即cvKmeans2,首先来看函数原型:

      从OpenCV manual看到的是:

    int cvKMeans2(const CvArr* samples, int nclusters,

            CvArr* labels, CvTermCriteria termcrit,

            int attempts=1, CvRNG* rng=0,int flags=0, 

            CvArr* centers=0,double* compactness=0);

    由于除去已经确定的参数,我们自己需要输入的为:

    void cvKMeans2( 

        const CvArr* samples, //输入样本的浮点矩阵,每个样本一行。 

        int cluster_count,  //所给定的聚类数目 

         * labels,    //输出整数向量:每个样本对应的类别标识 

         CvTermCriteria termcrit //指定聚类的最大迭代次数和/或精度(两次迭代引起的聚类中心的移动距离)

     ); 

     

    程序源码为:

    #ifdef _CH_

    #pragma package <opencv>

    #endif

    #define CV_NO_BACKWARD_COMPATIBILITY

    #ifndef _EiC

    #include "cv.h"

    #include "highgui.h"

    #include <stdio.h>

    #endif

    int main( int argc, char** argv )

    {

         #define MAX_CLUSTERS 5    //设置类别的颜色,个数(《=5

         CvScalar color_tab[MAX_CLUSTERS];

         IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );

         CvRNG rng = cvRNG(-1);

         CvPoint ipt;

         color_tab[0] = CV_RGB(255,0,0);

         color_tab[1] = CV_RGB(0,255,0);

         color_tab[2] = CV_RGB(100,100,255);

         color_tab[3] = CV_RGB(255,0,255);

         color_tab[4] = CV_RGB(255,255,0); 

         cvNamedWindow( "clusters", 1 ); 

         for(;;)

         {

             char key;

             int k, cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1;

             int i, sample_count = cvRandInt(&rng)%1000 + 1;

             CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 );

             CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 );

             cluster_count = MIN(cluster_count, sample_count);

             /** generate random sample from multigaussian distribution */

             for( k = 0; k < cluster_count; k++ )

             {

                 CvPoint center;

                 CvMat point_chunk;

                 center.x = cvRandInt(&rng)%img->width;

                 center.y = cvRandInt(&rng)%img->height;

                 cvGetRows( points, &point_chunk, k*sample_count/cluster_count,

                            k == cluster_count - 1 ? sample_count :

                            (k+1)*sample_count/cluster_count, 1 );

                 cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL,

                            cvScalar(center.x,center.y,0,0),

                            cvScalar(img->width*0.1,img->height*0.1,0,0));

             } 

             /** shuffle samples */

             for( i = 0; i < sample_count/2; i++ )

             {

                 CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count;

                 CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count;

                 CvPoint2D32f temp;

                 CV_SWAP( *pt1, *pt2, temp );

             }

             printf( "iterations=%d ", cvKMeans2( points, cluster_count, clusters,

                     cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 ),

                     5, 0, 0, 0, 0 ));

             cvZero( img );

             for( i = 0; i < sample_count; i++ )

             {

                 int cluster_idx = clusters->data.i[i];

                 ipt.x = (int)points->data.fl[i*2];

                 ipt.y = (int)points->data.fl[i*2+1];

                 cvCircle( img, ipt, 2, color_tab[cluster_idx], CV_FILLED, CV_AA, 0 );

             }

             cvReleaseMat( &points );

             cvReleaseMat( &clusters );

             cvShowImage( "clusters", img );

             key = (char) cvWaitKey(0);

             if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'

                 break;

         }

         cvDestroyWindow( "clusters" );

         return 0;

    }

    #ifdef _EiC

    main(1,"kmeans.c");

    #endif

  • 相关阅读:
    Mysql日志管理
    Mysql 安全和DCL语句
    Mysql DDL语句之视图
    Mysql增删改查(DML、DQL)
    Mysql操作之部分DDL语句
    如何做事情
    temp
    asp.net入门
    希望尽快回忆起来
    需求?
  • 原文地址:https://www.cnblogs.com/tryitboy/p/4231127.html
Copyright © 2011-2022 走看看