spark.mllib:NaiveBayes
樸素貝葉斯模型簡述:
貝葉斯模型通過使用后驗概率和類的概率分布來估計先驗概率,具體的以公式表達為
P(Y)可以使用訓練樣本的類分布進行估計。如果X是單特征也很好估計,但如果X={x1,x2,..,xn}等n個特征構成,那估計n個特征的聯合概率分布P(X)=P(x1,x2,...,xn)將變得非常困難。由于貝葉斯模型的參數難于估計,限制了其的應用。
樸素貝葉斯模型是貝葉斯模型的簡化版本,通過假設特征之間獨立不相關,那么?
通過求解每個特征的分布和每個特征的后驗概率來近似特征的聯合概率分布和特征的后驗概率。當然,通常情況下,特征相互獨立的假設不會成立,這里只是模型復雜度和模型精度的一個權衡。這樣樣本x屬于第i類和第k類的概率分別為:
由于P(X)聯合概率分布對每個類都是相同的,可以不求。
Spark下樸素貝葉斯的具體實現:
NaiveBayesModel
NaiveBayesModel保存了樸素貝葉斯模型的參數,繼承自ClassificationModel,并重寫了predict方法。
先看看NaiveBayesModel的貝葉斯模型參數,
labels:類別編號
pi: 類的先驗概率P(Y)的對數值
theta: 條件概率P(X|Y)的對數值
modelType:Multinomial,Bernoulli:實際上依據特征的分布不同,樸素貝葉斯又劃分為多個子類別,這實際上又是對特征的一種假設來簡化建模。
如果特征近似服從多項式分布,即特征只能取N個值,取到每個值的概率為pi,則p1+p2+..+pn=1。基于此假設構建的貝葉斯分類稱為Multinomial NaiveBayesModel,典型的例子是基于詞頻向量的文本分類。
如果特征服從伯努利分布,基于此假設構建的貝葉斯分類稱為 Bernoulli NaiveBayesModel,典型的例子是基于one-hot構建的文本分類。
不同的特征分布假設,將調用不同的概率計算函數:
private val (thetaMinusNegTheta, negThetaSum) = modelType match {
? case Multinomial => (None, None)
? case Bernoulli =>
? ? val negTheta = thetaMatrix.map(value => math.log(1.0 - math.exp(value)))//事件失敗的概率
? ? val ones = new DenseVector(Array.fill(thetaMatrix.numCols) {1.0})
? ? //事件不失敗的概率
? ? val thetaMinusNegTheta = thetaMatrix.map { value =>
? ? ? value - math.log(1.0 - math.exp(value))
? ? }
? ? (Option(thetaMinusNegTheta), Option(negTheta.multiply(ones)))
? case _ =>
? ? // This should never happen.
? ? throw new UnknownError(s"Invalid modelType: $modelType.")
}
//特征的分布不一樣,其估計也不一樣
@Since("1.0.0")
override def predict(testData: Vector): Double = {
? modelType match {
? ? case Multinomial =>
? ? ? labels(multinomialCalculation(testData).argmax)
? ? case Bernoulli =>
? ? ? labels(bernoulliCalculation(testData).argmax)
? }
}
另外為方便計算和避免小數值,根據對數運算法則,可將乘積運算轉換為加法運算,后驗概率的估計值為
//計算每個類別的概率p(yi|X1,x2)=p(yi)*p(x1|yi)*p(x2|yi)*.../P(X) ?實際計算的是log(p(yi)) + log(p(x1|yi)) +***
//全概率P(X)對每個類別一致,可以不算
private def multinomialCalculation(testData: Vector) = {
? val prob = thetaMatrix.multiply(testData)//求出
? BLAS.axpy(1.0, piVector, prob)
? prob
}
private def bernoulliCalculation(testData: Vector) = {
? testData.foreachActive((_, value) =>
? ? if (value != 0.0 && value != 1.0) {//伯努利事件的結果只有兩種狀態
? ? ? throw new SparkException(
? ? ? ? s"Bernoulli naive Bayes requires 0 or 1 feature values but found $testData.")
? ? }
? )
? val prob = thetaMinusNegTheta.get.multiply(testData)
? BLAS.axpy(1.0, piVector, prob)
? BLAS.axpy(1.0, negThetaSum.get, prob)
? prob
}
NaiveBayes
再來看NaiveBayesModel的參數估計,參數估計由NaiveBayes類開始。
NaiveBayes構造函數有個lambda參數,一般在估計P(Xi|Y)時,對于在訓練數據中沒有出現的Xi,會得到其估計P(Xij|Y)=0
在實際應用中,對于某個類別沒有出現在樣本集中或者某個特征沒有出現在某類樣本集中,這個時候就需要加入平滑因子lambda去調整,一般常用拉普拉斯平滑進行處理。??
類的分布估計調整為
多項式模型下的參數估計調整為:
伯努力模型下參數估計調整為:
樸素貝葉斯模型的訓練是在mllib.NaiveBayes中由用戶調用其run來完成訓練的。run方法調用了ml.NaiveBayes類的trainWithLabelCheck方法來完成參數估計的。
接下來看看trainWithLabelCheck進行參數估計的過程
private[spark] def trainWithLabelCheck(
? ? ? dataset: Dataset[_],
? ? ? positiveLabel: Boolean): NaiveBayesModel = {
? ? if (positiveLabel) {...}
? ? val modelTypeValue = $(modelType)
? ? val requireValues: Vector => Unit = {...}
? ? //估算argmax p(yi)*p(X|Yi) ==> argmax log(p(yi)) + log(p(X|Yi))
? ? //p(yi) = numDocuments in lable i / numDocuments all
? ? //p(X|Yi) = p(X1|Yi)*p(X2|Yi)... ==> log(p(X1|Yi)) + log(p(X2|Yi))
? ? //p(X1|Yi) = featureSum in lable i / featureSum all
? ? //特征數量
? ? val numFeatures = dataset.select(col($(featuresCol))).head().getAs[Vector](0).size
? ? //特征權重
? ? val w = if (!isDefined(weightCol) || $(weightCol).isEmpty) lit(1.0) else col($(weightCol))
?
? ? // Aggregates分布式計算 進行文檔和特征的統計計數
? ? //aggregateByKey再collect等價于aggregateByKeyLocally,返回的是一個HashMap<lable id,object>
? ? //aggregated具體形式為 [lable i, numDocuments in lable i, a vector contains <feature1Sum,feature2Sum,..>]
? ? val aggregated = dataset.select(col($(labelCol)), w, col($(featuresCol))).rdd
? ? ? .map { row => (row.getDouble(0), (row.getDouble(1), row.getAs[Vector](2)))
? ? ? }.aggregateByKey[(Double, DenseVector)]((0.0, Vectors.zeros(numFeatures).toDense))(//分類別統計featureSum
? ? ? seqOp = {
? ? ? ? ?case ((weightSum: Double, featureSum: DenseVector), (weight, features)) =>
? ? ? ? ? ?requireValues(features)
? ? ? ? ? ?BLAS.axpy(weight, features, featureSum)//常數乘以向量加另一個向量
? ? ? ? ? ?(weightSum + weight, featureSum)
? ? ? },
? ? ? combOp = {
? ? ? ? ?case ((weightSum1, featureSum1), (weightSum2, featureSum2)) =>
? ? ? ? ? ?BLAS.axpy(1.0, featureSum2, featureSum1)//featureSum2 + featureSum1
? ? ? ? ? ?(weightSum1 + weightSum2, featureSum1)
? ? ? }).collect().sortBy(_._1)//sortBy lable index
? ? //分類數
? ? val numLabels = aggregated.length
? ? //總的樣本數量
? ? val numDocuments = aggregated.map(_._2._1).sum
? ? val labelArray = new Array[Double](numLabels)
? ? //初始化存儲p(yi)的數組
? ? val piArray = new Array[Double](numLabels)
? ? //用于計算p(xi|yk)的參數,類別數numLabels*特征數量numFeatures大小的數組
? ? val thetaArray = new Array[Double](numLabels * numFeatures)
? ? val lambda = $(smoothing)//平滑參數
? ? val piLogDenom = math.log(numDocuments + numLabels * lambda)//這個是估計p(yi)的分母,見公式。。。
? ? var i = 0
? ? //迭代aggregated這個存在本地的HashMap
? ? aggregated.foreach { case (label, (n, sumTermFreqs)) =>
? ? ? labelArray(i) = label
? ? ? piArray(i) = math.log(n + lambda) - piLogDenom //計算log(p(yi)) , 是(numDocuments in lable i + lambda)/(numDocuments + numLabels * lambda)的對數形式
? ? ? val thetaLogDenom = $(modelType) match {//這個是計算公式。。。的分母部分
? ? ? ? case Multinomial => math.log(sumTermFreqs.values.sum + numFeatures * lambda)//實際上上加了一個平滑因子的
? ? ? ? case Bernoulli => math.log(n + 2.0 * lambda)
? ? ? ? case _ =>
? ? ? ? ? throw new UnknownError(s"Invalid modelType: ${$(modelType)}.")
? ? ? }
? ? ? var j = 0
? ? ? while (j < numFeatures) {
? ? ? ? //第i類別第j個特征的參數估計
? ? ? ? thetaArray(i * numFeatures + j) = math.log(sumTermFreqs(j) + lambda) - thetaLogDenom //計算log(p(Xk|Yi))
? ? ? ? j += 1
? ? ? }
? ? ? i += 1
? ? }
? ? val pi = Vectors.dense(piArray) //存儲log(p(yi))的數組
? ? val theta = new DenseMatrix(numLabels, numFeatures, thetaArray, true)
? ? new NaiveBayesModel(uid, pi, theta).setOldLabels(labelArray)
? }
Spark目前只實現了基于伯努利分布和二項分布的樸素貝葉斯算法,對于諸如高斯分布的樸素貝葉斯目前還沒有實現,在需要時可參照上述兩個模型的過程來自己實現(重寫NaiveBayesModel的predict方法和NaiveBayes的參數估計方法)。
————————————————
版權聲明:本文為CSDN博主「大愚若智_」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zbc1090549839/article/details/68067460
總結
以上是生活随笔為你收集整理的spark.mllib:NaiveBayes的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spark.mllib:回归算法
- 下一篇: 胆囊炎能不能吃吊瓜子