1、概率论的一些基础知识

考虑到一些小伙伴的概率论的知识有点忘了,这是有必要贴心地帮助大家简单地回顾一下一些基本的概率论的知识。

  • 我们记一个事件\(A\)发的概率为\(P(A)\),那么它是\(0 \le P(A) \le 1\)

  • 我们记事件\((A,\ B)\)发生的概率为\(P(A,\ B)\),有时也记为\(P(AB)\),这表示\(A, \ B\)同时发生的概率;或者记成\(P(A\cap B)\),这几种写法是等价的(如果你觉得不对,可以在下面留言讨论,欢迎拍砖😃)。

  • 我们记事件A或者B发生的概率为\(P(A\cup B)\),这里需要注意一下的是,如果\(A,\ B\)不是相互独立的,那么\(P(A\cup B)\ne P(A)+P(B)\)

  • 如果\(A,\ B,\ C\)是相互独立的,那么有\(P(ABC)=P(A)P(B)P(C)\)。这可推广到\(n\)种情况,如果事件\(A_1,\ A_2,\cdots,\ A_n\)是相互独立的,有如下关系(这个公式我们下面会用到,记住哦😎)
    \[P(A_1A_2\cdots A_n)=P(A_1)P(A_2)\cdots P(A_n)\tag{1-1}\]

  • 条件概率: 已知事件\(B\)发生的情况下,事件\(A\)发生的概率记为\(P(A|B)\)

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

  • 高斯分布,或者叫作正态分布:假设随机变量\(X\)的均值(更专业一点,称之为期望值)和方差分别为\((\mu,\ \sigma^2)\),且服从高斯分布,那么它的概率密度为
    \[f(x)=\frac{1}{\sqrt{2\pi}\sigma}e^{-\frac{(x-\mu)^2}{2\sigma^2}}=\frac{1}{\sqrt{2\pi}\sigma}\exp{\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)}\]

好了,我们需要回忆的基本的概率论的知识点就是这么多了。

2、贝叶斯公式

在前面的条件概率中我们回忆到,已知事件\(B\)发生的情况下,事件\(A\)发生的概率为

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

根据这个公式,我们可以得一个概率的乘法公式

\[P(AB)=P(A|B)P(B)=P(B|A)P(A)\]

如果我们想求\(P(B|A)\)怎么办呢,同样利用条件概率和乘法公式,有如下推论:

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

式(2-1)就是我们大名鼎鼎的贝叶斯公式了,这来得非常容易,并不困难😉。

接下来看看在这个贝叶斯问题是如何应用要我们的分类问题中的。我们现在有\(n\)类别,记为\(c_1,c_2,\cdots,c_n\),每个类别都有\(m\)特征\(x_1.x_2,\cdots,x_m\),我们可以把这些特征记为\(\boldsymbol{x}=(x_1.x_2,\cdots,x_m)\),就是把这些特征用一个向量\(\boldsymbol{x}\)来表示。分类问题可以描述为:已知特征\(\boldsymbol{x}=(x_1.x_2,\cdots,x_m)\)的情况下,判断出它是属于哪个类别\(c_i\)。很明显,这里要求\(c_i \in (c_1,c_2,\cdots,c_n)\)

据式(2-1)我们可以将上述问题描述为如下贝叶斯公式:
\[P(c_i|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_i)P(c_i)}{P(\boldsymbol{x})}\quad \text{or}\quadposterior=\frac{likelihood\times prior}{evidence}\tag{2-2}\]
其中\(\boldsymbol{x}\)就是我们上面所讲特征的向量记法,表示为\(\boldsymbol{x}=(x_1.x_2,\cdots,x_m)\),其中\(m\)表示特征个数。为了显示专业性,我们还得给上面的几个概率取几个听起来很厉害的名字。\(P(c_i|\boldsymbol{x})\)称为后验概率(posterior),\(P(\boldsymbol{x}|c_{i})\)称为似然概率(likelihood),\(P(c_i)\)称为先验概率(prior),\(P(\boldsymbol{x})\)称为证据因子(evidence)。嗯,我们把它们记下来,以后听到别的大佬讨论的时候,或许还可以弱弱地插上几句话,我们不装X,我们只是知识的搬运工😜。

接下来,我们的工作就是要求解出上面的式子,其中\(P(c_i)\)\(P(\boldsymbol{x})\),并不难求(为什么不难求,我们记着,后面再来解释),而求\(P(\boldsymbol{x}|c_i)\)是件比较麻烦的事情。在这里面,为了简化它的计算,我们假设特征向量\(\boldsymbol{x}\)中的所有特征是相互独立的。如果你还记得式(1-1)的话,我们可得到

\[P(\boldsymbol{x}|c_i)=P{(x_1,x_2,\cdots,x_n|c_i)}=P(x_1|c_i)P(x_2|c_i)\cdots P(x_n|c_i)\tag{2-3}\]

这样的话,我们只需计算出已知为类别\(c_i\)的情况下,每一个特征\(x_k\)的条件概率,然后将它们乘起来。式(2-3)也是我们常说的朴素贝叶斯(Naive Bayes,NB)了,为何朴素,因为简单!!。至于\(P(x_k|c_i)\)如何求,这成了我们分类问题的关键,这也是我们后面要继续探讨。去泡杯茶,我们继续💪!!

3、贝叶斯的两个简单的例子

为了说明如何利用上面的贝叶斯公式,有必要举2个典型的例子,应用我们的贝叶斯公式实战一下,这样才有点成就感。

3.1 据天气情况,我们是否出去玩

如下是一张根据天气情况是否出去玩的表格,据这张表,判断当有如下天气情况时,我们是否出去玩。现在提出这样一个问题,当天气状况为:<Outlook = sunny, Temperature = cool, Humidity = high, Wind = strong>,我们应出去玩吗?。

根据第2节讨论的贝叶斯,可以将上面的问题抽象成贝叶斯问题,列出如下表3.1。将各种天气的情况写成一个特征向量;是否出去玩,仅有两类,可以记为:\(c_1=\text{Yes},\ c_2=\text{No}​\)

因此,我们需要求解的问题是两个概率问题:

\[P(c_1|x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})=P(c_1|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_1)P(c_1)}{P(\boldsymbol{x})}\tag{3-1}\]

\[P(c_2|x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})=P(c_2|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_2)P(c_2)}{P(\boldsymbol{x})}\tag{3-2}\]

前面我提到过\(P(c_i)\)的计算很简单,但是没有给出理由,在这个例子里面,我们可以先计算\(P(c_1)\)\(P(c_2)\),看看我是否骗了你?
\[P(c_1)=P(\text{Yes})=\frac{9}{14}\\P(c_2)=P(\text{No})=\frac{5}{14}\]

我们再求\(P(\boldsymbol{x})=P(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})\)。同样我在前面也说过\(P(\boldsymbol{x})\)也不难求,但是这个是比上面的难一点点。首先我们作出第一个假设:各个天气特征是相互独立的,因此我们有
\[P(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})=P(\text{sunny})P(\text{cool})P(\text{high})P(\text{strong})\tag{3-3}\]
那么,\(P(\text{sunny})\)又如何求呢,查看上面的表3.1,我们知道Outlook总共才3种天气情况。天晴(Sunny)出现的次数为5次,因此有
\[P(\text{sunny})=\frac{5}{14}\]

同理,我们得到其他概率为\(P(\text{cool})=\dfrac{4}{14},\quad P(\text{high})=\dfrac{7}{14}, \quad P(\text{strong})=\dfrac{9}{14}\)

好了,我们只剩下最后\(P(\boldsymbol{x}|c_{1}),\ P(\boldsymbol{x}|c_{2})\)概率需要求了。我们先求\(P(\boldsymbol{x}|c_1)\),从数学的字面上理解它:已知\(c_1\)发生的情况下,\(\boldsymbol{x}\)发生的概率,这里,把它翻译成人话就是:我们想要去玩(\(c_1=\text{Yes}\)),而天气情况为\(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong}\),这时候我们出去玩的可能性多大呢(概率多大)?我们得用到前面的假设了:\(x_1.x_2.x_3,x_4\)是相互独立的。也就是说我们认为,天气晴不晴,温度低不低,温度高不高…,这些因素都是相互独立(你肯定会觉得,温度跟天气晴不晴肯定相关,但为了简单起见,就得认为它们不相关,否则你可能就算不出来了)。据这个假设,我们有
\[P(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong}|c_1)\\= P(\text{sunny}|c_1)P(\text{cool}|c_1)P(\text{high}|c_1)P(\text{strong}|c_1)\tag{3-4}\]

接下来,我们把已知为\(c_1=\text{Yes}\)的情况表列出来,如下表,共有9条出去玩的记录。

这时候,计算式(3-4)就很简单了,就是数数,这个我们应该很拿手吧。据数数的结果,我们计算出式(3-4)的概率如下:
\[P(\text{sunny}|c_1)=\frac{2}{9}\quad P(\text{cool}|c_1)=\frac{3}{9}\quad P(\text{high}|c_1)=\frac{3}{9} \quad P{(\text{strong}|c_1)}=\frac{3}{9}\]
式(3-1)里的该求的终于都求完了,接下来把它计算出来如下:
\[\begin{eqnarray}P(c_1|\boldsymbol{x})&=&P(c_1|x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})\\&=&\frac{P(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong}|c_1)P(c_1)}{P(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})}\\&=&\frac{P(\text{sunny}|c_1)P(\text{cool}|c_1)P(\text{high}|c_1)P(\text{strong}|c_1)P(c_1)}{P(\text{sunny})P(\text{cool})P(\text{high})P(\text{strong})}\\&=&\frac{\frac{2}{9}\times\frac{3}{9}\times\frac{3}{9}\times\frac{3}{9}\times\frac{9}{14}}{\frac{5}{14}\times\frac{4}{14}\times\frac{7}{14}\times\frac{9}{14}}=0.484\end{eqnarray}\]
\(P(c_2|\boldsymbol{x})\)可以根据\(P(c_1|\boldsymbol{x})\)的求法求出来,当然,我们也有\(P(c_2|\boldsymbol{x})=1-P(c_1|\boldsymbol{x})=0.516\),所以有\(P(c_2|\boldsymbol{x})>P(c_1|\boldsymbol{x})\),这个时候我们应该选择不出去玩

我们在这里,思考一个问题,我们是不是不需将具体的\(P(c_1|\boldsymbol{x}),\ P(c_2|\boldsymbol{x})\)计算出来,因为我们只需比较它们的大小,就决定哪个选择。我们回过头来看看式(3-1)和(3-2)

\[P(c_1|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_1)P(c_1)}{P(\boldsymbol{x})} \quadP(c_2|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_2)P(c_2)}{P(\boldsymbol{x})}\]

相信你不难看出,它们俩分母是相同的,所以我们只需要计算出哪个分子大,我们就选哪个。因此我们可以把它记成如下形式
\[C_{NB}=\max_{c_i\in c}\{P(\boldsymbol{x}|c_i)P(c_i)\}\]
其中\(C_{NB}\)为我们最终的决策。更一般地,我们可以将上式推广一下,假设现在有\(m\)个类\(c=\{c_1,c_2,\cdots,c_m\}\),这些类有\(n\)个特征\(\boldsymbol{x}=\{x_1,x_2,\cdots,x_n\}\),因此朴素贝叶斯分类器(Naïve Bayes Classifier)可以记为如下公式
\[C_{NB}=\max_{c_i\in c}\{P(\boldsymbol{x}|c_i)P(c_i)\}=\max_{c_i\in c}{\left(P(c_i)\prod_{j=1}^{n}P(x_j|c_i)\right)}\tag{3-5}\]

3.2 这是一朵什么样的花

我们现在有一些鸢尾花的数据,其数据如下表3.3所示,共有15条数据,它有4个属性,萼片长(septal length)、萼片宽(sepal width)、花瓣长(petal length)、花瓣宽(petal width),它共有3个种类,分别标号为0、1、2(对应实际的类名为:'setosa' 、'versicolor'、 'virginica'),每个类别有5条件数据。为了方便说明,我们还是像上面一样,将这些花的特征(属性)记为\(\boldsymbol{x}=(x_1,x_2,x_3,x_4)\),类别记为\(\boldsymbol{c}=(c_1,c_2,c_3)\)

接下来,我提出一个分类问题,我们有下面这样一组数据,需要判断它是属于哪种鸢尾花。

sepal length (cm)    5.4
sepal width (cm)     3.7
petal length (cm)    1.5
petal width (cm)     0.2

这个问题我觉得肯定难不倒你,因为我们在上面的3.1小节中详细地讲述了这类问题如何用贝叶斯求解,按照上面的方法我们需要求出如下条件概率
\[P(c_1|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_1)P(c_1)}{P(\boldsymbol{x})} \quadP(c_2|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_2)P(c_2)}{P(\boldsymbol{x})} \quadP(c_3|\boldsymbol{x})=\frac{P(\boldsymbol{x}|c_3)P(c_3)}{P(\boldsymbol{x})} \quad\]
同样有在上面的3.1小节中说过,我们只需要比较这几个概率的大小,因此无需计算出\(P(\boldsymbol{x})\)\(P(c_1),P(c_2),P(c_3)\)的计算相信也难不倒机智的我们,因为每个类有5条数据,因此有
\[P(c_1)=P(c_2)=P(c_3)=\frac{5}{15}=\frac{1}{3}\]
到这里一切都OK,因为\(P(c_1)=P(c_2)=P(c_3)\),所以我们只需计算出上面的似然概率。即,我们需要分别计算\(P(\boldsymbol{x}|c_1),\ P(\boldsymbol{x}|c_2),\ P(\boldsymbol{x}|c_3)\),然后比较它们的大小,即可判断出这条数据是属于哪种类别的鸢尾花。好,接下我们就按此思路,我们同样认为这是一个朴素贝叶斯问题,即花的各个属性不相关。我们先计算第一个概率
\[P(\boldsymbol{x}|c_1)=P(x_1=5.4|c_1)P(x_2=3.7|c_1)P(x_3=1.5|c_1)P(x_4=0.2|c_1)\]
你可以去试一试,努力把它计算出来……停在此处,拿起你的纸和笔开始计算吧……,一分钟……5分钟……1小时……过去了,你可能还是没有算出来,也许你在第一分钟的时候就放弃了。因为你发现上面式子中的第一个概率\(P(x_1=5.4|c_1)\)你就没法算,其中\(x_1=5.4\)根本没有在表3.3中出现过,可能你会觉得,没有出现过,那么它的概率不就是0?但是你觉得这样子合理吗?\(x_1\)它是一个连续的值,假如我们的\(x_1=5.099\),你也会觉得\(P(x_1=5.099|c_1)=0\ ?\),它只和\(x_1=5.1\)相差了\(0.001\),你就说它为0,这样是不是有点不公平。这就是我们这个例子与上面3.1小节的例子不同的地方,在上面的3.1小节中,天气状况的属性只有那几个确定的值(在数学上,称之为离散的),而在本节的例子中,这些属性都是一个连续的值。

在此,我们碰了问题,那我们该怎么解决?我们可以去请教一下大佬——高斯(Gauss),高斯大佬说,你可以将\(P(x_1|c_1)\)的概率分布看成一个高斯分布(也叫正态分布,参考我们第一小节中的高斯分布),高斯分布有2个参数\((\mu,\sigma^2)\),分别叫均值和方差。Okay,我们就听高斯大佬的,求出\(P(x_1|c_1)\)的均值和方差。我们将类别0的数据单独拿出来,如下表3.4所示。

根据表3.4,求出\(P(x_1|c_1)\)的均值\(\mu = 4.86\),方差\(\sigma ^2= 0.21^2\),因此我们可以认为\(P(x_1|c_1)\)的概率分布为

\[P(x_1|c_1)=\frac{1}{\sqrt{2\pi\cdot0.21^2}}\exp{\left(-\frac{(x-0.49)^2}{2\cdot 0.21^2}\right)}\]

我们有了上面的概率分布,求\(P(x_1=5.4|c_1)\)那就是小意思了

\[P(x_1=5.4|c_1)=\frac{1}{\sqrt{2\pi\cdot0.21^2}}\exp{\left(-\frac{(5.4-0.49)^2}{2\cdot 0.21^2}\right)}=0.0995\]

同理,我们可以计算其它几个概率\(P(x_2=3.7|c_1)=0.41,\ P(x_3=1.5|c_1)=2.08,\ P(x_4=0.2|c_1)=39.89\),最后我们再计算出\(P(x_1=5.4|c_1)P(x_2=3.7|c_1)P(x_3=1.5|c_1)P(x_4=0.2|c_1)=3.41\)

关于\(P(\boldsymbol{x}|c_2),\ P(\boldsymbol{x}|c_3),\ P(\boldsymbol{x}|c_4)\)计算都是同样的道理,在此不赘述了。为了方便大家的计算,我将它们的均值和方差列出如下表3.5

最终我们计算出
\[\begin{eqnarray}P(\boldsymbol{x}|c_1)&=&P(x_1=5.4|c_1)P(x_2=3.7|c_1)P(x_3=1.5|c_1)P(x_4=0.2|c_1)\approx 1\\P(\boldsymbol{x}|c_2)&=&P(x_1=5.4|c_2)P(x_2=3.7|c_2)P(x_3=1.5|c_2)P(x_4=0.2|c_2)\approx 0\\P(\boldsymbol{x}|c_3)&=&P(x_1=5.4|c_3)P(x_2=3.7|c_3)P(x_3=1.5|c_3)P(x_4=0.2|c_3)\approx 0\\\end{eqnarray}\]

因此我们认为有下如下属性的花

sepal length (cm)    5.4
sepal width (cm)     3.7
petal length (cm)    1.5
petal width (cm)     0.2

它属于类别0,即setosa(山尾鸢),我查看它的数据标签,它确实是setosa(你们可能没有数据,查不到它的标签,这里我告诉你就行了,我不会骗你们的),而且我们计算出为类别0的概率值几乎为1,所以它非常可能是类别0。

我在这里将表3.5的高斯概率分布画出来,直观感受一下它们的分布,如图1.1所示。


贝叶斯之朴素解读-LMLPHP

图1.1 鸢尾花各属性的高斯概率分布

从图中明显可以看出来,"petal length"属性的分布间隔很大,基本上靠这个属性就能判断出类别,再加上其它几个属性共同判别,可以更加准确地分类出来。

4、贝叶斯之图像分类

在这节,我们来用Python语言实现一个贝叶斯图像分类器。在这个Python大火的时候,不会点Python,都不好意思说自已在搞大数据的。

4.1 准备工作

工欲善其事,必先利其器。我们先准备好我们的工作环境:jupyter+python3.6,这是我目前用的环境,如果大家没有用过jupyter,我建议大家用一下,相信你会爱上它的。关于jupyter的安装和下载以及使用,我在这里就不说了,聪明的你自会百度或google。其次,我们再准备一下数据集:CIFAR-10图像数据,我将其放入了我的百度网盘,链接: https://pan.baidu.com/s/1yIkiL7xXHsqlXS53gxMkEg 提取码: wcc4。原始的CIFAR-10图像是一个用于普世物体识别的数据集,分为airplane、automobile、bird、cat、deer、dog、frog、horse、ship、truck共10类,但是这里为了简单起见,我用了其中5类。

读取数据

将数据下载好后,把它放在当前目录的data文件夹中。利用如下代码,读取数据,并查看一下数据的信息。一般来讲,数据都是一个字典类型。

from scipy.io import loadmat
import numpy as np

train_data_mat = loadmat("./data/train_data.mat")
test_data_mat = loadmat("./data/test_data.mat")
labels_name_mat = loadmat("./data/labels_name.mat")

经过上面的步骤,我们读取到了数据,分为训练集测试集标签名字。在jupyter的cell输入我们的数据变量,可以查一下它信息,让们知道如何进行一下处理。

train_data_mat
{'__header__': b'MATLAB 5.0 MAT-file, Platform: PCWIN64, Created on: Fri Nov 02 10:09:15 2018',
 '__version__': '1.0',
 '__globals__': [],
 'Data': array([[170, 168, 177, ...,  82,  78,  80],
        [159, 150, 153, ...,  14,  17,  19],
        [ 50,  51,  42, ..., 166, 166, 162],
        ...,
        [195, 151, 160, ..., 133, 135, 133],
        [137, 139, 141, ...,  24,  24,  19],
        [177, 174, 182, ..., 119, 127, 136]], dtype=uint8),
 'Label': array([[1],
        [1],
        [1],
        ...,
        [5],
        [5],
        [5]], dtype=uint8)}

从训练集输出的信息来看,它的标签是从1~5,即有5个类,我们看到了在"Data"里面有一堆数据,但是不知道它的结构,而且这都是一些字典类型的数据,我们需从这些字典中把数据提取出然后看看它们的结构。

# 训练数据和标签
train_data = train_data_mat["Data"]
train_data_label = train_data_mat["Label"]
# 测试数据和标签
test_data = test_data_mat["Data"]
test_data_label = test_data_mat["Label"]
# 标答的实际名字
label_names = labels_name_mat["label_names"]
# 因为原始的标签名字有误,我这里把它手动改一下
label_names[:,0]=['automobile','bird','cat','deer','dog']

然后利用shape属性查看一下它们大概的数据结构

print(train_data.shape, train_data_label.shape,test_data.shape, test_data_label.shape)
(9968, 3072) (9968, 1) (5000, 3072) (5000, 1)

从上面的的输出可以看出来,它共有9968个训练集,5000个测试集,每一个样本的特征个数为\(3072 = 32 \times 32 \times 3\),这是把一个\(32\times32\)的RGB图像平铺成了一个行向量(就是把一个3维数组变成一个1维数组)

从开始的类别中我们知道,这个数据共有5个类,下面我们来看看训练集中,每个类分别有多少样本数量。

import pandas as pd
pd.value_counts(train_data_label[:,0])
2    2042
3    2011
4    2009
1    1981
5    1925
dtype: int64

从上面输出可以看出,这个数据集的中每个类的数量不是相同的。

显示图像

下面我选几幅图,把测试集和训练集中的图片显示出来给大家看看,直观感受一下这些数据图片是不是有点难以分辨。

贝叶斯之朴素解读-LMLPHP
图4.1 训练集部分图像
贝叶斯之朴素解读-LMLPHP
图4.2 测试集部分图像

看了上面的图片,不知道你是什么感受。反正我是觉得,有的图片,人都很难分辨出来。

4.2 开始分类

回顾我们上面所提到的训练数据集,每一个样本的特征个数为\(3072 = 32 \times 32 \times 3\),因此它有3072个特征,可以记为\(\boldsymbol{x}=(x_1,x_2,\cdots,x_{3072})\);且数据集分成5个类\(\boldsymbol{c}=(c_1,c_2,c_3,c_4,c_5)\),分别对应'automobile','bird','cat','deer','dog'。因此我们需求出分布

\[P({x_j}|{c_i}){\rm{ = }}{1 \over {\sqrt {2\pi \sigma _{ji}^2} }}\exp \left( { - {{{{\left( {{x_j} - {\mu _{ji}}} \right)}^2}} \over {2\sigma _{ji}^2}}} \right),\;j = 1,2, \cdots ,3072,i = 1,2,3,4,5\tag{4-1}\]

其中\(\mu_{ij}\)表示第\(i\)类的第\(j\)个属性的均值,\(\sigma_{ij}^2\)表示第\(i\)类的第\(j\)个属性的方差。因为朴素贝叶斯,各特征间,相互独立,那么由式(4-1)有

\[P({\boldsymbol{x}}|{c_i}) = \mathop \prod \limits_{j = 1}^{3072} P({x_j}|{c_i}) = {1 \over {{{\left( {\sqrt {2\pi } } \right)}^{3072}}\mathop \prod \limits_{j = 1}^{3072} \sigma _{ji}}}\exp \left( {\mathop \sum \limits_{j = 1}^{3072} - {{{{\left( {{x_j} - {\mu _{ji}}} \right)}^2}} \over {2\sigma _{ji}^2}}} \right),\;j = 1,2, \cdots ,3072,i = 1,2,3,4,5\tag{4-2}\]

上面计算有一定的复杂性,大量的乘法运算,为了简化此运算,我们可以将式(4-2)两边同时取自然对数(\(\ln{x},\ \log{x}\))。因为对数运算是一个单调递增的,所以并不会改变我们对\(P({\boldsymbol{x}}|{c_i}),\ i=1,2,3,4,5\)大小的判定。对式(4-2)两边同时取对数,可以得到如下结果
\[\begin{eqnarray}\log{P(\boldsymbol{x}|c_i)}&=&-\frac{3072\log{\sqrt{2\pi}}}{2} - \sum_{j=1}^{3072}\log{\sigma_{ji}}+{\mathop \sum \limits_{j = 1}^{3072} - {{{{\left( {{x_j} - {\mu _{ji}}} \right)}^2}} \over {2\sigma _{ji}^2}}}\\&=&-\frac{3072\log{\sqrt{2\pi}}}{2} - \sum_{j=1}^{3072}\left(\log{\sigma_{ji}}+{{{{\left( {{x_j} - {\mu _{ji}}} \right)}^2}} \over {2\sigma _{ji}^2}}\right)\tag{4-3}\end{eqnarray}\]
式(4-3)中的常数项同样不影响我们的判决,所以可去将其去掉,最终我们得到如下一个结果
\[p(\boldsymbol{x}|c_i)=- \sum_{j=1}^{3072}\left(\log{\sigma_{ji}}+{{{{\left( {{x_j} - {\mu _{ji}}} \right)}^2}} \over {2\sigma _{ji}^2}}\right),\ j = 1,2, \cdots ,3072,i = 1,2,3,4,5\tag{4-4}\]
嗯,看着清爽多了,而且计算量看着也不是很大,我们将式(4-4)称为我们的判决式

根据上面我们辛辛苦苦推导出来的判别式来写我们的代码。首先,我将我们的训练集数据组织成pandasDataFrame数据的格式,这样子更方便我们后面的操作。

import pandas as pd
col_name_lst = [0]*3072

for i in range(1, 3073):
    col_name_lst[i-1] = "x" + str(i)

train_data = pd.DataFrame(train_data,columns=col_name_lst)
train_data_label = pd.DataFrame(train_data_label, columns=['class_no'])
train_dataFrm = train_data.join(train_data_label)
train_data.head()#查看前5行数据
5 rows × 3072 columns

我们同样假设每个特征(即每个像素点服从正态分布),因此据式(4-4),我们最关键的是要将均值方差求出来。我同样把均值和方差做成了DataFrame数据的格式。

train_data_mean = pd.DataFrame(dtype=np.float16)
train_data_std = pd.DataFrame(dtype=np.float16)

for i in range(0,5):
    mean_temp = train_data[train_dataFrm["class_no"]==i+1].mean()
    std_temp = train_data[train_dataFrm["class_no"]==i+1].std()

    train_data_mean = train_data_mean.append([mean_temp], ignore_index=True)
    train_data_std = train_data_std.append([std_temp], ignore_index=True)

我们在测试集中,随机取1条数据计算每个类别下的似然概率(即,式4-3)值。

some_img_index = 10
ftr_data = test_data[some_img_index]
determine_clf = [0]*5
prior_series = pd.value_counts(train_data_label.iloc[:,0])

for i in range(0,5):
    ftr_mean = train_data_mean.iloc[i]
    ftr_std = train_data_std.iloc[i]
    prob_temp = -(np.log(ftr_std)+0.5*((ftr_data-ftr_mean)/ftr_std)**2).sum()
    prob_temp = (prob_temp + (-3072*np.log(2*np.pi)/2))*prior_series[i+1]
    determine_clf[i]=prob_temp

# 输出各个类下的似然概率值,下面是归一化后的结果
print((determine_clf-min(determine_clf))/(max(determine_clf)-min(determine_clf)))
# 输出测试集的标签值
print(test_data_label[some_img_index])
[1.         0.35530925 0.50774247 0.         0.86720707]
[1]

从输出的结果可以看出,最大值在第1个数,因此它最有可能是类别1,而且输出的标签值正好是1,说明我们预测准确了。你可以将上面代码中的some_img_index换个值,看看是否还是准确的。

最后一步,计算出所有的测试集的预测值,并与测试集的标签对比,得出我们所得的模型在测试集上的准确率。

# 算出测试集中的每一条记录的类别,作出预测
prior_series = pd.value_counts(train_data_label.iloc[:,0])
# 存放预测的标签值
pred_label = [0]*test_data.shape[0]
for img_index in range(0,test_data.shape[0]):
    determine_clf = [0]*5
    ftr_data = test_data[img_index]

    for i in range(0,5):
        ftr_mean = train_data_mean.iloc[i]
        ftr_std = train_data_std.iloc[i]
        prob_temp = -(np.log(ftr_std)+0.5*((ftr_data-ftr_mean)/ftr_std)**2).sum()
        prob_temp = (prob_temp + (-3072*np.log(2*np.pi)/2))#这一行注释也不会影响结果
        determine_clf[i]=prob_temp
    pred_label[img_index] = determine_clf.index(max(determine_clf)) + 1

# 统计预测准确率
sum(pred_label == test_data_label[:,0])/test_data.shape[0]
0.4028

上面输出的准确率为0.4028,可以看到,这并不高,因为图像本身也很模糊。

4.3 使用Scikit-Learn的贝叶斯库

上面我们自己动手用贝叶斯算出来分类准确率并不高,下面我们来看看Scikit-Learn的库的贝叶斯函数模型准确率如何。

拟合数据

from sklearn.naive_bayes import GaussianNB
nbbayes_clf = GaussianNB()
#拟合数据
nbbayes_clf.fit(train_data, train_data_label)

利用Predict函数进行预测

print("==Predict result by predict==")
pred_label1 = nbbayes_clf.predict(test_data)
print(pred_label1)
# print "==Predict result by predict_proba=="
# print(clf.predict_proba([[-0.8, -1]]))
# print "==Predict result by predict_log_proba=="
# print(clf.predict_log_proba([[-0.8, -1]]))

sum(pred_label1 == test_data_label[:,0])/test_data.shape[0]
==Predict result by predict==
[3 1 1 ... 3 5 5]

0.403

其输出的准确率为0.403,跟我们的结果非常接近。

总结

在理解贝叶斯之前,我本着朴素理解贝叶斯的初心,然后开始了这篇文章,所以我回忆概率论的一些基本知识,接着顺便推了一个贝叶斯公式,然后又举了两个贝叶斯的例子,最后还把贝叶斯在图像分类中应用了一下,写到最后,我才发现我已经忘了初心,实在是对不起大家,文章写得有点长了,但是自认为已经把贝叶斯这个东西说得很明白了,希望能给大家带来一些帮助。

最后感谢一下我的女朋友,要不是20多年来她从未出现,我也不会在此双11之季,奋笔疾书,写下此篇博客,供诸君品读。日后若有他人问君贝叶斯分类,请君甩出此文,侃侃而谈,而后佛袖离去,留下潇洒背影,深藏功与名。

11-14 14:34