資料演算法 Hadoop/Spark大資料處理---第十二章

weixin_34365417發表於2018-07-08

本章為K-均值聚類

K-均值聚類的演算法思想

==(其中K是希望輸入資料生成多少個組合簇,輸入一般為:N個d維的點和一個目標K,輸出為:找到這些簇的位置ui,使得資料點到簇質心的距離最小)==

  • 1.隨機選擇的初始質心,非質心點指派給最近的質心而形成新的簇
  • 2.得到簇之後,重寫計算質心:假設k-means聚類過程中,得到某一個簇的集合Ci={p(x1,y1), p(x2,y2), …,p(xn,yn)},則簇Ci的質心,質心x座標為(x1+x2+ …+xn)/n,質心y座標為(y1+y2+ …+yn)/n。
  • 3.演算法的終止條件:質心在每一輪迭代中會發生變化,然後需要重新將非質心點指派給最近的質心而形成新的簇,如果只有==很少的一部分==點在迭代過程中,還在改變簇(如:更新一次質心,有些點從一個簇移動到另一個簇),那麼滿足這樣一個收斂條件,可以提前結束迭代過程。


    6231724-c89f76be40ae76bf.png
    image
  • 4.使用的距離函式是曼哈頓距離求值函式


    6231724-580d15a0be83b9a4.png
    image
//一個求曼哈頓距離的java實現
public class EuclideanDistance{
    public static double calculateDistance(Vector center,Vector data){
        double sum = 0.0;
        int length = center.length;
        for(int i=0;i<length;i++){
            sum+=Math.pow((center[i]-data[i]),2)
        }    
        return Math.sqrt(sum)
    }
}

本章實現方式

  • 1.基於Mapreduce的虛擬碼實現
  • 2.基於傳統Scala來實現

++基於傳統spark來實現++

1. K-均值聚類演算法

//k = 期望的簇數 delta=可接受的收斂誤差 data=輸入的資料
kmeans(k,delta,data){
    //初始化簇質心
    initial_cretroids = pick(k,data);
    //向對映器廣播中心
    writeToHDFS(initial_cretroids);
    current_cretroids=  initial_cretroids;
    while(true){
        //1.map()中使用current_cretroids
        //2.reduct() 建立new_cretroids並寫至HDFS
        theMapReduceJob();
        new_cretroids = readFromHDFS();
        //當那個簇心的差距很小時,停止求值
        if change(new_cretroids,current_cretroids)<=delta{
            break;
        }else{
            current_cretroids=new_cretroids;
        }
        return =  readFromHDFS();
        return result;
    }
}

2. map端求值

//輸入的value為D維的向量
map(Object key,Vector value){
    Vector nearest = null;
    double nearestDistance = Double.MAX_VALUE
    //對每個簇點進行求值
    for(Vector center : centers){
        double distance = EuclideanDistance.calculateDistance(center,value)
        //第一次初始時的賦值
        if(nearest = null){
            nearest = center;
            nearestDistance = distance;
        }else{
            if()nearestDistance>distance){
                 nearest = center;
                 nearestDistance = distance;
            }
        }
    }
    //輸入為最近簇nearest,value是一個距離(多維的向量)
    //為那個點發出一個最近簇和距離,所以要combine()
    emit(nearest,value);
}

3. combine對value進行累加求值,在後續的reduce中可用

combine(Vertor key ,Iterable<Vector> values){
    Vector sum = new Vector();
    for(Vector value :values ){
        for(int i=0;i<value.length;i++){
            sum[i]+=value[i];
        }
    }
    //將相同簇的向量的每一維度相加
    emit(key,sum)
}

4. reduce函式計算新的質心

//combine結果不等於reduce結果,combine只發生在買個小map階段
reduct(Vertor key , Iterable<Vertor> values){
    Vertor newCenter = new Vertor();
    int count=0;
    for(Vertor value : values){
        count++;
        for(int i=0;i<value.length;i++){
            newCenter+=value[i]
        }
    }
    for(int i=0;i<key.length;i++){
        newCenter[i] = newCenter[i]/count
    }
    emit(key.ID ,newCenter);
}

++基於傳統scala來實現++

def main(args: Array[String]): Unit = {
    if (args.size < 3) {
      println("Usage: ScalaKMeans <input-path> <k> <max-iterations> [<runs>]")
      sys.exit(1)
    }

    val sparkConf = new SparkConf().setAppName("KMeans")
    val sc = new SparkContext(sparkConf)
    //輸入
    val input = args(0)
    //簇的個數
    val k  = args(1).toInt
    //迭代的次數iterations
    val iterations = args(2).toInt
    //讀取輸入的檔案
    val lines = sc.textFile(input)

    //建立向量點
    val points = lines.map(line =>{
      val tokens = line.split("\\s+")
      Vectors.dense(tokens.map(_.toDouble))
    })
    //用k均值演算法進行訓練
    val model = KMeans.train(points,k,iterations,KMeans.K_MEANS_PARALLEL)
    //多次迭代之後的k均值模式model,計算它的損失值
    val cost = model.computeCost(points)
  }

相關文章