朴素贝叶斯

1, 概率基础

样本空间 :

随机试验 E 中, 实验的所有可能结果组成的集合, 称为 样本空间 S, 样本空间的每个元素, 即 E 的每个结果, 称 样本点

随机事件 :

进行随机试验时, 满足某种条件的样本点组成的集合, S 的子集, 称作 随机事件 , 只有一个样本点时, 称作 基本事件

概率 :

对于随机事件 A, 概率为:

$P(A)=\frac{A \text { 中基本事件数 }}{S \text { 中基本事件数 }}$

条件概率 :

定义事件 A 发生的前提下, 事件 B 发生的概率 P(B | A) 为条件概率:

$$P(B \mid A)=\frac{P(A B)}{P(A)}$$

由条件概率的定义可得, 事件 A 和 B 同时发生的概率 P(AB) 满足如下 乘法定理 :

$$P(A B)=P(B \mid A) P(A)$$

独立性:

定义 A 和 B 两个事件, 如果满足:

$$P(A B)=P(A) P(B)$$

则称事件 A, B 相互独立. 再结合乘法定理, 则有:

$$P(B \mid A) = P(B)$$

全概率公式:

设随机试验 E 的样本空间为 S, 若事件 $B_{1}$$B_{2}$,…, $B_{n}$ 构成一个完备事件组(即它们两两相互独立,事件并集为 S), 且都有正概率,则对任意一个 E 的事件 A,有如下公式成立:

$$P(A)=P\left(A \mid B_{1}\right) P\left(B_{1}\right)+P\left(A \mid B_{2}\right) P\left(B_{2}\right)+\ldots \ldots+P\left(A \mid B_{n}\right) P\left(B_{n}\right)$$

此公式即为全概率公式. 特别地,对于任意两随机事件 A 和 B,有如下成立:

$$P(B)=P(B \mid A) P(A)+P(B \mid \bar{A}) P(\bar{A})$$

贝叶斯公式:

设随机试验 E 的样本空间为 S, 若事件 $B_{1}$$B_{2}$,…, $B_{n}$ 构成一个完备事件组(即它们两两相互独立,事件并集为 S), 且都有正概率,则对任意一个 E 的正概率事件 A,有如下公式成立( i 为 1~n 的正整数):

$$P\left(B_{i} \mid A\right)=\frac{P\left(A B_{i}\right)}{P(A)}=\frac{P\left(A \mid B_{i}\right) P\left(B_{i}\right)}{P(A)} \ =\frac{P\left(A \mid B_{i}\right) P\left(B_{i}\right)}{\sum_{j=1}^{n} P\left(A \mid B_{j}\right) P\left(B_{j}\right)}$$

贝叶斯公式将求解 P(B | A) 的概率转换成求 P(A | B) 的概率, 在求解某个事件概率非常困难时, 转换一下更方便求解

例: 从以往数据得出, 机器调整良好时生产的产品合格的概率是 98%, 机器故障时合格的概率是 55%, 每天开机时机器调整良好的概率为 95%. 求某日开机生产的第一件产品是合格品时, 机器调整良好的概率?

解: 设事件 A 为产品合格, B 为机器调整良好, 则 $\bar{B}$ 为机器故障

$$P(B \mid A)=\frac{P(A \mid B) P(B)}{P(A \mid B) P(B)+P(A \mid \bar{B}) P(\bar{B})} \ =\frac{0.98 \times 0.95}{0.98 \times 0.95+0.55 \times 0.05} \ =0.97$$

先验概率和后验概率:

由以往的数据得出的概率称为 先验概率 , 如上例中的已知概率

得到某些信息后, 在先验概率的基础上进行修正而得到的概率, 称为 后验概率 , 如上例中求解的概率

2, 朴素贝叶斯算法原理

朴素贝叶斯是基于概率的分类算法, 前提假设各个特征(自变量)之间是相互独立的, 设类别(因变量)为 Y, Y 包含 m 个类别( $y_{1},\ldots, y_{m}$), 特征为 X, X 包含含有 n 个特征 ( $x_{1}, \ldots, x_{n}$), 然后通过计算比较, 在特征 X 确定的前提下, 类别 Y 中每个类别的概率大小, 概率最大者即为预测结果

设 Y 中任意一个类别为 y, 则:

$$P(y \mid X) = P\left(y \mid x_{1}, \ldots, x_{n}\right) \ =\frac{P(y) P\left(x_{1}, \ldots, x_{n} \mid y\right)}{P\left(x_{1}, \ldots, x_{n}\right)} \ =\frac{P(y) P\left(x_{1} \mid y\right) P\left(x_{2} \mid y\right) \ldots P\left(x_{n} \mid y\right)}{P\left(x_{1}, \ldots, x_{n}\right)} \ =\frac{P(y) \prod_{i=1}^{n} P\left(x_{i} \mid y\right)}{P\left(x_{1}, \ldots, x_{n}\right)}$$

上式分母为定值, 则:

$$P\left(y \mid X \right) \propto P(y) \prod_{i=1}^{n} P\left(x_{i} \mid y\right)$$

所以最终预测类别 $\hat{y}$ 为分子部分值最大对应的类别:

$$\hat{y}=\arg \max_{y} P(y) \prod_{i=1}^{n} P\left(x_{i} \mid y\right)$$

不同的朴素贝叶斯算法, 主要是对 $P\left(x_{i} \mid y\right)$ 的分布假设不同, 进而采取不同的参数估计方式. 最终主要就是通过计算 $P\left(x_{i} \mid y\right)$ 的概率来计算结果

例: 预测第 11 条记录, 学生是否上课

序号 天气 上课距离 成绩 课程 上课情况
1 选修 逃课
2 必修 上课
3 必修 上课
4 选修 逃课
5 选修 上课
6 必修 上课
7 选修 逃课
8 必修 上课
9 必修 逃课
10 选修 逃课
11 选修 ?
12 选修 ?

分别计算上课和逃课情况下, 各自的概率:

$$P(y=\text { 上课 }) \prod_{i=1}^{n} P\left(x_{i} \mid y=\text { 上课 }\right) \ =P(y=\text { 上课 }) P\left(x_{1}=\text { 阴 } \mid y=\text { 上课 }\right) P\left(x_{2}=\text { 近 } \mid y=\text { 上课 }\right) \ P\left(x_{3}=\text {差 } \mid y=\text { 上课 }\right) P\left(x_{4}=\text { 选修 } \mid y=\text { 上课 }\right) \ =0.5 \times 0.4 \times 1 \times 0.2 \times 0.2 \ =0.008$$ $$P(y=\text { 逃课 }) \prod_{i=1}^{n} P\left(x_{i} \mid y=\text { 逃课 }\right) \ =P(y=\text { 逃课 }) P\left(x_{1}=\text { 阴 } \mid y=\text { 逃课 }\right) P\left(x_{2}=\text { 近 } \mid y=\text { 逃课 }\right) \ P\left(x_{3}=\text { 差 } \mid y=\text { 逃课 }\right) P\left(x_{4}=\text { 选修 } \mid y=\text { 逃课 }\right) \ =0.5 \times 0.2 \times 0.2 \times 0.8 \times 0.8 \ =0.0128$$

可得预测结果为: 逃课

3, 平滑改进

当我们预测上例中, 第 12 条记录所属的类别时, 因为样本不是总体, 会出现上课的前提下, 距离远的概率为 0, 造成计算结果也为 0, 影响了预测结果, 因此需要平滑改进:

$$ P\left(x_{i} \mid y\right)=\frac{\text { 类别 } y \text { 中 } x_{i} \text { 取某个值出现的次数 }+ \alpha}{\text { 类别别 } y \text { 的总数 }+k * \alpha} $$

其中, k 为特征 $x_{i}$ 可能的取值数, α (α ≥ 0) 称为平滑系数, 当 α = 1 时, 称拉普拉斯平滑( Laplace smoothing)

4, 算法优点

即使训练集数据较少, 也能实现不错的预测; 算法训练速度非常快

因此算法假设特征之间是独立的, 可以单独考虑. 如果训练集有 N 个特征, 每个特征需要 M 个样本来训练, 则只需要训练 N*M 的样本数量, 而不是笛卡儿积的形式指数级增加

常用的朴素贝叶斯有: 高斯朴素贝叶斯, 伯努利朴素贝叶斯, 多项式朴素贝叶斯

5, 高斯朴素贝叶斯

适用于连续变量, 其假定各个特征 x 在各个类别 y 下服从正态分布:

$$x_{i} \sim N\left(\mu_{y}, \sigma_{y}^{2}\right)$$

算法使用概率密度函数来计算 $P\left(x_{i} \mid y\right)$ 的概率:

$$P\left(x_{i} \mid y\right)=\frac{1}{\sqrt{2 \pi \sigma_{y}^{2}}} \exp \left(-\frac{\left(x_{i}-\mu_{y}\right)^{2}}{2 \sigma_{y}^{2}}\right) $$

$\mu_{y}$: 在类别为 y 的样本中, 特征 $x_{i}$ 的均值
$\sigma_{y}$: 在类别为 y 的样本中, 特征 $x_{i}$ 的标件差

import numpy as np  
import pandas as pd  
from sklearn.naive_bayes import GaussianNB  

np.random.seed(0)  
x = np.random.randint(0, 10, size=(8, 3))  
y = np.array([0, 1, 0, 0, 0, 1, 1, 1])  
data = pd.DataFrame(np.concatenate([x, y.reshape(-1, 1)], axis=1),  
                   columns=['x1', 'x2', 'x3', 'y'])  
display(data[:3])  

gnb = GaussianNB()  
gnb.fit(x, y)  

print('类别标签:', gnb.classes_)  
print('每个类别的先验概率:', gnb.class_prior_)  
print('样本数量:', gnb.class_count_)  
print('每个类别下特征的均值:', gnb.theta_)  
print('每个类别下特征的方差:', gnb.sigma_)  

# 测试集  
x_test = np.array([[5, 7, 2]])  
print('预测结果:', gnb.predict(x_test))  
print('预测结果概率:', gnb.predict_proba(x_test))  
  x1	x2	x3	y
0	5	0	3	0
1	3	7	9	1
2	3	5	2	0


类别标签: [0 1]
每个类别的先验概率: [0.5 0.5]
样本数量: [4. 4.]
每个类别下特征的均值: [[5.   5.   3.  ]
 [6.5  5.75 7.5 ]]
每个类别下特征的方差: [[3.50000001 9.50000001 3.50000001]
 [5.25000001 7.68750001 2.75000001]]
预测结果: [0]
预测结果概率: [[0.99567424 0.00432576]]

6, 伯努利朴素贝叶斯

设实验 E 只有两个可能的结果, A 与 $\bar{A}$, 则称 E 为伯努利试验

伯努利朴素贝叶斯, 适用于离散变量, 其假设各个特征 x 在各个类别 y 下服从 n 重伯努利分布(二项分布), 因伯努利试验仅有两个结果, 算法会首先对特征值进行二值化处理(假设二值化结果为 1 和 0 )

$P\left(x_{i} \mid y\right)$ 的概率为:

$$P\left(x_{i} \mid y\right)=P\left(x_{i}=1 \mid y\right) x_{i}+\left(1-P\left(x_{i}=1 \mid y\right)\right)\left(1-x_{i}\right)$$

在训练集中, 会进行如下评估:

$$ P\left(x_{i}=1 \mid y\right)=\frac{N_{y i}+\alpha}{N_{y}+2 * \alpha} \ P\left(x_{i}=0 \mid y\right)=1-P\left(x_{i}=1 \mid y\right) $$

$N_{y i}$: 第 i 特征中, 属于类别 y, 数值为 1 的样本个数
$N_{y}$: 属于类別 y 的所有样本个数
$\alpha$: 平滑系数

from sklearn.naive_bayes import BernoulliNB  

np.random.seed(0)  
x = np.random.randint(-5, 5, size=(8, 3))  
y = np.array([0, 1, 0, 0, 0, 1, 1, 1])  
data = pd.DataFrame(np.concatenate([x, y.reshape(-1, 1)], axis=1),  
                   columns=['x1', 'x2', 'x3', 'y'])  
display(data[:3])  

bnb = BernoulliNB()  
bnb.fit(x, y)  

# 统计每个类别下, 特征中二值化后, 每个特征下值 1 出现的次数  
print('值 1 出现的次数:', bnb.feature_count_)  

# 每个类别的先验概率, 算法得到的该概率值是取对数后的结果,  
# 需要取指数还原  
print('每个类别的先验概率:', np.exp(bnb.class_log_prior_))  

# 每个类别下, 每个特征的概率(也需要取指数还原)  
print('每个特征的概率:', np.exp(bnb.feature_log_prob_))  

# 测试集  
x_test = np.array([[-5, 0, 2]])  
print('预测结果:', bnb.predict(x_test))  
print('预测结果概率:', bnb.predict_proba(x_test))  
  x1	x2	x3	y
0	0	-5	-2	0
1	-2	2	4	1
2	-2	0	-3	0


值 1 出现的次数: [[1. 2. 1.]
 [3. 3. 3.]]
每个类别的先验概率: [0.5 0.5]
每个特征的概率: [[0.33333333 0.5        0.33333333]
 [0.66666667 0.66666667 0.66666667]]
预测结果: [0]
预测结果概率: [[0.6 0.4]]

7, 多项式朴素贝叶斯

多项式朴素贝叶斯, 适用于离散变量, 其假设各个特征 x 在各个类别 y 下服从多项式分布(每个特征下的值之和, 就是该特征发生(出现)的次数), 因此每个特征值不能是负数

$P\left(x_{i} \mid y\right)$ 的概率为:

$$P\left(x_{i} \mid y\right)=\frac{N_{y i}+\alpha}{N_{y}+\alpha n} $$

$N_{y i}$: 特征 i 在类别 y 的样本中发生(出现)的次数
$N_{y}$: 类别 y 的样本中, 所有特征发生(出现)的次数
$n$: 特征数量
$\alpha$: 平滑系数

from sklearn.naive_bayes import MultinomialNB  

np.random.seed(0)  
x = np.random.randint(1, 5, size=(8, 3))  
y = np.array([0, 1, 0, 0, 0, 1, 1, 1])  
data = pd.DataFrame(np.concatenate([x, y.reshape(-1, 1)], axis=1),  
                   columns=['x1', 'x2', 'x3', 'y'])  
display(data[:3])  

mnb = MultinomialNB()  
mnb.fit(x, y)  

# 每个类别的样本数量  
print('每个类别的样本数:', mnb.class_count_)  

# 每个特征在每个类别下发生(出现)的次数  
print('每个特征发生(出现)次数:', mnb.feature_count_)  

# 每个类别下, 每个特征的概率(需要取指数还原)  
print('每个类别下特征的概率:', np.exp(mnb.feature_log_prob_))  

# 测试集  
x_test = np.array([[1, 1, 4]])  
print('预测结果:', mnb.predict(x_test))  
print('预测结果概率:', mnb.predict_proba(x_test))  
  x1	x2	x3	y
0	1	4	2	0
1	1	4	4	1
2	4	4	2	0


每个类别的样本数: [4. 4.]
每个特征发生(出现)次数: [[10. 14. 10.]
 [ 9. 11. 11.]]
每个类别下特征的概率: [[0.2972973  0.40540541 0.2972973 ]
 [0.29411765 0.35294118 0.35294118]]
预测结果: [1]
预测结果概率: [[0.36890061 0.63109939]]

利用鸢尾花数据集比较上述 3 个贝叶斯算法:

对不同的数据集, 根据其分布情况选择适合的算法, 能得到更好的结果

from sklearn.datasets import load_iris  
from sklearn.model_selection import train_test_split  

x, y = load_iris(return_X_y=True)  
x_train, x_test, y_train, y_test = train_test_split(x, y,  
                        test_size=0.25, random_state=0)  
models = [('高斯朴素贝叶斯分值:', GaussianNB()),  
          ('伯努利朴素贝叶斯分值:', BernoulliNB()),  
          ('多项式朴素贝叶斯分值:', MultinomialNB())]  
for name, m in models:  
    m.fit(x_train, y_train)  
    print(name, m.score(x_test, y_test))  
高斯朴素贝叶斯分值: 1.0
伯努利朴素贝叶斯分值: 0.23684210526315788
多项式朴素贝叶斯分值: 0.5789473684210527