机器学习系列--kmeans分类算法

简介

K-means算法是集简单和经典于一身的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为类簇是由距离靠近的对象组成的,因为把得到紧凑且独立的簇作为最终目标。

 

算法

核心思想

通过迭代寻找k个类簇的一种划分方案,使得用这k个类簇的均值来代表相应各类样本时所得的总体误差最小。

k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。k-means算法的基础是最小误差平方和准则

 

步骤

将样本聚类成k个簇,其中k是用户给定的,其求解过程非常直观简单

1.随机选取k个聚类质心点

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

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

 

机器学习系列--kmeans分类算法_第1张图片

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

 

伪代码

1.创建k个点作为初始的质心点(随机选择)

2.当任意一个点的簇分配结果发生改变时

对数据集中的每一个数据点
              对每一个质心
                     计算质心与数据点的距离
              将数据点分配到距离最近的簇(分组)
对每一个簇,计算簇中所有点的均值,并将均值作为质心 (新的均值等于原均值时候跳出迭代输出划分)

 

特点

各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开

 k-means算法的基础是最小误差平方和准则

机器学习系列--kmeans分类算法_第2张图片

μc(i)表示第i个聚类的均值

各类簇内的样本越相似,其与该类均值间的误差平方越小,对所有类所得到的误差平方求和,即可验证分为k类时,各聚类是否是最优的。

 

代码

spark2

Scala2.11

1.原生sparkcore

object KmeansTest {
    val k=2  //类个数
    val dim=2   //数据集维度
    val shold=0.0000000001  //阀值用于判断聚类中心偏移量
    val centers=new Array[Vector[Double]](k)    //聚类中心点(迭代更新)
    /**
      * 数据
      * 1.658985, 4.285136
      * -3.453687, 3.424321
      * 4.838138, -1.151539
      * -5.379713, -3.362104
      *
      * @param sc
      * @return
      */
    def loadDataSet(sc:SparkContext): Array[Vector[Double]] ={
        val file = sc.textFile("")
        val res=file.map(t=>{
            val value=t.split(" ").map(x=>{x.toDouble})
            var vector = Vector[Double]()
            for(i <- 0 until dim)
                vector ++= Vector(value(i))
            vector
        }).collect()
        res
    }

    /**
      *
      * 随机初始化聚类中心
      * k个聚类中心
      * 初始化中心点如下:
        3
        Vector(-5.379713, -3.362104)
       初始化中心点如下:
        4
        Vector(0.972564, 2.924086)
      * */
    def initialCenters(points:Array[Vector[Double]]): Unit ={
        val pointsNum=points.length
        val random = new Random()
        var index=0
        var flag=true
        var temp=0
        val array=new ListBuffer[Int]
        while(index < k){
            val temp: Int = random.nextInt(pointsNum)
            flag=true
            if(array.contains(temp)){
                flag=false
            }else{
                if(flag){
                    array.append(temp)
                    index+=1
                }
            }
        }

        for(i <- centers.indices){
            centers(i)=points(array(i))
            println("初始化中心点如下:")
            println(array(i))
            println(centers(i))
        }
    }

    /**
      * 迭代做聚类
      * @param points 随机下标
      * @param centers 中心点
      */
    def kmeans(points:Array[Vector[Double]],centers:Array[Vector[Double]]): Unit  = {
        var bool = true
        var newCenters = Array[Vector[Double]]()
        var move = 0.0
        var currentCost = 0.0 //当前的代价函数值
        var newCost = 0.0
        //根据每个样本点最近的聚类中心进行groupBy分组,最后得到的cluster是Map[Vector[Double],Array[Vector[Double]]]
        //Map中的key就是聚类中心,value就是依赖于该聚类中心的点集
        while(bool){//迭代更新聚类中心,直到最优
            move = 0.0
            //
            currentCost = computeCost(points,centers)
            val cluster = points.groupBy(v => closestCenter(centers,v))//聚类中心
            newCenters =
              centers.map(oldCenter => {
                  cluster.get(oldCenter) match {//找到该聚类中心所拥有的点集
                      case Some(pointsInThisCluster) =>
                          //均值作为新的聚类中心
                          vectorDivide(pointsInThisCluster.reduceLeft((v1,v2) => vectorAdd(v1,v2)),pointsInThisCluster.length)
                      case None => oldCenter
                  }
              })
            for(i <- centers.indices){
                //move += math.sqrt(vectorDis(newCenters(i),centers(i)))
                centers(i) = newCenters(i)
            }
            println("新的代价函数值:" + newCost)
            if(math.sqrt(vectorDis(Vector(currentCost),Vector(newCost))) < shold)
                bool = false
            newCost = computeCost(points,centers)//新的代价函数值
            println("当前代价函数值:" + currentCost)
        }//while-end
        println("寻找到的最优中心点如下:")
        for(i <- centers.indices){
            println(centers(i))
        }
    }
    /**
      * 输出聚类结果
      * @param points
      * @param centers
      */
    def printResult(points:Array[Vector[Double]],centers:Array[Vector[Double]]): Unit  = {
        //将每个点的聚类中心用centers中的下标表示,属于同一类的点拥有相同的下标
        val pointsNum = points.length
        val pointsLabel = new Array[Int](pointsNum)
        var closetCenter = Vector[Double]()
        println("聚类结果如下:")
        for(i <- 0 until pointsNum){
            closetCenter = centers.reduceLeft((c1,c2) => if (vectorDis(c1,points(i)) < vectorDis(c2,points(i))) c1 else c2)
            pointsLabel(i) = centers.indexOf(closetCenter)
            println(points(i) + "-----------" + pointsLabel(i))
        }

    }

    /**
      * 找到某样本点所属的聚类中心
      * @param centers
      * @param v
      * @return
      */
    def closestCenter(centers:Array[Vector[Double]],v:Vector[Double]):Vector[Double] = {
        centers.reduceLeft((c1,c2) =>
            if(vectorDis(c1,v) < vectorDis(c2,v)) c1 else c2
        )
    }
    /**
      * 计算代价函数(每个样本点到聚类中心的距离之和不再有很大变化)
      * @param points
      * @param centers
      * @return
      */
    def computeCost(points:Array[Vector[Double]],centers:Array[Vector[Double]]):Double = {
        //cluster:Map[Vector[Double],Array[Vector[Double]]
        //类分组
        val cluster = points.groupBy(v => closestCenter(centers,v))
        //欧式距离
        var costSum = 0.0

        for(i <- centers.indices){
            println(cluster.get(centers(i)).toBuffer)
            cluster.get(centers(i)) match{
                case Some(subSets) =>
                    for(j <- subSets.indices){
                        costSum += (vectorDis(centers(i),subSets(j)) * vectorDis(centers(i),subSets(j)))
                    }
                case None => costSum = costSum
            }
        }
        costSum
    }
    //--------------------------自定义向量间的运算-----------------------------
    //--------------------------向量间的欧式距离-----------------------------
    def vectorDis(v1: Vector[Double], v2: Vector[Double]):Double = {
        var distance = 0.0
        for(i <- v1.indices){
            distance += (v1(i) - v2(i)) * (v1(i) - v2(i))
        }
        distance = math.sqrt(distance)
        distance
    }
    //--------------------------向量加法-----------------------------
    def vectorAdd(v1:Vector[Double],v2:Vector[Double]):Vector[Double] = {
        var v3 = v1
        for(i <-  v1.indices){
            v3 = v3.updated(i,v1(i) + v2(i))
        }
        v3
    }
    //--------------------------向量除法-----------------------------
    def vectorDivide(v:Vector[Double],num:Int):Vector[Double] = {
        var r = v
        for(i <- v.indices){
            r = r.updated(i,r(i) / num)
        }
        r
    }

    def main(args: Array[String]): Unit = {
        val sparkConf = new SparkConf().setMaster("local[2]").setAppName("KmeansTest")
        val sc=new SparkContext(sparkConf)
        val rows=loadDataSet(sc)
        //ArrayBuffer(Vector(1.658985, 4.285136), Vector(-3.453687, 3.424321),
        // Vector(4.838138, -1.151539), Vector(-5.379713, -3.362104))
        initialCenters(rows)

        /**
          * 寻找到的最优中心点如下:
            Vector(-2.3914716666666664, 1.4491176666666667)
            Vector(4.838138, -1.151539)
            聚类结果如下:
            Vector(1.658985, 4.285136)-----------0
            Vector(-3.453687, 3.424321)-----------0
            Vector(4.838138, -1.151539)-----------1
            Vector(-5.379713, -3.362104)-----------0
          */
        kmeans(rows,centers)
        printResult(rows,centers)
    }
}

 

2.sparkmllib

object KmeansTest2 {
  def main(args: Array[String]): Unit = {
    val sparkConf=new SparkConf().setAppName("KmeansTest2").setMaster("local[2]")
    val sc=new SparkContext(sparkConf)

    val data=sc.textFile("")

    val parsedData=data.map(s=>Vectors.dense(s.split(" ").map(_.toDouble)))

    val numClusters=2
    val numIterations=30
    val model=KMeans.train(parsedData,numClusters,numIterations)

    // 数据模型的中心点
    println("Cluster centres:")
    for(c <- model.clusterCenters) {
      println("  " + c.toString)
    }

    // 使用误差平方之和来评估数据模型
    val cost = model.computeCost(parsedData)
    println("Within Set Sum of Squared Errors = " + cost)

    // 使用模型测试单点数据
    /*println("Vectors 7.3 1.5 10.9 is belong to cluster:" + model.predict(Vectors.dense("1.5 10.9".split(" ")
      .map(_.toDouble))))
    println("Vectors 4.2 11.2 2.7 is belong to cluster:" + model.predict(Vectors.dense("11.2 2.7".split(" ")
      .map(_.toDouble))))
    println("Vectors 18.0 4.5 3.8 is belong to cluster:" + model.predict(Vectors.dense("14.5 73.8".split(" ")
      .map(_.toDouble))))*/

    // 返回数据集和结果
    val result = data.map {
      line =>
        val linevectore = Vectors.dense(line.split(" ").map(_.toDouble))
        val prediction = model.predict(linevectore)
        line + " " + prediction
    }.collect.foreach(println)

    sc.stop

  }
}

 

 

你可能感兴趣的:(数据挖掘,机器学习)