算法小白的第一次嘗試---樸素貝葉斯

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