當前位置: 首頁>>機器學習>>正文


Spark機器學習庫指南[Spark 1.3.1版]——聚類(Clustering)

下麵是章節聚類的內容(其他內容參見全文目錄)

聚類是一個無監督學習問題,我們基於相似的特性將數據分組成多個子集。聚類通常用於探索性分析或者作為分層監督學習管道(每個簇訓練不同的分類或者回歸模型)的組件。

MLlib支持下麵的幾個模型:

K均值(K-means)

K均值(k-means)是最通用的聚類算法之一,該算法將數據點聚類為指定數量的簇(注:基本算法原理是隨機挑選N個中心點,每輪計算所有點到中心點的距離,並將點放到最近的中心,然後均值更新中心點,然後重複上述過程直至收斂,收斂的判斷依據是距離閾值)。MLLib的實現包含了 k-means++的並行計算變體,該算法也叫kmeans||。它有下列參數:

  • k 需要聚簇的數量
  • maxIterations 最大迭代次數
  • initializationMode 指定初始化的模式,可以是隨機初始化也可以是k-means||初始化 (k-means||初始化不全是隨機選點,而是使用一個算法使選的點盡可能分散).
  • runs 執行K均值聚簇算法的次數  (k-means不保證能找到全局最優解,同一數據集上執行多次的話,可以返回更好的聚簇結果)。
  • initializationSteps 使用k-means|| 算法選初始點時最多迭代的次數.
  • epsilon 判定k-means是否收斂的距離閾值(聚簇中心前後兩次的差值小於epsilon即達到收斂條件)

補充1:kmeans的損失函數。其中(x1, x2, …, xn)是點集,每個點是d維向量,S是聚類的k個簇,μi 是Si 中所有點的均值)。這個損失函數也叫WSSS( within set sum of square)

kmeans

 

 

補充2:kmeans++方法:

kmeans++算法的主要工作體現在種子點的選擇上,基本原則是使得各個種子點之間的距離盡可能的大,但是又得排除噪聲的影響。 以下為基本思路:[1]

1、從輸入的數據點集合(要求有k個聚類)中隨機選擇一個點作為第一個聚類中心
2、對於數據集中的每一個點x,計算它與最近聚類中心(指已選擇的聚類中心)的距離D(x)
3、選擇一個新的數據點作為新的聚類中心,選擇的原則是:D(x)較大的點,被選取作為聚類中心的概率較大
4、重複2和3直到k個聚類中心被選出來
5、利用這k個初始的聚類中心來運行標準的k-means算法

下麵的示例可以使用PySpark Shell來測試。

在下麵的示例中,首先導入並解析數據,然後使用KMeans將數據聚為2類(期望的簇數量需要作為參數傳遞給算法)。然後計算了WSSSE(集合內平方誤差和)。我們可以通過增加k來降低這個錯誤評估指標。事實上,最優的k通常對應WSSSE曲線中的拐點。

from pyspark.mllib.clustering import KMeans
from numpy import array
from math import sqrt

# Load and parse the data
data = sc.textFile("data/mllib/kmeans_data.txt")
parsedData = data.map(lambda line: array([float(x) for x in line.split(' ')]))

# Build the model (cluster the data)
clusters = KMeans.train(parsedData, 2, maxIterations=10,
        runs=10, initializationMode="random")

# Evaluate clustering by computing Within Set Sum of Squared Errors
def error(point):
    center = clusters.centers[clusters.predict(point)]
    return sqrt(sum([x**2 for x in (point - center)]))

WSSSE = parsedData.map(lambda point: error(point)).reduce(lambda x, y: x + y)
print("Within Set Sum of Squared Error = " + str(WSSSE))

 

高斯混合

高斯混合模型 表達的是一種混合分布,所有點都來自於k個高斯子分布中的一個,每個點都對應一個相應的概率。在MLlib的實現中,對於給定的樣本集,使用最大期望算法(EM)來引導最大似然模型。算法實現由下列參數:

  • k 目標聚簇數量
  • convergenceTol 兩次迭代損失(log-likelihood)變化的容忍度.
  • maxIterations 收斂之前可以運行的最大迭代次數
  • seed 隨機數的種子。

 

補充:

多維度(多分量)數據的高斯混合聚類原理

目標函數是log似然函數(log-likelihood):

1353293351_6835

 

 

相關符號說明:

  • πk : 第k個分布被選中的概率。
  • uk:  第k個分布的均值向量(維度是d)。
  • Σk:  第k個分布的協方差矩陣(d x d的矩陣)。
  • N(xi |uk , Σk)是多分量高斯分布的概率密度函數。

1. 初始化:為πk , uk , Σk生成隨機初始值。(滿足約束:K個πk的和為1)

2. Expectation計算:估計樣本由每個高斯分布生成的概率:對於數據x它由k個分布生成的概率為(第二個等式是D維變量的高斯密度函數):

rik

n

 

3. 最大化似然函數:在當前這一輪迭代中, 取值如下時,似然函數最大(這些公式經過一係列的數學推導得到(省略1000字)):

lk

4. 重複2,3直至收斂。


示例

下麵的示例中,首先導入並解析數據,然後使用高斯混合 將數據聚為兩類。最後輸出混合模型的參數。

from pyspark.mllib.clustering import GaussianMixture
from numpy import array

# Load and parse the data
data = sc.textFile("data/mllib/gmm_data.txt")
parsedData = data.map(lambda line: array([float(x) for x in line.strip().split(' ')]))

# Build the model (cluster the data)
gmm = GaussianMixture.train(parsedData, 2)

# output parameters of model
for i in range(2):
    print ("weight = ", gmm.weights[i], "mu = ", gmm.gaussians[i].mu,
        "sigma = ", gmm.gaussians[i].sigma.toArray())

冪迭代聚類 (PIC)

對於圖的頂點聚類(頂點相似度作為邊的屬性)問題,冪迭代聚類(PIC)是高效並且易擴展的算法(參考: Lin and Cohen, Power Iteration Clustering)。MLlib包含了一個使用GraphX(MLlib)為基礎的實現。算法的輸入是RDD[srcID, dstID, similarity],輸出是每個頂點對應的聚類的模型。相似度(similarity)必須是非負值。PIC假設相似度的衡量是對稱的,也就是說在輸入數據中,(srcID, dstID)順序無關(例如:<1, 2, 0.1>, <2, 1, 0.1等價),但是隻能出現一次。輸入中沒有指定相似度的點對,相似度會置0。MLlib中的PIC實現具有下列參數:

  • k:  聚簇的數量
  • maxIterations: 最大迭代次數
  • initializationMode: 初始化模式:默認值“random”,表示使用一個隨機向量作為頂點的聚類屬性;也可以是“degree”,表示使用歸一化的相似度和(作為頂點的聚類屬性)。

示例

下麵的代碼片段說明了如何使用MLlib中的PIC(這裏是Scala版,Python版後續才會實現)

PowerIterationClustering 實現了PIC算法。它的輸入是以RDD[srcId :Long, dstId: Long, similarity: Double]元組表示的關係矩陣。然後調用PowerIterationClustering.run並返回PowerIterationClusteringModel,它包含了計算出的類分配信息。

import org.apache.spark.mllib.clustering.PowerIterationClustering
import org.apache.spark.mllib.linalg.Vectors

val similarities: RDD[(Long, Long, Double)] = ...

val pic = new PowerIteartionClustering()
  .setK(3)
  .setMaxIterations(20)
val model = pic.run(similarities)

model.assignments.foreach { a =>
  println(s"${a.id} -> ${a.cluster}")
}

隱含狄利克雷分布 (LDA)

隱含狄利克雷分布(LDA) 是一個主題模型,它能夠推理出一個文本文檔集合的主體。LDA可以認為是一個聚類算法,原因如下:

  • 主題對應聚類中心,文檔對應數據集中的樣本(數據行)
  • 主題和文檔都在一個特征空間中,其特征向量是詞頻向量。
  • 跟使用傳統的距離來評估聚類不一樣的是,LDA使用評估方式是一個函數,該函數基於文檔如何生成的統計模型。

LDA以詞頻向量表示的文檔集合作為輸入。然後在最大似然函數上使用期望最大(EM)算法 來學習聚類。完成文檔擬合之後,LDA提供:

  • Topics: 推斷出的主題,每個主體是單詞上的概率分布。
  • Topic distributions for documents: 對訓練集中的每個文檔,LDA給了一個在主題上的概率分布。

LDA參數如下:

  • k:  主題數量(或者說聚簇中心數量)
  • maxIterations: EM算法的最大迭代次數。
  • docConcentration: 文檔在主題上分布的先驗參數。當前必須大於1,值越大,推斷出的分布越平滑。
  • topicConcentration: 主題在單詞上的先驗分布參數。當前必須大於1,值越大,推斷出的分布越平滑。
  • checkpointInterval: 檢查點間隔。maxIterations很大的時候,檢查點可以幫助減少shuffle文件大小並且可以幫助故障恢複。

注意:當前在MLlib中,LDA是一個新特性,部分函數還沒有實現。特別是,目前還不支持新文檔的預測。另外也沒有Python的API。這些功能後續會添加進來。

示例(Scala)

下麵的例子中,首先導入詞頻向量表示的文檔預料,然後使用LDA 推測文檔的3個主題。最後,輸出主題在單詞上的概率分布。

import org.apache.spark.mllib.clustering.LDA
import org.apache.spark.mllib.linalg.Vectors

// Load and parse the data
val data = sc.textFile("data/mllib/sample_lda_data.txt")
val parsedData = data.map(s => Vectors.dense(s.trim.split(' ').map(_.toDouble)))
// Index documents with unique IDs
val corpus = parsedData.zipWithIndex.map(_.swap).cache()

// Cluster the documents into three topics using LDA
val ldaModel = new LDA().setK(3).run(corpus)

// Output topics. Each is a distribution over words (matching word count vectors)
println("Learned topics (as distributions over vocab of " + ldaModel.vocabSize + " words):")
val topics = ldaModel.topicsMatrix
for (topic <- Range(0, 3)) {
  print("Topic " + topic + ":")
  for (word <- Range(0, ldaModel.vocabSize)) { print(" " + topics(word, topic)); }
  println()
}

流式K均值

當數據以流式到達,就需要動態預測分類,每當新數據到來時要更新模型。MLlib提供了流式k均值聚類,該方法使用參數來控製數據的衰減。這個算法使用mini-batch k均值更新規則的一種泛化版本。對於每一批數據,將所有點賦給最近的簇,計算新的簇中心,然後使用下麵的方法更新簇:

111

其中c
衰減可以通過使用halfLife參數指定。對於時刻t取得的數據,在t+halfLife時刻貢獻度會降到0.5。

 

示例

 

下麵的例子(scala)說明了如果對流式數據預測分類。

首先包含需要的類。

import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.clustering.StreamingKMeans

然後為訓練和測試分別創建輸入流。假設StreamingContext ssc已經創建好(參考Spark Streaming Programming Guide)。

val trainingData = ssc.textFileStream("/training/data/dir").map(Vectors.parse)
val testData = ssc.textFileStream("/testing/data/dir").map(LabeledPoint.parse)

創建以隨機數方式生成中心的模型,並指定聚類數量。

val numDimensions = 3
val numClusters = 2
val model = new StreamingKMeans()
  .setK(numClusters)
  .setDecayFactor(1.0)
  .setRandomCenters(numDimensions, 0.0)

 

注冊訓練和測試數據流,並啟動任務。每當有新數據到達的時候,輸出預測的類。

model.trainOn(trainingData)
model.predictOnValues(testData.map(lp => (lp.label, lp.features))).print()

ssc.start()
ssc.awaitTermination()

當添加新文本文件的時候,聚類中心會被更新。訓練點格式:[x1, x2, x3], 測試點格式(y, [x1,x2,x3]),y是類型標記。任意時間有文本放到/training/data/dir下,模型將被更新。任意時間,文本放到/testing/data/dir下預測值就會輸出。對於新的數據,聚類中心會改變。

參考

[1] http://www.jb51.net/article/49395.htm

[2] http://en.wikipedia.org/wiki/Expectation%E2%80%93maximization_algorithm

[3] http://en.wikipedia.org/wiki/Maximum_likelihood

[4] http://en.wikipedia.org/wiki/Power_iteration

[5] http://blog.csdn.net/abcjennifer/article/details/8198352

本文由《純淨天空》出品。文章地址: https://vimsky.com/zh-tw/article/609.html,未經允許,請勿轉載。