朴素贝叶斯算法原理及实现

一、贝叶斯算法原理

1.条件概率公式

根据文氏图,可以看到在事件B发生的情况下,事件A发生的概率为:

,其中又可写为

因此:

同理又有:

所以:称为联合概率。

即为条件概率公式。

2.全概率公式

如果事件B1,B2,B3,…,Bn 构成一个完备事件组,即它们两两互不相容,其和为全集;并且P(Bi)大于0,则对任一事件A有:

3.贝叶斯定理

贝叶斯定理是关于随机事件A和B的条件概率(或边缘概率)的一则定理。其中P(A|B)是在B发生的情况下A发生的可能性。

举个例子:一个鸢尾花数据有四个特征,其特征分别为:sepal length、sepal width、petal length、petal width,三个类别,分别为:Iris Setosa、Iris Versicolour、Iris Virginica,现在有一组鸢尾花数据:5.5,2.6,4.4,1.2,判断此鸢尾花属于哪种。

由贝叶斯定理我们可以计算此条件下属于各个类别的概率,

同理,依次可以计算属于类别 Versicolour、Virginica的概率。概率最大的类别即为最有可能的鸢尾花类别。

但是,可以看到并不好计算,此时,我们可以将鸢尾花数据的四个特征看作相互独立(即朴素贝叶斯的核心),所以有:

=

此时我们只需要计算等单个特征值概率即可。

4.高斯分布

上述例子中,sepal length、sepal width、petal length、petal width等特征均为连续变量,该如何计算?我们可以假设在setosa的条件下,sepallen服从高斯分布(正态分布)。根据正态分布的概率密度函数即可计算出,公式如下:

二、朴素贝叶斯处理过程

  • 加载数据:原始数据集载入

  • 分类数据:根据贝叶斯定理对数据进行分类

  • 训练算法:计算不同的独立特征的条件概率

  • 测试算法:计算正确率

  • 使用算法:数据分类

三、朴素贝叶斯分类实现

1.数据集说明

朴素贝叶斯分类实现鸢尾花数据分类,数据集来源:https://archive.ics.uci.edu/ml/datasets/iris。算法实现之前,按照8:2的比例将原始数据集分为训练数据集和测试数据集,原始数据集中每个类别抽取10个样本组成一个总共30个样本的测试数据集。

数据集说明:

Attribute Information:

  1. sepal length in cm
  2. sepal width in cm
  3. petal length in cm
  4. petal width in cm
  5. class:
    — Iris Setosa
    — Iris Versicolour
    — Iris Virginica

2.加载数据集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""
iris数据集载入
"""
def load_data(file):
x_data=[]
y_data=[]
data=[]
with open(file,'r') as fp:
lines=fp.readlines()
for line in lines:
line=line.strip().split(',')
x_data.append(line[:-1])
y_data.append((line[-1]))
data.append(line)
return x_data,y_data,data

3.计算训练集均值

1
2
3
4
5
6
7
8
9
10
def get_mean(x,lable):
#获取每个类别的均值
means=[]
for j in range(4):
sum=0
for i in range(120):
if (lable==x[i,4]):
sum+=float(x[i,j])
means.append(sum/40)
return means

4.计算训练集方差

1
2
3
4
5
6
7
8
9
10
def get_var(x,mean,label):
#获取每个类别的方差
vars=[]
for j in range(4):
sum=0
for i in range(120):
if (label==x[i,4]):
sum+=(float(x[i,j])-mean[j])**2
vars.append(sum/40)
return vars

5.计算先验概率

1
2
3
4
5
6
7
8
def get_prior(x,label):
#获取每个类别的先验概率
cnt=0
for i in range(len(x)):
if (label==x[i,4]):
cnt+=1
prior=cnt/len(x)
return prior

6.计算后验概率

1
2
3
4
def get_postpro(mean,var,xi):
#获取每个类别的后验概率
p=(1/np.sqrt(2*math.pi*var))*np.exp(-((float(xi)-mean)**2)/(2*var))
return p

7.计算似然度

1
2
3
4
5
6
7
def get_likelihood(mean,var,x):
#根据后验概率计算似然度
p=1
for i in range(4):
p=get_postpro(mean[i],var[i],x[i])
p*=p
return p

8.训练模型

1
2
3
4
5
6
7
8
9
def fit_model(x):
#训练模型
mean_set=get_mean(x,'Iris-setosa')
mean_ver=get_mean(x,'Iris-versicolor')
mean_vir=get_mean(x,'Iris-virginica')
var_set=get_var(x,mean_set,'Iris-setosa')
var_ver=get_var(x,mean_ver,'Iris-versicolor')
var_vir=get_var(x,mean_vir,'Iris-virginica')
return mean_set,mean_ver,mean_vir,var_set,var_ver,var_vir

9.预测类别

1
2
3
4
5
6
7
8
9
10
11
12
def predict(x):
#预测类别
proba=[]
mean_set,mean_ver,mean_vir,var_set,var_ver,var_vir=fit_model(X_train)
p1=get_likelihood(mean_set,var_set,x[:-1])
p2=get_likelihood(mean_ver,var_ver, x[:-1])
p3=get_likelihood(mean_vir,var_vir, x[:-1])
proba.append(p1)
proba.append(p2)
proba.append(p3)
sort=np.argmax(proba)
return sort

10.效果评估

对于运算结果,在此只截取了一部分,可以看到总准确率为100%,朴素贝叶斯得到的各个类别的相对概率值差别很大,结果显而易见。这个结果并不能说明朴素贝叶斯的分类效果特别好,只是这个鸢尾花测试数据集数据太理想,没有一点误差,数据集很干净。

通过散点图同时展示该测试数据集的四个不同维度:图中的(x, y)位置代表每个样本的花萼的长度和宽度(sepal length、sepal width),散点的大小代表每个样本的花瓣的长度(petal length),而散点的颜色代表一种特定的鸢尾花类型。

四、附录

load_data.py文件,实现数据载入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""
iris数据集载入
"""
def load_data(file):
x_data=[]
y_data=[]
data=[]
with open(file,'r') as fp:
lines=fp.readlines()
for line in lines:
line=line.strip().split(',')
x_data.append(line[:-1])
y_data.append((line[-1]))
data.append(line)
return x_data,y_data,data

naive_bayes.py文件,实现贝叶斯模型训练,测试数据集分类。

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import numpy as np
import math
from load_data import *

def get_mean(x,lable):
#获取每个类别的均值
means=[]
for j in range(4):
sum=0
for i in range(120):
if (lable==x[i,4]):
sum+=float(x[i,j])
means.append(sum/40)
return means

def get_var(x,mean,label):
#获取每个类别的方差
vars=[]
for j in range(4):
sum=0
for i in range(120):
if (label==x[i,4]):
sum+=(float(x[i,j])-mean[j])**2
vars.append(sum/40)
return vars

def get_prior(x,label):
#获取每个类别的先验概率
cnt=0
for i in range(len(x)):
if (label==x[i,4]):
cnt+=1
prior=cnt/len(x)
return prior

def get_postpro(mean,var,xi):
#获取每个类别的后验概率
p=(1/np.sqrt(2*math.pi*var))*np.exp(-((float(xi)-mean)**2)/(2*var))
return p

def get_likelihood(mean,var,x):
#根据后验概率计算似然度
p=1
for i in range(4):
p=get_postpro(mean[i],var[i],x[i])
p*=p
return p

def fit_model(x):
#训练模型
mean_set=get_mean(x,'Iris-setosa')
mean_ver=get_mean(x,'Iris-versicolor')
mean_vir=get_mean(x,'Iris-virginica')
var_set=get_var(x,mean_set,'Iris-setosa')
var_ver=get_var(x,mean_ver,'Iris-versicolor')
var_vir=get_var(x,mean_vir,'Iris-virginica')
return mean_set,mean_ver,mean_vir,var_set,var_ver,var_vir

def predict(x):
#预测类别
proba=[]
mean_set, mean_ver, mean_vir, var_set, var_ver, var_vir = fit_model(X_train)
p1=get_likelihood(mean_set,var_set,x[:-1])
p2=get_likelihood(mean_ver, var_ver, x[:-1])
p3=get_likelihood(mean_vir, var_vir, x[:-1])
proba.append(p1)
proba.append(p2)
proba.append(p3)
print('proba is ',proba)
sort=np.argmax(proba)
return sort

file1='C:\\Users\\wnxy\\PycharmProjects\\NaiveBayesian\\iris_data\\iris_train.data'
file2='C:\\Users\\wnxy\\PycharmProjects\\NaiveBayesian\\iris_data\\iris_test.data'
x_train_data,y_train_data,x_train=load_data(file1)
X_train_data=np.mat(x_train_data)
Y_train_data=np.mat(y_train_data)
X_train=np.mat(x_train)
mean_set,mean_ver,mean_vir,var_set,var_ver,var_vir=fit_model(X_train)
prior_set=get_prior(X_train,'Iris-setosa')
prior_ver=get_prior(X_train,'Iris-versicolor')
prior_vir=get_prior(X_train,'Iris-virginica')
print('prior_set',prior_set)
cnt = 0
with open(file2,'r') as fp:
x_test=[]
lines=fp.readlines()
for line in lines:
line = line.strip().split(',')
x_test.append(line)
sort=predict(x_test[-1])
if (x_test[-1][4]=='Iris-setosa'):
y_test=0
elif (x_test[-1][4]=='Iris-versicolor'):
y_test=1
else:
y_test=2
if (y_test==sort):
cnt+=1
print('The prediction is classified as: ',sort)
print('The fact is classified as: ', y_test)
print('Accuracy is: ',cnt/len(lines))
y_test=np.array(y_test)
seplen = []
sepwid = []
petlen = []
petwid = []
colors=['purple','y','b']
for i in range(30):
seplen.append(x_test[i][0])
sepwid.append(x_test[i][1])
petlen.append(x_test[i][2])
petwid.append(x_test[i][3])
index=y_test[i]
plt.scatter(float(seplen[i]), float(sepwid[i]), alpha=0.2, s=100*float(petlen[i]), c=colors[index], cmap='viridis')
plt.ylim(2.0,4.0)
plt.title('Iris Data Classification')
plt.xlabel('sepal length in cm')
plt.ylabel('sepal width in cm')
plt.show()
Author: wnxy
Link: https://wnxy.xyz/2020/11/10/Naive-bayes/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.