`
鬼大来晚了
  • 浏览: 66042 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

《机器学习实战》4:朴素贝叶斯

 
阅读更多

之前的两种算法都需要分类器指出实例究竟属于什么类别。这次讲到的贝叶斯算法,是从概率的角度进行分类的。

1、条件概率:

p(A|B)=p(AB)/p(B)

也就是在B的条件下A出现的概率。

交换条件中的条件与结果:

p(B|A)=p(A|B)*p(B)/p(A)

2、朴素贝叶斯算法的两个假设:

(1)每个特征之间都是独立的,这就使得公式:

p((f1,f2,...fn)|c)=p(f1|c)p(f2|c)...p(fn|c)

(2)每个特征同等重要,我们拿文本分类做例子,把文档中的单词作为特征。这种假设使得我们在进行分类的过程中无需考虑单词出现的次数,只考虑单词出现与否。这也就贝叶斯算法的贝努利模型实现方式。

注:贝叶斯的另一种实现方式为多项式模型,在这种模型中则需要考虑单词的出现次数。我们在后面会进行介绍。

3、输入数据

依照机器学习的步骤,首先是准备输入数据。我们同样生成一个简单的训练数据。

新建文件bayes.py,编辑代码如下:

 

#产生训练数据
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']]
    #标注每条数据的分类,这里0表示正常言论,1表示侮辱性留言
    classVec = [0,1,0,1,0,1]    
    return postingList,classVec[/code]

 

 

4、接下来我们就要统计文档中的单词种类了,也就是数据集中的特征。编辑如下代码:

 

 

#建立词汇表
def createVocabList(dataSet):
    #首先建立一个空集
    vocabSet=set([])
    #遍历数据集中的每条数据
    for document in dataSet:
        #这里就显示出了python的强大
        #这条语句中首先统计了每条数据的词汇集,然后与总的词汇表求并集
        vocabSet=vocabSet|set(document)
    return list(vocabSet)

 

 

 

有了词汇表,我们就可以通过词汇表对输入的数据进行分析了。我们构建一个函数分析输入数据。该函数的输入参数为:词汇表及数据词条。输出为何词汇条同样大小的向量,其中向量中的值非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

 接下来,看一下函数的执行效果:

 

 

5、接下来我们就要实现贝叶斯算法了,让我们再次回想刚才的条件概率公式:

p(B|A)=p(A|B)*p(B)/p(A)

 

对于我们的问题可以写为:

p(ci|w)=p(w|ci)*p(ci)/p(w)

这里的w表示一个向量,即将文本解析之后的向量。利用第二个假设,公式可以写为:

p(ci|w)=(p(w1|ci)p(w2|ci)...p(wn|ci))*p(ci)/p(w)

 

首先说一下,我们在程序中要做的一些近似。由于有的单词的数目为0,则P(Wi|Ci)=0,那么会造成p(ci|w)=(p(w1|ci)p(w2|ci)...p(wn|ci))*p(ci)/p(w)=0,这样就会影响概率结果,因此我们在初始化的过程,将所有单词的初始化数目设为1。

另一问题就是下溢出,每个概率可能都很小,那么相乘之后就更小,会造成四舍五入之后为0,解决这个问题的办法是我们对概率取对数。

 

下面我们就通过代码实现这个概率:

#朴素贝叶斯分类器训练函数
#输入参数trainMatrix表示输入的文档矩阵,trainCategory表示每篇文档类别标签所

构成的向量
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...else...即可,如果分类较多,则需要更改代码
        if trainCategory[i]==1:
            p1Num+=trainMatrix[i]
            p1Denom+=sum(trainMatrix[i])
        else:
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])

    p1Vec=log(p1Num/p1Denom)
    p0Vec=log(p0Num/p0Denom)
    #函数返回两个概率向量,及一个概率
    return p0Vec,p1Vec,pAbusive

 

 

测试函数:



 

有了之前的所有准备,我们写贝叶斯算法就非常简单了。

 

#朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass):
    p1=sum(vec2Classify*p1Vec)+log(pClass)
    p0=sum(vec2Classify*p0Vec)+log(1-pClass)
    if p1>p0:
        return 1;
    else:
        return 0;

 

我们在程序里再写一个内嵌的测试函数:

 

#内嵌测试函数
def testingNB():
    listOPosts,listClasses=loadDataSet()
    myVocabList=createVocabList(listOPosts)
    trainMat=[]
    for postinDoc in listOPosts:
      trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
    p0V,p1V,p1=trainNB0(trainMat,listClasses)
    testEntry=['love','my','dalmation']
    thisDoc=setOfWords2Vec(myVocabList,testEntry)
    print testEntry,"classified as:",classifyNB(thisDoc,p0V,p1V,p1)
    testEntry=['garbage','stupid']
    thisDoc=setOfWords2Vec(myVocabList,testEntry)
    print testEntry,"classified as:",classifyNB(thisDoc,p0V,p1V,p1)

 

测试算法:

 

 

6、之前的算法我们只考虑了单词出现与否,使用的是一种词集模型。贝叶斯有两种实现方式,另一种多项式模型,需要考虑每个单词出现的次数,就是所谓的词袋模型。为了适应这种词袋模型,我们需要对函数setOfWords2Vec作一下修改:

#词袋模型的转换函数
def bagOfWords2VecMN(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

 

 

好了,至此我们完成了整个分类器的实现.接下来我们同样适用一个实例,来测试我们的分类器。

7、使用朴素贝叶斯进行垃圾邮件过滤

(1)准备输入数据:我们读取两个邮件文件夹中的内容,并 把其中的每行都切分成可处理的单词。在文件中继续编辑如下代码,实现切分文本。

#该函数将每个句子都解析成单词,并忽略空格,标点符号以及长度小于3的单词
def textParse(bigString):
    import re
    listOfTokens=re.split(r'\W*',bigString)
    return [tok.lower() for tok in listOfTokens if len(tok)>2]

(2)有了可以使用的输入数据类型,接下来我们就可以利用我们之前的算法进行邮件过滤了,继续编辑代码:

#检测垃圾邮件
def spamTest():
    #存放输入数据
    docList=[]
    #存放类别标签
    classList=[]
    #所有的文本
    fullText=[]
    #分别读取邮件内容
    for i in range(1,26):
        wordList=textParse(open('email/spam/%d.txt'%i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList=textParse(open('email/ham/%d.txt'%i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList=createVocabList(docList)
    #range(50)表示从0到50,不包括50
    trainingSet=range(50)
    #测试集    
    testSet=[]
    #随机抽取是个作为测试集
    for i in range(10):
        #从50个数据集中随机选取十个作为测试集,并把其从训练集中删除
        randIndex=int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat=[];trainClasses=[];
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
        trainClasses.append(classList[docIndex])
    #使用训练集得到概率向量
    p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClasses))
    #测试分类器的错误率
    errorCount=0
    for docIndex in testSet:
        wordVector=setOfWords2Vec(vocabList,docList[docIndex])
        if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:
            errorCount+=1
            print "Classification error:"
            print docList[docIndex]
    print errorCount
    print "the error rate is:",float(errorCount)/len(testSet)

 

测试结果:



 

经过多次测试,我们发现分类器的分类准确率还是很高了。而且,通过输出结果我们发现大部分时候分类器把垃圾邮件划分到正常邮件中了,这种要比把正常邮件划分为垃圾邮件好得多。

 

好了,后续的章节还会进行分类器的优化。这一节的内容就已经讲完了。另外一个例子涉及到RSS源就不在这里说了。

 

总之,希望自己能渐渐进步!

 

 

 

 

  • 大小: 14.6 KB
  • 大小: 5.9 KB
  • 大小: 27.9 KB
  • 大小: 5.1 KB
  • 大小: 15.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics