朴素贝叶斯原理及python实现
朴素贝叶斯原理
如果你压根不知道贝叶斯是啥,建议你先读读如何理解贝叶斯以便更好地读懂本文。
朴素贝叶斯在分类问题中有很广泛的应用。具体是如何应用的呢?
老祖宗贝叶斯公式给出了答案—事件A发生了,是由事件B造成的概率为:
P
(
B
i
∣
A
)
=
P
(
B
i
)
P
(
A
∣
B
i
)
∑
j
=
1
n
P
(
B
j
)
P
(
A
∣
B
j
)
P({B_i}|A) = \frac{{P({B_i})P(A|{B_i})}}{{\sum\limits_{j = 1}^n {P({B_j})P(A|{B_j})} }}
P(Bi∣A)=j=1∑nP(Bj)P(A∣Bj)P(Bi)P(A∣Bi)
对于我们实际的问题:
给定数据集
T
=
{
(
x
1
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
,
(
x
N
,
y
N
)
}
T=\{(x_1y_1),(x_2,y_2),...,(x_N,y_N)\}
T={(x1y1),(x2,y2),...,(xN,yN)},其中
x
x
x为特征,
y
y
y为特征
x
x
x对应的标签,标签
Y
Y
Y的取值集合为
{
c
1
,
c
2
,
.
.
.
,
c
K
}
\{c_1,c_2,...,c_K\}
{c1,c2,...,cK};这样的数据集通常是已知的,一个或一连串的特征对应一个标签,在大量的可观测的已知数据(训练集)给出后,突然来一个或好多数据(测试集),我们想知道这些未知应该属于哪个类,即:
P
(
Y
=
c
k
∣
X
=
x
)
P(Y = {c_k}|X = x)
P(Y=ck∣X=x)(在
X
X
X为
x
x
x的条件下,
Y
Y
Y为
c
k
c_k
ck的概率)— 我们观测到了某个数据的特征,而且还知道了该数据属于每个类别的概率,我们只要找到最有可能(概率最大)属于的类别不就是这个数据的分类了?
那么问题来了,
P
(
Y
=
c
k
∣
X
=
x
)
P(Y = {c_k}|X = x)
P(Y=ck∣X=x)怎么算呢?— 套贝叶斯公式:
P
(
Y
=
c
k
∣
X
=
x
)
=
P
(
X
=
x
∣
Y
=
c
k
)
P
(
Y
=
c
k
)
∑
k
P
(
X
=
x
∣
Y
=
c
k
)
P
(
Y
=
c
k
)
(
1
)
P(Y = {c_k}|X = x) = \frac{{P(X = x|Y = {c_k})P(Y = {c_k})}}{{\sum\nolimits_k {P(X = x|Y = {c_k})P(Y = {c_k})} }} \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;(1)
P(Y=ck∣X=x)=∑kP(X=x∣Y=ck)P(Y=ck)P(X=x∣Y=ck)P(Y=ck)(1)
在已知数据集给出后,先验概率
P
(
Y
=
c
k
)
P(Y=c_k)
P(Y=ck)—
y
y
y的标签为
c
k
c_k
ck的概率是很容易得到的(每个标签的总数除以总的标签数),但
P
(
X
=
x
∣
Y
=
c
k
)
P(X = x|Y = {c_k})
P(X=x∣Y=ck)呢?
P
(
X
=
x
∣
Y
=
c
k
)
=
P
(
X
(
1
)
=
x
(
1
)
,
.
.
.
,
X
(
n
)
=
x
(
n
)
∣
Y
=
c
k
)
P(X = x|Y = {c_k}) = P({X^{(1)}} = {x^{(1)}},...,{X^{(n)}} = {x^{(n)}}|Y = {c_k})
P(X=x∣Y=ck)=P(X(1)=x(1),...,X(n)=x(n)∣Y=ck)
现实还是很骨感,这可是指数级数量的参数啊!怎么办???
我们可以假设特征是独立的,即每个特征之间互不影响,互相独立。这个假设也有点太强吧。对!就是这么强!这也是朴素贝叶斯的朴素之处。基于条件独立假设,我们刚刚的条件概率就可以表示为:
P
(
X
=
x
∣
Y
=
c
k
)
=
P
(
X
(
1
)
=
x
(
1
)
,
.
.
.
,
X
(
n
)
=
x
(
n
)
∣
Y
=
c
k
)
=
P
(
X
(
1
)
=
x
(
1
)
∣
Y
=
c
k
)
×
P
(
X
(
2
)
=
x
(
2
)
∣
Y
=
c
k
)
×
⋯
×
P
(
X
(
n
)
=
x
(
n
)
∣
Y
=
c
k
)
=
∏
j
=
1
n
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
\begin{array}{l} P(X = x|Y = {c_k}) = P({X^{(1)}} = {x^{(1)}},...,{X^{(n)}} = {x^{(n)}}|Y = {c_k}) \\= P({X^{(1)}} ={x^{(1)}}|Y = {c_k}) \times P({X^{(2)}} = {x^{(2)}}|Y = {c_k}) \times \cdots \times P({X^{(n)}} = {x^{(n)}}|Y = {c_k})\\ = \prod\limits_{j = 1}^n {P({X^{(j)}} = {x^{(j)}}|Y = {c_k})} \end{array}
P(X=x∣Y=ck)=P(X(1)=x(1),...,X(n)=x(n)∣Y=ck)=P(X(1)=x(1)∣Y=ck)×P(X(2)=x(2)∣Y=ck)×⋯×P(X(n)=x(n)∣Y=ck)=j=1∏nP(X(j)=x(j)∣Y=ck)
OK,现在先验概率和条件概率都有了,我们只需要将它带入
(
1
)
(1)
(1)中即可,带入得到:
P
(
Y
=
c
k
∣
X
=
x
)
=
P
(
Y
=
c
k
)
∏
j
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
∑
k
P
(
Y
=
c
k
)
∏
j
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
P(Y = {c_k}|X = x) = \frac{{P(Y = {c_k})\prod\nolimits_j {P({X^{(j)}} = {x^{(j)}}|Y = {c_k})} }}{{\sum\nolimits_k {P(Y = {c_k})\prod\nolimits_j {P({X^{(j)}} = {x^{(j)}}|Y = {c_k})} } }}
P(Y=ck∣X=x)=∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)P(Y=ck)∏jP(X(j)=x(j)∣Y=ck)
终于算出来了,现在给定特征
x
x
x,我们就可以找到它最有可能属于哪个类了,我们只需要把属于每个类的概率算一下,然后找到概率最大的那个就可以了。
总结一下:
给定训练集和测试集,先提取特征,然后计算先验概率和条件概率。之后对于给定测试数据,计算一下属于每个类别的概率,然后返回概率最大的那个标签就完成了预测或分类。
朴素贝叶斯的python实现
实验数据用的是李航老师《统计学习方法》里的数据,当然,上面的很多东西参考了该书。
训练数据:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
X ( 1 ) {X^{(1)}} X(1) | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 3 | 3 | 3 |
X ( 2 ) {X^{(2)}} X(2) | S | M | M | S | S | S | M | M | L | L | L | M | M | L | L |
Y Y Y | -1 | -1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 |
测试数据:
给定输入第一个特征为
1
1
1,第二个特征为
S
S
S
实验结果:
预测结果为
−
1
-1
−1
import numpy as np
def judge(actual,prediction):
'''
该函数用于判断实际值和预测值是否一致
:param actual: 实际值
:param prediction: 预测值
:return: 如果实际值和预测值一致,返回为True,否则返回False
'''
return actual==prediction
def generate_data():
'''
该函数用于生成数据
:return: 生成好的数据,特征对应于标签
'''
feature=[(1,'S'),(1,'M'),(1,'M'),(1,'S'),(1,'S'),
(2,'S'),(2,'M'),(2,'M'),(2,'L'),(2,'L'),
(3,'L'),(3,'M'),(3,'M'),(3,'L'),(3,'L')]
label=[-1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1]
return feature,label
class bayes:
### bayes类,用于bayes相关
def transform(self,feature):
'''
对原始输入的特征进行相应的转换,以更好地进行模型训练
:param feature: 需要转换的特征
:return: 转换后的特征
'''
fea_transform=[]
for seq in feature:
for i in range(seq.__len__()):
try:
fea_transform[i].append(seq[i])
except:
fea_transform.append([])
fea_transform[i].append(seq[i])
return fea_transform
def train(self,feature,label):
'''
训练函数
:param feature: 特征集
:param label: 标签集
:return: 训练好的模型(相应的条件概率和先验概率)
'''
feature=np.array(self.transform(feature))
label=np.array(label)
classifier=set(label)
probability_classifier={}
probability_feature={}
feature_record={}
### 计算先验概率
for i in classifier:
probability_classifier[i]=label.tolist().count(i)/label.__len__()
for fea in range(feature.__len__()):
feature_record[fea]=set(feature[fea])
### 计算条件概率
for fea in range(feature.__len__()):
for f in feature_record[fea]:
for c in classifier:
position= np.where(label==c)### 找到分类为 c 的那些位置
numerator=feature[fea][position][feature[fea][position]==f].__len__()### 找到类别为 c,同时该特征为 f 的元素个数
denominator=feature[fea][label==c].__len__()
try:
probability_feature[(fea,c)][f]=numerator/denominator
except:
probability_feature[(fea,c)]={}
probability_feature[(fea,c)][f]=numerator/denominator
return probability_feature,probability_classifier
def predict(self,actual,model):
'''
预测函数
:param actual: 想要预测的实际值
:param model: 训练好的模型
:return: 预测结果
'''
actual=np.array(actual)
probability_feature,probability_classifier=model
prediction={}
for classifier in probability_classifier:
multi=1
for i in range(actual.__len__()):
multi*=probability_feature[(i,classifier)][actual[i]]
prediction[classifier]=probability_classifier[classifier]*multi
return max(prediction,key=prediction.get)
if __name__ == '__main__':
feature,label=generate_data()
bayes=bayes()
model=bayes.train(feature,label)
print("预测结果为:",bayes.predict([1,"S"],model))
运行结果:
才疏学浅,难免有错误和不当之处,欢迎交流批评指正!
同时有问题的话欢迎留言或邮箱联系(ljt_IT@163.com)。
Reference:
李航.《统计学习方法》[M].2012.3.北京:清华大学出版社,2019.5(重印):14-15.