机器学习实战——朴素贝叶斯
初识贝叶斯:
说起来朴素贝叶斯法,首先想起来的肯定是贝叶斯准则,贝叶斯准则告诉我们如何交换条件概率中的条件和结果,通过朴素贝叶斯准则,可以通过已知的三个概率值来计算未知的概率值。贝叶斯准则公式如下:
一、朴素贝叶斯基本概念
1.朴素贝叶斯法简述
朴素贝叶斯法(Native Bayes)是基于贝叶斯定理和特征条件独立假设的分类方法,对于给定的训练数据集,首先基于特征条件独立假设学习输入\输出的联合概率分布,然后基于此模型,对给定的输入x,利用贝叶斯定理求出后验概率最大的输出y。
2.何为朴素?
朴素包含两个假设:(1)假设每个特征互相独立,所谓独立是统计意义上的独立,即一个特征或者单词出现的可能性与它与其他单词相邻没关系,举个栗子,假设单词bacon出现在unhealthy后边与出现在delicious后面的概率相同。当然我们知道这种假设并不正确,bacon常常出现在delicious附近,很少出现在unhealthy附近,但是我们强行假设,是为了方便概率估计。(2)假设每个特征同等重要,当然这个假设也有问题,如果要判断留言板的留言是否得当,那么可能不需要看完所有的1000个单词,只需要看10-20个特征就足以做出判断了。尽管上述假设存在一些小瑕疵,但是朴素贝叶斯的实际效果很好。
3.朴素贝叶斯分类思想
给出待分类项,求解在此项出现的条件下其他各个类别的出现的概率,哪个概率较大就认为待分类项属于哪个类别。
4.朴素贝叶斯法的参数估计
(1)极大似然估计
在朴素贝叶斯方法中,学习意味着估计概率,可以用极大似然估计法估计相应的概率。先验概率的极大似然估计是
条件概率的极大似然估计是
朴素贝叶斯算法的步骤就是先计算先验概率和条件概率,然后对于给定实例计算它属于类C的概率,比较哪个概率大,就属于哪一类。
举个例子更直观:
(2)贝叶斯估计
贝叶斯估计就是解决极大似然估计中一个可能会出现所要估计的概率值为0的问题,这时会影响到后验概率的计算结果,解决这一问题的方法是采用贝叶斯估计,具体的只需要在极大似然估计的基础上加多一个参数即可。
其中λ≥0。等价于在随机变量各个取值的频数上赋予一个正数λ>0;λ=0是极大似然估计。常取λ=1,称为拉普拉斯平滑。
显然对于任何l=1,2,⋯,S_j;k=1,2,⋯,K有:
同样,先验概率有:
二、朴素贝叶斯代码实现
#---------------------------从文本中构建词条向量-------------------------
#1 要从文本中获取特征,需要先拆分文本,这里特征是指来自文本的词条,每个词
#条是字符的任意组合。词条可以理解为单词,当然也可以是非单词词条,比如URL
#IP地址或者其他任意字符串
# 将文本拆分成词条向量后,将每一个文本片段表示为一个词条向量,值为1表示出现
#在文档中,值为0表示词条未出现
#---------------------
import numpy as np
#生成文档数据集合
def loadDataSet():
#词条切分后的文档集合,列表每一行代表一个文档
postingList=[['my','dog','has','flea',\
'problems','help','please'],
['maybe','not','take','him',\
'to','dog','park','stupid'],
['my','dalmation','is','so','cute',
'I','love','him'],
['stop','posting','stupid','worthless','garbage'],
['my','licks','ate','my','steak','how',\
'to','stop','him'],
['quit','buying','worthless','dog','food','stupid']]
#由人工标注的每篇文档的类标签
classVec=[0,1,0,1,0,1]
return postingList,classVec
#创建文档词条列表,相当于词条库
def creatVocabList(dataSet):
#新建一个存放词条的集合
vocabSet=set([])
for i in dataSet:
#将文档列表转为集合的形式,保证每个词条的唯一性
#然后与vocabSet取并集,向vocabSet中添加没有出现
#的新的词条
vocabSet=vocabSet|set(i)
#再将集合转化为列表,便于接下来的处理
return list(vocabSet)
##根据词条列表中的词条是否在文档中出现(出现1,未出现0),将文档转化为词条向量
def setOfWords2Vec(vocabSet,inputSet):
#新建一个长度为vocabSet的列表,并且各维度元素初始化为0
returnVec=[0]*len(vocabSet)
#遍历文档中的每一个词条
for word in inputSet:
#如果词条在词条列表中出现
if word in vocabSet:
#通过列表获取当前word的索引(下标)
#将词条向量中的对应下标的项由0改为1
returnVec[vocabSet.index(word)]=1
else:
print('the word:%s is not in my vocabulary!'%'word')
#返回inputet转化后的词条向量
return returnVec
#训练算法,从词向量计算概率p(w0|ci)...及p(ci)
#@trainMatrix:由每篇文档的词条向量组成的文档矩阵
#@trainCategory:每篇文档的类标签组成的向量
def trainNB0(trainMatrix,trainCategory):
#获取文档矩阵中文档的数目
numTrainDocs=len(trainMatrix) #6
#获取词条向量的长度
numWords=len(trainMatrix[0]) #均为31
#pAbusive代表所有文档中属于类别1的概率
pAbusive=sum(trainCategory)/float(numTrainDocs) #sum(trainCategory)为3,float(numTrainDocs)为6.0
#创建一个长度为词条向量等长的列表
#注意:为了防止o概率的出现,可以将分子初始化为1,分母初始化为2
p0Num=np.zeros(numWords); p1Num=np.zeros(numWords)
p0Denom=0.0; p1Denom=0.0
#遍历每一篇文档的词条向量
for i in range(numTrainDocs):
#如果该词条向量对应的标签为1
if trainCategory[i]==1:#当i=1时
#统计所有类别为1的词条向量中各个词条出现的次数
p1Num+=trainMatrix[i] #[0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0]
#统计类别为1的词条向量中出现的所有词条的总数
#即统计类1所有文档中出现单词的数目
p1Denom+=sum(trainMatrix[i])#8.0
else:
p0Num+=trainMatrix[i]
p0Denom+=sum(trainMatrix[i])
#利用NumPy数组计算p(wi|c1)
p1Vect=p1Num/p1Denom#为避免下溢出问题,后面会改为log()
#利用NumPy数组计算p(wi|c0)
p0Vect=p1Num/p0Denom#为避免下溢出问题,后面会改为log()
return p0Vect,p1Vect,pAbusive
#朴素贝叶斯分类函数
#@vec2Classify:待测试分类的词条向量
#@p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0)
#@p0Vec:类别1所有文档中各个词条出现的频数p(wi|c1)
#@pClass1:类别为1的文档占文档总数比例
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
#根据朴素贝叶斯分类函数分别计算待分类文档属于类1和类0的概率
p1=sum(vec2Classify*p1Vec)+log(pClass1)
p0=sum(vec2Classify*p0Vec)+log(1.0-pClass1)
if p1>p0:
return 1
else:
return 0
#分类测试整体函数
from numpy import *
def testingNB():
#由数据集获取文档矩阵和类标签向量
listOPosts,listClasses=loadDataSet()
#统计所有文档中出现的词条,存入词条列表
myVocabList=creatVocabList(listOPosts)
#创建新的列表
trainMat=[]
for postinDoc in listOPosts:
#将每篇文档利用words2Vec函数转为词条向量,存入文档矩阵中
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
#将文档矩阵和类标签向量转为NumPy的数组形式,方便接下来的概率计算
#调用训练函数,得到相应概率值
p0V,p1V,pAb=trainNB0(array(trainMat),np.array(listClasses))
#测试文档
testEntry=['love','my','dalmation']
#将测试文档转为词条向量,并转为NumPy数组的形式
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
#利用贝叶斯分类函数对测试文档进行分类并打印
print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))
testEntry1=['stupid','garbage']
#同样转为词条向量,并转为NumPy数组的形式
thisDoc1=array(setOfWords2Vec(myVocabList,testEntry1))
print(testEntry1,'classified as:',classifyNB(thisDoc1,p0V,p1V,pAb))
测试结果如下:
三、朴素贝叶斯法优缺点
朴素贝叶斯的主要优点有:
1)朴素贝叶斯模型有稳定的分类效率。
2)对小规模的数据表现很好,能处理多分类任务,适合增量式训练,尤其是数据量超出内存时,可以一批批的去增量训练。
3)对缺失数据不太敏感,算法也比较简单,常用于文本分类。
朴素贝叶斯的主要缺点有:
1) 理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型给定输出类别的情况下,假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。而在属性相关性较小时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法通过考虑部分关联性适度改进。
2)需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。
3)由于我们是通过先验和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率。
4)对输入数据的表达形式很敏感。
优缺点原文总结的很好:https://blog.csdn.net/zrh_CSDN/article/details/81007851
作者:小新新
来源:CSDN
版权声明:本文为博主原创文章,转载请附上博文链接!欢迎转载!
参考文献:
[1]《机器学习实战》的第四章内容
[2] 《统计学习方法(李航)》