python朴素贝叶斯调参_数据分析|自编程实现朴素贝叶斯

朴素贝叶斯是一种生成式模型,不同于之前讲过的k-近邻,支持向量机,决策树等判别式模型。生成式模型:需要计算x,y的联合概率分布!并且是比较各分类概率大小,来决定分类。

判别式模型:只需要计算y的概率分布!需要从训练出的模型中计算分类概率。

关于判别式模型与生成式模型,请参考这篇文章求知鸟:关于统计学的思考(3)

作为一名励志成为技师的我来说,当然不想被人嘲笑为”调包侠“,于是就有了接下来这篇文章:如何自编程实现朴素贝叶斯。

本文内容如下:

demo:

原理公式

算法实现

实战:使用Python进行文本分类

数据准备:从文本中构建词向量

伪代码:从词向量计算概率

算法实现

原理公式

贝叶斯原理解决了数学中的“逆向概率问题”,在这个基础上,人们设计出了贝叶斯分类器,朴素贝叶斯是贝叶斯分类中最简单的分类器,之所以称为朴素,是假设属性是相互独立的;而实际上,相互独立的属性在生活中是不存在的,这就会导致分类正确率降低,好在错误率并不高!朴素贝叶斯适应于离散性数据 ;

高斯朴素贝叶斯:特征变量是连续变量,符合高斯分布

多项式朴素贝叶斯:特征变量是离散变量,符合多项分布

伯努利朴素贝叶斯:特征变量是0,1分布

有了朴素贝叶斯模型,还需要对模型进行求解,一是用极大似然估计进行求解

一是用贝叶斯估计求解参数

由于极大似然估计无法应对分母为0的情形,所以一般采用贝叶斯估计:公式1.1公式1.2公式1.3

可能,你不了解上述公式代表的意义,没关系,接下来这张图片会帮你搭建起”积木中的全局视野“,这样就可以清晰的理解上述公式了:想要更清楚的了解,可以参考李航博士的《统计学习方法》

或是之前写的一篇文章求知鸟:朴素贝叶斯分类:原理公式1.4

等式左边是我们要求的后验概率,我们就依据这个后验值的比较,来进行分类;请注意:我用的关键字是:后验概率值的比较!比较就意味着有多个!

等式右边:分母在分类中,不同类别的计算是一样的,既然没有区别,我们不care它,分子可以抽出两部分:

对应着本文公式的1.2;

对应1.1!

算法实现

先构建主函数

import numpy as np

import pandas as pd

def main():

X_train=np.array([

[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"]

])

y_train=np.array([-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1])

clf=NaiveBayes(lambda_=0.2)

clf.fit(X_train,y_train)

X_new=np.array([2,"S"])

y_predict=clf.predict(X_new)

print("{}被分类为:{}".format(X_new,y_predict))

if __name__=="__main__":

main()主函数中出现了训练样本集:输入特征与输出类别标签;

还定义了测试样本集:输入特征X_new;(demo就取了一个样本)

主函数还引用了类-实例中的方法进行分类预测。

#定义朴素贝叶斯中的类-方法-实例

class NaiveBayes():

def __init__(self,lambda_): #变量的初始化

self.lambda_=lambda_ #贝叶斯系数 取0时,即为极大似然估计

self.y_types_count=None #y的(类型:数量)

self.y_types_proba=None #y的(类型:概率)

self.x_types_proba=dict() #(xi 的编号,xi的取值,y的类型):概率

#对应公式1.1

def fit(self,X_train,y_train):

self.y_types=np.unique(y_train) #y的所有去除重复取值类型

X=pd.DataFrame(X_train) #转化成pandas DataFrame数据格式,下同

y=pd.DataFrame(y_train)

# DataFrame数据结构,先从列开始,y[0]就是第一列;y的(类型:数量)统计:告诉你-1出现了9次,1出现了6次

self.y_types_count=y[0].value_counts()

# y的(类型:概率)计算

self.y_types_proba=(self.y_types_count+self.lambda_)/(y.shape[0]+len(self.y_types)*self.lambda_)

#对应公式1.2

# (xi 的编号,xi的取值,y的类型):概率的计算

for idx in X.columns: # 遍历X的列名0,1

for j in self.y_types: # 选取每一个y的类型,-1,1

p_x_y=X[(y==j).values][idx].value_counts() #选择所有y==j为真的数据点的第idx个特征的值,并对这些值进行(类型:数量)统计

for i in p_x_y.index: #计算(xi 的编号,xi的取值,y的类型):概率;元组

self.x_types_proba[(idx,i,j)]=(p_x_y[i]+self.lambda_)/(self.y_types_count[j]+p_x_y.shape[0]*self.lambda_)

#对应公式1.3

def predict(self,X_new):

res=[] #空列表,可变数据

for y in self.y_types: #遍历y的可能取值

p_y=self.y_types_proba[y] #计算y的先验概率P(Y=ck),字典

p_xy=1

for idx,x in enumerate(X_new):

p_xy*=self.x_types_proba[(idx,x,y)] #计算P(X=(x1,x2...xd)/Y=ck)

res.append(p_y*p_xy)

for i in range(len(self.y_types)):

print("[{}]对应概率:{:.2%}".format(self.y_types[i],res[i]))

#返回最大后验概率对应的y值

return self.y_types[np.argmax(res)]数据结构可以参考这篇,求知鸟:Python科学计算:庖丁解牛之Pandas

计算起来还是有点饶的,p_x_y=X[(y==j).values][idx].value_counts();这一行尤其难理解!

我们先来看单个索引:

df1 = pd.DataFrame( np.array([[1, 2, 3], [4, 2, 6],[7,8,9]]) )

print(df1)

print(df1[1])

print(df1[1].value_counts)#对第二列统计出现次数

print(df2[2:2])

print(df1[2:2].value_counts)#不能对行统计出现次数

print(df2[1][1].value_counts)#不能对索引值统计出现次数以上说明,value_counts()只能用在某列上,不能用在某行,某个具体值上!

索引某个值,遵循[columns][index]的格式,可下面的功能显然不是!

p_x_y=X[(y==j).values][idx].value_counts()

idx可选项{0,1},循环遍历

j的可选项{-1,1},循环遍历!

y是[-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1]

当idx=0时

当j=-1时,依次判断j==y,匹配上,统计第1列出现次数

当j=1时,依次判断j==y;匹配上,统计第1列出现次数

内层循环结束;

外层循环idx=1时

当j=-1时,依次判断j==y,匹配上,统计第2列出现次数

当j=1时,依次判断j==y;匹配上,统计第2列出现次数

打印结果如下:

1 3

2 2

3 1

Name: 0, dtype: int64

3 4

2 3

1 2

Name: 0, dtype: int64

S 3

M 2

L 1

Name: 1, dtype: int64

M 4

L 4

S 1

实战:使用Python进行文本分类

从文本中构建词向量

#创建了一个实验样本。返回的第一个变量是词条集合,第二个变量是类别标签(人工标注)

def loadDataSet():

postingList=[['my','dog','is','ugly'],['my','girlfriend', 'will', 'be','wd'],['the', 'day', 'is', 'garbage'],['tomorrow', 'will', 'be', 'sunny']]

classVec=[1,0,1,0]

return postingList,classVec

#创建一个不重复出现词的列表

def createVocabList(dataSet):

vocabSet=set([])#创建一个无序不重复元素集

for document in dataSet:

vocabSet=vocabSet|set(document)#&交,|或,-差运算

return list(vocabSet)

def setOfWorlds2Vec(vocabList,inputSet):

returnVec=[0]*len(vocabList)#创建一个和词汇表等长的全0向量

for word in range(inputSet):#注意加range,否则报错TypeError: ‘int’ object is not iterable!

if word in vocabList:

returnVec[vocabList.index(word)]=1#如果出现了词汇表中的单词,则将输出文档向量中的值设为1

else:

print("the world :%s is not in my Vocabulary!" %word)

return returnVec

#检测下上述函数的运行效果

listOPosts,listClasses=loadDataSet()

myVocabList=createVocabList(listOPosts)

print(myVocabList)

print(setOfWorlds2Vec(myVocabList,listClasses[0]))def():要知道传入的是你的输入,要写进括号内,返回的是你想要的结果,用return!

循环中使用的应该是一个数组:for word in range(inputSet):

伪代码:从词向量计算概率

计算每个类别中的文档数目

对每篇训练文档:

对每个类别:

如果词条出现在文档中---》增加该词条的计数值

增加所有词条的计数值

对每个类别:

对每个词条:

将该词条的数目除以总词条数目得到条件概率

返回每个类别的条件概率

def trainNB0(trainMatrix,trainCategory): #传入参数:文档矩阵trainMatrix,和每篇文档类别标签所构成的向量trainCategory

numTrainDocs=len(trainMatrix)

numWords=len(trainMatrix[0])

pAbusive=sum(trainCategory)/float(numTrainDocs)

p0Deom=0.0

plDemon=0.0

for i in range(numTrainDocs):

if trainCategory[i]==1:

plNum+=trainMatrix[i]

plDenom+=sum(trainMatrix[i])

else:

p0Num+=trainMatrix[i]

p0Denom+=sum(trainMatrix[i])

p1Vect=p1Num/p1Denom

p0Vect=p0Num/p0Denom

return p0Vect,p1Vect,pAbusive

#加载数据

listOPosts,listClasses=loadDataSet()

myVocabList=createVocabList(listOPosts)

#构建一个包含所有词的列表

trainMat=[]

for postinDoc in listOPosts:

trainMat.append(setOfWorlds2Vec(myVocabList,postinDoc))

p0V,p1V,pAb=trainNB0(trainMat,listClasses)还没仔细研究,感觉没有demo中的方法巧妙!

总结:

朴素贝叶斯算法原理:朴素贝叶斯提出是为了解决“逆向概率问题”---知道结果推原因举例:患癌症的概率是万分之一(先验概率),医院判断一个人是否得病的正确率是99.9%,误判的概率是0.1%;--条件概率(先验概率)

如果一个人被查出来患有癌症,实际上患有的可能性有多大?---这就是求后验概率!

用数学语言来讲:

知道每个类别概率和各个属性下的条件概率,来求后验概率!

朴素贝叶斯分类器的实现步骤:

朴素贝叶斯分类器模型,本质上是个生成模型:需要知道x,y的联合概率分布,不像判别模型只需要知道y的概率分布就好!具体在分类上,是通过比分类概率大小来预测类别!举例:现在有一只羊,想知道它是山羊还是绵羊?我们先把这只羊的特征带入山羊的训练模型中,得出一个概率值,然后再带入绵羊的训练模型中得出一个概率,比较这两个概率大小,大的就是我们最终的分类!

用数学语言讲:1、求各类别概率P(Y=Ck)

2、求联合概率分布

3、比较选择概率最大的类(不涉及分母,因为类别之间都一样)

朴素贝叶斯的假设:各属性之间相互独立!

对数据要求:朴素贝叶斯适应于离散性数据 ;

高斯朴素贝叶斯:特征变量是连续变量,符合高斯分布(正态分布)

多项式朴素贝叶斯:特征变量是离散变量,符合多项分布

伯努利朴素贝叶斯:特征变量是0,1分布

朴素贝叶斯估计参数的方法:极大似然估计:因为不能应对极端情况--分母为0的情况,一般选择贝叶斯估计!

贝叶斯估计:在样本量无穷,且先验概率靠谱的局面下,极大似然估计与贝叶斯估计的结果是一样的!

若是数据量有限的情况下,就计算复杂度来说,选择极大似然估计(极大似然估计就涉及求导,贝叶斯不仅求导,还涉及多重积分),就计算准确性而言,选择贝叶斯估计,毕竟贝叶斯估计有很强的理论基础!

朴素贝爷斯算法优缺点:

优点:对小规模的数据表现很好,能个处理多分类任务,适合增量式训练,尤其是数据量超出内存时,我们可以一批批的去增量训练。

对缺失数据不太敏感,算法也比较简单,常用于文本分类。

缺点:朴素贝叶斯假设各属性之间独立,现实很难满足,这就导致分类有天然的误差!

需要知道先验概率,而先验概率依赖于假设!

对输入数据的表达形式很敏感。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值