• 基于概率论的分类方法:朴素贝叶斯


    #基于概率论的分类方法:朴素贝叶斯
    '''
    优点:在数据较少的情况下任然有效,可以处理多类别问题。
    缺点:对于输入数据的准备方式较为敏感。
    适用数据类型:标称型数据
    
    贝叶斯概率引入先验知识和逻辑推理来处理不确定命题。
    
    朴素贝叶斯的一般过程:
        1.收集数据:可以使用任何方法。本章使用RSS源。
        2.准备数据:需要数值型或布尔型数据。
        3.分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。
        4.训练算法:计算不同的独立特征的条件概率。
        5.测试算法:计算错误率。
        6.一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。
    '''
    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 代表侮辱性文字, 0代表正常言论
        return postingList,classVec
    
    def createVocabList(dataSet):
        #创建词表变量
        vocabSet = set([])
        for document in dataSet:
            #创建两个集合的并集
            vocabSet = vocabSet | set(document)
        return list(vocabSet)
    
    def setOfWords2Vec(vocabList, inputSet):
        #创建一个其中所含元素都为0的向量
        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
    
    #朴素贝叶斯训练算法伪代码
    '''
    计算每个类别中的文档数目
    对每篇训练文档:
        对每个类别:
            如果词条出现在文档中->增加该词条的计数值
            增加所有词条的计数值
    对每个类别:
        对每个词条:
            将该词条的数目除以总词条数目得到条件概率
    返回每个类别的条件概率
    '''
    def trainNB0(trainMatrix, trainCategory):
        numTrainDocs = len(trainMatrix)
        numWords = len(trainMatrix[0])
        #侮辱性评论 概率
        pAbusive = sum(trainCategory)/float(numTrainDocs) #sum(list)= 0+1+0+1+0+1 
        #初始化概率
        #p0Num = zeros(numWords); p1Num = zeros(numWords)    #分子,词的出现次数
        #p0Denom = 0.0; p1Denom = 0.0                        #分母
        #利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,如果
        #其中一个概率值为0,那么左后的乘积也为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
        
        p1Vect = log(p1Num/p1Denom)
        p0Vect = log(p0Num/p0Denom)
        return p0Vect,p1Vect,pAbusive
    
    #根据实际情况修改分类器,主要修改上面接口trainNB0() 内的初始化概率大的分子分母和元素做除法
    '''
    1.利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,如果其中一个概率值为0,那么左后的乘积也为0。
        为降低这种影响,可以将所有词的出现次数初始化为1,并将分母初始化为2
    2.另一个问题是 下溢出,由于太多很小的数相乘造成的。一种解决办法是对乘积取自然对数。ln(a*b) = ln(a)+ln(b)
    '''
    
    #朴素贝叶斯分类函数
    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 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))
        print(testEntry, 'classified as:', classifyNB(thisDoc, p0V, p1V, pAb))
        testEntry = ['stupid', 'garbage']
        thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
        print(testEntry, 'classified as:', classifyNB(thisDoc, p0V, p1V, pAb))
    
    
    #文档词袋模型
    '''
    目前为止,我们将每个词测出现与否作为一个特征,这可以被描述为词集模型(set-of-words model)。
    如果一个词在文档中出现不止一次,,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法
    称为词袋模型(bag-of-words model).在词袋中,每个单词可以出现多次,而词集中,每个词智能出现一次。
    我们对代码做些修改,使分类器工作得更好。
    '''
    def bagOfWords2vecMN(vocabList, inputSet):
        returnVec = [0]*len(vocabList)
        for word in inputSet:
            if word in vocabList:
                returnVec[vocabList.index(word)] += 1
        return returnVec
    
    #示例:使用朴素贝叶斯过滤垃圾邮件
    def textParse(bigString):
        import re
        listOfTokens = re.split(r'W+', bigString)
        return [tok.lower() for tok in listOfTokens if len(tok) > 2]
    
    def spamTest():
        docList=[]; classList=[]; fullText=[]
        #导入并解析文本文件
        for i in range(1,26):
            '''
            #原始文件email/ham/6.txt及email/spam/17.txt有个字符存在乱码,删除乱码就好了
            try:
                wordList=textParse(open('email/spam/%d.txt' % i,encoding='UTF-8' ).read())
            except UnicodeDecodeError:
                print(i)    
            '''
            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)
        trainingSet = list(range(50)); testSet=[]
        for i in range(10):
            #随机构建训练集
            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 ('the error rate is:',float(errorCount)/len(testSet))
    
    
    if __name__ == '__main__':
        '''
        #从文本中构建词向量
        listOPosts,listClasses = loadDataSet()
        myVocabList = createVocabList(listOPosts)
        print(myVocabList)
        retVec0 = setOfWords2Vec(myVocabList, listOPosts[0])
        print (retVec0)
        '''
    
        '''
        #从词向量计算概率
        listOPosts,listClasses = loadDataSet()
        myVocabList = createVocabList(listOPosts)
        trainMat=[]
        for postinDoc in listOPosts:
            trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
        p0V,p1V,pAb=trainNB0(trainMat,listClasses)
        print('词表:',myVocabList)
        print('正常评论各词出现的频率:',p0V)
        print('侮辱性评论各词出现的频率:',p1V)
        print('侮辱类评论出现的概率:',pAb)
        '''
    
        #示例1.测试 恶意留言 分类器效果
        #testingNB()
    
        示例2.测试 垃圾邮件 分类效果
        #spamTest()
    
    
    
    '''
    对于分类而言,使用概率有时比使用硬规则更为有效。贝叶斯概率及贝叶斯准则提供了一种利用已知值来
    估计未知概率的有效方法。
    可以通过特征之间的条件独立性假设,降低对数据量的需求。独立性假设是指一个词的出现概率并不依赖于
    文档中的其他词。当然我们也知道这种假设过于简单。这就是之所以称为朴素贝叶斯的原因。尽管条件独立
    性假设并不正确,但是朴素贝叶斯任然是一种有效的分类器。
    '''

    邮件训练测试链接:https://pan.baidu.com/s/1R8tXk4E_1RzoGowzZzKJ2g
    提取码:af2o

  • 相关阅读:
    递归函数写法
    海量数据问题总结
    文本分类项目总结
    梯度提升树-负梯度和残差的理解
    正则化方法L1 L2
    c++-虚函数与多态
    数据结构-并查集
    剑指offer 面试63题
    剑指offer 面试62题
    剑指offer 面试60题
  • 原文地址:https://www.cnblogs.com/go-ahead-wsg/p/13453288.html
Copyright © 2020-2023  润新知