从零开始实战机器学习(3)—朴素贝叶斯算法

来源:互联网 发布:淘宝劲舞团情侣衣服 编辑:程序博客网 时间:2024/05/16 08:26

朴素贝叶斯算法原理其实比较简单,就是基于贝叶斯原理。在此先介绍一下贝叶斯原理。条件概率:P(A|B)就是在B发生的条件下A发生的概率。而P(AB) = P(A|B) *P(B) = P(B|A) *P(A),由此可以推出P(B|A) = [P(A|B) *P(B)]/P(A),这个公式就给出了P(A|B)和P(B|A)之间相互转换的公式。也就是先验概率与后验概率之间的相互转换,这也是贝叶斯学派与频率学派之间最大的区别,频率学派只关注事件本身,不涉及先验概率。下面就进入朴素贝叶斯算法的实现过程,会具体将贝叶斯公式应用到我们的例子中。
首先,生成文档列表,该文档列表由六段组成,并在classVec中将这六段文档分类,1代表侮辱类,0代表非侮辱类。

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]    return postingList,classVec

创建词列表,定义一个空集,遍历整个文档列表,通过并集的方式将所有词添加到该集合里,最后将集合转换为列表并返回。

def createVocabList(dataSet):    vocabSet = set([])    for document in dataSet:        vocabSet = vocabSet | set(document)    return list(vocabSet)

将每一段文字转换为词向量,首先定义一个全0向量,向量维数与词的总数相同,然后遍历输入文档,将输入文档中的词对应词库中所在位置置1,返回一个0,1向量,1的位置对应着该文档中出现的词。

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

在这里,我们重写贝叶斯准则,P(ci|w)=P(w|ci)P(ci)P(w), 其中ci 是类别,w代表着每个词出现的概率,将w看成独立特征的话可以写成P(w0,w1,...,wn|ci) ,这里假设每个词之间相互独立,故可以写成P(w0|ci)P(w1|ci),...,P(wn|ci) 。故最后得到的结果是在给定一组词的条件下判断其属于每个类别的概率。

def trainNB0(trainMatrix,trainCategory):    numTrainDocs = len(trainMatrix)                  #计算文档的数目    numWords = len(trainMatrix[0])                   #计算每篇文档的长度    pAbusive = sum(trainCategory)/float(numTrainDocs)#计算侮辱性文档的概率,因为侮辱性分类标签是1,所以可以直接累加    p0Num = ones(numWords); p1Num = ones(numWords)   #将向量置0,p0Num和p1Num是词向量,下文中将其累加代表着每个词出现的次数。    p0Denom = 0.0; p1Denom = 0.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                           #计算条件概率    return p0Vect,p1Vect,pAbusive

上述代码中还存在一些问题需要修改一下,在使用朴素贝叶斯进行分类时,要计算多个概率的乘积以计算文档所属的类别,即计算p(w0|1)p(w1|1)….p(wn|1),其中若有一个为零则结果为零,为消除这种影响,将初始值置0.5。即:

p0Num = ones(numWords); p1Num = ones(numWords)      p0Denom = 2.0; p1Denom = 2.0   

另一个问题就是下溢出,当计算p(w0|1)p(w1|1)….p(wn|1)时,由于每个概率都是很小的数,在计算多个乘积最终结果可能会下溢出。取自然对数是个不错的选择,而且不会对结果造成任何影响。

p1Vect = log(p1Num/p1Denom)         p0Vect = log(p0Num/p0Denom)          

朴素贝叶斯分类函数。vec2Classify*p1Vec得到了所有分类为1的词的对数概率,在对其求和根据对数的运算法则就得到了概率乘积的对数。再加上类别概率的对数,就得到了p(w0|1)p(w1|1)….p(wn|1)p(1) = p(w0,w1,…,wn,1)的对数值。分别比较p0和p1的大小就能判断出这些词是属于哪一类。

def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):    p1 = sum(vec2Classify*p1Vec) + log(pClass1)    p0 = sum(vec2Classify*p0Vec) + log(1 - pClass1)    if p1 > p0:        return 1    else:        return 0

定义一个测试函数,分别测试[‘love’,’my’,’dalmation’]和[‘stupid’,’garbage’]两组词。

def testingNB():    dataSet,classVec = loadDataSet()    voset = createVocabList(dataSet)    trainmat = []    for i in dataSet:        trainmat.append(setOfWords2Vec(voset,i))    p0,p1,pAb = trainNB0(trainmat,classVec)    testEntry = ['love','my','dalmation']    thisDoc = array(setOfWords2Vec(voset,testEntry))    print testEntry,'classified as:',classifyNB(thisDoc,p0,p1,pAb)    testEntry = ['stupid','garbage']    thisDoc = array(setOfWords2Vec(voset,testEntry))    print testEntry,'classified as:',classifyNB(thisDoc,p0,p1,pAb)

测试结果如下所示:

['love', 'my', 'dalmation'] classified as: 0['stupid', 'garbage'] classified as: 1

上述代码中setOfWords2Vec函数采用的是词集模型,即每个单词只出现一次,出现记1,未出现记0,但是实际中每个单词未必只出现一次,故要将词集模型修改为词袋模型(bagOfWords2Vec),其实和词集模型并没有太大不同,只是在计数时将置1改为加1。

def bagOfWords2Vec(vocabList, inputSet):    returnVec = [0]*len(vocabList)    for word in inputSet:        if word in vocabList:            returnVec[vocabList.index(word)] += 1    return returnVec
原创粉丝点击