朴素贝叶斯
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