学习笔记——K-means(1) 简要介绍、算法优劣、简单k-means的实现

l  P6  2013.10.22 早上八点

问题:简单Kmeans(随机选取初始类中心)

         简要介绍:

    K-means是聚类算法中最简单的一种,聚类属于无监督学习,与监督学习中回归、朴素贝叶斯、SVM等有类别标签y不一样,聚类没有给定y,只有特征x,比如架设宇宙中的星星可以表示成三维空间中的点集(x,y,z),聚类的目的是找到每个样本x潜在的类别y,并将同类别y的样本x放在一起,比如上面提到的星星问题,聚类后的结果是一个一个的星团,星团里面的点相互之间的距离较近,星团与星团之间的距离就比较远了。

K-means算法是将样本聚类成k个簇(cluster),具体算法描述如下:

1随机选取k个聚类质心点(cluster centroids)为

2重复下面过程直到收敛 {

               对于每一个样例i,计算其应该属于的类

               

               对于每一个类j,重新计算该类的质心

               

}

 

 

K-means算法存在的问题

         K-means算法的优势就是简洁和效率,算法的时间复杂度是O(tkn),其中n是数据点的个数,t是循环迭代的次数,k是聚类中心的个数,k-means算法被认为相对于数据点的数目来说是线性的。

         但是k-means算法同样存在许多问题:(参考至《Web 数据挖掘》一书 作者 Bing Liu)

                   算法只能应用于那些均值能够被定义的数据集上

                   用户需要事先指定聚类数目k

                   算法对异常值十分敏感

                   算法对初始种子的选取十分敏感,即那些被初选为初始聚类中心的数据点

                   k-means算法不适合于发现那些形状不是超维椭圆体(或超维球体)的聚类   

缺点主要有三个:(参考至http://hi.baidu.com/cuijianyong73/item/d741cd8022070050e63d19b9)

  K-means 算法中 K 是事先给定的,这个 K 值的选定是非常难以估计的。很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。这也是 K-means 算法的一个不足。有的算法是通过类的自动合并和分裂,得到较为合理的类型数目 K,例如 ISODATA 算法。关于 K-means 算法中聚类数目值的确定在文献[23]中,是根据方差分析理论,应用混合 F 统计量来确定最佳分类数,并应用了模糊划分熵来验证最佳分类数的正确性。在文献[24]中,使用了一种结合全协方差矩阵的 RPCL 算法,并逐步删除那些只包含少量训练数据的类。而文献[25]中使用的是一种称为次胜者受罚的竞争学习规则,来自动决定类的适当数目。它的思想是:对每个输入而言,不仅竞争获胜单元的权值被修正以适应输入值,而且对次胜单元采用惩罚的方法使之远离输入值。

  K-means 算法中,首先需要根据初始聚类中心来确定一个初始划分,然后对初始划分进行优化。这个初始聚类中心的选择对聚类结果有较大的影响[26-29],一旦初始值选择的不好,可能无法得到有效的聚类结果,这也成为 K-means算法的一个主要问题。对于该问题的解决,许多算法采用遗传算法(GA),例如文献中采用遗传算法(GA)进行初始化,以内部聚类准则作为评价[30]指标。

  K-means 算法框架可以看出,该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。所以需要对算法的时间复杂度进行分析、改进,提高算法应用范围。在文献[31,32]中从该算法的时间复杂度进行分析考虑,通过一定的相似性准则来去掉聚类中心的侯选集。而在文献[33]中,使用的 K-means 算法是对样本数据进行聚类,无论是初始点的选择还是一次迭代完成时对数据的调整,都是建立在随机选取的样本数据的基础之上,这样可以提高算法的收敛速度。

 

以上只是对k-means的简要陈述,详细的参考资料有《数据挖掘-概念与技术》作者:Jiawei Han和《Web 数据挖掘》作者 Bing Liu

 

下面是两个用C#实现的简单k-means算法(即随机选取初始类中心)

 

C#实现

代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

 

/// <summary>

/// 简单的kmeans,计算二维空间的点,随机选择k个初始类中心

/// </summary>

namespace PracticeLog

{

   public class Point

    {

       /// <summary>

       /// X轴

       /// </summary>

       public int X

       {

           get;

           set;

       }

       /// <summary>

       /// Y轴

       /// </summary>

       public int Y

       {

           get;

           set;

       }

       /// <summary>

       /// point所属中心

       /// </summary>

       public int Focus

       {

           get;

           set;

       }

       public Point()

       {

 

       }

    }

 

   /// <summary>

   /// Kmeans

   /// </summary>

   class Kmeans

    {

       /// <summary>

       /// 类中心的个数

       /// </summary>

      public int K

       {

           get;

           set;

       }

 

       /// <summary>

       /// 数据量(point点的个数)

       /// </summary>

      public int Num

      {

          get;

          set;

      }

 

       /// <summary>

       /// 迭代的次数

       /// </summary>

       public int Iterative

      {

          get;

          set;

      }

 

       /// <summary>

       /// 点集合

       /// </summary>

       public Point[] Points;

 

       /// <summary>

       /// 类中心点集合

       /// </summary>

       public Point[] Center;

 

       /// <summary>

        /// 构造函数,初始化相关参数

       /// </summary>

       /// <param name="k">类中心的个数</param>

       /// <param name="num">点集的个数</param>

       /// <param name="iterative">迭代的次数</param>

       public Kmeans(int k, int num, int iterative)

       {

           this.K = k;

           this.Num = num;

           this.Iterative = iterative;

           this.Points = new Point[this.Num];

           this.Center = new Point[this.K];

       }

 

       /// <summary>

       /// 初始化数据和类中心

       /// </summary>

       private void Init()

       {

           Random randXY = new Random();

           //初始化数据,构造待聚类点集

           for (int i = 0; i < this.Num; ++i)

           {

                //数据对象

                Points[i] = new Point();

                Points[i].X = randXY.Next();

                Points[i].Y = randXY.Next();

                //随机类中心

                Random randFocus=new Random();

                Points[i].Focus =randFocus.Next(0, this.K - 1);

               //Console.WriteLine(Points[i].Focus);

           }

 

           //初始化类中心

           for (int j = 0; j < this.K; ++j)

           {

                Center[j] = new Point();

                Center[j].X = randXY.Next();

                Center[j].Y = randXY.Next();

           }

       }

 

       /// <summary>

       /// 计算每个点与类中的距离,并重新分配点所属类中心

       /// </summary>

       private void Compute()

       {

           for (int i = 0; i < this.Num;++i )

           {

                double distance = -1;

                for (int j = 0; j <this.K;++j )

                {

                    double tmpDistance =EuclidDistance(Points[i], Center[j]);

                    if(tmpDistance<distance||j==0)

                    {

                        distance = tmpDistance;

                        Points[i].Focus = j;

                    }

                }

           }

       }

 

       /// <summary>

       /// 计算二维空间中两点的欧式距离

       /// </summary>

       /// <param name="a">点a</param>

       /// <param name="b">点b</param>

       /// <returns></returns>

       private double EuclidDistance(Point a, Point b)

       {

           double dou = 0;

           dou=(a.X-b.X)*(a.X-b.X)+(a.Y-b.Y)*(a.Y-b.Y);

           return Math.Sqrt(dou);

       }

 

       /// <summary>

       /// 重新计算类中心

       /// </summary>

       private void UpdateCenter()

       {

           for (int i = 0; i < this.K;++i )

           {

                int x = 0;

                int y = 0;

                int count = 0;

                for (int j = 0; j < this.Num;++j)

                {

                    if (Points[j].Focus==i)

                    {

                        x += Points[j].X;

                        y += Points[j].Y;

                        count++;

                    }

                }

               //防止在二次分配类中心的时候出现没有点集的类中心出现

                x = x / (count == 0 ? 1 :count);

                y = y / (count == 0 ? 1 :count);

                Center[i].X = x;

                Center[i].Y = y;

           }

       }

 

       /// <summary>

       /// 打印计算完成的类中心

       /// </summary>

       private void Print()

       {

           foreach (Point p in Center)

           {

                Console.WriteLine(p.X +"," + p.Y);

           }

       }

 

       public void Do()

       {

            this.Init();

           while (this.Iterative > 0)

           {

                this.Compute();

                this.UpdateCenter();

                this.Iterative--;

           }

           this.Print();

       }

    }

}

 

 

代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace PracticeLog

{

   public class NPoint

    {

       /// <summary>

       /// N维向量data

       /// </summary>

       public double[] data

       {

           get;

           set;

       }

 

       /// <summary>

       /// point所属中心

       /// </summary>

       public int Focus

       {

           get;

           set;

       }

       public NPoint()

       {

 

       }

    }

   class KmeansNDim

    {

 

       /// <summary>

       /// 类中心的个数

       /// </summary>

      public int K

       {

           get;

           set;

       }

 

       /// <summary>

       /// 数据量(point点的个数)

       /// </summary>

      public int Num

      {

          get;

          set;

      }

 

       /// <summary>

       /// 迭代的次数

       /// </summary>

       public int Iterative

      {

          get;

          set;

      }

 

       /// <summary>

       /// 数据的维数

       /// </summary>

       public int N

       {

           get;

           set;

       }

 

       /// <summary>

       /// 点集合

       /// </summary>

       public NPoint[] Points;

 

       /// <summary>

       /// 类中心点集合

       /// </summary>

       public NPoint[] Center;

 

       /// <summary>

       /// 构造函数,初始化相关参数

       /// </summary>

       /// <param name="k">类中心的个数</param>

       /// <param name="num">点集的个数</param>

       /// <param name="iterative">迭代的次数</param>

       public KmeansNDim(int k, int num, int N, int iterative)

       {

           this.K = k;

           this.Num = num;

           this.N=N;

           this.Iterative = iterative;

           this.Points = new NPoint[this.Num];

           this.Center = new NPoint[this.K];

       }

 

       /// <summary>

       /// 初始化数据和类中心

       /// </summary>

       private void Init()

       {

           Random randData = new Random();

           for (int i = 0; i < this.Num; ++i)

           {

                Points[i] = new NPoint();

                Points[i].data = newdouble[this.N];

                for (int j=0;j<this.N;++j)

                {

                   Points[i].data[j]=randData.Next();d

                }

                Random randFocus=new Random();

                Points[i].Focus=randFocus.Next(0,this.K-1);

 

           }

 

           for (int m=0;m<this.K;++m)

           {

                Center[m]=new NPoint();

                Center[m].data = newdouble[this.N];

                for (int n=0;n<this.N;++n)

                {

                   Center[m].data[n]=randData.Next();

                }

           }

       }

 

       /// <summary>

       /// 计算每个点与类中的距离,并重新分配点所属类中心

       /// </summary>

       private void Compute()

       {

           for (int i = 0; i < this.Num;++i )

           {

                double distance = -1;

                for (int j = 0; j <this.K;++j )

                {

                    double tmpDis =EuclidDistance(Points[i].data, Center[j].data);

                    if (tmpDis<distance||j==0)

                    {

                        distance = tmpDis;

                        Points[i].Focus = j;

                    }

                }

           }

       }

 

       /// <summary>

       /// 计算二维空间中两点的欧式距离

       /// </summary>

       /// <param name="a">点A</param>

       /// <param name="b">点B</param>

       /// <returns></returns>

       private double EuclidDistance(double[] pointA, double[] pointB)

       {

           int len = pointA.Length;

           double dist = 0;

           for (int i = 0; i < len;++i )

           {

                dist += ((pointA[i] -pointB[i]) * (pointA[i] - pointB[i]));

           }

           return Math.Sqrt(dist);

       }

 

       /// <summary>

       /// 重新计算类中心

       /// </summary>

       private void UpdateCenter()

       {

           for (int i = 0; i < this.K;++i )

           {

                double[] tmp=newdouble[this.N];

                int count = 0;

                for (int j = 0; j <this.Num;++j )

                {

                    if (Points[j].Focus==i)

                    {

                        for (int k=0; k <this.N;++k )

                        {

                            tmp[k] +=Points[j].data[k];

                        }

                        count++;

                    }

                    //防止在二次分配类中心的时候出现没有点集的类中心出现

                    count = (count == 0 ? 1 :count);

                    for (int m = 0; m <this.N; ++m)

                    {

                        Center[i].data[m] =tmp[m] / count;

                    }

                }

           }

       }

 

       /// <summary>

       /// 打印计算完成的类中心

       /// </summary>

       private void Print()

       {

           foreach (NPoint p in Center)

           {

                for (int i = 0; i <this.N;++i )

                {

                    Console.Write(p.data[i] +"_");

                }

                Console.WriteLine();

           }

       }

 

       public void Do()

       {

           this.Init();

           while (this.Iterative > 0)

           {

                this.Compute();

                this.UpdateCenter();

                this.Iterative--;

           }

           this.Print();

       }

      

    }

}

 

未完待续……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值