package Bayes
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import scala.collection.mutable.ArrayBuffer
import org.apache.spark.ml.feature.LabeledPoint
import org.apache.spark.ml.linalg.Vectors
/**
* 簡單實現統計學習方法上的樸素貝葉斯算法案例
*/
object Naive {
def main(args: Array[String]): Unit = {
val conf=new SparkConf().setMaster("local").setAppName("ML")
val sc=new SparkContext(conf)
//train數據源
val X1=Array(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3)
val X2=Array("S","M","M","S","S","S","M","M","L","L","L","M","M","L","L")
val label=Array(-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1)
val mEresult=maxLikelihoodEstimate(X1, X2, label, sc)
val lamda=1
val K=sc.parallelize(label).map { x =>(x,1)}.reduceByKey(_+_).count().toInt
val s1=sc.parallelize(X1).map { x =>(x,1)}.reduceByKey(_+_).count().toInt
val s2=sc.parallelize(X2).map { x =>(x,1)}.reduceByKey(_+_).count().toInt
val sj=Array(s1,s2)
val bEresult=bayesEstimate(X1, X2, label, sc, lamda, K, sj)
println("極大似然估計:"+mEresult(0))
println("貝葉斯估計:"+bEresult(0))
}
/**極大似然估計
* @param x1 特徵列
* @param x2 特徵列
* @param label 標籤列
* @return 標籤 + 概率
*/
def maxLikelihoodEstimate(X1:Array[Int],X2:Array[String],label:Array[Int],sc:SparkContext):Array[(Double, Double)]={
//將Array數據源轉爲RDD
val labelCount=sc.parallelize(label).map { x =>(x,1)}.reduceByKey(_+_).collect()
//特徵與label數據拼接
val x1Label=ArrayBuffer[(Int,Int)]()
val x2Label=ArrayBuffer[(String,Int)]()
for(i<-0 until label.length){
x1Label.append((X1(i),label(i)))
x2Label.append((X2(i),label(i)))
}
val x1LabelCount=sc.parallelize(x1Label).map(x=>(x,1)).reduceByKey(_+_).collect()
val x2LabelCount=sc.parallelize(x2Label).map(x=>(x,1)).reduceByKey(_+_).collect()
//總記錄數
val totalRecords=label.length
//計算先驗概率
val proProb=ArrayBuffer[LabeledPoint]()
for(lb<-labelCount) proProb.append(LabeledPoint(lb._1,Vectors.dense(lb._2/totalRecords.toDouble)))
//計算條件概率
val x1conProb=ArrayBuffer[LabeledPoint]()
val x2conProb=ArrayBuffer[(Double,(String,Int))]()
for(lab<-labelCount){
//針對每一個lab,計算特徵X1的條件概率
for(t1<-x1LabelCount){
if(lab._1==t1._1._2) x1conProb.append(LabeledPoint((t1._2/lab._2.toDouble).formatted("%.3f").toDouble,Vectors.dense(t1._1._1,t1._1._2)))
}
//針對每一個lab,計算特徵X2的條件概率
for(t2<-x2LabelCount){
if(lab._1==t2._1._2) x2conProb.append(((t2._2/lab._2.toDouble).formatted("%.3f").toDouble,(t2._1._1,t2._1._2)))
}
}
//對於給定的測試數據,確定實例x的類
val testData=Array(2,"S")
//計算該數據對應的所有可能label的概率
val testProb=ArrayBuffer[LabeledPoint]()
for(lab<-proProb){
val tx1=testData(0).toString().toDouble
val tx2=testData(1).toString()
var resultx1con=0.0
var resultx2con=0.0
for(xlcon<-x1conProb){
if(lab.label == xlcon.features(1) && xlcon.features(0)==tx1) resultx1con=xlcon.label
}
for(x2con<-x2conProb){
if(lab.label == x2con._2._2.toDouble && x2con._2._1.equals(tx2.toString())) resultx2con=x2con._1
}
testProb.append(LabeledPoint(lab.label,Vectors.dense(lab.features(0)*resultx1con*resultx2con)))
}
//取概率最大值的label作爲最終的label
val resultLabel=sc.parallelize(testProb).map { x =>(x.label,x.features(0))}.map{case(k,v)=>(v,k)}.sortByKey(false).collect().take(1)
resultLabel
}
/**
* 貝葉斯估計
* 貝葉斯估計在求先驗概率和條件概率的時候,分別加上了lamda、K、lamda,避免了極大似然估計概率值爲0的情況
* @param x1 特徵列
* @param x2 特徵列
* @param label 標籤列
* @param lamda 貝葉斯估計參數(lamda=0時,極大似然估計 ;lamda=1時,拉普拉斯平滑估計)
* @param K label種類數
* @param sj sj(i)表示第i特徵可能取值的個數
* @return 標籤 + 概率
*/
def bayesEstimate(X1:Array[Int],X2:Array[String],label:Array[Int],sc:SparkContext,lamda:Int,K:Int,Sj:Array[Int]):Array[(Double, Double)]={
//將Array數據源轉爲RDD
val labelCount=sc.parallelize(label).map { x =>(x,1)}.reduceByKey(_+_).collect()
//特徵與label數據拼接
val x1Label=ArrayBuffer[(Int,Int)]()
val x2Label=ArrayBuffer[(String,Int)]()
for(i<-0 until label.length){
x1Label.append((X1(i),label(i)))
x2Label.append((X2(i),label(i)))
}
val x1LabelCount=sc.parallelize(x1Label).map(x=>(x,1)).reduceByKey(_+_).collect()
val x2LabelCount=sc.parallelize(x2Label).map(x=>(x,1)).reduceByKey(_+_).collect()
//總記錄數
val totalRecords=label.length
//計算先驗概率
val proProb=ArrayBuffer[LabeledPoint]()
for(lb<-labelCount) proProb.append(LabeledPoint(lb._1,Vectors.dense((lb._2+lamda)/(totalRecords.toDouble+K*lamda))))
//計算條件概率
val x1conProb=ArrayBuffer[LabeledPoint]()
val x2conProb=ArrayBuffer[(Double,(String,Int))]()
for(lab<-labelCount){
//針對每一個lab,計算特徵X1的條件概率
for(t1<-x1LabelCount){
if(lab._1==t1._1._2) x1conProb.append(LabeledPoint(((t1._2+lamda)/(lab._2.toDouble+lamda*Sj(0))).formatted("%.3f").toDouble,Vectors.dense(t1._1._1,t1._1._2)))
}
//針對每一個lab,計算特徵X2的條件概率
for(t2<-x2LabelCount){
if(lab._1==t2._1._2) x2conProb.append((((t2._2+lamda)/(lab._2.toDouble+lamda*Sj(1))).formatted("%.3f").toDouble,(t2._1._1,t2._1._2)))
}
}
//對於給定的測試數據,確定實例x的類
val testData=Array(2,"S")
//計算該數據對應的所有可能label的概率
val testProb=ArrayBuffer[LabeledPoint]()
for(lab<-proProb){
val tx1=testData(0).toString().toDouble
val tx2=testData(1).toString()
var resultx1con=0.0
var resultx2con=0.0
for(xlcon<-x1conProb){
if(lab.label == xlcon.features(1) && xlcon.features(0)==tx1) resultx1con=xlcon.label
}
for(x2con<-x2conProb){
if(lab.label == x2con._2._2.toDouble && x2con._2._1.equals(tx2.toString())) resultx2con=x2con._1
}
testProb.append(LabeledPoint(lab.label,Vectors.dense(lab.features(0)*resultx1con*resultx2con)))
}
//取概率最大值的label作爲最終的label
val resultLabel=sc.parallelize(testProb).map { x =>(x.label,x.features(0))}.map{case(k,v)=>(v,k)}.sortByKey(false).collect().take(1)
resultLabel
}
}
最終結果:
極大似然估計:(0.0666,-1.0)
貝葉斯估計:(0.06088023529411765,-1.0)
算法小白的第一次嘗試---樸素貝葉斯
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.