第4章 朴素贝叶斯
朴素贝叶斯的基本思想是: 基于特征条件独立假设学习输入和输出的联合概率分布,然后,基于此模型,对给定的输入 x x x,利用贝叶斯订立求出后验概率最大的输出 y y y,根据《统计学习方法》书中的规划,这里将简述朴素贝叶斯的学习和分类、朴素贝叶斯的参数估计方法。
4.1 朴素贝叶斯的学习和分类
4.1.1简述
输入:训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
(
x
i
,
y
i
)
,
.
.
.
,
(
x
N
,
y
N
)
}
T=\{ \left( x_1,y_1 \right),\left( x_2,y_2 \right),\left( x_i,y_i \right),...,\left( x_N,y_N \right) \}
T={(x1,y1),(x2,y2),(xi,yi),...,(xN,yN)}
朴素贝叶斯的目的是: 通过训练数据集 T T T学习到联合概率分布 P ( X , Y ) P(X,Y) P(X,Y),具体来讲,这个联合概率分布的求解根据联合概率密度求解的公式 P ( x ∣ y ) = P ( x , y ) P Y ( y ) P(x|y)=\frac{P(x,y)}{P_Y(y)} P(x∣y)=PY(y)P(x,y)又分为两个部分:
先验概率分布:
P ( Y = c k ) , k = 1 , 2 , . . . , K P(Y=c_k),k=1,2,...,K P(Y=ck),k=1,2,...,K
条件概率分布:
KaTeX parse error: Got function '\left' with no arguments as superscript at position 17: …(X=x|Y=c_k)=P(X^̲\left(1\right)=…
其中,条件独立性假设
KaTeX parse error: Got function '\left' with no arguments as superscript at position 32: …_k \right)=P( X^̲\left(1\right)=…
关于生成模型和判别模型:
根据上面的式子,这里由于要求解的是
P
(
x
∣
y
)
=
P
(
x
,
y
)
P
Y
(
y
)
P(x|y)=\frac{P(x,y)}{P_Y(y)}
P(x∣y)=PY(y)P(x,y),实质上是一种生成数据的机制,所以属于生成模型,可能有的同学会听说过生成对抗网络(GAN)的概念,没错,里面的生成也是生成模型,在GAN中,生成模型也是用来生成数据的,但是可能它生成的数据更加复杂,不再单单是一个高斯分布了,很有可能是一张图什么的,这个是后话了,这里只要知道朴素贝叶斯是生成模型、它学习的是一种生成数据的机制,就可以了。但是话又说回来了,你都知道如何生成数据了,当然就可以知道如何对数据进行区分了,不是么哈哈~
4.1.2算法描述
好了,这里简言之,直接描述朴素贝叶斯的算法过程
输入:
- 训练数据 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x i , y i ) , . . . , ( x N , y N ) } T=\{ \left( x_1,y_1 \right),\left( x_2,y_2 \right),\left( x_i,y_i \right),...,\left( x_N,y_N \right) \} T={(x1,y1),(x2,y2),(xi,yi),...,(xN,yN)},其中,KaTeX parse error: Got function '\left' with no arguments as superscript at position 16: x_i = \left(x_i^̲\left(1\right),…;
- KaTeX parse error: Got function '\left' with no arguments as superscript at position 4: x_i^̲\left(j\right)是第 i i i个样本的第 j j j个特征,KaTeX parse error: Got function '\left' with no arguments as superscript at position 4: x_i^̲\left(j\right) …, a j l a_{jl} ajl是第 j j j个特征可能取的第 l l l个值, j = 1 , 2 , . . . , n j=1,2,...,n j=1,2,...,n, l = 1 , 2 , . . . , S j l=1,2,...,S_j l=1,2,...,Sj, y i ∈ { c 1 , c 2 , . . . , c K } y_i \in \left\{c_1,c_2,...,c_K \right\} yi∈{c1,c2,...,cK};
- 实例 x x x;
输出:
- 实例 x x x的分类结果
算法过程:
- 计算先验概率以及条件概率
P ( Y = c k ) = ∑ i = 1 N I ( y i = c i ) N , k = 1 , 2 , . . . , K P\left( Y = c_k \right) = \frac{\sum_{i=1}^N I(y_i=c_i)}{N},k=1,2,...,K P(Y=ck)=N∑i=1NI(yi=ci),k=1,2,...,K
KaTeX parse error: Got function '\left' with no arguments as superscript at position 10: P\left( X^̲\left(j\right)=…
j = 1 , 2 , . . . , n ; l = 1 , 2 , . . . , S j , k = 1 , 2 , . . . , K j=1,2,...,n; l=1,2,...,S_j,k=1,2,...,K j=1,2,...,n;l=1,2,...,Sj,k=1,2,...,K
- 对于给定的实例
x
x
x,计算
KaTeX parse error: Got function '\left' with no arguments as superscript at position 26: …rod_{j=1}^n P(X^̲\left(j\right) … - 确定实例
x
x
x的类
KaTeX parse error: Got function '\left' with no arguments as superscript at position 40: …x} P(Y=c_k) P(X^̲\left(j\right) …
4.1.3编写代码测试朴素贝叶斯算法
# 给定数据集,其中SML分别替换为1,2,3
import numpy as np
x = np.asarray([[1,1],[1,2],[1,2],[1,1],[1,1],
[2,1],[2,2],[2,2],[2,3],[2,3],
[3,3],[3,2],[3,2],[3,3],[3,3]])
y = np.asarray([-1,-1,1,1,-1,
-1,-1,1,1,1,
1,1,1,1,-1])
计算 P ( Y = 1 ) P(Y=1) P(Y=1)和 P ( Y = − 1 ) P(Y=-1) P(Y=−1)
AllNum = np.size(y)
# P(Y=1)
PosY = np.sum(y==1)/AllNum
#P(Y=-1)
NegY = np.sum(y==-1)/AllNum
print('P(Y=1)=',PosY,', P(Y=-1)=',NegY)
P(Y=1)= 0.6 , P(Y=-1)= 0.4
计算KaTeX parse error: Got function '\left' with no arguments as superscript at position 10: P\left( X^̲\left(j\right)=…
# y 取值范围
y_ = np.unique(y)
# x 的维度
feanum = np.size(x,1)
min_result_dictionary = {}
for k in range(len(y_)):# 对于每个Y类别
P_Y = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
print('P(Y =',y_[k],') =',P_Y)
k_x = x[y==y_[k],:]# 保留在P(Y=k)条件下所有的x样本
for j in range(feanum): # 对于每个特征
fea_val_range = np.unique(x[:,j])# 该维度下属性值的取值范围
for l in range(len(fea_val_range)): # 对于特征下的第l个取值
P_X_Y = np.sum(k_x[:,j] == fea_val_range[l])/np.sum(y==y_[k])
print('P(X特征维度=',j,'特征值=',fea_val_range[l],'| Y=',y_[k],')=',P_X_Y)
split = ','
key = split.join([str(y_[k]),str(j),str(fea_val_range[l])])
min_result_dictionary[key] = P_X_Y
min_result_dictionary
输出:
P(Y = -1 ) = 0.4
P(X特征维度= 0 特征值= 1 | Y= -1 )= 0.5
P(X特征维度= 0 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= -1 )= 0.166666666667
P(X特征维度= 1 特征值= 1 | Y= -1 )= 0.5
P(X特征维度= 1 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 1 特征值= 3 | Y= -1 )= 0.166666666667
P(Y = 1 ) = 0.6
P(X特征维度= 0 特征值= 1 | Y= 1 )= 0.222222222222
P(X特征维度= 0 特征值= 2 | Y= 1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= 1 )= 0.444444444444
P(X特征维度= 1 特征值= 1 | Y= 1 )= 0.111111111111
P(X特征维度= 1 特征值= 2 | Y= 1 )= 0.444444444444
P(X特征维度= 1 特征值= 3 | Y= 1 )= 0.444444444444
{’-1,0,1’: 0.5,
‘-1,0,2’: 0.33333333333333331,
‘-1,0,3’: 0.16666666666666666,
‘-1,1,1’: 0.5,
‘-1,1,2’: 0.33333333333333331,
‘-1,1,3’: 0.16666666666666666,
‘1,0,1’: 0.22222222222222221,
‘1,0,2’: 0.33333333333333331,
‘1,0,3’: 0.44444444444444442,
‘1,1,1’: 0.1111111111111111,
‘1,1,2’: 0.44444444444444442,
‘1,1,3’: 0.44444444444444442}
当给定 x = ( 2 , S ) T x=(2,S)^T x=(2,S)T,也即 x = ( 2 , 1 ) T x=(2,1)^T x=(2,1)T,计算KaTeX parse error: Got function '\left' with no arguments as superscript at position 26: …rod_{j=1}^n P(X^̲\left(j\right) …:
testx = np.array([2,1])
labelvalue =[]
for k in range(len(y_)):
P_Y = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
j_p = 1# 记录每个类别下的概率值
for j in range(feanum):
split = ','
key = split.join([str(y_[k]),str(j),str(testx[j])])
j_p = j_p * min_result_dictionary[key]
labelvalue.append(P_Y*j_p)
返回值最大时候得到的类别标签KaTeX parse error: Got function '\left' with no arguments as superscript at position 40: …x} P(Y=c_k) P(X^̲\left(j\right) …
y_[labelvalue.index(max(labelvalue))]
输出:-1
综上可以得知,实际上朴素贝叶斯是在给定每个类别标签的情况下,查看这个数据“合理性”有多高,“合理性”越高,说明越有可能是这个类别,而这个“合理性”的度量,就是使用了概率来衡量,最后动过一个
arg
max
\arg \max
argmax操作,得到了对应的类别标签。
4.1.4贝叶斯估计
用心的读者也许会发现,如果用以上的方法(实际是“极大似然估计”)可能会得到概率值为0的情况,然后会影响后续一系列操作(如求最大值等)。根据《统计学习方法》一书,我们在这里加一个步骤,将原来的式子做小的修改:
KaTeX parse error: Got function '\left' with no arguments as superscript at position 10: P\left( X^̲\left(j\right)=…
其中,
λ
≥
0
\lambda \geq 0
λ≥0,当
λ
=
0
\lambda = 0
λ=0的时候等同于原始的极大似然估计,而通常,这里取
λ
=
1
\lambda = 1
λ=1,称为拉普拉斯平滑,这时,对于
l
=
1
,
2
,
.
.
.
,
S
j
l=1,2,...,S_j
l=1,2,...,Sj,
k
=
1
,
2
,
.
.
.
,
K
k=1,2,...,K
k=1,2,...,K,有
KaTeX parse error: Got function '\left' with no arguments as superscript at position 12: P_\lambda(X^̲\left(j\right)=…
KaTeX parse error: Got function '\left' with no arguments as superscript at position 28: …S_j}P_\lambda(X^̲\left(j\right)=…
这两个式子说明拉普拉斯平滑之后的式子确实是一个概率分布(即有每个概率大于0,总概率和等于1),此时,得到鲜艳概率贝叶斯估计为:
P
λ
(
Y
=
c
k
)
=
∑
i
=
1
N
I
(
y
i
=
c
k
)
+
λ
N
+
K
λ
P_\lambda(Y=c_k)=\frac{\sum_{i=1}^N I(y_i=c_k)+\lambda}{N+K\lambda}
Pλ(Y=ck)=N+Kλ∑i=1NI(yi=ck)+λ
此时我们修改上面的代码,运行可以得到如下结果
# y 取值范围
y_ = np.unique(y)
# x 的维度
feanum = np.size(x,1)
lmda = 1
min_result_dictionary = {}
for k in range(len(y_)):# 对于每个Y类别
P_Y = (np.sum(y==y_[k])+lmda)/(np.size(y)+lmda*len(y_)) # 计算P(Y=k)的值
print('P(Y =',y_[k],') =',P_Y)
k_x = x[y==y_[k],:]# 保留在P(Y=k)条件下所有的x样本
for j in range(feanum): # 对于每个特征
fea_val_range = np.unique(x[:,j])# 该维度下属性值的取值范围
for l in range(len(fea_val_range)): # 对于特征下的第l个取值
P_X_Y = (np.sum(k_x[:,j] == fea_val_range[l])+lmda)/(np.sum(y==y_[k])+lmda*len(fea_val_range))
print('P(X特征维度=',j,'特征值=',fea_val_range[l],'| Y=',y_[k],')=',P_X_Y)
split = ','
key = split.join([str(y_[k]),str(j),str(fea_val_range[l])])
min_result_dictionary[key] = P_X_Y
min_result_dictionary
输出:
P(Y = -1 ) = 0.411764705882
P(X特征维度= 0 特征值= 1 | Y= -1 )= 0.444444444444
P(X特征维度= 0 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= -1 )= 0.222222222222
P(X特征维度= 1 特征值= 1 | Y= -1 )= 0.444444444444
P(X特征维度= 1 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 1 特征值= 3 | Y= -1 )= 0.222222222222
P(Y = 1 ) = 0.588235294118
P(X特征维度= 0 特征值= 1 | Y= 1 )= 0.25
P(X特征维度= 0 特征值= 2 | Y= 1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= 1 )= 0.416666666667
P(X特征维度= 1 特征值= 1 | Y= 1 )= 0.166666666667
P(X特征维度= 1 特征值= 2 | Y= 1 )= 0.416666666667
P(X特征维度= 1 特征值= 3 | Y= 1 )= 0.416666666667
{’-1,0,1’: 0.44444444444444442,
‘-1,0,2’: 0.33333333333333331,
‘-1,0,3’: 0.22222222222222221,
‘-1,1,1’: 0.44444444444444442,
‘-1,1,2’: 0.33333333333333331,
‘-1,1,3’: 0.22222222222222221,
‘1,0,1’: 0.25,
‘1,0,2’: 0.33333333333333331,
‘1,0,3’: 0.41666666666666669,
‘1,1,1’: 0.16666666666666666,
‘1,1,2’: 0.41666666666666669,
‘1,1,3’: 0.41666666666666669}
testx = np.array([2,1])
labelvalue =[]
for k in range(len(y_)):
P_Y = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
j_p = 1# 记录每个类别下的概率值
for j in range(feanum):
split = ','
key = split.join([str(y_[k]),str(j),str(testx[j])])
j_p = j_p * min_result_dictionary[key]
labelvalue.append(P_Y*j_p)
print(labelvalue)
输出:[0.059259259259259262, 0.033333333333333333]
返回值最大时候得到的类别标签
-1
注:上面由于浮点精度问题,这里的结果与《统计学习方法》书中的结果存在一点差异,但是原理就是这样了!
4.2 定义自己的朴素贝叶斯算法
# 4.2定义自己的朴素贝叶斯算法
import numpy as np
class myNaiveBayes():
def train(self,x,y,lmda=None):
"""
x: 训练集
y: 类别标签向量
lmda: 拉普拉斯平滑参数值,默认为1
"""
self.x = x
self.y = y
self.lmda = lmda
model = []
# y 取值范围
y_ = np.unique(y)
# x 的维度
feanum = np.size(x,1)
min_result_dictionary = {}
P_Y = np.empty(len(y_))
for k in range(len(y_)):# 对于每个Y类别
P_Y[k] = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
k_x = x[y==y_[k],:]# 保留在P(Y=k)条件下所有的x样本
for j in range(feanum): # 对于每个特征
fea_val_range = np.unique(x[:,j])# 该维度下属性值的取值范围
for l in range(len(fea_val_range)): # 对于特征下的第l个取值
P_X_Y = (np.sum(k_x[:,j] == fea_val_range[l])+lmda)/(np.sum(y==y_[k])+lmda*len(fea_val_range))
split = ','
key = split.join([str(y_[k]),str(j),str(fea_val_range[l])])
min_result_dictionary[key] = P_X_Y
model.append(feanum) # 特征维度数目 0
model.append(y_) # 类别取值范围 1
model.append(min_result_dictionary) # dictionary保存的概率值 2
model.append(np.size(y)) # 样本个数 3
model.append(P_Y)
return model
def predict(self,x,model):
"""
x 应该与train中的x具有相同的维度数
y 为预测类别标签
"""
y = []
self.model = model
self.x = x
if self.x.ndim==1:
self.x = self.x.reshape(1,len(x))
for i in range(len(self.x)):
testx = np.asarray(self.x[i,:])
labelvalue =[]
for k in range(len(self.model[1])):
P_Y = model[4][k]
j_p = 1# 记录每个类别下的概率值
for j in range(model[0]):
split = ','
key = split.join([str(model[1][k]),str(j),str(testx[j])])
j_p = j_p * model[2][key]
labelvalue.append(P_Y*j_p)
y.append(y_[labelvalue.index(max(labelvalue))])
return y
def accuracy(self,y,realy):
self.y = y
self.realy = realy
return 1-sum(np.sign(np.abs(y-realy)))/len(y)
定义课本的15个实例
x = np.asarray([[1,1],[1,2],[1,2],[1,1],[1,1],
[2,1],[2,2],[2,2],[2,3],[2,3],
[3,3],[3,2],[3,2],[3,3],[3,3]])
y = np.asarray([-1,-1,1,1,-1,
-1,-1,1,1,1,
1,1,1,1,-1])
验证课本的15个实例
mynb = myNaiveBayes()
model = mynb.train(x,y,lmda=1)
newy = mynb.predict(x,model)
print('正确率:',mynb.accuracy(y,newy))
正确率: 0.733333333333
课本测试用例
temp = np.asarray([2,1])
newy = mynb.predict(temp,model)
print(newy)
[-1]