《Machine Learning in Action》—— 白话贝叶斯,“恰瓜群众”应该恰好瓜还是恰坏瓜

《Machine Learning in Action》—— 白话贝叶斯,“恰瓜群众”应该恰好瓜还是恰坏瓜

概率论,可以说是在机器学习当中扮演了一个非常重要的角色了。Taoye对概率论知识的掌握目前也还仅仅只是停留在本科期间所接触到的,而且还都已经忘了不少。快速的复习回顾一下之后,用来理解机器学习中的贝叶斯算法,还是足够的。

手撕机器学习系列文章目前已经更新了支持向量机SVM、决策树、K-近邻(KNN),现在我们来玩玩贝叶斯算法,其他机器学习系列文章可根据自己需求来食用(持续更新中):

本篇文章主要包括以下几个部分的内容:

  • 到底啥是贝叶斯,很厉害嘛???主要介绍了贝叶斯的一些基础知识,然后写了下笔者对贝叶斯的个人看法,这个部分的内容不难,认真阅读应该都能理解
  • 介绍贝叶斯决策所涉及到的一些理论,其中包括条件概率、全概率、贝叶斯推断等内容,并且通过几个比较生动的案例或是题目帮助大家对理论的理解。这里需要特别值得注意的是:最后一个案例(罐子和石头)务必要理解清楚贝叶斯真正所要表达的实际意义。
  • 这第三部分的内容主要是通过一个西瓜的案例来给大伙进行一下贝叶斯实战,Taoye命名为:“吃瓜群众”应该恰好瓜还是坏瓜。此“吃瓜群众”就单纯字面上的意思,而非网络用语之“梗”。另外,在这部分内容里面,还会详细介绍标称型数据和数值型数据的具体处理方式,以及常用的“平滑”处理——拉普拉斯修正。当然了,这部分内容还是会照常给大家通过代码的形式来实战这个案例。

一、到底啥是贝叶斯,很厉害嘛???

贝叶斯定理由英国数学家贝叶斯 ( Thomas Bayes 1702-1761 ) 发展,用来描述两个条件概率之间的关系,比如 P(A|B) 和 P(B|A)。按照乘法法则,可以立刻导出:P(A∩B) = P(A)P(B|A)=P(B)P(A|B)。如上公式也可变形为:P(A|B)=P(B|A)*P(A)/P(B)。

托马斯·贝叶斯(Thomas Bayes,1702-1761),18世纪英国神学家、数学家、数理统计学家和哲学家,概率论理论创始人,贝叶斯统计的创立者,“归纳地”运用数学概率,“从特殊推论一般、从样本推论全体”的第一人。

放一张大佬的图片镇文:

图片来源网络

对于贝叶斯算法来讲,其优点是在简单易懂,学习效率高,数据较少的情况下仍然有效,并且可以处理多分类问题。

至于缺点嘛,在贝叶斯算法中,假设属性与属性之间是有一定的相关性的,这个时候计算量相对会比较复杂,而且处理起来也不是那么的容易。所以就衍生出了朴素贝叶斯来降低属性与属性之间的关系(一句话,没有任何关系),也就是属性之间是完全独立的,但我们知道在实际问题中,属性之间很难做到完全独立。即使这样,朴素贝叶斯依然会有广的应用。

上面所提到的属性之间是独立的,这句话应该怎么来理解呢???

独立性可以说是概率论当中的常客了。就是说,两者之间没有什么任何关系,我不管你,请你也不要管我,你过你的独木桥,我走我的阳光道,单人做的事情都不会对他人造成任何的影响。(需要重点理解)

关于对上述的理解,如有些许疑惑也没关系,Taoye会在下面通过案例来详细介绍的,以帮助大家对独立性的理解。

另外,在《机器学习实战》这本书中,还提到了该算法的适用数据类型为标称型数据。但从实际来讲,除了适用标称型数据来之外,还能适用数值型数据。

这里稍微解释下标称型数据和数值型数据: 标称型数据一般用来表示离散属性,比如身高我们不对其做具体的多少cm,而是高和矮两种结果。而数值型数据一般针对于连续属性,比如身高我们可以具体到:170cm、175cm、180cm等等。

在贝叶斯算法中,不同类型的数据,我们会有不一样的方式来处理。对于标称型数据来讲,可以直接通过频率来处理,高的人有几个???矮的人有几个???而对于数值型数据或者说是连续性数据来讲,我们一般考虑概率密度函数来处理,假设连续性数据满足高斯分布。(这里不理解也没关系,我们后面会详细来抠抠高斯分布在这里的应用)

以上就是关于贝叶斯算法所涉及到的一些基础概念了,Taoye尽可能做到白话了,对于有基础的读者来讲应该不是很难理解。有些许疑问也没关系,下面我们来具体看看贝叶斯究竟是何方神圣???

二、贝叶斯决策相关理论

条件概率:

关于条件概率,其实早在中学时期就有接触过吧,我还犹新记得当时Taoye学这部分内容的时候贼起劲,上课总是与老师疯狂互动。

先这样,再那样,最后再这样搞一下不就解决了嘛,小意思啦

Taoye还特意给大家找出了这本书,就是数学 选修2-3。如果有机会再夕拾这本书的话,一定会非常的有意思,想想就有点刺激。总共分为A、B版两本,喏,就是紫色皮皮和蓝色皮皮的这本“神书”(有机会一定要夕拾一哈):

好了,好了,我们来快速回顾一下条件概率吧!

上图的术语,我们可以把它叫做文氏图、Venn图、温氏图、维恩图、范氏图,都行。它主要用来帮助我们理解集合与集合之间的关系,根据上图,我们可以很清楚的看到在B事件发生的情况下,事件A发生的概率\(P(A|B)\)(不明的话可以把它理解成面积,是不是秒懂???):

\[P(A|B) = \frac{P(A \cap B)}{P(B)} \tag{2-1} \]


为了表达对“神书”的敬意,我们从其中抽一道题目来看看吧~~~

题目来源:人教版高中数学B版选修2-3

Q:抛掷红、蓝两颗骰子,设事件B=“蓝色骰子的点数为3或6”,事件A=“两颗骰子的点数之和大于8”,那么问题来了,在已知蓝色骰子的点数为3或6的时候,事件A发生的概率是多少呢?

我们知道,每颗筛子有6种可能,抛掷两颗筛子总共有36种可能(6x6=36),对吧?而事件A和事件B同时发生的可能有5种,即\(P(A \cap B)=\frac{5}{36}\),而事件A发生的可能有12种,所以\(P(A)=\frac{12}{36}\),所以我们可以得到\(P(A|B)\)的值如下:

\[\begin{aligned} P(A|B) & = \frac{P(A \cap B)}{P(B)} \\ & = \frac{\frac{5}{36}}{\frac{12}{36}}=\frac{5}{12} \end{aligned} \]


怎么样,挺简单的吧?上述的条件概率的表达形式是我们中学时所接触到的,而在贝叶斯算法中的条件概率则稍微有点不同,只是做了小小的变动。

是酱紫的

前面我们不是得到了\(P(A|B) = \frac{P(A \cap B)}{P(B)}\)嘛,变动后即:

\[P(A \cap B) = P(A|B) * P(B) \tag{2-2} \]

同理:

\[P(A \cap B) = P(B|A) * P(A) \tag{2-3} \]

所以:

\[P(A|B) * P(B) = P(B|A) * P(A) \tag{2-4} \]

即:

\[P(A|B) = \frac{P(B|A) * P(A)}{P(B)} \tag{2-5} \]

其中,式2-5就是我们贝叶斯当中得所使用的到的条件概率公式。

全概率公式:

假设样本空间S,是两个事件A与A'的和,如下图:

上图中,事件A和事件A'共同构成了样本空间S。

这种情况下,事件B能划分成两个部分。如下图:

即:

\[P(B) = P(B \cap A) + P(B \cap A^{'}) \tag{2-6} \]

由上面的推导可知:

\[P(B \cap A) = P(B|A)P(A) \tag{2-7} \]

所以

\[P(B) = P(B|A)P(A) + P(B|A^{'})P(A^{'}) \tag{2-8} \]

这就是全概率公式。它的含义是,如果A和A'构成样本空间的一个划分,那么事件B的概率,就等于A和A'的概率分别乘以B对这两个事件的条件概率之和。

将这个全概率公式代入到上面的条件概率公式中,就可以得到条件概率的另一种写法:

\[P(A|B) = \frac{P(B|A)P(A)}{P(B|A)P(A) + P(B|A^{'})P(A^{'})} \tag{2-9} \]

贝叶斯推断

对条件概率公式进行变形,可以得到如下形式:

\[P(A|B) = P(A)\frac{P(B|A)}{P(B)} \tag{2-10} \]

我们把P(A)称为"先验概率"(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断,也就是说这个时候单纯的考虑事件A,与事件B无关。
P(A|B)称为"后验概率"(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估,这个时候就要考虑事件B了。
P(B|A)/P(B)称为"可能性函数"(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。

所以,条件概率可以理解成下面的式子:

\[后验概率 = 先验概率 x 调整因子 \]

这就是贝叶斯推断的含义。我们先预估一个"先验概率",然后加入实验结果,看这个实验到底是增强还是削弱了"先验概率",由此得到更接近事实的"后验概率"。

在这里,如果"可能性函数"P(B|A)/P(B)>1,意味着"先验概率"被增强,事件A的发生的可能性变大;如果"可能性函数"=1,意味着B事件无助于判断事件A的可能性;如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。

上述内容,来自于阮一峰老师的网络日志: http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.html

为了加深对贝叶斯推断的理解,我们来看看下面的一个例子:


例子参考于:《机器学习实战》

现在有完全一样的1、2号两个罐子,其中1号罐有4块石头,分别是2白2黑;2号罐子有3块石头,分别1白2黑。现在随机选择一个罐子,从中抓起1块石头,发现是白色的,请问这块白色石头更可能来自于哪一个罐子???

这道题目是Taoye根据《机器学习实战》这本书上的例子进行改动的,主要是为了方便大家更容易理解贝叶斯在分类问题中的应用。注意这道题问的是:更可能来自于哪一个罐子?

总共就两个罐子,不是1号罐子就是2号罐子,而更可能描述的是一个可能性,其实就相当于一个分类问题。来自哪一个罐子的可能性更大,我们最终就把这个白石头归类于哪一个罐子。

换句话讲,我们可以把石头的颜色表示为样本的属性特征,而罐子的类别则表示为样本所对应的标签。(这种问题的转化思维一定要引起重视)至此的话,我们就可以分别计算出来自于1、2号罐子的概率,哪一个更大,那么就将该石头归类于那一个罐子。

我们不妨通过上述的条件概率公式来进行分析,条件概率重现如下:

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

对此,我们令事件A=“来自1号罐子”,事件B=“选中了白色石头”。

则我们可以知道,因为2个罐子是完全一样的,所以:

\[P(A) = \frac{1}{2} \]

\(P(B|A)\)表示的是在1号罐子中选中白色石头的概率,我们知道1号罐子中有四块石头,其中有两块是白色的,所以:

\[P(B|A) = \frac{2}{4}=\frac{1}{2} \]

\(P(B)\)很简单,就是在全局中选中白色石头的概率,全局有7块,其中白色石头有3块,所以:

\[P(B) = \frac{3}{7} \]

综上,我们就可以得到我们的条件概率结果,即在发现是白色的前提下,这块石头来自1号罐子的概率为:

\[\begin{aligned} P(A|B) & = P(A)\frac{P(B|A)}{P(B)} \\ & = \frac{1}{2}\frac{\frac{1}{2}}{\frac{3}{7}}=\frac{7}{12} \end{aligned} \]

同理可知,假设事件C=“来自2号罐子”,我们可以计算出此时的条件概率:

\[\begin{aligned} P(C|B) & = P(A)\frac{P(C|A)}{P(C)} \\ & = \frac{1}{2}\frac{\frac{1}{3}}{\frac{3}{7}}=\frac{7}{18} \end{aligned} \]

对于这道题来讲,先验概率\(P(A)=P(C)=\frac{1}{2}\),经过调整因子(可能性函数)处理之后得到的后验概率\(P(A|B)=\frac{7}{12}\),而\(P(C|B)=\frac{7}{18}\)。也就是说在取出一个白石头之前,事件A和事件C的可能性是相同的,而在取出一个白石头之后,事件A的可能性得到了增强,而事件C的可能性得到了的减弱。且\(P(A|B) > P(C|B)\),为此,我们更情愿将取到的这个白球归类于1号罐子。

上面这段话,各位读者一定要重点理解清楚,这个对于理解朴素贝叶斯的实际意义有着非常重要的作用。我们可以这样说:在被检测样本的各个属性已知的前提下,我们需要通过贝叶斯算法来计算该样本分别为各类别的概率情况,以此来判断该被检测样本的最终分类。

不知道各位读者有没有注意噢,对于上述问题,白球的归类不是1号罐就是2号罐,按道理来讲\(P(A|B)+P(C|B)=1\),然而我们发现,将计算出来的这两个值相加之后并没有等于1,这不是完全不讲道理嘛,真的是讨厌。。。

首先,值得肯定的是,存在这种疑问的读者非常的棒,说明在阅读的过程有认真的在思考。其实,有这疑问的读者忽视了“域”的问题,我们所理解的\(P(A|B)+P(C|B)=1\)是在整体“域”当中,也就是7个石头,而考虑条件概率的时候,“域”就已经发生了改变,此时的“域”就不再是一个整体了,而是被分割成了两个子“域”,所以此时计算的两个概率和并不会一定为1。(重点理解)

关于上述“域”的问题,为Taoye在学习过程中独立思考所得,暂时没有参考任何的权威资料,所以不能完全保证上述说法的正确性。如有问题,还请各位读者不吝赐教,在下方留言或是私聊Taoye。

最后再提醒一句,将这道题的真正意义搞懂,对于理解贝叶斯算法真的尤为重要。


三、贝叶斯实战之恰瓜群众应该恰好瓜还是坏瓜

在本节内容中,我们主要用周志华老师的西瓜书上的一个例子来理解下贝叶斯的应用,随后会通过代码的形式来解决问题。总体上的过程与上述例子大致相同,主要在于读者对于不同的问题要学会变通,要学会对问题的转换。就像《周易》里说的那样,穷则变,变则通,通则久。这一点还是挺重要的,尤其是对于我们学生来讲。

下面我们开始进入到正题。

例子参考于:周志华-《机器学习》第四章

为了让读者在阅读的过程中不是那么的无趣,或是能更好的进入到这个案例,Taoye编个简短的故事来作为引子吧。

注意:此“吃瓜群众”就单纯字面上的意思,而非网络用语之“梗”。

从前有座山,山上有座庙,庙里有位吃瓜群众在吃瓜。(唱起来还挺顺口的,hhhhhh)

同时,庙里面也有成千上百的西瓜可供吃瓜群众食用。一开始的时候,吃瓜群众还是非常开心的,一口一个西瓜吞吞的下肚。但是这西瓜恰到一定数量的时候,他发现有的瓜是好瓜,而有的瓜是坏瓜,当时就困惑着:我滴乖乖,这坏瓜该不会是过期了吧???

那咋行呢?我要每次恰之前能够挑选出坏瓜才行,至少说挑选出好瓜的概率要比坏瓜大才可以吧。为此,他收集了之前恰西瓜时候的一些属性特征以及对应标签,以此来作为他判别好瓜还是坏瓜的依据。

吃瓜群众收集到的西瓜数据如下所示:

这个数据样本集总共有17个西瓜,其中好瓜有8个,坏瓜有9个。

这个吃瓜群众的案例相较于前面罐子石头的来讲就复杂一点点,但也只是一点点而已。这里涉及到了多个属性特征,而且除了标称型数据之外,还有数值型数据,这些不同类型的数据我们需要怎么处理呢???另外还有一点需要说的是,假如说我们的检测西瓜中的特征值在17个样本里面不存在,那么这个时候又应该需要怎么处理呢???

以上所提到的都是我们这节内容中所需要解决的问题。

  • 属性问题的解决

在前面罐子石头的案例中,我们的属性特征只有颜色一个,而在这个吃瓜群众的案例里面,属性特征却有色泽、根蒂、敲声、纹理、脐部、触感、密度、含糖率8个。

通过前面几篇手撕机器学习的文章来看,我们可以知道,当一个样本数据中的属性特征有多个的时候,这个时候我们可以把这多个属性特征看做是一个整体,什么整体呢???没错,就是一个特征向量。

我们不妨将这里的特征向量表示为\(x=(色泽,根蒂,敲声,纹理,脐部,触感,密度,含糖率)\),而好瓜、坏瓜标签表示为\(c\),则在已知一个被检测西瓜样本的属性特征的前提下,我们要来判断这个瓜是好瓜还是坏瓜,则通过贝叶斯定理,我们有

\[P(c|x)=\frac{P(c)P(x|c)}{P(x)} \tag{3-1} \]

通过上式,我们不不难发现,基于贝叶斯公式3-1来估计后验概率\(P(c|x)\)的主要困难在于:类条件概率\(P(x|c)\)是所有属性上的联合概率,很难从有限的训练样本中直接估计而得。还有一点就是,这种形式的表示就相当于笛卡尔积,这个对计算也不是很友好。(关于笛卡尔积,读者可自行了解,后期有机会的话Taoye也会来介绍)

为了避开上述这个问题,“朴素贝叶斯分类器”就采用了“属性条件独立性假设”:对已知类别,假设所有属性相互独立,互相不会产生任何的影响。这个时候,我们再来重新阅读前面所说到的一句话: 我不管你,请你也不要管我,你过你的独木桥,我走我的阳光道,单人做的事情都不会对他人造成任何的影响。

是不是独立的理解有点感觉了???

基于属性条件独立性假设,式子3-1,我们可以重写为:

\[P(c|x)=\frac{P(c)P(x|c)}{P(x)}=\frac{P(c)}{P(x)}\Pi_{i=1}^8P(x_i|c) \tag{3-2} \]

而我们知道,对于一个检测西瓜样本来讲,该样本每一个属性特征值在每个样本类别里计算得到的结果都是一样的,比如说对于好瓜与坏瓜的判别来讲,计算得到的\(P(x)\)都是相同的。换句话讲,\(P(x)\)的计算结果并不会对不同标签计算后验概率结果产生任何的影响,也就是说,要想判别这个是好瓜还是坏瓜,我们没有必要去计算\(P(x)\)的值,这是画蛇添脚、多此一举。对此,我们得到如下所示:

\[h_{nb}(x) = arg \ maxP(c)\Pi_{i=1}^8P(x_i|c) \tag{3-3} \]

这也就是我们的朴素贝叶斯的表达式,表达的意思就是比较不同类别时候的\(P(c)\Pi_{i=1}^8P(x_i|c)\),值最大者所对应的标签就是我们想要的分类结果。

不难吧???应该能看懂吧???也应该能理解吧???感觉解释的已经很白话了 (ノへ ̄、)

  • 不同数据类型的处理方式

我们观察数据,可以发现样本的属性特征有两类,一类是标称型属性数据:色泽、根蒂、敲声、纹理、脐部、触感,另一类是数值型数据:密度、含糖率。我们可以把标称型数据理解成离散型的,而把数值型数据理解成连续型的,而在贝叶斯算法中,不同类型的数据会有不同的处理方式。

对于离散属性来讲,令\(D_{c,x_i}\)表示\(D_c\)中在第i属性上取值为\(x_i\)的样本组成的集合,则条件概率\(P(x_i|c)\)可以估计为:

\[P(x_i|c)=\frac{|D_{c,x_i}|}{|D_c|} \tag{3-4} \]

换言之,就是频率的一种计算。

而对于连续属性来讲,我们可以考虑概率密度函数,假定\(p(x_i|c)\)服从\(N(u_{c,i},\sigma_{c,i}^2)\),其中\(u_{c,i}\)\(\sigma_{c,i}^2\)分别是第c类样本在第i个属性上取值的均值和方差,则有:

\[p(x_i|c)=\frac{1}{\sqrt{2\pi}\sigma_{c,i}}exp(-\frac{(x_i-u_{c,i})^2}{2\sigma_{c,i}^2}) \tag{3-5} \]

也就是说此时的\(p(x_i|c)\)就相当于把数据样本集中所对应特征的所有数值型数据服从高斯分布,依次来计算\(p(x_i|c)\)的结果

ok,这两个问题搞懂了之后,我们就可以来计算下吃瓜群众所恰西瓜的好坏了

我们不妨假设此时恰瓜群众此时拿到的一个西瓜所对应属性特征如下,我们通过贝叶斯来判断该西瓜的好坏:

我们首先计算先验概率\(P(c)\),由于总共有17个瓜,其中好瓜8个,坏瓜9个,所以有:

\[\begin{aligned} & P(好瓜=是)=\frac{8}{17}=0.471 \\ & P(好瓜=否)=\frac{9}{17}=0.529 \end{aligned} \]

之后,为每个属性估计条件概率\(P(x_i|c)\)

\[\begin{aligned} & P_{青绿|是}=P(色泽=青绿|好瓜=是)=\frac{3}{8}=0.375 \\ & P_{青绿|否}=P(色泽=青绿|好瓜=否)=\frac{3}{9}=0.333 \\ & P_{蜷缩|是}=P(根蒂=蜷缩|好瓜=是)=\frac{5}{8}=0.625 \\ & P_{蜷缩|否}=P(根蒂=蜷缩|好瓜=否)=\frac{3}{9}=0.333 \\ & P_{浊响|是}=P(敲声=浊响|好瓜=是)=\frac{6}{8}=0.750 \\ & P_{浊响|否}=P(敲声=浊响|好瓜=否)=\frac{4}{9}=0.444 \\ & P_{清晰|是}=P(纹理=清晰|好瓜=是)=\frac{7}{8}=0.875 \\ & P_{清晰|否}=P(纹理=清晰|好瓜=否)=\frac{2}{9}=0.222 \\ & P_{凹陷|是}=P(脐部=凹陷|好瓜=是)=\frac{6}{8}=0.625 \\ & P_{凹陷|否}=P(脐部=凹陷|好瓜=否)=\frac{2}{9}=0.222 \\ & P_{硬滑|是}=P(触感=硬滑|好瓜=是)=\frac{6}{8}=0.750 \\ & P_{硬滑|否}=P(触感=硬滑|好瓜=否)=\frac{6}{9}=0.667 \\ \end{aligned} \]

\[\begin{aligned} P_{密度:0.697|是} & =P(密度=0.697|好瓜=是) \\ & = \frac{1}{\sqrt{2\pi}0.129}exp(-\frac{(0.697-0.574)^2}{2*0.129^2}) \\ & = 1.962 \\ P_{密度:0.697|否} & =P(密度=0.697|好瓜=否) \\ & = \frac{1}{\sqrt{2\pi}0.195}exp(-\frac{(0.697-0.496)^2}{2*0.195^2}) \\ & = 1.194 \\ P_{含糖:0.460|是} & =P(含糖=0.460|好瓜=是) \\ & = \frac{1}{\sqrt{2\pi}0.101}exp(-\frac{(0.460-0.279)^2}{2*0.101^2}) \\ & = 0.669 \\ P_{含糖:0.460|否} & =P(含糖=0.460|好瓜=否) \\ & = \frac{1}{\sqrt{2\pi}0.108}exp(-\frac{(0.460-0.154)^2}{2*0.108^2}) \\ & = 0.42 \\ \end{aligned} \]

这里有必要说一点:在周志华西瓜书中\(P_{凹陷|是}\)计算结果是有错误的,实际结果应该是0.625,而非0.750,读者可自行计算从而验证

于是,我们可以计算得到该瓜是好瓜和坏瓜的可能性如下

\[\begin{aligned} & P(好瓜=是)*P_{青绿|是}*P_{蜷缩|是}*P_{浊响|是}*P_{清晰|是} \\ & \quad \quad \quad \quad \quad \quad *P_{凹陷|是}*P_{硬滑|是}*P_{密度:0.697|是}*P_{含糖:0.460|是}=0.046 \\ & P(好瓜=否)*P_{青绿|否}*P_{蜷缩|否}*P_{浊响|否}*P_{清晰|否}\\ & \quad \quad \quad \quad \quad \quad*P_{凹陷|否}*P_{硬滑|否}*P_{密度:0.697|否}*P_{含糖:0.460|否}=4.36*10^{-5} \end{aligned} \]

由计算可以得到,\(0.046>4.36*10^{-5}\),所以我们理应将这个判别样本归类于“好瓜”

下面,我们不妨通过代码来描述上述贝叶斯的判别过程。

首先,建立一个establish_data方法用于准备数据:

定义一个calc_label_countcalc_p_c方法,分别用于统计不同类别标签的数量,以及计算各类别在数据样本集中的频率,即各类别的\(P(c)\)值:

程序运行结果如下,可见与我们前面手动计算的结果一致

根据上述贝叶斯分类的流程,还需定义一个calc_dispersed_p_xi_c方法以及calc_continuity_p_xi_c来分别计算\(P(x_i|c)\)的值,方法分别对应着离散型数据和连续型数据

不过有一点还需要说明的是,在计算连续型数据的\(P(x_i,c)\)的时候,我们还应该提前得知数据的均值以及方差,为此还需定义一个calc_mean_standard方法来实现这个功能,该三个核心方法的具体代码如下(都挺简单的):

运算结果如下图所示:

可以看到,此时的贝叶斯算法判断该瓜为好瓜,与我们实际的标签一致,说明预测正确。当然了,这个代码只是预测了一个西瓜样本,读者可自行根据程序代码预测多个样本西瓜,从而判断该贝叶斯的正确率。

完整代码:

import numpy as np

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 用于生成样本的属性特征以及对应的标签
    Return:
        x_data: 数据样本的属性,其中包括8个属性
        y_label: 样本属性所对应的标签
"""
def establish_data():
    x_data = [[1, 1, 1, 1, 1, 1, 0.697, 0.460],
             [2, 1, 2, 1, 1, 1, 0.774, 0.376],
             [2, 1, 1, 1, 1, 1, 0.634, 0.264],
             [1, 1, 2, 1, 1, 1, 0.608, 0.318],
             [3, 1, 1, 1, 1, 1, 0.556, 0.215],
             [1, 2, 1, 1, 2, 2, 0.403, 0.237],
             [2, 2, 1, 2, 2, 2, 0.481, 0.149],
             [2, 2, 1, 1, 2, 1, 0.437, 0.211],
             [2, 2, 2, 2, 2, 1, 0.666, 0.091],
             [1, 3, 3, 1, 3, 2, 0.243, 0.267],
             [3, 3, 3, 3, 3, 1, 0.245, 0.057],
             [3, 1, 1, 3, 3, 2, 0.343, 0.099],
             [1, 2, 1, 2, 1, 1, 0.639, 0.161],
             [3, 2, 2, 2, 1, 1, 0.657, 0.198],
             [2, 2, 1, 1, 2, 2, 0.360, 0.370],
             [3, 1, 1, 3, 3, 1, 0.593, 0.042],
             [1, 1, 2, 2, 2, 1, 0.719, 0.103]]
    y_label = [0, 0, 0, 0, 0, 0, 0, 0,
              1, 1, 1, 1, 1, 1, 1, 1, 1]
    return np.array(x_data), np.array(y_label)

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 用于统计不同标签的样本数量
    Parameters:
        y_label: 样本属性所对应的标签
    Return:
        label_count: 不同样本标签的数量
"""
def calc_label_count(y_label):
    label_count_0, label_count_1 = 0, 0; data_number = y_label.shape[0]
    for label in y_label:    # 遍历y_label,统计不同类别的数量
        if int(label) == 0: label_count_0 += 1
        if int(label) == 1: label_count_1 += 1
    return label_count_0, label_count_1

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 用于计算各类别在数据样本集中的频率,即各类别的$P(c)$值:
    Parameters:
        y_label: 样本属性所对应的标签
    Return:
        pc: 指定对应标签的频率值
"""
def calc_p_c(y_label):
    data_number = y_label.shape[0]
    label_count_0, label_count_1 = calc_label_count(y_label)
    return label_count_0 / data_number, label_count_1 / data_number

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 用于计算各类别在数据样本集中的频率,即各类别的$P(c)$值,主要用于标称型数据
    Parameters:
        y_label: 样本属性所对应的标签
    Return:
        pc: 指定对应标签的频率值
"""
def calc_dispersed_p_xi_c(test_data, x_data, y_label, attribute_index):
    label_count_0, label_count_1  = calc_label_count(y_label)
    attribute_count_0, attribute_count_1 = 0, 0
    for item in x_data[:label_count_0]:
        if test_data[attribute_index] == item[attribute_index]:
            attribute_count_0 += 1
    for item in x_data[label_count_0:]:
        if test_data[attribute_index] == item[attribute_index]:
            attribute_count_1 += 1
    return attribute_count_0 / label_count_0, attribute_count_1 / label_count_1

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 用于计算均值和标准差
"""
def calc_mean_standard(x_data):
    mean_value_0, mean_value_1 = np.mean(x_data[:8, 6:8], axis = 0), np.mean(x_data[8:, 6:8], axis = 0)
    std_value_0, std_value_1 = np.std(x_data[:8, 6:8], axis = 0), np.std(x_data[8:, 6:8], axis = 0)
    return mean_value_0, mean_value_1, std_value_0, std_value_1

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 将数据进行高斯转化
"""
def calc_gaussian(data, mean_value, std_value):
    return (1 / (np.sqrt(2*np.pi) * std_value)) * (np.e ** ((- (data - mean_value) ** 2) / (2 * (std_value) ** 2)))

"""
    Author: Taoye
    微信公众号: 玩世不恭的Coder
    Explain: 计算数值型数据的p_xi_c
"""
def calc_continuity_p_xi_c(test_data, x_data):
    mean_value_0, mean_value_1, std_value_0, std_value_1 = calc_mean_standard(x_data)  
    pxi_density_0 = calc_gaussian(test_data[6], mean_value_0[0], std_value_0[0])
    pxi_density_1 = calc_gaussian(test_data[6], mean_value_1[0], std_value_1[0])
    pxi_sugar_0 = calc_gaussian(test_data[7], mean_value_0[1], std_value_0[1])
    pxi_sugar_1 = calc_gaussian(test_data[7], mean_value_1[1], std_value_1[1])
    return pxi_density_0, pxi_density_1, pxi_sugar_0, pxi_sugar_1

if __name__ == "__main__":
    test_data = [1, 1, 1, 1, 1, 1, 0.697, 0.460]
    x_data, y_label = establish_data()
    attr0 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 0)
    attr1 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 1)
    attr2 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 2)
    attr3 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 3)
    attr4 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 4)
    attr5 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 5)
    print("标称型数据的P_{(x_i,c)}:", attr0, attr1, attr2, attr3, attr4, attr5)
    pxi_density_0, pxi_density_1, pxi_sugar_0, pxi_sugar_1 = calc_continuity_p_xi_c(test_data, x_data)
    print("数值型数据的P_{(x_i,c)}:", pxi_density_0, pxi_density_1, pxi_sugar_0, pxi_sugar_1)
    p1, p2 = calc_p_c(y_label)
    print("数据样集中的各类别的概率情况分别为:", p1, p2)
    p_good_melon = p1 * attr0[0] * attr1[0] * attr2[0] * attr3[0] * attr4[0] * attr5[0] * pxi_density_0 * pxi_sugar_0
    p_bad_melon = p2 * attr0[1] * attr1[1] * attr2[1] * attr3[1] * attr4[1] * attr5[1] * pxi_density_1 * pxi_sugar_1
    print("分类为好瓜和坏瓜的可能性分别为:", p_good_melon, p_bad_melon)
    print("恰瓜群众拿到这个是好瓜") if p_good_melon >= p_bad_melon else print("恰瓜群众拿到这个是好瓜")

在本节开始的时候,我们提出了三个问题,其中已经已经解决了两个,现在我们来解决最后一个问题。

  • 假如说我们的检测西瓜中的特征值在17个样本里面都不存在,那么这个时候又应该需要怎么处理呢???

就是说,若某个属性值在训练集中没有与某个类同时出现,则我们根据前面所提到的朴素贝叶斯进行判别将会出现问题。例如,在使用西瓜数据集训练朴素贝叶斯的时候,对一个“敲声=清脆”的测试样例,有:

\[P_{清脆|是}=P(敲声=清脆|好瓜=是)=\frac{0}{8}=0 \]

由连乘式计算出的概率值为0,因此,无论该样本的其他属性特征是什么,哪怕在其他属性上明显是好瓜,分类的结果都是“好瓜=否”,这个显然不是很合理。

为了避免其他属性携带的信息被训练集中未出现的属性值“抹去”,在估计概率值时通常要进行“平滑”操作,常用的是“拉普拉斯修正”。具体来说,令N表示训练集D中可能的类别数,\(N_i\)表示第i个属性可能的取值数,则此时的式子3-4和3-5分别修正为:

\[\hat{P}(c) = \frac{|D_c| + 1}{|D| + N} \\ \hat{P}(x_i|c)=\frac{|D_{c,x_i}|+1}{|D_c|+N_i} \]

例如,在本节例子中,类先验概率可估计为:

\[\hat{P}(好瓜=是)=\frac{8+1}{17+2},\hat{P}(好瓜=否)=\frac{9+1}{17+2}=0.526 \]

这个拉普拉斯修正没什么难点,主要是处理单个属性在数据样本中不存在的问题,读者可根据拉普拉斯修正的方式来将上述完整代码进行改进。

关于贝叶斯算法,其实后面还有些内容,限于篇幅原因,我们留着后面有机会再来肝。

如果这篇文章对您有所帮助,点个赞、分享下吧~~~

这篇文章就不唠嗑了。

我是Taoye,爱专研,爱分享,热衷于各种技术,学习之余喜欢下象棋、听音乐、聊动漫,希望借此一亩三分地记录自己的成长过程以及生活点滴,也希望能结实更多志同道合的圈内朋友,更多内容欢迎来访微信公主号:玩世不恭的Coder。

我们下期再见,拜拜~~~

参考资料:

[1] 《机器学习实战》:Peter Harrington 人民邮电出版社
[2] 《统计学习方法》:李航 第二版 清华大学出版社
[3] 《机器学习》:周志华 清华大学出版社
[4] 人教版高中数学B版选修2-3
[5] 贝叶斯推断及其互联网应用:http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.html

推荐阅读

《Machine Learning in Action》—— 女同学问Taoye,KNN应该怎么玩才能通关
《Machine Learning in Action》—— 懂的都懂,不懂的也能懂。非线性支持向量机
《Machine Learning in Action》—— hao朋友,快来玩啊,决策树呦
《Machine Learning in Action》—— Taoye给你讲讲决策树到底是支什么“鬼”
《Machine Learning in Action》—— 剖析支持向量机,优化SMO
《Machine Learning in Action》—— 剖析支持向量机,单手狂撕线性SVM
print( "Hello,NumPy!" )
干啥啥不行,吃饭第一名
Taoye渗透到一家黑平台总部,背后的真相细思极恐
《大话数据库》-SQL语句执行时,底层究竟做了什么小动作?

posted @ 2020-11-30 20:06  玩世不恭的Coder  阅读(371)  评论(0编辑  收藏  举报