Spark集羣下的K-Means算法

Spark集羣下的K-Means算法

引言

1.1背景

由於剛剛開始學習Spark平臺,希望通過學習基礎的Spark機器學習算法的使用來對Spark平臺以及Scala語言進行一個簡單的瞭解和使用。在這裏我首先以最常見的機器學習的K-Means聚類算法。後期希望能夠在Spark上實現AHP算法。

1.2編寫目的

在學習過程中,發現了有許多介紹K-Means算法原理博客和文章,也有許多關於K-Means的代碼(其中包括有C、C++、Java、Scal
a等等),但是從項目的構建,數據的選取到最後的聚類結果,很少有對整個運行過程有一個系統的介紹。在這裏我避開K-Means
原理的介紹,重點闡述一下整個Spark環境上如何運行Scala的K-Means算法。

1.3參考資料

在這篇博客的編寫過程中,主要參考了《kmeans算法詳解與spark實戰》,《Spark下的K-Means算法》,感謝他們。
在項目的運行中,採用的也是博客中推薦的數據集《Wholesale customers Data Set

K-Means實現簡介

2.1完整代碼

我們採用上述註明的參考博客中的代碼進行運行,具體的代碼如下所示:

package KMeansTest

import org.apache.spark.mllib.clustering.{KMeans, KMeansModel}
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.{SparkConf, SparkContext}

object Kmeans {
  def main(args: Array[String]): Unit = {
    val conf =new SparkConf()
    val sc = new SparkContext(conf)
    val rawTrainingData = sc.textFile("file:///usr/local/spark/data_train")
    val parsedTrainingData =rawTrainingData.map{
      line=>
        Vectors.dense(line.split(',').map(_.toDouble))
      }.cache()
    val numClusters = 8
    val numIterations = 30
    val runTimes = 3
    var clusterIndex: Int = 0
    val clusters:KMeansModel= KMeans.train(parsedTrainingData,numClusters,numIterations,runTimes)
    println("cluster Number:"+clusters.clusterCenters.length)
    println("Cluster centers Information Overview")
    clusters.clusterCenters.foreach(
      x => {
        println("Center Point of Cluster" + clusterIndex + ":")
        println(x)
        clusterIndex +=1
      })
    val rawTestData = sc.textFile("file:///usr/local/spark/data_test")
    val parsedTestData =rawTestData.map{
      line=>
        Vectors.dense(line.split(',').map(_.toDouble))
    }
    parsedTestData.collect().foreach(
      testDataLine => {
        val predictedClusterIndex:
          Int = clusters.predict(testDataLine)
        println("The data "+ testDataLine.toString + "belongs to cluster"
        + predictedClusterIndex)
      })
    println("Spark MLlib K-means clustering test finished")
  }


}

2.2依賴分析

在上述代碼中,程序的依賴如下所示:

import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.mllib.clustering.{KMeans, KMeansModel}
import org.apache.spark.mllib.linalg.Vectors

其中調用不同的庫的作用分別如下:
(1)SparkContext是Spark Driver的核心,用於連接Spark集羣,創建RDD、累加器、廣播變量等等。
(2)SparkConf爲SparkContext的組件,是SparkContext的配置類,配置信息以鍵值對的形式存在。
(3)mllib.clustering是Spark Mlib庫中提供的用於做聚類的依賴。
(4)mllib.linalg.Vectors是分別用來保存MLlib的本地向量的,其中包含Dense和Sparse兩種分別用力來保存稠密向量和稀疏向量。

2.3屬性設置

val conf = new SparkConf().setAppName("K-Means Clustering")
val sc = new SparkContext(conf)

在上述代碼中,我們通過SparkConf()設置程序的參數,並傳遞到SparkContext中。

2.4讀取並分析數據

sc.textFile("file:///usr/local/spark/data_train")
   val parsedTrainingData =rawTrainingData.map{
      line=>
        Vectors.dense(line.split(',').map(_.toDouble))
      }.cache()

在上述代碼,中我們通過textFile讀取聚類文件中的數據,並將數據處理轉化成double類型,在數據集中數據之間通過‘,’隔開。

由於我們在UCI上下載到的數據集合中第一列是每一行屬性代表的含義,我們通過isColumnNameLine(_)來進行判斷。判斷代碼如下:

private def isColumnNameLine(line: String): Boolean = {
    if (line != null && line.contains("Channel")) true
    else false
  }

如果在該行中存在“Channel”子串,我們則不對該行進行處理。

2.5進行聚類訓練

val numClusters = 8
val numIterations = 30
val runTimes = 3
var clusterIndex: Int = 0
val clusters: KMeansModel =KMeans.train(parsedTrainingData, numClusters, numIterations, runTimes)
println("Cluster Number:" + clusters.clusterCenters.length)
println("Cluster Centers Information Overview:")
clusters.clusterCenters.foreach(
    x => {
        println("Center Point of Cluster " + clusterIndex + ":")
        println(x)
        clusterIndex += 1
})

根據上述代碼可知,首先我們設置具體的聚類參數:

val numClusters = 8
val numIterations = 30
val runTimes = 3
var clusterIndex: Int = 0

其中:numClusters = 8表示一共聚成了多少個簇,val numIterations = 30表示迭代的次數,val runTimes =
3表示運行的時間,var clusterIndex: Int = 0用來記錄簇的索引。
在設置完聚類參數後,我們通過具體的代碼利用KMeans.train()傳入參數對數據進行聚類分析,並返回聚類模型,輸出每一
個簇的中心點。

val clusters: KMeansModel =KMeans.train(parsedTrainingData, numClusters, numIterations, runTimes)
println("Cluster Number:" + clusters.clusterCenters.length)
println("Cluster Centers Information Overview:")
clusters.clusterCenters.foreach(
    x => {
        println("Center Point of Cluster " + clusterIndex + ":")
        println(x)
        clusterIndex += 1
})

2.6結果顯示

在聚類完成後,我們再次讀取數據集合中的數據信息,通過數據集合中的數據信息,並通過predict來預測每一個數據對象所
屬的簇。具體的代碼如下所示:

val rawTestData = sc.textFile("file:///usr/local/spark/data_train")
    val parsedTestData =rawTestData.map{
      line=>
        Vectors.dense(line.split(',').map(_.toDouble))
      }.cache()
    parsedTestData.collect().foreach(testDataLine => {
      val predictedClusterIndex:
      Int = clusters.predict(testDataLine)
      println("The data " + testDataLine.toString + " belongs to cluster " +
        predictedClusterIndex)
    })

數據集格式如下:

Channel Region Fresh Milk Grocery Frozen Detergents_Paper Delicassen
2 3 12669 9656 7561 214 2674 1338
2 3 7057 9810 9568 1762 3293 1776
2 3 6353 8808 7684 2405 3516
1 3 13265 1196 4221 6404 507 1788
2 3 22615 5410 7198 3915 1777 5185

根據UCI上對數據的介紹和上圖可以知道,該數據集合爲8維的數據集,其中對應的不同屬性分別爲:
Fresh:表示在新鮮產品的年度支出上。
Milk:表示一年內在奶制產品上的消費。
GROCERY:表示一年在零食上的消費。
FROZEN:表示一年在冷凍食品上的消費
DETERGENTS_PAPER:表示一年在洗滌用品和紙上的消費。
DELICATESSEN:表示一年在熟食上的消費。
CHANNEL:表示購買的渠道:分別爲團購和零售購買。
REGION:表示顧客所屬的地區,分別爲Lisnon, Oporto or Other
注意:在運行過程中,下載的文件爲.csv文件,我們只需要把後綴名去掉,則文件中每一個數據對象的屬性之間用‘,’隔開。

項目構建

3.1 Maven 構建項目

項目的IDE使用的是IDEA 2017,項目依賴管理採用的是Maven(當然也可以採用SBT,一樣的)。具體的建立過程就不在詳細敘述,不懂可以自行百度(很多博客都有寫)

這裏寫圖片描述

3.2 導入spark的jar包

在項目中加入spark-assembly-1.6.1-hadoop2.6.0.jar包,編寫程序是會用到。
該jar的路徑位於Spark 解壓目錄下的lib文件夾,如下:

這裏寫圖片描述
這裏寫圖片描述

3.3 將項目編譯並生成Jar包

在IDEA 中依次打開File—>Project Structure—>Artifacts, 點擊“+”號,選擇需要生成JAR包的項目和main函數。

這裏寫圖片描述

在IDEA 中點擊Build—>Build Artifacts—>build .如下面的圖中所示,生成的jar包放在項目文件下的\out目錄中。

這裏寫圖片描述

3.4 在Spark平臺上運行KMeans算法Jar包

啓動Spark平臺,本文中使用的集羣方式爲Spark on YARN,將相應的訓練數據和測試數據copy到對應的路徑中。Spark submit 提交的格式如下:

/usr/local/spark/bin/spark-submit --class KMeansTest.Kmeans /home/hadoop/Downloads/dl.jar

3.5 聚類結果

本文的數據量較小,運算時間比較快,結果如下:

這裏寫圖片描述

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章