原理 Spark是一個極爲優秀的大數據框架,在大數據批處理上基本無人能敵,流處理上也有一席之地,機器學習則是當前正火熱AI人工智能的驅動引擎,在大數據場景下如何發揮AI技術成爲優秀的大數據挖掘工程師必備技能。本文結合機器學習思想與Spark框架代碼結構來實現分佈式機器學習過程,希望與大家一起學習進步~
目錄
本文采用的組件版本爲:Ubuntu 19.10、Jdk 1.8.0_241、Scala 2.11.12、Hadoop 3.2.1、Spark 2.4.5,老規矩先開啓一系列Hadoop、Spark服務與Spark-shell窗口:
1.LDA原理介紹
隱式狄利克雷分佈(LDA) 是一種主題模型,可以從文本文檔集合中推斷出主題。可以將LDA視爲一種聚類算法,如下所示:
-
主題對應於聚類中心,文檔對應於數據集中的示例(行)。
-
主題和文檔都存在於特徵空間中,其中特徵向量是字數(詞袋)的向量。
-
LDA不會使用傳統的距離來估計聚類,而是使用基於文本文件生成方式的統計模型的功能。
DA涉及到的先驗知識有:二項分佈、Gamma函數、Beta分佈、多項分佈、Dirichlet分佈、馬爾科夫鏈、MCMC、Gibs Sampling、EM算法等。限於篇幅,本文不設計具體理論推導,具體可參考知乎深度文章:https://zhuanlan.zhihu.com/p/31470216
2.LDA參數
LDA通過setOptimizer功能支持不同的推理算法。EMLDAOptimizer使用似然函數的期望最大化學習聚類併產生綜合結果,同時 OnlineLDAOptimizer使用迭代小批量採樣進行在線變異推斷 ,並且通常對內存友好。
LDA將文檔集合作爲單詞計數和以下參數(使用構建器模式設置)的向量:
-
k:主題數(即羣集中心)
-
optimizer:用於學習LDA模型的優化程序, EMLDAOptimizer或者OnlineLDAOptimizer
-
docConcentration:Dirichlet參數,用於優先於主題的文檔分佈。較大的值鼓勵更平滑的推斷分佈。
-
topicConcentration:Dirichlet參數,用於表示主題(單詞)在先主題的分佈。較大的值鼓勵更平滑的推斷分佈。
-
maxIterations:限制迭代次數。
-
checkpointInterval:如果使用檢查點(在Spark配置中設置),則此參數指定創建檢查點的頻率。如果maxIterations很大,使用檢查點可以幫助減少磁盤上的隨機文件大小,並有助於故障恢復。
所有spark.mllib的LDA模型都支持:
-
describeTopics:以最重要的術語和術語權重的數組形式返回主題
-
topicsMatrix:返回一個vocabSize由k矩陣,其中各列是一個主題
期望最大化在EMLDAOptimizer和DistributedLDAModel中實現。對於提供給LDA的參數:
-
docConcentration:僅支持對稱先驗,因此提供的k維向量中的所有值都必須相同。所有值也必須> 1.0。提供Vector(-1)會導致默認行爲(值(50 / k)+1的統一k維向量)
-
topicConcentration:僅支持對稱先驗。值必須> 1.0。提供-1會導致默認值爲0.1 + 1。
-
maxIterations:EM迭代的最大數量。
-
注意:進行足夠的迭代很重要。在早期的迭代中,EM通常沒有用的主題,但是經過更多的迭代後,這些主題會大大改善。根據您的數據集,通常至少合理使用20次甚至50-100次迭代。
-
EMLDAOptimizer生成一個DistributedLDAModel,它不僅存儲推斷出的主題,還存儲完整的訓練語料庫和訓練語料庫中每個文檔的主題分佈。DistributedLDAModel支持:
-
topTopicsPerDocument:訓練語料庫中每個文檔的主要主題及其權重
-
topDocumentsPerTopic:每個主題的頂部文檔以及該主題在文檔中的相應權重。
-
logPrior:給定超參數docConcentration和topicConcentration時,估計主題和文檔主題分佈的對數概率
-
logLikelihood:給定推斷的主題和文檔主題分佈,訓練語料庫的對數可能性
3.Spark示例
在以下示例中,我們加載代表文檔語料庫的單詞計數向量。然後,我們使用LDA從文檔中推斷出三個主題。所需聚類的數量傳遞給算法。然後,我們輸出主題,表示爲單詞上的概率分佈。
import org.apache.spark.mllib.clustering.{DistributedLDAModel, LDA}
import org.apache.spark.mllib.linalg.Vectors
// 加載和解析數據
val data = sc.textFile("data/mllib/sample_lda_data.txt")
val parsedData = data.map(s => Vectors.dense(s.trim.split(' ').map(_.toDouble)))
// 用唯一ID索引文章
val corpus = parsedData.zipWithIndex.map(_.swap).cache()
// 使用LDA將文章聚類爲3類主題
val ldaModel = new LDA().setK(3).run(corpus)
// 輸出主題。每個主題都是單詞分佈(匹配單詞向量)
println(s"Learned topics (as distributions over vocab of ${ldaModel.vocabSize} words):")
val topics = ldaModel.topicsMatrix
for (topic <- Range(0, 3)) {
print(s"Topic $topic :")
for (word <- Range(0, ldaModel.vocabSize)) {
print(s"${topics(word, topic)}")
}
println()
}
// 保存和加載模型
ldaModel.save(sc, "target/org/apache/spark/LatentDirichletAllocationExample/LDAModel")
val sameModel = DistributedLDAModel.load(sc,
"target/org/apache/spark/LatentDirichletAllocationExample/LDAModel")
4.源碼解析
以上代碼主要做了兩件事:加載和切分數據、訓練模型。在樣本數據中,每一行代表一篇文檔,經過處理後,corpus的類型爲List((id,vector)*),一個(id,vector)代表一篇文檔。將處理後的數據傳給org.apache.spark.mllib.clustering.LDA類的run方法, 就可以開始訓練模型。run方法的代碼如下所示:
def run(documents: RDD[(Long, Vector)]): LDAModel = {
val state = ldaOptimizer.initialize(documents, this)
var iter = 0
val iterationTimes = Array.fill[Double](maxIterations)(0)
while (iter < maxIterations) {
val start = System.nanoTime()
state.next()
val elapsedSeconds = (System.nanoTime() - start) / 1e9
iterationTimes(iter) = elapsedSeconds
iter += 1
}
state.getLDAModel(iterationTimes)
}
這段代碼首先調用initialize方法初始化狀態信息,然後循環迭代調用next方法直到滿足最大的迭代次數。在我們沒有指定的情況下,迭代次數默認爲20。需要注意的是, ldaOptimizer有兩個具體的實現類EMLDAOptimizer和OnlineLDAOptimizer,它們分別表示使用EM算法和在線學習算法實現參數估計。在未指定的情況下,默認使用EMLDAOptimizer。
Spark kmeans族的聚類算法的內容至此結束,有關Spark的基礎文章可參考前文:
Spark MLlib分佈式機器學習源碼分析:K-means聚類
參考鏈接:
http://spark.apache.org/docs/latest/mllib-clustering.html
https://github.com/endymecy/spark-ml-source-analysis
歷史推薦
數據分析與挖掘
數據結構與算法
機器學習與大數據組件
歡迎關注,感謝“在看”,隨緣稀罕~