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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/s291547/article/details/77658926

知识储备:

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

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

朴素贝叶斯假设: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)”

小结

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

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

猜你喜欢

转载自blog.csdn.net/s291547/article/details/77658926