总说
都知道条件概率吧:
p(c|x)=p(x|c)p(c)p(x)
贝叶斯分类类似:
p(ci|w)=p(w|ci)p(ci)p(w)
记忆方法:既然是用条件概率进行分类的,因此绝对是已知某件事情( w )发生了,然后判断这件事情属于哪一类。
拿文本分类来说:给出几个关键字,为了方便称为“句子”吧,判断这个句子是abusive还是not abusive。这个句子是有某些关键字组成的。 w 就是这些关键字组成的向量。 c1 =1, c0 =0,如此一来。就是已知 w ,求 p(1|w) 和 p(0|w)
根据公式,我们可以利用右边的式子,如果求得 p(1|w)>p(0|w) ,那么就认为 w 属于abusive.
右侧3个概率式子的处理
- p(w) 是没有必要参与计算的,根本不影响比较关系。
- p(ci) 是最容易求得的。比如数据库中共有10条句子(注意:句子仍旧是指一组关键词,可以先看下面的代码),如果有4条是abusive,那么就是 p(1)=4/10,p(0)=6/10 ;
- p(w|ci)=p(w0|ci)p(w2|ci)⋯p(wn|ci) ,这里假设每个单词出现是独立的,不会相互影响。如何求每一个 p(wj|ci) 呢?
求 p(wj|ci) 的方法
比如求
p(good|c0)
,就是说在not abusive的句子中出现”good”单词的概率。显然就是
p(good|c0)=notabusive的句子中good出现的次数notabusive中所有句子的单词出现的次数总和
有2个问题:
- 如果一个单词在某个类别中没有出现,比如good在abusive类别中没有出现,那么计算 p(good|c1) 就是0,就会导致 p(c1|w) =0。显然出现以偏概全的现象。
- 由于计算 p(w|ci) 是连乘形式,多个小数连乘会导致下溢,导致结果不准。
解决方法:
- 为了防止出现概率为0的情况,直接初始化令每个单词在每个类都出现1次。同时将分母(not abusive或是abusive中所有句子的单词出现的次数总和)初始化为2,这是为了使在进行数据训练前,两种类别的概率各占一半。
- 利用自然对数 ln .将计算出的 p(wj|ci) 取自然对数,相加即可。
总结
可见,我们求出的并不是真正意义上的
p(ci|w)
,只是类似”
p(w|ci)p(ci)
”,值得注意的是计算
p(w|ci)
是初始化更改了一些东西,并且采用了
ln
.
另外如果一个句子中出现了多个相同的单词,在转换的vec中就不再是0和1的组合,1被替换成单词出现的次数,而不是原先的是否出现,详见代码
# -*- coding: utf-8 -*-
from numpy import *
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'],
['mr','licks','ate','my','steak','how',\
'to','stop','him'],
['quit','buying','worthless','dog','food','stupid']]
classVec = [0,1,0,1,0,1] #1 is abusive, 0 not
return postingList,classVec
#主要是将dataSet中的句子全部拿出来,作为集合的一个个元素。
#集合的元素具有唯一性,用 'union'或是'|'来进行集合的并。
def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
#inputSet是要输入的集合,如: bayes.setOfWords2Vec(myVocabList,listOPosts[0])
#如果输入的集合的word在myVocabList中,则返回的returnVec的响应的位置为1
#其他位置为0,通过查找vec中哪些位置为1,即可知道该句子含有哪些词
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
# returnVec[vocabList.index(word)] += 1
# 如果是一个句子中出现多个相同的单词,这种模型就是'set-of-words model 就要用+=
else: print "the word :%s is not in my Vocabulary" % word
return returnVec
# Naive Bayes classfier traning function
# trainMatrix的每一行是上方的returnVec,也就是说是32长度的vec,当然这里是有6行
# 对于某句话,如果分类是1,那么 p1Num += trainMatrix[i]是vec的对应位置相加,
# 最终p1Num是每个单词在类别1所出现的次数。
# p1Denom += sum(trainMatrix[i])是句子中出现的单词总数。最后p1Denom是所有abusive的句子
# 的单词数。 因此 p1Vect = p1Num/p1Denom 就是“(每个单词出现在类别1的次数)/(类别1出现的所有句子的单词总和)”,
# 得到的就是p(w1|c1),p(w2|c1),...,p(w32|c1)
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
# p0Num = zeros(numWords); p1Num = zeros(numWords)
# p0Denom = 0.0; p1Denom = 0.0
# 由于计算 p(w|c1) = p(w1|c1)p(w2|c1)...p(w32|c1),如果其中的一个单词并未出现在c1中,那么p(wx|c1)=0
# 为了避免这种情况,因而出现次数初始化为1,分母初始化为2
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 = p1Num/p1Denom
# p0Vect = p0Num/p0Denom
# 由于小数过多的练乘,可能会得到不精确的非常小的数,因此转换为log
p1Vect = log(p1Num/p1Denom)
p0Vect = log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
# vec2Classify比如是[1 0 0 1 0 1 1...1],代表句子中有哪些单词。所以 vec2Classify*p1Vec就是
# 每一个单词在类1中出现的概率的log,sum来讲这些概率全部乘起来。
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, p1
else:
return 0, p0
def testingNB():
listOPosts,listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
testEntry = ['love','my','dalmation']
thisDoc = array(setOfWords2Vec(myVocabList,testEntry))
calssfier,pp = classifyNB(thisDoc,p0V,p1V,pAb)
print testEntry,'value is ',pp,'classfied as: ',calssfier
testEntry = ['stupid','garbage']
thisDoc = array(setOfWords2Vec(myVocabList,testEntry))
calssfier,pp = classifyNB(thisDoc,p0V,p1V,pAb)
print testEntry,'value is ',pp, 'classfied as: ',calssfier