前言

概率论是很多机器学习算法的基础,在上一章中使用了信息熵的计算这一公式,这一章朴素贝叶斯中则重点在于对于贝叶斯公式的理解与应用,实现流程并不是十分的复杂。

贝叶斯决策理论

朴素贝叶斯是贝叶斯决策理论的一部分,如果我们用p1(x,y)来表示数据点(x,y)属于类别1的概率,用p2(x,y)表示数据点(x,y)属于类别2的概率。可以用以下的规则来判断它的类别:

如果p1(x,y) > p2(x,y),那么类别为1

如果p1(x,y) < p2(x,y),那么类别为2

贝叶斯理论的核心思想,即选择具有最高概率的决策

贝叶斯公式

如果知道P(x|c),要求P(c|x)

1

使用贝叶斯公式,可以通过计算已知的三个概率来计算未知的概率值,下面给出使用贝叶斯公式来计算概率并对数据进行分类的代码。

使用朴素贝叶斯进行文档分类

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

函数loadDataSet()创建了一些实验样本。

函数createVocabList()会创建一个包含在所有文档中出现的不重复词的列表

setOfWords2Vec()函数在获得词汇表之后,该函数的输入参数为词汇表及某个文档,输出的是文档向量,向量的每一元素为1或0,分别表示词汇表中的单词在输入文档中是否出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def loadDataSet():
postingList=[['my','dog','has','flea','problems','help','please'],
['maybe','not','take','him','to','dog','park','stupid'],
['my','dalamation','is','so','cute','I','love','him'],
['stop','posting','stupid','worthless','garbage'],
['mr','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 createVocabList(dataSet):
vocabSet=set([])
for document in dataSet:
vocabSet=vocabSet|set(document)#取并集
return list(vocabSet)
def setOfWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]=1
else:print "the word : %s is not in my Vocabulary!" %word
return returnVec

训练算法:从词向量计算概率

贝叶斯准则:

1

这一段函数我们要计算p(x|c)的值,即已经知道类别,计算这个向量出现的概率,伪代码如下:

1
2
3
4
5
6
7
8
9
计算每个类别中的文档数目
对每篇训练文档:
对每个类别:
如果词条出现在文档中,则增加该词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率

我们用p0Vect数组存储类别0中每个词条出现的概率,用p1Vect来存储类别1中每个词条出现的概率,用pAbusive来表示$$p(c_i)$$的值,由于只有两个类别所以只用算出一个值另外一个用1减去它即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def trainNB0(trainMatrix,trainCategory):
numTrainDocs=len(trainMatrix)
numWords=len(trainMatrix[0])
pAbusive=sum(trainCategory)/float(numTrainDocs)
p0Num=ones(numWords);p1Num=ones(numWords)
p0Denom=2.0;p1Denom=2.0
for i in range(numTrainDocs):
if trainCategory[i]==1:
p1Num+=trainMatrix[i]
p1Denom+=sum(trainMatrix[i])
else :
p0Num+=trainMatrix[i]
p0Denom+=sum(trainMatrix[i])
p1Vect=log(p1Num/p1Denom)
p0Vect=log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive

测试算法:根据现实情况修改分类器

要计算多个概率的乘积以获得文档属于某个类别的概率,即计算$$p(w_0|1)p(w_1|1)p(w_2|1)$$。如果其中一个概率值为0,那么最后的乘积也为0。为降低这种影响,可以将所有词的出现次数初始化为1,并将分母初始化为2.

另外一个问题是下溢出,这是由于太多很小的数相乘造成。当计算乘积$$p(w_0|c_i)p(w_1|c_i) \cdots p(w_N|c_i)$$时,由于大部分因子非常小,所以程序下溢出得不到正确答案。解决方法是取自然对数,使用ln(a*b)=ln(a)+ln(b),将上述做法用到分类器中:

2

分类函数中计算出$$p(W|c_i)p(c_i)$$,并用求自然对数后,可以用相加来进行表示

3

1
2
3
4
5
6
7
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1=sum(vec2Classify*p1Vec)+log(pClass1)
p0=sum(vec2Classify*p0Vec)+log(1.0-pClass1)
if p1>p0:
return 1
else :
return 0

最后我们把所有的函数整合起来进行测试,整体步骤为:创建实验样本,创建不重复词列表,计算词向量概率,进行分类

1
2
3
4
5
6
7
8
9
10
11
12
13
def testingNB():
listOPost,listClasses=loadDataSet()
myVocabList=createVocabList(listOPost)
trainMat=[]
for postinDoc in listOPost:
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses))
testEntry=['love','my','dalamation']
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
print testEntry,'classified as ',classifyNB(thisDoc,p0V,p1V,pAb)
testEntry=['stupid','garbage']
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
print testEntry,'classified as ',classifyNB(thisDoc,p0V,p1V,pAb)

总结

对于分类而言,使用概率有时要比使用硬规则更为有效。贝叶斯概率及贝叶斯准则提供了一种利用已知值来估计未知概率的有效方法。

可以通过特征之间的独立性假设,降低对数据量的要求。独立性假设是指一个词的出现概率并不依赖于文档中的其他词。当然我们也知道这个假设过于简单。这就是之所以称为朴素贝叶斯的原因。

Comments

⬆︎TOP