本文参考自鲁东大学人工智能学院课程内容
百度百科解释:朴素贝叶斯法(Naive Bayes model)是基于贝叶斯定理与特征条件独立假设的分类方法。
最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。和决策树模型相比,朴素贝叶斯分类器(Naive Bayes Classifier 或 NBC)发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给NBC模型的正确分类带来了一定影响。
理论
简单来说,朴素贝叶斯就是一种基于统计分类的方法,通过计算样本发生的概率,来实现分类。并且朴素贝叶斯要求各个事件之间发生的概率是独立的。
概率相关知识介绍
这是之前上两个博文,我在决策树算法中使用的是否放贷的数据集,基于这个数据集来简单介绍下朴素贝叶斯分类中所涉及的概率知识。
-
独立分布
在假设所有事件都独立的情况下,某一个事件发生的概率就称为独立概率。
例如:P ( Y = y e s ) = 9 15 = 0.6 P(Y = yes) = \frac{9}{15} =0.6 P(Y=yes)=159=0.6 P ( Y = n o ) = 6 15 = 0.4 P(Y = no) = \frac{6}{15} =0.4 P(Y=no)=156=0.4
表示在该数据集中,放贷条件为yes的概率为0.6(总样本15个,其中为yes的有9个),放贷条件为no的概率为0.4(总样本15个,其中为yes的有6个)
-
联合分布
联合概率表示某两个或多个事件同时发生的概率,通常表示为P(XY),即X和Y事件同时发生的概率。
例如:P(x 3Y)P ( x 3 = 1 , Y = y e s ) = 6 15 = 0.4 P(x_3=1,Y=yes) = \frac{6}{15} =0.4 P(x3=1,Y=yes)=156=0.4 P ( x 3 = 1 , Y = n o ) = 0 15 = 0 P(x_3=1,Y=no) = \frac{0}{15} =0 P(x3=1,Y=no)=150=0
P ( x 3 = 0 , Y = y e s ) = 3 15 = 0.2 P(x_3=0,Y=yes) = \frac{3}{15} =0.2 P(x3=0,Y=yes)=153=0.2 P ( x 3 = 0 , Y = n o ) = 6 15 = 0.4 P(x_3=0,Y=no) = \frac{6}{15} =0.4 P(x3=0,Y=no)=156=0.4
表示在该数据集中,x3列值等于1同时放贷条件为yes的概率为0.4(总样本15个,其中为x3=1并于放贷等于yes的有6个),x3列值等于1同时放贷条件为no的概率为0.4(总样本15个,其中为x3=1并于放贷等于no的有0个)
… … -
条件分布
条件概率分布是指,在某一个条件发生的情况下另一个条件发生的概率,通常表示为P(X|Y),即在条件B发生的情况下条件X发生的概率。
例如:P(x 3|Y) 和 P(Y|x 3)P ( x 3 = 1 ∣ Y = y e s ) = 6 9 = 0.67 P(x_3=1|Y=yes) = \frac{6}{9} =0.67 P(x3=1∣Y=yes)=96=0.67 P ( x 3 = 0 ∣ Y = y e s ) = 3 9 = 0.33 P(x_3=0|Y=yes) = \frac{3}{9} =0.33 P(x3=0∣Y=yes)=93=0.33
P ( Y = y e s ∣ x 3 = 0 ) = 3 9 = 0.33 P(Y=yes|x_3=0) = \frac{3}{9} =0.33 P(Y=yes∣x3=0)=93=0.33 P ( Y = n o , x 3 = 0 ) = 6 9 = 0.67 P(Y=no,x_3=0) = \ \frac{6}{9} =0.67 P(Y=no,x3=0)= 96=0.67
表示在该数据集中,在条件放贷等于yes的情况下x3列值等于1的概率为0.67(条件为yes的样本有9个,在这9个样本中x3=1的样本有6个),在条件放贷等于yes的情况下x3列值等于0的概率为0.33(条件为yes的样本有9个,在这9个样本中x3=0的样本有3个)
-
几个概率之间的关系
这几个概率之间存在可以相互转换的关系,关系公式如下:P ( X Y ) = P ( X ∣ Y ) ∗ P ( Y ) P(XY)=P(X|Y)*P(Y) P(XY)=P(X∣Y)∗P(Y)
同理也可以写为
P ( X Y ) = P ( Y ∣ X ) ∗ P ( X ) P(XY)=P(Y|X)*P(X) P(XY)=P(Y∣X)∗P(X)
即:
P ( X Y ) = P ( X ∣ Y ) ∗ P ( Y ) = P ( Y ∣ X ) ∗ P ( X ) P(XY)=P(X|Y)*P(Y)=P(Y|X)*P(X) P(XY)=P(X∣Y)∗P(Y)=P(Y∣X)∗P(X)去掉 P ( X Y ) P(XY) P(XY)可以得到
P ( X ∣ Y ) = P ( Y ∣ X ) ∗ P ( X ) P ( Y ) P(X|Y)= \frac{P(Y|X)*P(X)}{P(Y)} P(X∣Y)=P(Y)P(Y∣X)∗P(X) 这就是著名的贝叶斯公式
在已知数据的情况下求取结果就可以转换为在已知X的情况下求Y。
求得的值越大,就说明属于该类别的可能性就越高。
你可能看起来好像很简单,前面已经算过了各个概率的值,直接带进去查表就可以得到相应的值,并根据大小就可以推断出它属于那种类别,但是这里有个问题,例如在[x1,x2,x3,x4] = [1,0,1,0] 的情况下,算得的
P(Y = yes|x1,x2,x3,x4) = 0 P(Y = no|x1,x2,x3,x4) = 0
两个结果都为0,你能说它属于两个概率得情况相等吗?显然不行,所以需要将这个共识在做一个变形。
在假设条件独立得情况下有:
P
(
x
1
,
x
2
,
x
3
,
x
4
∣
Y
=
y
e
s
)
=
P
(
x
1
∣
Y
)
P
(
x
2
∣
Y
)
P
(
x
3
∣
Y
)
P
(
x
4
∣
Y
)
P(x_1,x_2,x_3,x_4|Y = yes)=P(x_1|Y)P(x_2|Y)P(x_3|Y)P(x_4|Y)
P(x1,x2,x3,x4∣Y=yes)=P(x1∣Y)P(x2∣Y)P(x3∣Y)P(x4∣Y)
则原公式可以变形为:
P ( Y = y e s ∣ x 1 , x 2 , x 3 , x 4 ) = P ( x 1 , x 2 , x 3 , x 4 ∣ Y = y e s ) P ( Y = y e s ) = P ( x 1 ∣ Y = y e s ) P ( x 2 ∣ Y = y e s ) P ( x 3 ∣ Y = y e s ) P ( x 4 ∣ Y = y e s ) P ( Y = y e s ) P(Y = yes|x~1~,x~2~,x~3~,x~4~)= \frac{P(x_1,x_2,x_3,x_4|Y = yes)}{P(Y = yes)}=\frac{P(x_1|Y = yes)P(x_2|Y = yes)P(x_3|Y = yes)P(x_4|Y = yes)}{P(Y = yes)} P(Y=yes∣x 1 ,x 2 ,x 3 ,x 4 )=P(Y=yes)P(x1,x2,x3,x4∣Y=yes)=P(Y=yes)P(x1∣Y=yes)P(x2∣Y=yes)P(x3∣Y=yes)P(x4∣Y=yes)
但是这里还有一个问题,由于分母上的概率是相乘起来的,当数据维数过多的情况下,分数相乘的式子就会很多,导致程序越界无法储存,为了解决这个问题,我们可以对等式两边取对数log
L L K ( Y ∣ X ) = l o g ( P ( Y ) ) + ∑ i = 1 4 l o g ( P ( x i ∣ Y ) ) LLK(Y|X)=log(P(Y))+\sum^{4}_{i = 1}log(P(x_i|Y)) LLK(Y∣X)=log(P(Y))+∑i=14log(P(xi∣Y)) 这就称为似然函数
分类问题就转换为了:
代码实现
导入numpy包
import numpy as np
加载数据集
#创建数据集
def createDataSet():
"""DateSet 基础数据集
Args:
无需传入参数
Returns:
返回数据集和对应的label标签
"""
dataSet = [[0, 0, 0, 0, 'no'],
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no'],]
labels = ['年龄', '有工作', '有自己的房子', '信贷情况'] #特征标签
return dataSet, labels
获取概率模型, 输入feat,np.array格式,大小[N,D]
此函数是求取P(X|Y),传入的参数是在假设已经处理好了原始数据,只有满足Y条件的数据,然后根据统计求取在已经满足Y条件下的各个x的概率。
# 获取概率模型, 输入feat np.array格式 大小[N,D]
def trainPbmodel_X(feats):
N,D = np.shape(feats)
model = {}
# 对每一维度的特征进行概率统计
for d in range(D):
data = feats[:,d].tolist()
keys = set(data)
N = len(data)
model[d] ={}
for key in keys:
model[d][key] = float(data.count(key)/N)
return model
整体模型训练函数
# datas: list格式 每个元素表示1个特征序列
# labs: list格式 每个元素表示一个标签
def trainPbmodel(datas,labs):
# 定义模型
model = {}
# 获取分类的类别
keys = set(labs)
for key in keys:
# 获得P(Y)
Pbmodel_Y = labs.count(key)/len(labs)
# 收集标签为Y的数据
index = np.where(np.array(labs)==key)[0].tolist()
feats = np.array(datas)[index]
# 获得 P(X|Y)
Pbmodel_X = trainPbmodel_X(feats)
# 模型保存
model[key]={}
model[key]["PY"] = Pbmodel_Y
model[key]["PX"] = Pbmodel_X
return model
返回结果如下格式:
表示在yes的条件下,P(Y) = 0.6,其中在第0维也就是第一个条件中是0,1,2的,即P(x0=0,1,2|Y) = 0.22222, 0.33333, 0.4444。同理在1,2,3维的概率,以及在no条件下各维度的概率。
该函数用于在已经训练好的模式下预测结果
# feat : list格式 一条输入特征
# model: 训练的概率模型
# keys :考察标签的种类
def getPbfromModel(feat,model,keys):
results ={}
eps = 0.00001 #这个值是防止在概率为0的情况下似然函数取log会溢出的问题,将概率为0替换为该数
for key in keys:
# 获取P(Y)
PY = model.get(key,eps).get("PY")
# 分别获取 P(X|Y)
model_X = model.get(key,eps).get("PX")
list_px=[]
for d in range(len(feat)):
pb = model_X.get(d,eps).get(feat[d],eps)
list_px.append(pb)
result = np.log(PY) + np.sum(np.log(list_px))
results[key]= result
return results
最后测试一下
if __name__ == '__main__':
# 获取数据集
dataSet, labels = createDataSet()
# 截取数据和标签
datas = [i[:-1] for i in dataSet]
labs = [i[-1] for i in dataSet]
# 获取标签种类
keys = set(labs)
# 进行模型训练
model = trainPbmodel(datas,labs)
# print(model)
# 根据输入数据获得预测结果
feat = [0,1,0,1]
result = getPbfromModel(feat,model,keys)
print(result)
# 遍历结果找到概率最大值进行数据
for key,value in result.items():
if(value == max(result.values())):
print("预测结果是",key)
运行结果为:
{'no': -14.220975666072437, 'yes': -4.512232190328822}
预测结果是 yes
上面是个二分类问题,下面看在多分类问题上适不适用
数据集为:
青年 近视 否 干涩 不配镜
青年 近视 否 正常 软镜片
青年 近视 是 干涩 不配镜
青年 远视 否 干涩 不配镜
青年 远视 是 干涩 不配镜
中年 近视 否 干涩 不配镜
中年 近视 否 正常 软镜片
中年 近视 是 干涩 不配镜
中年 近视 是 正常 硬镜片
中年 远视 否 干涩 不配镜
中年 远视 否 正常 软镜片
中年 远视 是 干涩 不配镜
中年 远视 是 正常 不配镜
老年 近视 否 正常 不配镜
老年 近视 是 干涩 不配镜
老年 近视 是 正常 硬镜片
老年 远视 否 干涩 不配镜
老年 远视 否 正常 软镜片
老年 远视 是 干涩 不配镜
老年 远视 是 正常 不配镜
青年 远视 是 正常 硬镜片
测试集为:
青年 远视 否 正常 软镜片
老年 近视 否 干涩 不配镜
青年 近视 是 正常 硬镜片
将上述两个数据集分别保存为train-lenses.txt 和 test-lenses.txt
函数定义都不变,只是不需要createDataSet()函数
if __name__ == '__main__':
# 读取数据文件 截取数据和标签
with open("train-lenses.txt",'r',encoding="utf-8") as f:
lines = f.read().splitlines()
dataSet = [line.split('\t') for line in lines]
datas = [i[:-1] for i in dataSet]
labs = [i[-1] for i in dataSet]
# 获取标签种类
keys = set(labs)
# 进行模型训练
model = trainPbmodel(datas,labs)
print(model)
# 测试
# 读取测试文件
with open("test-lenses.txt",'r',encoding="utf-8") as f:
lines = f.read().splitlines()
# 逐行读取数据并测试
for line in lines:
data = line.split('\t')
lab_true = data[-1]
feat = data[:-1]
result = getPbfromModel(feat,model,keys)
key_out = ""
for key,value in result.items():
if(value == max(result.values())):
key_out=key
print("输入特征:")
print(data)
print(result)
print("预测结果 %s 医生推荐 %s"%(key_out,lab_true))
运行结果为:
其中训练的模型为
{'不配镜': {'PY': 0.6666666666666666, 'PX': {0: {'老年': 0.35714285714285715, '中年': 0.35714285714285715, '青年': 0.2857142857142857}, 1: {'近视': 0.42857142857142855, '远视': 0.5714285714285714}, 2: {'否': 0.42857142857142855, '是': 0.5714285714285714}, 3: {'正常': 0.21428571428571427, '干涩': 0.7857142857142857}}}, '硬镜片': {'PY': 0.14285714285714285, 'PX': {0: {'老年': 0.3333333333333333, '中年': 0.3333333333333333, '青年': 0.3333333333333333}, 1: {'近视': 0.6666666666666666, '远视': 0.3333333333333333}, 2: {'是': 1.0}, 3: {'正常': 1.0}}}, '软镜片': {'PY': 0.19047619047619047, 'PX': {0: {'老年': 0.25, '中年': 0.5, '青年': 0.25}, 1: {'近视': 0.5, '远视': 0.5}, 2: {'否': 1.0}, 3: {'正常': 1.0}}}}
测试结果为:
输入特征:
['青年', '远视', '否', '正常', '软镜片']
{'不配镜': -4.605586765873308, '硬镜片': -15.656060191361762, '软镜片': -3.737669618283368}
预测结果 软镜片 医生推荐 软镜片
输入特征:
['老年', '近视', '否', '干涩', '不配镜']
{'不配镜': -3.370842302880618, '硬镜片': -26.475838475772044, '软镜片': -15.250595083253597}
预测结果 不配镜 医生推荐 不配镜
输入特征:
['青年', '近视', '是', '正常', '硬镜片']
{'不配镜': -4.605586765873308, '硬镜片': -3.4499875458315876, '软镜片': -15.250595083253597}
预测结果 硬镜片 医生推荐 硬镜片
以上就是朴素贝叶斯的相关理论和代码实现,希望对你的学习和理解有所帮助。