关于上一篇的一些总结
上一次写了一下基于伯努利概率模型的朴素贝叶斯分类器的简单实现,但是没有找到数据集,自己随便编了几个数据运行一下居然得到了大于一的概率,进行拉普拉斯平滑之后大部分情况都是小于一,但是仍然有一种情况是略微大于一,不知道是不是数据精度的问题。
在网上找到了用于朴素贝叶斯分类器的数据集,比较典型的鸢尾花数据集,但是鸢尾花数据集中鸢尾花的特征变量是连续变量,而且符合高斯分布,所以我决定学一下基于高斯概率模型的朴素贝叶斯分类器。
当然其实也可以将连续的特征变量离散化,直接简单地转化为离散的0-1分布,比如说与特征变量的均值相差大于某个度量时,认为不具备该特征记为0,小于此度量则认为具备该特征记为1,但是很明显这样可能会丢失较多的细节,可能不如直接使用高斯概率模型来进行计算。
高斯概率模型
适用于特征变量为连续型变量且服从高斯分布的情况。在鸢尾花数据集中,花萼长度、花萼宽度、花瓣长度和花瓣宽度这四种特征变量军事连续型变量,比较适合使用高斯概率模型进行计算。
在sklearn
模块中,内置了鸢尾花数据集,可以直接导入使用。
无论基于何种概率模型,贝叶斯分类器的核心都是贝叶斯公式
上一次的实践中我还忽略了一个问题,在测试集中,在一个具体的例子中,对于某一个样本X
,其具备特征为F
,对X
进行分类时,这个P(F)
是一个确定的值,那么分类时我们就只需要计算对于每一类的P(F|Ci)P(Ci)
,然后比较其大小,取最大的作为最终结果即可
在高斯概率模型中,特征变量取某个具体值的概率为0,不能直接连乘求概率,可使用概率密度来计算,正态分布的概率密度函数:
代码实现
实现思路
其实在sklearn
这个模块中已经有GaussianBayes
函数可以实现高斯模型的朴素贝叶斯算法,我觉得但是直接用封装好的函数或者模块和直接copy代码没啥区别,我决定自己尝试实现一下。
思路大概就是使用概率论中刚学习的正态分布的概率密度函数方面的知识,将一般的正态分布转换为标准正态分布,然后编写一个函数计算标准正态分布的概率密度,然后考虑到上面所说的,其实有些概率(也就是P(F)
)是不用计算的
总结一下需要从训练集中得到的数据:先验概率P(Ci)
和在已知分类情况下的某特征出现的概率P(Fj|Ci)
,连乘即可得到P(F|Ci)
代码
# author: lzy
from sklearn import datasets
from math import e, pi, sqrt
import random
def f_gaussian(x, f1, f2):
# 正态分布概率密度函数,均值为f1,方差为f2
temp1 = 1 / (sqrt(2 * pi) * sqrt(f2))
temp2 = pow(e, -1 * (x - f1) * (x - f1) / (2 * f2))
return temp1 * temp2
def get_data_set():
iris = datasets.load_iris()
iris_data = iris.data # 特征,Sepal.Length,Sepal.Width,Petal.Length,Petal.Width
iris_target = iris.target # 类别,0: setosa,1: versicolor,2: virginica
data_set = []
for i in range(0, 150):
data = [iris_data[i], iris_target[i]]
data_set.append(data)
return data_set
def split(data_set):
# 划分数据集
training_set = []
test_set = []
for i in range(0, 30):
ran = random.randint(0, 4) # 80%的数据用于训练,20%用于测试
for j in range(0, 5):
if j != ran:
training_set.append(data_set[i * 5 + j])
else:
test_set.append(data_set[i * 5 + j])
return training_set, test_set
def calculate(data_set):
# 对数据集data_set求各个特征的均值和方差
sum0, sum1, sum2, sum3 = 0, 0, 0, 0
s0, s1, s2, s3 = 0, 0, 0, 0
length = len(data_set)
for i in range(0, length):
sum0 += data_set[i][0][0]
sum1 += data_set[i][0][1]
sum2 += data_set[i][0][2]
sum3 += data_set[i][0][3]
ave0, ave1, ave2, ave3 = sum0 / length, sum1 / length, sum2 / length, sum3 / length
for i in range(0, length):
s0 += (data_set[i][0][0] - ave0) ** 2
s1 += (data_set[i][0][1] - ave1) ** 2
s2 += (data_set[i][0][2] - ave2) ** 2
s3 += (data_set[i][0][3] - ave3) ** 2
d0, d1, d2, d3 = s0 / (length - 1), s1 / (length - 1), s2 / (length - 1), s3 / (length - 1)
res0, res1, res2, res3 = [ave0, d0], [ave1, d1], [ave2, d2], [ave3, d3]
# 封装为数组返回结果
return [res0, res1, res2, res3]
def judge(x, p, pfc):
p0, p1, p2 = p[0], p[1], p[2]
for i in range(0, 4):
p0 *= f_gaussian(x[i], pfc[0][i][0], pfc[0][i][1])
p1 *= f_gaussian(x[i], pfc[1][i][0], pfc[1][i][1])
p2 *= f_gaussian(x[i], pfc[2][i][0], pfc[2][i][1])
if p0 > p1 and p0 > p2:
return 0
elif p1 > p2:
return 1
else:
return 2
def main():
data_set = get_data_set()
training_set, test_set = split(data_set)
count0, count1, count2 = 0, 0, 0
len_of_training, len_of_test = len(training_set), len(test_set)
class0, class1, class2 = [], [], []
for every_x in training_set:
if every_x[1] == 0:
count0 += 1
class0.append(every_x)
elif every_x[1] == 1:
count1 += 1
class1.append(every_x)
elif every_x[1] == 2:
count2 += 1
class2.append(every_x)
p0, p1, p2 = count0 / len_of_training, count1 / len_of_training, count2 / len_of_training
p = [p0, p1, p2]
# 计算先验概率
pfc0, pfc1, pfc2 = calculate(class0), calculate(class1), calculate(class2)
pfc = [pfc0, pfc1, pfc2]
# pfc中保存每一类的每个特征的均值和方差
correct = 0
for test in test_set:
if test[1] == judge(test[0], p, pfc):
correct += 1
final_res = correct / len_of_test
print('正确率为:' + str(final_res))
main()
emmm,运行结果相当奈斯,正确率基本90%以上,第一次运行正确率居然是1,吓我一跳,还以为脚本写错了