Spark-MLlib学习日记3:使用朴素贝叶斯识别手写数字图片

前言

上一篇讲完了MNIST手写数字数据集的读取使用,今天呢就来讲讲使用分类算法——朴素贝叶斯算法(Naive Bayesian classification)来对这个数据集进行训练,看看使用这个算法训练出来的模型,对手写数字的识别率有多高。本人也是初学者,所以对于算法的讲述只是基于自身粗浅的理解,如有不同意见欢迎指正。话不多说,我们来进入正题。

朴素贝叶斯算法原理

不得不说,这个名字起得太贴切了,在我了解完这个算法之后,才知道他真的很“朴素”。。。

关于分类问题

先来感性认识一下这个算法,朴素贝叶斯算法是一个分类算法,对于分类,其实我们并不陌生,在生活中我们也常常会下意思地对事物进行“分类”: 例如走在街上,看到一个路人,我们会下意思地去判断是男是女;别人请你吃水果,看一眼就分辨出是苹果还是橘子;在奶茶店买一杯茶,看茶汤颜色会判断是红茶还是绿茶或者是乌龙茶。这里例子都说明了一件事,我们生活中无时无刻都在对事物进行着分类。

但是分类的前提,是我们得对这个事物有基本的认知,比如,舍友给了我一个牛油果,但是我没吃过牛油果,所以我不能准确地把它分类到“牛油果”这个类别去,虽然我不能明确他的分类,但是我能确定它不是“衣服”,因为它的特征跟衣服完全不一致,根据已知的事物中,它的特征跟“水果”最像,所以它是水果的概率最高,所以我们会把他分类到“水果”去。

看到是不是觉得有点“朴素”。。。对的,朴素贝叶斯算法,听起来好像很厉害,实际上就是这么一个“朴素”的思想,在已知的条件下,选出概率最高的一个类别作为该项的分类,“朴素”到我这种数学渣渣都露出了会心的微笑2333333

朴素贝叶斯分类的数学原理与流程

朴素贝叶斯分类的正式定义如下:

  1. 设$x={a_1,a_2,\ldots,a_m}$为一个待分类项,而每个$a$为$x$的一个特征属性。
  2. 有类别集合$C={y_1,y_2,\ldots,y_n}$。
  3. 计算$P(y_1|x),P(y_2|x),\ldots,P(y_n|x)$。
  4. 如果$P(y_k|x)=max{P(y_1|x),P(y_2|x),\ldots,P(y_n|x)}$,则$x \in y_k$。

那么现在的关键就是如何计算第3步中的各个条件概率。我们可以这么做:

  1. 找到一个已知分类的待分类项集合,这个集合叫做训练样本集。
  2. 统计得到在各类别下各个特征属性的条件概率估计,即:
    $P(a_1|y_1),P(a_2|y_1),\ldots,P(a_m|y_1);$
    $P(a_1|y_2),P(a_2|y_2),\ldots,P(a_m|y_2);$
    $\vdots$
    $P(a_1|y_n),P(a_2|y_n),\ldots,P(a_m|y_n);$
  3. 如果各个特征属性是条件独立的,则根据贝叶斯定理有如下推导:
    $$P(y_i|x)=\frac{P(x|y_i)P(y_i)}{P(x)} $$

因为分母对于所有类别为常数,因为我们只要将分子最大化皆可。又因为各特征属性是条件独立的,所以有:
$$P(x|y_i)P(y_i)=P(a_1|y_i)P(a_2|y_i)\ldots P(a_m|y_i)=P(y_i)\prod_{j=1}^{n} P(a_j|y_i)$$

对于朴素贝叶斯在机器学习上解决分类问题,主要是分为三个阶段:
第一阶段——准备工作阶段,这个阶段的任务是为朴素贝叶斯分类做必要的准备,主要工作是根据具体情况确定特征属性,并对每个特征属性进行适当划分,然后由人工对一部分待分类项进行分类,形成训练样本集合。这一阶段的输入是所有待分类数据,输出是特征属性和训练样本。这一阶段是整个朴素贝叶斯分类中唯一需要人工完成的阶段,其质量对整个过程将有重要影响,分类器的质量很大程度上由特征属性、特征属性划分及训练样本质量决定。

第二阶段——分类器训练阶段,这个阶段的任务就是生成分类器,主要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。

第三阶段——应用阶段。这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。

以上引用自: 《算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)》

朴素贝叶斯的应用场景和局限

优点:

  1. 算法简单容易理解,对分类结果的可解释性较强
  2. 对小规模的数据表现很好,能个处理多分类任务,适合增量式训练。

缺点:

  1. 需要明确有哪些分类,如果给定没有出现过的类和特征,则该类别的条件概率估计将出现0,该问题被称为“零条件概率问题”。
  2. 特征之间独立的假设非常强。 在现实生活中几乎很难找到这样的数据集。在属性个数比较多或者属性之间相关性较大时,朴素贝叶斯分类模型的分类效率低。而在属性相关性较小时,朴素贝叶斯分类模型的性能最为良好。

应用场景:

  1. 欺诈检测中使用较多
  2. 垃圾邮件过滤
  3. 文本分类
  4. 人脸识别

朴素贝叶斯在Spark MLlib中的使用

Spark MLlib中提供了朴素贝叶斯算法工具,我们调用的时候只需要把数据处理成spark的标准输入格式即可轻松得到训练模型。下面我将使用MNIST手写数字数据集来做训练,并用其提供的测试集计算准确率。(ps:后续会加入对自己手写数字图片进行识别,请等下一次更新_(:з」∠)_)

1、首先把标签数据和图片数据处理成 LabeledPoint 标签向量数据格式:

1
2
3
4
val data = trainLabe.zip(trainImages).map( d =>
LabeledPoint(d._1.toInt, Vectors.dense(d._2.map(p => (p & 0xFF).toDouble)))
)
val trainRdd = sc.makeRDD(data)

2、调用MLlib的api,输入数据进行训练,得到分类模型:

1
val model = NaiveBayes.train(trainRdd)

3、利用训练好的模型,对测试数据进行检验,看看识别准确率有多少:

1
2
3
4
5
6
7
8
9
10
11
val testData = testImages.map(d => Vectors.dense(d.map(p => (p & 0xFF).toDouble )))
val testRDD = sc.makeRDD(testData)
val res = model.predict(testRDD).map(l => l.toInt).collect()

val tr = res.zip(testLabe)

val sum = tr.map( f =>{
if(f._1 == f._2.toInt) 1 else 0
}).sum

println("准确率为:"+ sum.toDouble /tr.length)


如图,利用它提供的测试集呢,我这里得到的识别率约为0.8365,看样子识别效率并不怎么样啊,因为据我所知这份数据最高的识别率能高达99%甚至100%的,下一期我将会尝试使用随机森林算法来对这个数据集进行训练,稍微期待一下吧~

此处只贴了部分代码,完整代码可前往github查看: 完整代码点我

参考链接

0%