1. 程式人生 > >在Spark SQL對人類資料實現K-Means聚類,並對聚類中心格式化輸出

在Spark SQL對人類資料實現K-Means聚類,並對聚類中心格式化輸出

簡介

本篇博文對UCI提供的 Machine-Learning-Databases 資料集進行資料分析,並通過K-Means模型實現聚類,最後格式化輸出聚類中心點。

本文主要包括以下內容:

  • 通過VectorAssembler來將多列資料合成一列features資料,作為聚類模型的inputCol
  • K-Means模型的基礎理論和引數的意義

配置

  • 語言:Scala 2.11
  • Spark版本:Spark 2.3.1

聚類理論基礎

  • 聚類屬於“無監督學習”,即訓練樣本的標記資訊是未知的,目標是通過對無標記訓練樣本的學習來揭示資料的內在性質及規律,為進一步的資料分析提供基礎。
  • 聚類試圖將資料集中的樣本劃分為若干個通常是不相交的子集,每個子集稱作“簇”,簇所對應的概念語義需由使用者來把握和命名。
  • “簇內相似度”高且“簇間相似度”低的簇類結果是最理想的情況,我們可以用“外部指標”和“內部指標”來對簇類結果進行效能度量,前者是通過“參考模型”進行比較,後者是直接考察聚類結果。
  • 常用外部指標:雅卡爾指數FM指數Rand指數
  • 常用內部指標:DB指數Dunn指數

本篇文章中我們使用K-Means,即k均值演算法來計算聚類
給定一個觀測資料集(x1, x2, …, xn),每個資料為一個d維度的向量,K-Means演算法的目的是最小化WCSS(within-cluster sum of squares), 即聚類內部的平方和,即最小化以下值:
在這裡插入圖片描述
具體演算法步驟如下:

  1. Assignment Step(賦值操作):對每一個數據分配到與其有最小的
    歐幾里得距離
    的聚類中:在這裡插入圖片描述
  2. Update Step:通過聚類中的所有點取平均來更新聚類中心點座標:
    在這裡插入圖片描述

聚類模型引數設定

聚類模型在Spark 2.3.2中分為ml與mllib兩種,之前博主嘗試寫過mllib類的K-Means模型,當時水平有限,方法比較笨,大家可以參考一下,在此我們使用ml中的KMeans來對DataFrame類的資料集計算聚類。
首先查閱一下官方文件中給出的資訊:Spark 2.3.2 ScalaDoc - KMeans
可見其有6個引數:

引數名 意義
featuresCol 用於生成聚類的資訊所在列名,其型別必須為Array
k 簇的個數
maxIter 迭代的最大次數
seed 用於生成隨機數的種子
predictionCol 用於輸出演算法所得所屬簇序號的列名
tol 收斂偏差(Convergence Tolerance)的引數值

我們可以通過如下示例程式碼所示呼叫KMeans演算法:

      val kmeans = new KMeans()
        .setK(8)
        .setMaxIter(500)
        .setSeed(1L)
        .setFeaturesCol("features")

可見此段程式碼設定了四個引數值,我們可以用一下程式碼對其賦予資料集,並得到模型的引數:

      val model = kmeans.fit(indexed_assembled_table)
      val predictions = model.transform(indexed_assembled_table)
      println(model.explainParams())

      val evaluator = new ClusteringEvaluator()
      val silhouette = evaluator.evaluate(predictions)

我們用以上程式碼將資料集indexed_assembled_table賦予KMeans模型,用explainParams可以輸出引數資訊,用ClusteringEvaluator可以分析聚類模型,並輸出得到的Silhouette值

featuresCol: features column name (default: features, current: features)
initMode: The initialization algorithm. Supported options: 'random' and 'k-means||'. (default: k-means||)
initSteps: The number of steps for k-means|| initialization mode. Must be > 0. (default: 2)
k: The number of clusters to create. Must be > 1. (default: 2, current: 8)
maxIter: maximum number of iterations (>= 0) (default: 20, current: 500)
predictionCol: prediction column name (default: prediction)
seed: random seed (default: -1689246527, current: 1)
tol: the convergence tolerance for iterative algorithms (>= 0) (default: 1.0E-4)

0.389470051502839

我們還得到clusterCenters,即聚類中心:

      model.clusterCenters.foreach(println)

輸出如下:

[37.406960769348196,7.173255991451687,12.741413524652726,5.199969470309877,10.209738971149443,3.9113112501908107,3.8009464203938332,0.7029461150969318,40.70691497481301]
[56.7424205675479,6.8239146252728595,12.242056754790202,5.122726170264371,10.039291777831675,4.166626243026922,3.839437302934756,0.7203492602473927,40.690031530439]
[46.18890761452428,6.9128236555844955,12.565650375363873,5.177110464225525,10.363260303355293,4.017925540064348,3.811858434196415,0.7099739543434962,40.74490577600735]
[21.371826371826373,7.3641277641277645,12.695986895986897,5.0175266175266175,8.841769041769043,3.111875511875512,3.7890253890253893,0.5580671580671581,40.75479115479116]
[48.94514767932489,7.261603375527425,10.656118143459915,4.881856540084388,10.09704641350211,3.6497890295358646,3.1561181434599153,0.6265822784810126,19.567510548523206]
[69.74337957124843,6.372005044136191,11.478562421185371,4.66078184110971,9.004413619167718,4.171500630517024,3.8549810844892813,0.6740226986128626,40.433165195460276]
[29.456709628506445,7.339802880970432,12.796512509476877,5.178468536770281,10.080515542077332,3.7164518574677787,3.7707354056103113,0.6694465504169826,40.711902956785444]
[28.708333333333336,7.346666666666668,11.891666666666667,5.091666666666667,9.626666666666667,3.2800000000000002,3.0083333333333337,0.655,19.125]

其他函式介紹

VectorAssembler

由於聚類的生成需要我們輸入的資料集有features列提供聚合的資料,我們需要用VectorAssembler將不同行的資料合成到一個Array中,首先我們檢視其文件:Spark 2.3.2 ScalaDoc - VectorAssembler
可見其需要兩個引數:

引數 意義
inputCols StringArrayParam類資料,一般是一個由字串組成的Array,規定了需要聚合的列名
outputCol 聚合所得結果的名稱,預設為features

示例程式碼如下:

      val assembler = new VectorAssembler()
          .setInputCols(info_elements)
          .setOutputCol("features")
      val indexed_assembled_table = assembler.transform(indexed_table)

效果對比如下:

+---+--------------+--------------+--------------------+---------------+-----------------+---------+--------+-------------------+
|age|workclassIndex|educationIndex|maritial_statusIndex|occupationIndex|relationshipIndex|raceIndex|sexIndex|native_countryIndex|
+---+--------------+--------------+--------------------+---------------+-----------------+---------+--------+-------------------+
| 50|           8.0|          14.0|                 6.0|           13.0|              5.0|      4.0|     1.0|               41.0|
| 34|           6.0|          13.0|                 6.0|           14.0|              5.0|      4.0|     1.0|               41.0|
| 44|           8.0|          15.0|                 4.0|           10.0|              4.0|      4.0|     1.0|               41.0|
| 21|           8.0|          15.0|                 5.0|            2.0|              3.0|      4.0|     1.0|               41.0|
| 37|           8.0|          14.0|                 6.0|            8.0|              5.0|      4.0|     1.0|               41.0|
| 34|           5.0|           8.0|                 4.0|            7.0|              4.0|      4.0|     1.0|               21.0|
| 18|           8.0|           4.0|                 5.0|           11.0|              4.0|      4.0|     0.0|               41.0|
| 34|           5.0|          15.0|                 6.0|            7.0|              5.0|      4.0|     1.0|               41.0|
+---+--------------+--------------+--------------------+---------------+-----------------+---------+--------+-------------------+

+--------------------+
|            features|
+--------------------+
|[37.0,7.0,13.0,5....|
|[57.0,7.0,12.0,5....|
|[46.0,7.0,13.0,5....|
|[21.0,7.0,13.0,5....|
|[49.0,7.0,11.0,5....|
|[70.0,6.0,11.0,5....|
|[29.0,7.0,13.0,5....|
|[29.0,7.0,12.0,5....|
+--------------------+

聚類中心的格式化輸出

+----+----------------+---------+---------------+-------------+-------------+-----+----+
|age |workclass       |education|maritial_status|occupation   |relationship |race |sex |
+----+----------------+---------+---------------+-------------+-------------+-----+----+
|37.0|Self-emp-not-inc|Bachelors|Never-married  |Sales        |Not-in-family|White|Male|
|57.0|Self-emp-not-inc|Masters  |Never-married  |Sales        |Not-in-family|White|Male|
|46.0|Self-emp-not-inc|Bachelors|Never-married  |Sales        |Not-in-family|White|Male|
|21.0|Self-emp-not-inc|Bachelors|Never-married  |Other-service|Own-child    |White|Male|
|49.0|Self-emp-not-inc|Assoc-voc|Never-married  |Sales        |Not-in-family|Black|Male|
|70.0|Local-gov       |Assoc-voc|Never-married  |Other-service|Not-in-family|White|Male|
|29.0|Self-emp-not-inc|Bachelors|Never-married  |Sales        |Not-in-family|White|Male|
|29.0|Self-emp-not-inc|Masters  |Never-married  |Sales        |Own-child    |Black|Male|
+----+----------------+---------+---------------+-------------+-------------+-----+----+

參考資料