1、朴素贝叶斯分类算法 朴素贝叶斯(Naive Bayes ,NB)算法是基于贝叶斯定理与特征条件独立假设的分类方法,该算法是有监督的学习算法,解决的是分类问题,是将一个未知样本分到几个预先已知类别的过程。 朴素贝叶斯的思想就是根据某些个先验概率计算Y变量属于某个类别的后验概率,也就是根据先前事件的有关数据估计未来某个事件发生的概率。 2、理解朴素贝叶斯 假设现在有一堆邮件,正常邮件的比例饿是80%,垃圾邮件的比例是20%,这堆邮件中,5%的邮件出现Viagra单词,如果有一封邮件,这封邮件中包含Viagra单词,求这封邮件是垃圾邮件的概率。 显然不能使用5%*20%=1%得到这封邮件是垃圾邮件的概率,因为垃圾邮件中有可能出现Viagra也有可能不会出现Viagra单词。那么根据贝叶斯公式可得包含Viagra单词的邮件是垃圾邮件的概率为: P(spam)是已知20%,也是已知 5%,那么如果求出的概率,结果就可以知道。我们可以根据邮件的数据集统计得到单词频率表: 其中P(Viagra|spam)表示在垃圾邮件中出现Viagra单词的概率,通过统计得出为4/20。可以得出如果一封邮件中有Viagra单词,这封邮件是垃圾邮件的概率为: 通过同样的计算可以得到,含有Viagra单词但不是垃圾邮件的概率为0.2。那么可以认为这封邮件是垃圾邮件的概率比较大。这里的Viagra可以理解为邮件中的一个特征。那么当一封邮件有额外更多的特征时,贝叶斯如何扩展? 假设所有历史邮件中只出现了4个单词,也就是4个特征,根据历史邮件统计的单词频率表如下: 假设现在给定一封邮件中有Viagra和Unsubscribe(取消订阅)两个单词,求这封邮件是垃圾邮件的概率、不是垃圾邮件的概率? 利用贝叶斯公式,我们可以得到: 是垃圾邮件的概率: 不是垃圾邮件的概率: 3、拉普拉斯估计 根据以上例子,假设有一封邮件这4个单词都出现了,求这封邮件是垃圾邮件的概率: 由于的概率为0/20,会导致整个结果是垃圾邮件的概率为0,那么就否定了其他单词出现的权重。 拉普拉斯估计本质上是给频率表中的每个单词的计数加上一个较小的数,这样就保证每一类中每个特征发生的概率非零。通常,拉普拉斯估计中加上的数值为1,这样就保证了每一个特征至少在数据中出现一次。 以上例子如果四个单词都出现情况下计算是否是垃圾邮件,出现的概率为0/20,可以增加4封垃圾邮件,使4封邮件中每个邮件中只有一个单词出现,这样就避免了垃圾邮件中有的单词出现概率为0的情况。同样在不是垃圾邮件中也增加4封,避免在正常邮件中出现有的单词出现概率为0的情况。
代码实现
import org.apache.spark.ml.classification.NaiveBayesModel import org.apache.spark.mllib.classification.NaiveBayes import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.regression.LabeledPoint import org.apache.spark.{SparkConf, SparkContext} object Native_bayes { def main(args: Array[String]): Unit = { val conf = new SparkConf().setAppName("").setMaster("") val sc = new SparkContext(conf) //读取样本数据1 val data = sc.textFile("sample_naive_bayes_data.txt") val parseData = data.map(line => { val parts = line.split("") LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split("").map(_.toDouble) )) }) //样本数据划分训练样本与测试样本 val splits = parseData.randomSplit(Array(0.9,0.1),seed = 11L) val training = splits(0) val test = splits(1) //新建贝叶斯分类模型模型,并训练 ,lambda 拉普拉斯估计 val model = NaiveBayes.train(training,lambda = 1.0) //对测试样本进行测试 val predictionAndLabel = test.map(p => { (model.predict(p.features), p.label) }) val print_predict = predictionAndLabel.take(10020) println("prediction"+"\t"+"label") for(i <- 0 to print_predict.length - 1){ println(print_predict(i)._1+"\t"+print_predict(i)._2) } val accurscy = 1.0 * predictionAndLabel.filter(x=>x._1==x._2).count() println(accurscy) //保存模型 val ModelPath = "native_bayes_model" model.save(sc,ModelPath) val sameModel = NaiveBayesModel.load(ModelPath) } }