朴素贝叶斯算法+Spark MLlib代码Demo

目录

引言

一、贝叶斯定理

1.定义

2.公式推导

二、朴素贝叶斯

1.算法介绍

2.朴素贝叶斯分类器

3.学习与分类算法

4.拉普拉斯平滑

5.离散型和连续型特征属性

6.分类流程

7.朴素贝叶斯三种模型

8.案例说明

三、Spark MLlib朴素贝叶斯

四、总结


引言

目前很多分类算法都能实现分类的效果,朴素贝叶斯算法(naive Bayes)是一种比较特别的分类算法。其它比如决策树,KNN,逻辑回归,支持向量机等,他们都是判别方法,也就是直接学习出特征输出Y和特征X之间的关系,要么是决策函数Y=f(X),要么是条件分布P(Y|X)。而朴素贝叶斯是生成方法,即先找出特征输出Y和特征X的联合分布P(X,Y),针对给定输入,利用贝叶斯定理计算得到对应的输出。

朴素贝叶斯实现简单,学习与预测的效率都很高,是一种常用的方法。其主要用在垃圾邮件分类、文本分类,下面就让我们一起感受一下naive Bayes有多naive。


一、贝叶斯定理

朴素贝叶斯是基于贝叶斯定理条件独立假设推导而来的,所以有必要先弄明白贝叶斯定理,对于学过统计学或者概率论的同学,应该对贝叶斯定理很熟悉了吧,所以简单的概念就不细说了,如有不懂请自行百度!

1.1 定义

贝叶斯定理由英国数学家贝叶斯发展,用来描述两个条件概率之间的关系

贝叶斯公式:                             

                                                         P(A|B)=\frac{P(B|A)P(A)}{P(B)}

这里涉及了四个概率

  • P(A):事件A的先验概率(之所以称为“先验”,是因为它不考虑任何方面的因素)。

  • P(B):事件B的先验概率

  • P(A|B):已知B发生后A发生的概率,称作A后验概率或称为条件概率)。

  • P(B|A):已知A发生后B发生的概率,称作B后验概率

后验概率是指:当与事件相关的一些证据或背景也被考虑进来时的条件概率

1.2 公式推导

由条件概率公式得     P(A|B)=\frac{P(AB)}{P(B)}                                              (1)

同理得                       P(B|A)=\frac{P(AB)}{P(A)}                                             (2)

结合(1)(2)得               P(A|B)P(B)=P(B|A)P(A)                        

得到贝叶斯公式:

                                 P(A|B)=\frac{P(B|A)P(A)}{P(B)}                                   (3)

由全概率公式P(B)=\sum_{i=1}^{n}P(B_{i})P(A|B_{i}),因此(3)也可写为

                              P(A|B)=\frac{P(B|A)P(A)}{\sum_{i=1}^{n}P(B_{i})P(A|B_{i})}                          (4)

解释:P(A|B)通常与P(A|B)是不相等的,然而这两者是有关系的,贝叶斯定理就是这种关系的陈述。贝叶斯公式的用途就是在于通过已知三个概率来推测第四个概率。通俗地讲,就是当你不能确定某一个事件发生的概率时,你可以依靠与该事件本质属性相关的事件发生的概率去推测事件发生的概率。用数学语言表达就是:支持某项属性的事件发生得越多,则该事件发生的可能性就越大。


二、朴素贝叶斯

2.1 算法介绍

朴素贝叶斯(naive Bayes)算法是基于贝叶斯公式特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入/输出的联合概率分布;然后基于此模型,对给定的输入,利用贝叶斯定理求出后验概率最大的输出。

(朴素贝叶斯中的“朴素”二字突出了这个算法的简易性。简易性是因为该算法基于一个很朴素的假设:假设各特征之间相互独立,不会互相影响。这种假设在现实中往往是不成立的。)

通俗来讲就是:对于给出的待分类项x,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为x属于哪个类别。

比如说,你在路边见到一个黑人,你的第一感觉就是这黑哥哥是从非洲来的,因为黑人就非洲最多了,而事实是欧美也有很多黑人。但在没有其他信息提供时,往往我们就会选择条件概率最大的那个类别,这也就是朴素贝叶斯的思想基础。

2.2 朴素贝叶斯分类器

朴素贝叶斯分类器是基于贝叶斯公式和条件独立性假设推导出来的,最终可表示为

                                             y=arg\; \underset{c_{k}}{max}P(Y=c_{k})\prod_{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k})

(具体推导过程较长,这里就不po出来了,如有兴趣可见《统计学习方法 李航》的第四章,建议了解一下推导过程,加深理解)

按照这个公式,属于某个类别的概率即可表示为若干概率乘积的函数,其中这些概率包括某个特征在给定某个类别的条件下出现的概率(条件概率),以及该类别的概率(先验概率)。

说明:

  1. 输入是特征向量x,为待分类对象;输出是类标记(class label)yy代表由朴素贝叶斯算法训练出来的一个假设,它的值就是分类器对于给定待分类对象后,计算得到x最大概率所属的那个类。比如有{老年,中年,青年}三类,计算得到某个人(视为特征向量x)属于“中年”的概率是最大的,则y即为“中年”。
  2. P(Y=c_{k})为先验概率。例:已知在训练集中有10条数据,其中“青年”这一类占了3个,则P(Y=c_{k})=\frac{3}{10}
  3. x^{(j)}表示输入特征向量x某个特征中的某个特征值,一个特征向量有多个特征,一个特征也有多个取值,比如“年龄”这个特征可取很多数值:10、20、30等等。
  4. P(X^{(j)}=x^{(j)}|Y=c_{k})为条件概率。例:当已先确定计算某个所属类为“中年”时,且输入向量“年龄”的特征值为20,那么就可表示为
  5. \prod_{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k}):条件概率的累积。

2.3 学习与分类算法

下面给出基于朴素贝叶斯的分类算法:

2.4 拉普拉斯平滑

到这里似乎已经把算法都介绍完了,但实则还是有个问题:当某个分量在总样本某个分类中(训练集)从没出现过,会导致整个实例的计算结果为0。解决这个方法是采用贝叶斯估计,即在随机变量各个取值的频数上赋予一个正数\lambda >0(该数称为平滑因子,常取\lambda =1),即可避免这个问题,这种方法称为拉普拉斯平滑。如上式(1)(2)平滑处理后得到式(3)(4):

2.5 离散型和连续型特征属性

在上述估计条件概率P(X^{(j)}=x^{(j)}|Y=c_{k})时,是x^{(j)}以特征值为离散值而进行计算的。如果x^{(j)}连续型数值时,则不能这样做了,而是要用概率密度函数。需假定所有特征的取值都是服从高斯分布(如下图),即则有:

                                                    P(X^{(j)}=x^{(j)}|Y=c_{k})=\frac{1}{\sqrt{2\pi \sigma }}e^{-\frac{(x^{(j)}-\mu )^{2}}{2\sigma ^{2}}}

\eta\sigma ^_2分别是类样本在第j特征上取值的均值方差

                                           

2.6 分类流程

根据上述分析,朴素贝叶斯分类的流程可以由下图表示:

                                            

2.7 朴素贝叶斯三种模型

多项式模型

在多项式朴素贝叶斯模型中,特征向量的特征通常为离散型变量,并且假定所有特征的取值是符合多项分布的。如“年龄”包括:青年、中年、老年。

伯努利模型

在伯努利朴素贝叶斯模型中,每个特征的取值是布尔型,或以0和1表示,所以伯努利模型中,每个特征值为0或者1。 当数据均为0和1组成,则可使用该模型。

高斯模型

在高斯朴素贝叶斯模型中,特征向量的特征为连续型变量,并且假定所有特征的取值是符合高斯分布的。

2.8 案例说明

(源自西瓜书《机器学习——周志华》)

                          

基于如上已有的数据,利用朴素贝叶斯算法训练出一个分类器,以判断一个具有特征:

x_{test}={青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.460}的测试样例瓜是否为好瓜。

(1) 首先根据上表估计先验概率,即每个分类各自的概率:

                                                       

(2) 计算每个特征值的条件概率:

(3) 计算测试瓜分别属于好瓜和坏瓜的概率:


三、Spark MLlib朴素贝叶斯

Spark MLlib目前只支持多项模型和伯努利模型,没有支持高斯模型,所以暂且不能使用朴素贝叶斯算法对连续数据进行分类。如有兴趣,关于自行实现高斯模型的方法可参见该博客

以上面那个测试西瓜是否为好瓜为例,可先按以下规则将数据转换为具体数值(不考虑密度和含糖率这两个连续型特征):

是否为好瓜     是:0 否:1
色泽                青绿:0 乌黑:1 浅白:2
根蒂                蜷缩:0 稍蜷:1 硬挺:2
敲声                浊响:0 沉闷:1 清脆:2
纹理                清晰:0 稍糊:1 模糊:2
脐部                凹陷:0 稍凹:1 平坦:2
触感                硬滑:0 软粘:1 

按照如上表格,转换为数值后如下所示(逗号前为分类,逗号后面对应表中特征值):

0,0 0 0 0 0 0
0,1 0 1 0 0 0
0,1 0 0 0 0 0
0,0 0 1 0 0 0
0,2 0 1 0 0 0
0,0 1 1 0 1 1
0,1 1 1 1 1 1
0,1 1 1 0 1 0
1,1 1 1 1 1 0
1,0 2 2 0 2 1
1,2 2 2 2 2 0
1,2 0 0 2 2 1
1,0 1 0 1 0 0
1,2 1 1 1 0 0
1,1 1 0 0 1 1
1,2 0 0 2 2 0
1,0 0 1 1 1 0

这个案例里面的测试瓜x_{test}={青绿,蜷缩,浊响,清晰,凹陷,硬滑}={0 0 0 0 0 0},全是零。尴尬了,这不用说肯定就个好瓜啦。所以我改一下特征值为x_{test2}={青绿,稍糊,清脆,清晰,稍凹,硬滑}={0 1 2 0 1 0},让训练得到的分类器来判断一下这个x_{test2}是个好瓜还是坏瓜。

代码如下:

package sparkMllib.classification
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 NaiveBayesTest {

  def main(args: Array[String]): Unit = {
    Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
    Logger.getLogger("org.apache.jetty.server").setLevel(Level.OFF)
    val conf=new SparkConf().setAppName("NaiveBayesTest").setMaster("local")
    val sc=new SparkContext(conf)

    //处理数据
    val data = sc.textFile("E:\\test\\sample_naive_bayes_data.txt")
    val parsedData = data.map(line => {
      val parts = line.split(",")
      LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split(" ").map(_.toDouble)))
    })

    //交叉验证:数据分为训练数据(60%),测试数据(40%)
    val splits = parsedData.randomSplit(Array(0.6,0.4),seed = 11L)
    val trainingData = splits(0)     //训练数据
    val testingData = splits(1)      //测试数据

    //训练朴素贝叶斯模型。
    //参数lambda:平滑因子,防止条件概率等于0;modelType:模型类型,spark支持的有multinomial(多项式,默认)和bernoulli(伯努利)两种。
    val model = NaiveBayes.train(trainingData,lambda = 1,modelType = "multinomial")

    //输入测试数据
    val predictionAndLabel = testingData.map(p=>(model.predict(p.features),p.label))
    //计算准确度:预测正确的测试数据数目/全部测试数据
    val accuracy = 1.0 * predictionAndLabel.filter(x => x._1 == x._2).count() / testingData.count()

    println("result:")
    println("training.count:" + trainingData.count())
    println("test.count:" + testingData.count())
    println("model.modelType:" + model.modelType)
    println("accuracy:" + accuracy)
    println("prediction"+"\t"+"label")
    val prin = predictionAndLabel.take(10)
    for (i <- 0 to prin.length-1){
      println(prin(i)._1+"\t"+"\t"+"\t"+prin(i)._2)
    }

    //输入测试瓜向量,并为其分类
    val testV = Vectors.dense(Array[Double](0,1,2,0,1,0))
    val result = model.predict(testV)
    if(result==0){
      println("测试瓜是个好瓜")
    }else{
      println("测试瓜不是个好瓜")
    }
  }
}

输出结果:

从输出结果可看出,该模型的预测准确度只有0.714(本来训练数据就不多), 对于上述的测试瓜,模型预判其为一个好瓜。


四、总结

最后针对朴素贝叶斯算法做一个优点和缺点的总结(参考[4]):

主要优点:

  • 朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率。
  • 对小规模的数据表现很好,能个处理多分类任务,适合增量式训练,尤其是数据量超出内存时,我们可以一批批的去增量训练。
  • 对缺失数据不太敏感,算法也比较简单,常用于文本分类。

主要缺点:   

  • 理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型给定输出类别的情况下,假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。而在属性相关性较小时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法通过考虑部分关联性适度改进。
  • 需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。
  • 由于我们是通过先验和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率。
  • 对输入数据的表达形式很敏感。

引用及参考:

[1] 《机器学习》周志华著

[2] 《统计学习方法》 李航著

[3] https://blog.csdn.net/guoyunfei20/article/details/78911721

[4] https://www.cnblogs.com/pinard/p/6069267.html

(欢迎转载,转载请注明出处)

猜你喜欢

转载自blog.csdn.net/qq_42267603/article/details/88111742