《统计学习方法》第四章 朴素贝叶斯法

文章目录
  1. 1. 先验概率
  2. 2. 后验概率
  3. 3. 条件概率
  4. 4. 朴素贝叶斯法
  5. 5. 代码实现朴素贝叶斯算法
  6. 6. 参考来源

这个方法在很多场景有用,但是自身对于朴素贝叶斯的熟悉度还不够,因此补课。
后续会对《统计学习方法》一书进行系统的学习,博客按章更新。

先验概率

事件发生前的预判概率。可以是基于历史数据的统计,可以由背景常识得出,也可以是人的主观观点给出。一般都是单独事件概率,如P(x),P(y)。

后验概率

事件发生后求的反向条件概率;或者说,基于先验概率求得的反向条件概率。概率形式与条件概率相同。

条件概率

一个事件发生后另一个事件发生的概率。一般的形式为P(x|y)表示y发生的条件下x发生的概率。
条件概率公式:

其他公式补充:
(1)乘法公式:

乘法公式可以推广到:

(2)全概率公式:
假设 E 的样本空间为S,A为E的时间,B1,B2,…,Bn为S的一个划分,且P(Bi)>0(i=1.2,…,n),则

(3)贝叶斯公式:

上式可以理解为:

其中A以及B为随机事件,且P(B)不为0。P(A|B)是指在事件B发生的情况下事件A发现的概率。
在贝叶斯定理中,每个名词都有约定俗称的名称:
P(A|B)是已知B发生后,A的条件概率。也由于得自B的取值而被称作A的后验概率。
P(A)是A的先验概率(或边缘概率)。之所以称为“先验”是因为它不用考虑任何B方面的因素。
P(B|A)是已知A发生后,B的条件概率。也由于得自A的取值而被称作B的后验概率。
P(B)是B的先验概率。

按这些术语,贝叶斯定理可表述为:
后验概率 = (似然性*先验概率)/标准化常量
也就是说,后验概率与先验概率和相似度的乘积成正比。
另外,比例 P(B|A)/P(B)也有时被称作标准似然度,贝叶斯定理可以表示为:
后验概率=标准似然度*先验概率

完整的贝叶斯公式:

(4)全概率公式和贝叶斯公式的应用

(5)联合概率分布公式

(6)条件概率分布

朴素贝叶斯法

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入输出的联合概率分布;然后基于此模型,对给定的输入x,利用贝叶斯定理求出后验概率最大的输出y。

先验概率分布:

条件概率分布:

朴素贝叶斯法对条件概率分布作了独立性的假设:

朴素贝叶斯法分类时,对给定的输入x,通过学习到得模型计算后验概率分布P(Y=ck|X=x),将后验概率最大的类作为 x 的类输出。

将独立性假设的式子带入后验概率,可以得到:

其中 ck 可以理解为类别,j 可以理解为特征个数。

因此,朴素贝叶斯分类器可以表示为:

在朴素贝叶斯分类器中,分母对所有ck都是相同的,所以,

在查资料的过程中我看到了这样一篇文章通俗易懂理解朴素贝叶斯我觉得文章写得很好,在这里分享出来。

代码实现朴素贝叶斯算法

举个例子,一个分类问题也许有 k 个分类标签,y1,y2,…,yk 还有 n 个输入变量,X1,X2,…,Xn。我们可以通过一个例子或一系列给定的列值来计算一个条件概率:

条件概率可以用来计算问题中每个类标签的概率然后返回概率最高的标签作为最相似的分类结果。
我们可以把分类通过贝叶斯理论看作是一个条件分类问题:

其中,先验概率 P(yi) 容易从数据集中进行估计得到,除非拥有足够大的数据集,否则 P(x1,x2,…,xn|yi) 的结果则很难获取。至此,直接的贝叶斯理论就变得棘手,尤其是当特征数不断增加时。
贝叶斯定理假设每个输入变量都依赖于所有其他变量,这就是计算复杂的原因。假设所有的变量彼此独立,首先将分母计算P(x1,x2,…,xn)移除,因为它是用于计算给定实例的每个类别的条件概率的常数,并且具有将结果归一化的效果。

接下来,将给定类标签的所有变量的条件概率转换为给定类标签的每个变量值的独立条件概率。再将这些独立条件变量相乘:

可以通过上式来计算每一个类别标签,概率最大的则作为给定序列的分类结果,这样简化的结果也就是贝叶斯分类。

给定类标签的特征值的条件概率也可以从数据中估计出来。具体来说,就是属于给定类的那些数据示例,以及每个变量的一个数据分布。这意味着如果有K个类和n个变量,那么必须创建和维护K * n个不同的概率分布。

下面使用Python完成一个朴素贝叶斯分类的例子,Python版本:3.6,IDE:Pycharm。
使用skit-learn这个库的make_blobs功能成了100个带有两个数值输入变量的样例,每个变量都指定了两个类中的一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.datasets.samples_generator import make_blobs

X,y=make_blobs(n_samples=100,centers=2,n_features=2,random_state=1)
'''
make_blobs 参数说明
n_samples: 生成的样例数
n_features: 每个样本的特征数
centers: 类别数
random_state: 随机数种子
注:random_state参数设置为1,以确保每次运行代码时都生成相同的随机观察样本。
'''
print(X.shape,y.shape)
print(X[:5])
print(y[:5])

运行结果如下:

对模型使用高斯概率分布。这个可以通过使用SciPy这个库来实现norm,首先,可以指定分布参数(比如均值和标准偏差)来构造分布,然后可以使用norm.pdf()函数为特定值采样概率密度函数。
可以使用numpy函数中的mean()和std()从数据集中估计分布参数。
下面的fit_distribution()函数为一个变量获取数据样本,并拟合数据分布。

1
2
3
4
5
6
7
8
9
# 为一个单变量的数据样本拟合一个概率分布
def fit_distribution(data):
# 估计参数
mu=np.mean(data)
sigma=np.std(data)
print(mu,sigma)
# 拟合分布
dist=norm(mu,sigma)
return dist

我们对每个输入变量的条件概率感兴趣,这就意味着我们需要为每个输入变量分配一个分布,为每个类标签分配一组分布,也就是总共需要四组分布。

将数据分为每个类标签的样本组。

1
2
3
4
# 为数据分类
Xy0=X[y==0]
Xy1=X[y==1]
print(Xy0.shape,Xy1.shape)

然后我们可以利用这些组来计算属于每个组的数据样本的先验概率。

假设我们在两个类的每一个中创建了相同数量的样例,这将准确地达到50%。尽管如此,为了完整性,我们仍然需要计算这些先验概率。

1
2
3
4
# 计算先验概率
priory0=len(Xy0)/len(X)
priory1=len(Xy1)/len(X)
print(priory0,priory1)

最后,使用 fit_distribution() 函数,我们定义来为每个变量和标签准备得概率分布。

1
2
3
4
5
6
# 创建y=0的概率分布
X1y0=fit_distribution(Xy0[:,0])
X2y0=fit_distribution(Xy0[:,1])
# 创建y=1的概率分布
X1y1=fit_distribution(Xy0[:,0])
X2y1=fit_distribution(Xy0[:,1])

将上述代码整合到一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env python
#coding:utf-8

from sklearn.datasets.samples_generator import make_blobs
from scipy.stats import norm
import numpy as np
X,y=make_blobs(n_samples=100,centers=2,n_features=2,random_state=1)
'''
make_blobs 参数说明
n_samples: 生成的样例数
n_features: 每个样本的特征数
centers: 类别数
random_state: 随机数种子
注:random_state参数设置为1,以确保每次运行代码时都生成相同的随机观察样本。
'''
print(X.shape,y.shape)
print(X[:5])
print(y[:5])

# 为一个单变量的数据样本拟合一个概率分布
def fit_distribution(data):
# 估计参数
mu=np.mean(data)
'''
np.mean(a,axis=,dtype=,keepdims=) 求平均值
axis:0表示纵轴,axis=1表示横轴
keepdims:表示是否保持维度不变
'''
sigma=np.std(data)
'''
np.std 计算矩阵标准差,axis等参数同上
'''
print(mu,sigma)
# 拟合分布
dist=norm(mu,sigma)
'''
scipy.stats.norm函数 可以实现正态分布
'''
return dist

# 为数据分类
Xy0=X[y==0]
Xy1=X[y==1]
print(Xy0.shape,Xy1.shape)

# 计算先验概率
priory0=len(Xy0)/len(X)
priory1=len(Xy1)/len(X)
print(priory0,priory1)

# 创建y=0的概率分布
X1y0=fit_distribution(Xy0[:,0])
X2y0=fit_distribution(Xy0[:,1])
# 创建y=1的概率分布
X1y1=fit_distribution(Xy0[:,0])
X2y1=fit_distribution(Xy0[:,1])

运行之后可以得到:

下面,我们要使用已经准备好的概率模型来做预测。
每个类标签的独立条件概率可以使用类的先验(50%)和每个变量的值的条件概率来计算。在给定每个变量的先验和条件概率分布的情况下,下面的概率函数对一个输入示例(两个值的数组)执行此计算。由于数量未归一化,返回的值是分数而不是概率。

1
2
3
# 计算独立条件概率
def probability(X,prior,dist1,dist2):
return prior*dist1.pdf(X[0])*dist2.pdf(X[1])

可以通过这个函数计算属于每个类的样例的概率。

首先,可以选择一个示例进行分类,使用数据集中的第一个样例。

1
2
# 对一个样例进行分类
Xsample,ysample=X[0],y[0]

然后,计算属于第一类的样例的得分,再计算属于第二类样例的得分,输出结果。

1
2
3
4
5
py0=probability(Xsample,priory0,distX1y0,distX2y0)
py1=probability(Xsample,priory1,distX1y1,distX2y1)
print('P(y=0 | %s) = %.3f' % (Xsample, py0*100))
print('P(y=1 | %s) = %.3f' % (Xsample, py1*100))
print('Truth:y=%d'%ysample)

完整版的朴素贝叶斯分布代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env python
#coding:utf-8

from sklearn.datasets.samples_generator import make_blobs
from scipy.stats import norm
import numpy as np

# 为一个单变量的数据样本拟合一个概率分布
def fit_distribution(data):
# 估计参数
mu=np.mean(data)
'''
np.mean(a,axis=,dtype=,keepdims=) 求平均值
axis:0表示纵轴,axis=1表示横轴
keepdims:表示是否保持维度不变
'''
sigma=np.std(data)
'''
np.std 计算矩阵标准差,axis等参数同上
'''
#print(mu,sigma)
# 拟合分布
dist=norm(mu,sigma)
'''
scipy.stats.norm函数 可以实现正态分布
'''
return dist

X,y=make_blobs(n_samples=100,centers=2,n_features=2,random_state=1)
'''
make_blobs 参数说明
n_samples: 生成的样例数
n_features: 每个样本的特征数
centers: 类别数
random_state: 随机数种子
注:random_state参数设置为1,以确保每次运行代码时都生成相同的随机观察样本。
'''
# print(X.shape,y.shape)
# print(X[:5])
# print(y[:5])

# 为数据分类
Xy0=X[y==0]
Xy1=X[y==1]
#print(Xy0.shape,Xy1.shape)

# 计算先验概率
priory0=len(Xy0)/len(X)
priory1=len(Xy1)/len(X)
#print(priory0,priory1)

# 创建y=0的概率分布
distX1y0=fit_distribution(Xy0[:,0])
distX2y0=fit_distribution(Xy0[:,1])
# 创建y=1的概率分布
distX1y1=fit_distribution(Xy0[:,0])
distX2y1=fit_distribution(Xy0[:,1])

# 计算独立条件概率
def probability(X,prior,dist1,dist2):
return prior*dist1.pdf(X[0])*dist2.pdf(X[1])

# 对一个样例进行分类
Xsample,ysample=X[0],y[0]

py0=probability(Xsample,priory0,distX1y0,distX2y0)
py1=probability(Xsample,priory1,distX1y1,distX2y1)
print('P(y=0 | %s) = %.3f' % (Xsample, py0*100))
print('P(y=1 | %s) = %.3f' % (Xsample, py1*100))
print('Truth:y=%d'%ysample)

参考来源

[1]https://blog.csdn.net/yangang908/article/details/62215209
[2]https://zh.wikipedia.org/wiki/%E8%B4%9D%E5%8F%B6%E6%96%AF%E5%AE%9A%E7%90%86
[3]李航《统计学习方法》第二版
[4]概率论与数理统计(课本)
[5]https://zhuanlan.zhihu.com/p/22467549
[6][代码实现部分参考]https://machinelearningmastery.com/classification-as-conditional-probability-and-the-naive-bayes-algorithm/