《机器学习实战》代码片段学习3 朴素贝叶斯

来源:互联网 发布:java方法重载意义 编辑:程序博客网 时间:2024/05/20 08:41

知识储备:

贝叶斯决策核心思想:选择具有最高概率的决策。

贝叶斯准则:
这里写图片描述
利用贝叶斯准则我们可以交换条件概率中的条件与结果。

朴素贝叶斯假设:1.每个特征相互独立。2.每个特征同等重要。

朴素贝叶斯分类器的优缺点:
优点:在数据较少的情况下仍然有效,可以处理多类别问题。
缺点:对于输入数据的准备方式较为敏感。
适用数据类型:标称型数据。

例子中朴素贝叶斯分类器的工作流程:
1.获取训练用的文档集合list以及保存了文档分类结果的向量listClasses。
2.从所有文档中建立不重复的词汇列表VocabList。
3.将每一文档转换为文档向量,建立所有文档向量组成的矩阵trainMat
4.利用trainMat与listClasses训练朴素贝叶斯分类器,获取参数p0v,p1v,pAb
5.利用训练好的分类器对未知样本进行分类。

代码学习:

词表到向量的转换函数:

#创建在文档中出现的不重复词的列表def createVocabList(dataSet):    vocabSet = set([])  #create empty set    for document in dataSet:        vocabSet = vocabSet | set(document) #union of the two sets求并集    return list(vocabSet)#创建与词汇表等长的所有元素都为0的向量def setOfWords2Vec(vocabList, inputSet):    returnVec = [0]*len(vocabList)    for word in inputSet:        if word in vocabList:            #使用list.index()取索引            returnVec[vocabList.index(word)] = 1        else: print "the word: %s is not in my Vocabulary!" % word    return returnVec

重写以方便编程的贝叶斯准则:
这里写图片描述

其中w粗体表示向量,本例中向量包含的数值个数与词汇列表长度相同。

朴素贝叶斯分类器训练函数:

#trainMatrix为文档矩阵,其中每一行由上例的setOfWords2Vec()生成,记录文档中对词汇列表中词语的包含情况,0为该文档不包含词汇列表中对应的词语,1为包含#trainCategory为记录每篇文档类别标签的列表,本例中记录每篇文档是否包含侮辱性词汇的情况,0即为对应文档没有侮辱性词汇,1为对应文档包含侮辱性词汇。#值得注意的是,trainMatrix每一行长度与trainCategory相同,都为词汇列表的长度,即为词汇列表中词的个数。def trainNB0(trainMatrix,trainCategory):    numTrainDocs = len(trainMatrix)  #训练文档数    numWords = len(trainMatrix[0])  #词列表的长度,即不重复的词的数量    pAbusive = sum(trainCategory)/float(numTrainDocs)  #p(ci)    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

朴素贝叶斯分类器:

#vec2Classify为需要分类的向量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将词集模型修改为词袋模型:def bagOfWords2VecMN(vocabList, inputSet):    returnVec = [0]*len(vocabList)    for word in inputSet:    if word in vocabList:        #累计计算词语的出现次数        returnVec[vocabList.index(word)] += 1    return returnVec

在分词部分,书上为了去掉除数字与单词以外的部分书中结合正则表达式使用了split()方法进行分词,并在后期通过判断分词后的长度是否大于0来去掉空格,通过.lower()方法将单词全部转换为小写。具体代码在以下的测试函数里有。实际分词操作应该结合停用词表比较好,中文分词还得使用专业的分词程序,例如结巴分词。

完整的垃圾邮件测试函数,使用了留存交叉验证的方法进行测试:

def textParse(bigString):    import re    #正则表达式分词,\W匹配任何非单词字符    listOfTokens = re.split(r'\W*', bigString)      #又是这种酷炫的列表操作方法:小写转换、去掉长度小于2的分词结果,    return [tok.lower() for tok in listOfTokens if len(tok) > 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) #create vocabulary    trainingSet = range(50); testSet=[]      #随机选取测试集    for i in range(10):        #random.uniform()从0到len(trainingSet)中随机取样        randIndex = int(random.uniform(0,len(trainingSet)))          testSet.append(trainingSet[randIndex])        del(trainingSet[randIndex])   #从训练集中删除之    trainMat=[]; trainClasses = []    for docIndex in trainingSet:        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))        trainClasses.append(classList[docIndex])    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))    errorCount = 0    #计算错误率    for docIndex in testSet:  #classify the remaining items        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])        if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:            errorCount += 1            print "classification error",docList[docIndex]    print 'the error rate is: ',float(errorCount)/len(testSet)    #return vocabList,fullText

书中关于留存交叉验证的说明:“这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation)”

小结

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

编程实现朴素贝叶斯分类器时需要考虑的一些问题:“利用现代编程语言来实现朴素贝叶斯时需要考虑很多实际因素。下溢出就是其中一个问题,它可以通过对概率取对数来解决。词袋模型在解决文档分类问题上比词集模型有所提高。还有其他一些方面的改进,比如说移除停用词,当然也可以花大量时间对切分器进行优化。”