本文源自微信公众号【Python编程和深度学习】原文链接:[机器学习系列(三)朴素贝叶斯(Naive Bayes)](https://mp.weixin.qq.com/s?__biz=MzUxNTY1MjMxNQ==&mid=2247484400&idx=1&sn=d58f9c7608559e8f6302b31f752fd92f&chksm=f9b22c44cec5a552841c47ee048db9d914b4991046cf7fac59048aaebdda4ffd98740939cab2&token=826655880&lang=zh_CN#rd),欢迎扫码关注鸭! ![扫它!扫它!扫它](http://cookdata.cn/media/bbs/images/公众号_1602413881117_5d14.jpg =200x200) 贝叶斯原理可以解决“逆向概率”问题,解答在没有太多可靠证据的情况下,怎样做出更符合数学逻辑的推测。 **正向概率问题**:如果袋子里共有N个黑球白球,黑球的数量为M,那么摸一个球是黑球的概率是M/N。 **逆向概率问题**:如果只知道袋子里有黑球和白球,数量未知,通过摸出来的球的颜色来判断黑球和白球的比例。 **先验概率**:通过经验来判断事情发生的概率。 **后验概率**:事情发生之后推测发生原因的概率。 贝叶斯原理就是引入先验概率和逻辑推理来求解后验概率,处理不确定命题的。 # 目录 [一、条件概率](#1)<br> [二、贝叶斯定理](#2)<br> [三、朴素贝叶斯分类](#3)<br> [四、拉普拉斯修正](#4)<br> [五、Python代码](#5)<br> [六、朴素贝叶斯例子](#6)<br> <div id="1"></div> ##一、条件概率 条件概率是指事件A在另外一个事件B发生条件下的发生概率,记为*P(A|B)*,计算公式为 $$ P(A \mid B)=\frac{P(A B)}{P(B)} $$ <div id="2"></div> ##二、贝叶斯定理 贝叶斯定理是关于随机事件A和B的条件概率的一则定理,记为 $$ P(B \mid A)=\frac{P(A \mid B) P(B)}{P(A)} $$ 贝叶斯定理之所以有用,是因为我们在生活中经常遇到这种情况:我们可以很容易直接得出*P(A|B)*,*P(B|A)*则很难直接得出,但我们更关心*P(B|A)*,贝叶斯定理就为我们打通从*P(A|B)*获得*P(B|A)*的道路,**将求后验概率的问题转变为求先验概率和条件概率**。 下面给大家展示一个更容易理解的贝叶斯定理的讲解,转自知乎用户Jason Huang的一篇介绍贝叶斯定理的文章。 作者是用集合的思想来看贝叶斯定理的,首先把全空间分割成若干集合Bi,如图 ![](http://cookdata.cn/media/bbs/images/微信截图_20201209114554_1607485556858_5d14.jpg =360x*) $$图1: 全空间Ω$$ 接着全空间里还有另外一个集合(事件) A,见图灰色区域 ![](http://cookdata.cn/media/bbs/images/微信截图_20201209114756_1607485670364_5d14.jpg =350x*) $$图2: 全空间Ω-集合事件A$$ 现在全空间可以更加细致的分割为图 ![](http://cookdata.cn/media/bbs/images/微信截图_20201209115024_1607485821542_5d14.jpg =350x*) $$图3: 全空间Ω细致分割$$ 现在考察绿色方块,也就是区域 $$ P\left(A \bigcap B_{2}\right)=P\left(A B_{2}\right) $$ 我们借用物理学中的参考系概念,以全空间为参考系,则事件A和B2发生的概率分别为 $$ P(A \mid \Omega)=P(A), P\left(B_{2} \mid \Omega\right)=P\left(B_{2}\right) $$ 上述的概率其实也可以等效于图2中相应的方块面积,但是事件 在不同的参考系下看的结果是不一样的。 ![](http://cookdata.cn/media/bbs/images/微信截图_20201209115345_1607486016374_5d14.jpg =350x*) $$图4: 不同视角下事件发生的概率$$ 如果以 A为参考系(以 A为视角),看待A∩B<sub>2</sub>图片发生的概率(也就是所谓的条件概率)为 $$ P\left(B_{2} \mid A\right)=\frac{P\left(A B_{2}\right)}{P(A)} $$ 我们也可以从B2的视角来看待(关注右上角的方块),得到 $$ P\left(A \mid B_{2}\right)=\frac{P\left(A B_{2}\right)}{P\left(B_{2}\right)} $$ 于是有了 $$ P\left(A B_{2}\right)=P\left(A B_{2}\right) P(\Omega)=P\left(B_{2}\right) P\left(A \mid B_{2}\right)=P(A) P\left(B_{2} \mid A\right) $$ <div id="3"></div> ##三、朴素贝叶斯分类 朴素贝叶斯是一种非常简单的用于不同相互独立特征下的分类,对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。 基于贝叶斯公式来估计后验概率P( c | x )的主要困难在于:类条件概率P( x | c )是所有属性上的**联合概率**,难以从有限的训练样本直接估计而得。因此朴素贝叶斯分类器采用了**“属性条件独立性假设”**:对已知类别,假设所有属性相互独立。也就是说,假设每个属性独立的对分类结果发生影响。 基于属性独立性假设,后验概率P(c|x)中的x往往包含多个相关因素,即它可能有多个需要考虑的属性值x = (x1,x2,…,xn)代表所有相关因素,在信用卡审批中可能是是否有房产,是否有车辆,学历情况,月收入等相关因素, 可写为 $$ P(c \mid x)=\frac{P(c) P(x \mid c)}{P(x)}=\frac{P(c)}{P(x)} \Pi_{i=1}^{d} P\left(x_{i} \mid c\right) $$ 这里将P(xi|c)条件联合概率表示成 $$ \Pi_{i=1}^{d} P\left(x_{i} \mid c\right) $$ 表示所有条件概率的相乘。 对于离散数据来说,采用统计的方法(频率)来代替概率。而对于连续数据来说,需要把样本的属性看成正态分布,通过训练样本得到正态分布的均值和方差,代入标准公式得到高斯密度函数,标准公式为: $$ p\left(x_{i} \mid c\right)=\frac{1}{\sqrt{2 \pi} \sigma_{c, i}} \exp \left(-\frac{\left(x_{i}-\mu_{c, i}\right)^{2}}{2 \sigma_{c, i}^{2}}\right) $$ <div id="4"></div> ##四、拉普拉斯修正 若某个离散类型的属性值在训练集中没有有某个类同时出现过,那么在进行概率计算时就会产生0的结果,不管其他属性怎么取值,最终$ P\left(x_{i} \mid c\right) $一直为0,这是不合理的,拉普拉斯修正可以解决这个问题。前面的类先验概率公式 $$ P(c)=\frac{\left|D_{c}\right|}{|D|} $$ Dc表示训练集D中c类样本组成的集合,条件概率公式 $$ P\left(x_{i} \mid c\right)=\frac{\left|D_{c, x_{i}}\right|}{\left|D_{c}\right|} $$ $ D_{c, x_{i}} $图片表示Dc中在第i个属性熵取值为xi的样本组成的集合。 采用拉普拉斯修正后,两个式子变为 $$ \hat{P}(c)=\frac{\left|D_{c}\right|+1}{|D|+N} $$ $$ \hat{P}\left(x_{i} \mid c\right)=\frac{\left|D_{c, x_{i}}\right|+1}{\left|D_{c}\right|+N_{i}} $$ **拉普拉斯修正避免了因训练集不充分而导致概率估值为零的问题**,并且在训练集变大时,修正过程所引入的先验的影响也会逐渐变得可忽略,使得估值渐趋向于实际概率值。 如果数据样本的特征属性之间具有较强的相关性,并不是条件独立的,就会限制朴素贝叶斯分类的能力。 <div id="5"></div> ##五、Python代码 下面给出一个Python代码的朴素贝叶斯模型,来自LY豪: ```python import numpy as np import pandas as pd dataset = pd.read_csv('watermelon_3.csv', delimiter=",") del dataset['编号'] print(dataset) X = dataset.values[:, :-1] m, n = np.shape(X) for i in range(m): X[i, n - 1] = round(X[i, n - 1], 3) X[i, n - 2] = round(X[i, n - 2], 3) y = dataset.values[:, -1] columnName = dataset.columns colIndex = {} for i in range(len(columnName)): colIndex[columnName[i]] = i Pmap = {} # 函数P很耗时间,而且经常会求一样的东西,因此我加了个记忆化搜索,用map存一下,避免重复计算 kindsOfAttribute = {} # kindsOfAttribute[0] = 3,因为有3种不同的类型的"色泽" for i in range(n): kindsOfAttribute[i] = len(set(X[:, i])) continuousPara = {} # 记忆一些参数的连续数据,以避免重复计算 goodList = [] badList = [] for i in range(len(y)): if y[i] == '是': goodList.append(i) else: badList.append(i) import math def P(colID, attribute, C): # P(colName=attribute|C) P(色泽=青绿|是) if (colID, attribute, C) in Pmap: return Pmap[(colID, attribute, C)] curJudgeList = [] if C == '是': curJudgeList = goodList else: curJudgeList = badList ans = 0 if colID >= 6: mean = 1 std = 1 if (colID, C) in continuousPara: curPara = continuousPara[(colID, C)] mean = curPara[0] std = curPara[1] else: curData = X[curJudgeList, colID] mean = curData.mean() std = curData.std() # print(mean,std) continuousPara[(colID, C)] = (mean, std) ans = 1 / (math.sqrt(math.pi * 2) * std) * math.exp((-(attribute - mean) ** 2) / (2 * std * std)) else: for i in curJudgeList: if X[i, colID] == attribute: ans += 1 ans = (ans + 1) / (len(curJudgeList) + kindsOfAttribute[colID]) Pmap[(colID, attribute, C)] = ans # print(ans) return ans def predictOne(single): ansYes = math.log2((len(goodList) + 1) / (len(y) + 2)) ansNo = math.log2((len(badList) + 1) / (len(y) + 2)) for i in range(len(single)): # 书上是连乘,但在实践中要把“连乘”通过取对数的方式转化为“连加”以避免数值下溢 ansYes += math.log2(P(i, single[i], '是')) ansNo += math.log2(P(i, single[i], '否')) # print(ansYes,ansNo,math.pow(2,ansYes),math.pow(2,ansNo)) if ansYes > ansNo: return '是' else: return '否' def predictAll(iX): predictY = [] for i in range(m): predictY.append(predictOne(iX[i])) return predictY predictY = predictAll(X) print(y) print(np.array(predictAll(X))) confusionMatrix = np.zeros((2, 2)) for i in range(len(y)): if predictY[i] == y[i]: if y[i] == '否': confusionMatrix[0, 0] += 1 else: confusionMatrix[1, 1] += 1 else: if y[i] == '否': confusionMatrix[0, 1] += 1 else: confusionMatrix[1, 0] += 1 print(confusionMatrix) ``` <div id="6"></div> ##六、朴素贝叶斯例子 本例子摘自阮一峰的朴素贝叶斯分类器的应用,某个医院早上收了六个门诊病人,如下表: ![](http://cookdata.cn/media/bbs/images/6_1607487056422_5d14.jpg) 现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大? 根据贝叶斯定理: $$ \mathrm{P}(\mathrm{A} \mid \mathrm{B})=\mathrm{P}(\mathrm{B} \mid \mathrm{A}) \mathrm{P}(\mathrm{A}) / \mathrm{P}(\mathrm{B}) $$ 可得 **P(感冒|打喷嚏x建筑工人)= P(打喷嚏x建筑工人|感冒) x P(感冒)/ P(打喷嚏x建筑工人)** 假定"打喷嚏"和"建筑工人"这两个特征是独立的,因此,上面的等式就变成了 **P(感冒|打喷嚏x建筑工人)= P(打喷嚏|感冒) x P(建筑工人|感冒) x P(感冒)/ P(打喷嚏) x P(建筑工人)** 这是可以计算的。 **P(感冒|打喷嚏x建筑工人)= 0.66 x 0.33 x 0.5 / 0.5 x 0.33= 0.66** 因此,这个打喷嚏的建筑工人,有66%的概率是得了感冒。 参考: http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html https://www.jianshu.com/p/a4ddf754357b