Spark分佈式機器學習源碼分析:模型評估指標

 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窗口:

    spark.mllib附帶了許多機器學習算法,可用於學習數據並進行數據預測。 將這些算法應用於構建機器學習模型時,需要根據某些標準評估模型的性能,具體取決於應用程序及其要求。spark.mllib還提供了一組度量標準,用於評估機器學習模型的性能。

    

    特定的機器學習算法屬於更廣泛的機器學習應用程序類型,例如分類,迴歸,聚類等。這些類型中的每一種都有完善的性能評估指標,而此部分將詳細介紹spark.mllib中當前可用的那些指標。

1

二分類

    儘管分類算法有許多不同的類型,但是分類模型的評估都具有相似的原理。在監督分類問題中,每個數據點都存在真實輸出和模型生成的預測輸出。因此,可以將每個數據點的結果分配給以下四個類別之一:

  • 真陽性(TP)-標籤爲陽性,預測也爲陽性

  • 真陰性(TN)-標籤爲負,預測也爲負

  • 假陽性(FP)-標籤爲負,但預測爲正

  • 假陰性(FN)-標籤爲陽性,但預測爲陰性

    這四個數字是大多數分類器評估指標的基礎。考慮分類器評估時的基本要點是,單純的準確性(即預測正確與否)通常不是一個好的指標。其原因是因爲數據集可能高度不平衡。例如,如果模型被設計爲從數據集中預測欺詐的模型,其中95%的數據點不是欺詐,而5%的數據點是欺詐,則無論輸入如何,預測都不欺詐的樸素分類器將爲95 %準確。因此,通常會使用諸如精度和召回率之類的指標,因爲它們考慮到了錯誤的類型。在大多數應用中,精度和查全率之間存在一些理想的平衡,可以通過將兩者合併爲一個度量標準(稱爲F度量)來捕獲。

2

二分類

    二分類器用於將給定數據集的元素分爲兩個可能的組(例如欺詐或非欺詐)之一,這是多類分類的一種特殊情況。大多數二元分類指標可以概括爲多類分類指標。

    重要的是要理解,許多分類模型實際上爲每個類別輸出“分數”(通常是概率的乘積),其中較高的分數表示較高的可能性。在二元情況下,模型可以輸出每個類別的概率:P(Y=1|X)和P(Y=0|X)。在某些情況下,可能需要調整模型,以便僅在概率很高的情況下預測類別(例如,如果模型預測欺詐概率> 90%,則阻止信用卡交易),而不是簡單地採用較高的概率)。因此,存在一個預測閾值,該閾值可根據模型輸出的概率來確定預測類別。

    調整預測閾值將改變模型的精度和召回率,這是模型優化的重要組成部分。爲了可視化精度,召回率和其他指標如何隨閾值變化,通常的做法是將競爭指標相互繪製,並按閾值進行參數設置。P-R曲線繪製(精確度,召回率)點以表示不同閾值,而接收器工作特性曲線或ROC曲線繪製(召回率,誤報率)點。

3

多分類

    以下代碼段說明了如何加載樣本數據集,如何在數據上訓練二分類算法以及如何通過幾種二分類評估指標評估算法的性能。

import org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS
import org.apache.spark.mllib.evaluation.BinaryClassificationMetrics
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
// 加載libsvm格式的訓練數據
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_binary_classification_data.txt")
// 將數據分爲訓練(60%)和測試(40%)
val Array(training, test) = data.randomSplit(Array(0.6, 0.4), seed = 11L)
training.cache()
// 運行訓練算法以建立模型
val model = new LogisticRegressionWithLBFGS().setNumClasses(2).run(training)
// 清除預測閾值,以便模型返回概率
model.clearThreshold
// 計算測試集上的原始分數
val predictionAndLabels = test.map { case LabeledPoint(label, features) =>
  val prediction = model.predict(features)
  (prediction, label)
}
// 實例化指標對象
val metrics = new BinaryClassificationMetrics(predictionAndLabels)
// 通過閾值預測精度
val precision = metrics.precisionByThreshold
precision.foreach { case (t, p) =>
  println(s"Threshold: $t, Precision: $p")
}
// 通過閾值預測召回率
val recall = metrics.recallByThreshold
recall.foreach { case (t, r) =>
  println(s"Threshold: $t, Recall: $r")
}
//  PR曲線
val PRC = metrics.pr
// F值
val f1Score = metrics.fMeasureByThreshold
f1Score.foreach { case (t, f) =>
  println(s"Threshold: $t, F-score: $f, Beta = 1")
}
val beta = 0.5
val fScore = metrics.fMeasureByThreshold(beta)
f1Score.foreach { case (t, f) =>
  println(s"Threshold: $t, F-score: $f, Beta = 0.5")
}
// AUPRC
val auPRC = metrics.areaUnderPR
println(s"Area under precision-recall curve = $auPRC")
// 計算在ROC和PR曲線中使用的閾值
val thresholds = precision.map(_._1)
// ROC曲線
val roc = metrics.roc
// AUROC
val auROC = metrics.areaUnderROC
println(s"Area under ROC = $auROC")

3

多分類

    多分類描述了一個分類問題,其中每個數據點有M> 2個可能的標籤(其中M = 2是二分類問題)。例如,將手寫樣本分類爲具有10種可能類別的數字0到9。

    對於多類別指標,肯定和否定的概念略有不同。預測和標籤仍然可以是肯定的或否定的,但必須在特定類別的上下文中加以考慮。每個標籤和預測採用多個類別之一的值,因此對於它們的特定類別而言,它們被認爲是正的,而對於所有其他類別而言,它們都被認爲是負的。因此,每當預測和標籤匹配時,就會出現真陽性,而當預測和標籤都不採用給定類的值時,就會出現真陰性。按照這種約定,給定的數據樣本可能有多個真實的負數。從肯定標籤和否定標籤的先前定義中擴展假陰性和假陽性很簡單。

    與只有兩個可能的標籤的二分類相反,多類分類問題有很多可能的標籤,因此引入了基於標籤的度量的概念。 準確性衡量所有標籤的準確性-通過數據點的數量對任何類別進行正確預測(正確肯定)的次數。 按標籤的精度僅考慮一類,並根據標籤出現在輸出中的次數來衡量正確預測特定標籤的時間。

    定義類或標籤,設置爲,真實輸出向量y由N個元素組成,多類預測算法生成N個元素的預測向量。對於本節,修改後的增量函數δ^(x)將被證明是有用的

4

多分類實例

    以下代碼段說明了如何加載樣本數據集,如何在數據上訓練多分類算法以及如何通過幾種多類分類評估指標來評估算法的性能。

import org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS
import org.apache.spark.mllib.evaluation.MulticlassMetrics
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_multiclass_classification_data.txt")
val Array(training, test) = data.randomSplit(Array(0.6, 0.4), seed = 11L)
training.cache()
val model = new LogisticRegressionWithLBFGS().setNumClasses(3).run(training)
val predictionAndLabels = test.map { case LabeledPoint(label, features) =>
  val prediction = model.predict(features)
  (prediction, label)
}
val metrics = new MulticlassMetrics(predictionAndLabels)
// 混淆矩陣
println("Confusion matrix:")
println(metrics.confusionMatrix)
// 總體統計
val accuracy = metrics.accuracy
println("Summary Statistics")
println(s"Accuracy = $accuracy")
// 通過標籤預測
val labels = metrics.labels
labels.foreach { l =>
  println(s"Precision($l) = " + metrics.precision(l))
}
// 通過標籤計算召回率
labels.foreach { l =>
  println(s"Recall($l) = " + metrics.recall(l))
}
// 通過標籤計算假陽性率
labels.foreach { l =>
  println(s"FPR($l) = " + metrics.falsePositiveRate(l))
}


// 通過標籤計算F1
labels.foreach { l =>
  println(s"F1-Score($l) = " + metrics.fMeasure(l))
}
// 加權統計
println(s"Weighted precision: ${metrics.weightedPrecision}")
println(s"Weighted recall: ${metrics.weightedRecall}")
println(s"Weighted F1 score: ${metrics.weightedFMeasure}")
println(s"Weighted false positive rate: ${metrics.weightedFalsePositiveRate}")

5

多標籤分類

    多標籤分類問題涉及將數據集中的每個樣本映射到一組類標籤。在這種類型的分類問題中,標籤不是互斥的。例如,將一組新聞文章分類爲主題時,一篇文章可能既是科學又是政治。

    由於標籤不是互斥的,因此預測和真實標籤現在是標籤集的向量,而不是標籤的向量。因此,多標籤度量將精度,召回率等基本概念擴展到集合操作上。例如,當給定類別的某個特定數據點存在於預測集中且該類別存在於真實標籤集中時,則該類別爲真陽性。

    在這裏,我們定義了N個文檔的集合D:,將L0,L1,...,LN-1定義爲標籤集的族,並將P0,P1,...,PN-1定義爲預測集的族,其中Li和Pi分別是標籤集和預測集記錄di。所有唯一標籤的集合由

    對集合A的指標函數IA(x)的以下定義將是必要的

6

多標籤分類實例

    以下代碼段說明了如何評估多標籤分類器的性能。這些示例將僞造的預測和標籤數據用於多標籤分類。

import org.apache.spark.mllib.evaluation.MultilabelMetrics
import org.apache.spark.rdd.RDD
val scoreAndLabels: RDD[(Array[Double], Array[Double])] = sc.parallelize(
  Seq((Array(0.0, 1.0), Array(0.0, 2.0)),
    (Array(0.0, 2.0), Array(0.0, 1.0)),
    (Array.empty[Double], Array(0.0)),
    (Array(2.0), Array(2.0)),
    (Array(2.0, 0.0), Array(2.0, 0.0)),
    (Array(0.0, 1.0, 2.0), Array(0.0, 1.0)),
    (Array(1.0), Array(1.0, 2.0))), 2)
val metrics = new MultilabelMetrics(scoreAndLabels)
println(s"Recall = ${metrics.recall}")
println(s"Precision = ${metrics.precision}")
println(s"F1 measure = ${metrics.f1Measure}")
println(s"Accuracy = ${metrics.accuracy}")
metrics.labels.foreach(label =>
  println(s"Class $label precision = ${metrics.precision(label)}"))
metrics.labels.foreach(label => println(s"Class $label recall = ${metrics.recall(label)}"))
metrics.labels.foreach(label => println(s"Class $label F1-score = ${metrics.f1Measure(label)}"))


println(s"Micro recall = ${metrics.microRecall}")
println(s"Micro precision = ${metrics.microPrecision}")
println(s"Micro F1 measure = ${metrics.microF1Measure}")
println(s"Hamming loss = ${metrics.hammingLoss}")
println(s"Subset accuracy = ${metrics.subsetAccuracy}")

7

排序算法

    排名算法(通常被認爲是推薦系統)的作用是根據一些訓練數據向用戶返回一組相關項目或文檔。相關性的定義可能會有所不同,並且通常是特定於應用程序的。排名系統指標旨在量化這些排名或建議在各種情況下的有效性。一些度量將一組推薦的文檔與一組相關文檔的真實性進行比較,而其他度量可能會明確包含數字等級。

    排名系統通常處理一組M個用戶

    每個用戶(ui)都有一組Ni真實情況相關文檔

    並列出齊推薦文件,以降序排列

    排名系統的目標是爲每個用戶生成最相關的文檔集。集合的相關性和算法的有效性可以使用下面列出的指標進行衡量。

    有必要定義一個函數,該函數提供了一個推薦文檔和一組與地面事實相關的文檔,並返回了該推薦文檔的相關性得分。

8

排序算法實例

    以下代碼段說明了如何加載樣本數據集,如何在數據上訓練交替的最小二乘推薦模型以及如何通過幾個排名指標評估推薦器的性能。下面提供了該方法的簡要概述。

    

    此映射表示未觀察到的條目通常介於“正常”和“相當差”之間。在這個非正數權重的擴展世界中,0的語義“與從未交互過的相同”。

import org.apache.spark.mllib.evaluation.{RankingMetrics, RegressionMetrics}
import org.apache.spark.mllib.recommendation.{ALS, Rating}
// 讀取收視率數據
val ratings = spark.read.textFile("data/mllib/sample_movielens_data.txt").rdd.map { line =>
  val fields = line.split("::")
  Rating(fields(0).toInt, fields(1).toInt, fields(2).toDouble - 2.5)
}.cache()
// 將等級映射爲1或0,其中1表示應該推薦的電影
val binarizedRatings = ratings.map(r => Rating(r.user, r.product,
  if (r.rating > 0) 1.0 else 0.0)).cache()
// 彙總評分
val numRatings = ratings.count()
val numUsers = ratings.map(_.user).distinct().count()
val numMovies = ratings.map(_.product).distinct().count()
println(s"Got $numRatings ratings from $numUsers users on $numMovies movies.")
// 建立模型
val numIterations = 10
val rank = 10
val lambda = 0.01
val model = ALS.train(ratings, rank, numIterations, lambda)
// 定義一個函數以將等級從0縮放到1
def scaledRating(r: Rating): Rating = {
  val scaledRating = math.max(math.min(r.rating, 1.0), 0.0)
  Rating(r.user, r.product, scaledRating)
}
// 獲取每個用戶的排名前十的預測,然後從[0,1]開始縮放
val userRecommended = model.recommendProductsForUsers(10).map { case (user, recs) =>
  (user, recs.map(scaledRating))
}
// 假設用戶評分3或更高(對應於1)的任何電影都是相關文檔
// 與最相關的十大文件進行比較
val userMovies = binarizedRatings.groupBy(_.user)
val relevantDocuments = userMovies.join(userRecommended).map { case (user, (actual,
predictions)) =>
  (predictions.map(_.product), actual.filter(_.rating > 0.0).map(_.product).toArray)
}
// 實例化指標對象
val metrics = new RankingMetrics(relevantDocuments)
//精度
Array(1, 3, 5).foreach { k =>
  println(s"Precision at $k = ${metrics.precisionAt(k)}")
}
// 平均平均精度
println(s"Mean average precision = ${metrics.meanAveragePrecision}")
// 歸一化折現累計收益
Array(1, 3, 5).foreach { k =>
  println(s"NDCG at $k = ${metrics.ndcgAt(k)}")
}
// 獲取每個數據點的預測
val allPredictions = model.predict(ratings.map(r => (r.user, r.product))).map(r => ((r.user,
  r.product), r.rating))
val allRatings = ratings.map(r => ((r.user, r.product), r.rating))
val predictionsAndLabels = allPredictions.join(allRatings).map { case ((user, product),
(predicted, actual)) =>
  (predicted, actual)
}
// 使用迴歸指標獲取RMSE
val regressionMetrics = new RegressionMetrics(predictionsAndLabels)
println(s"RMSE = ${regressionMetrics.rootMeanSquaredError}")
// 計算R方
println(s"R-squared = ${regressionMetrics.r2}")

9

迴歸算法評估

    從多個自變量預測連續輸出變量時,將使用迴歸分析。

    

    以下代碼段說明了如何加載樣本數據集,在數據上訓練線性迴歸算法以及如何通過多個迴歸指標評估算法的性能。

import org.apache.spark.mllib.evaluation.RegressionMetrics
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.mllib.regression.{LabeledPoint, LinearRegressionWithSGD}
// 加載數據
val data = spark
  .read.format("libsvm").load("data/mllib/sample_linear_regression_data.txt")
  .rdd.map(row => LabeledPoint(row.getDouble(0), row.get(1).asInstanceOf[Vector]))
  .cache()
// 建立模型
val numIterations = 100
val model = LinearRegressionWithSGD.train(data, numIterations)
// 獲取預測
val valuesAndPreds = data.map{ point =>
  val prediction = model.predict(point.features)
  (prediction, point.label)
}
// 實例化指標對象
val metrics = new RegressionMetrics(valuesAndPreds)
// Squared error
println(s"MSE = ${metrics.meanSquaredError}")
println(s"RMSE = ${metrics.rootMeanSquaredError}")
// R-squared
println(s"R-squared = ${metrics.r2}")
// Mean absolute error
println(s"MAE = ${metrics.meanAbsoluteError}")
// Explained variance
println(s"Explained variance = ${metrics.explainedVariance}")

    Spark模型評估指標以及源碼分析的全部內容至此結束,有關Spark的基礎文章可參考前文:

    Spark分佈式機器學習源碼分析:矩陣向量

    Spark分佈式機器學習源碼分析:基本統計

    Spark分佈式機器學習源碼分析:線性模型

    Spark分佈式機器學習源碼分析:樸素貝葉斯

    Spark分佈式機器學習源碼分析:決策樹模型

    Spark分佈式機器學習源碼分析:集成樹模型

    Spark分佈式機器學習源碼分析:協同過濾

    Spark分佈式機器學習源碼分析:K-means

    Spark分佈式機器學習源碼分析:隱式狄利克雷分佈

    Spark分佈式機器學習源碼分析:奇異值分解與主成分分析

    Spark分佈式機器學習源碼分析:特徵提取與轉換

    Spark分佈式機器學習源碼分析:頻繁模式挖掘

    參考鏈接:

    http://spark.apache.org/docs/latest/mllib-evaluation-metrics.html

歷史推薦

“高頻面經”之數據分析篇

“高頻面經”之數據結構與算法篇

“高頻面經”之大數據研發篇

“高頻面經”之機器學習篇

“高頻面經”之深度學習篇

爬蟲實戰:Selenium爬取京東商品

爬蟲實戰:豆瓣電影top250爬取

爬蟲實戰:Scrapy框架爬取QQ音樂

數據分析與挖掘

數據結構與算法

機器學習與大數據組件

歡迎關注,感謝“在看”,隨緣稀罕~

 

一個贊,晚餐加雞腿

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