zoukankan      html  css  js  c++  java
  • 朴素贝叶斯分类器

    1、加载训练数据集,用于训练分类器

    #加载数据集,用于训练分类器
    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 is abusive, 0 not
        return postingList,classVec

    2、去除训练数据集的重复词,并且添加到一个列表中

    #处理数据集,去除所有分词向量的重复词,添加到一个列表中
    #先创建一个集合筛选出不重复的词向量,然后转为列表
    def createVocabList(dataSet):
        #这里的集合里面也是列表
        vocabSet = set([])  #create empty set
        for document in dataSet:
            #利用并集将每个向量添加到set集合中
            vocabSet = vocabSet | set(document) #union of the two sets
        return list(vocabSet)
    
    if __name__ == '__main__':
        postingList,classVec = loadDataSet()
        print(postingList,classVec)
        vocaList = createVocabList(postingList)
        print(vocaList)
    '''
    ['food', 'garbage', 'to', 'has', 'my', 'maybe', 'stop', 'so', 'him', 'posting', 'cute', 'flea', 'quit', 'help', 'I', 'love', 'park', 'stupid', 'is', 'how', 'problems', 'dog', 'licks', 'dalmation', 'steak', 'take', 'please', 'worthless', 'mr', 'not', 'buying', 'ate']
    '''
    

    3、将输入文本转化为0/1向量

    #统计待分类的词向量在词列表中是否出现
    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
    
    if __name__ == '__main__':
        postingList,classVec = loadDataSet()
        vocaList = createVocabList(postingList)
        returnVec = setOfWords2Vec(vocaList,postingList[0])
        print(returnVec)
    '''[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0]'''
    

    4、计算输入文档属于侮辱性文档的概率p(ci),以及在已知该文档属于侮辱性文档的情况下,计算每个单词属于侮辱性的概率P(w/ci)

    #朴素贝叶斯分类器训练函数,根据训练数据集训练得到分类器
    #trainMatrix 所有文档的词条向量
    #trainCategory 每篇文档对应的类别标签向量组成的矩阵
    def trainNB0(trainMatrix, trainCategory):
        #文档数目
        numTrainDocs = len(trainMatrix)
        #每篇文档的单词数
        numWords = len(trainMatrix[0])
        #侮辱性文档占总文档数的比例(因为侮辱性文档标签为1,加起来就是文档总数)
        pAbusive = sum(trainCategory) / float(numTrainDocs)
        p0Num = zeros(numWords)
        p1Num = zeros(numWords)  # change to ones()
        p0Denom = 0.0
        p1Denom = 0.0  # change to 2.0
        for i in range(numTrainDocs):
            #如果该文档属于类别1
            if trainCategory[i] == 1:
                #将该文档的词向量添加到向量p1Num中
                #p1Num向量为1行numWords列
                #最后p1Num为所有属于类别1的所有文档的词向量之和
                #因为这里的输入文本trainMatrix[i]已经转换为了0/1向量
                #eg:[0,0,0,0,0,0,0,0] + [1,0,1,1,0,0,0,0]+[1,1,1,1,0,0,0,0]+......
                #这里的p1Num为已知文档类型,求各个单词在词库中出现的总数
                p1Num += trainMatrix[i]
                #侮辱类的总单词数
                p1Denom += sum(trainMatrix[i])
            else:
                #否则如果是类别0,则将该文档的词向量添加到向量p0Num
                p0Num += trainMatrix[i]
                #然后将该文档的所有词向量加起来给p0Num
                p0Denom += sum(trainMatrix[i])
        #p1Vect也是一个向量
        p1Vect = p1Num / p1Denom  # change to log()
        p0Vect = p0Num / p0Denom  # change to log()
        #pAbusive 侮辱性文档占所有文档数的概率
        #p0Vect  已知该文档属于侮辱性文档的情况下,词汇表中的每个单词属于侮辱性的概率
        #p1Vect  已知该文档属于侮辱性文档的情况下,词汇表中的每个单词不属于侮辱性的概率
        return p0Vect, p1Vect, pAbusive
    

    5、针对第四步需要对两个地方进行优化

       p0Num = ones(numWords)
        p1Num = ones(numWords)  # change to ones()
        p0Denom = 2.0
        p1Denom = 2.0  # change to 2.0

    采用拉普拉斯平滑,在分子上添加a(一般为1),分母上添加ka(k表示类别总数),即在这里将所有词的出现数初始化为1,并将分母初始化为2*1=2

        p1Vect = [math.log(x) for x in (p1Num / p1Denom)]
        p0Vect = [math.log(x) for x in (p0Num / p0Denom)]

    由于有的单词出现的概率很小,导致乘积在四舍五入的情况下导致下溢出

    if __name__ == '__main__':
        postingList,classVec = loadDataSet()
        print(postingList,classVec)
        vocaList = createVocabList(postingList)
        print(vocaList)  
        returnVec = setOfWords2Vec(vocaList,postingList[0])
        #trainMat 所有的文档矩阵
        trainMat = []
        for postDoc in postingList:
            trainMat.append(setOfWords2Vec(vocaList,postDoc))
        print(trainMat)
        p0Vect, p1Vect, pAbusive = trainNB0(trainMat, classVec)
        print('已知该文档属于侮辱性文档的情况下,词汇表中的每个单词属于侮辱性的概率:',p0Vect)
        print('已知该文档属于侮辱性文档的情况下,词汇表中的每个单词不属于侮辱性的概率:', p1Vect)
        print('侮辱性文档占所有文档数的概率:',pAbusive)
     
    

    6、根据trainNB0()函数返回的p(wi|c0),p(wi/c1),p(c1) 判断待分类文档是否属于污蔑行文档

    #@vec2Classify:待测试分类的词条向量
    #@p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0)
    #@p1Vec:类别1所有文档中各个词条出现的频数p(wi|c1)
    #@pClass1:类别为1的文档占文档总数比例p(c1)
    
    def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
        # 根据朴素贝叶斯分类函数分别计算待分类文档属于类1和类0的概率
        #vec2Classify * p1Vec得到的是p(w/ci)
        #由于贝叶斯p(w/c)=p(w1,w2,w3..../c1)=p(w1)/p(c1) * p(w2)/p(c1) * .....
        #由于这里的每一项p(w1)/p(c1)是一个对数,所以最后需要加起来
        #最后由公式:p(w/c)*p(c) 还需要乘以p(c1)  对于对数而言,就是相加
        #最后根据公式p(c/w) = p(w/c)*p(c)  /   p(w)
        #p1就是该文档属于污蔑性文档的概率
        p1 = sum(vec2Classify * p1Vec) + log(pClass1)  # element-wise mult
        #p0就是该文档不属于污蔑行文档的概率
        p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
        if p1 > p0:
            return 1
        else:
            return 0

    7、封装函数的所有操作,便于分类测试

    def testingNB():
        #获取训练数据集的文档矩阵,和类标签矩阵
        listOPosts, listClasses = loadDataSet()
        #将文档矩阵转换为一个不重复的列表
        myVocabList = createVocabList(listOPosts)
        trainMat = []
        for postinDoc in listOPosts:
            #根据postinDoc是否在myVocabList,将postinDoc转换为0/1向量
            trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
        #将文档矩阵和类标签向量转为数组,利用trainNB0()得到
        '''#@p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0)
            #@p1Vec:类别1所有文档中各个词条出现的频数p(wi|c1)
            #@pClass1:类别为1的文档占文档总数比例p(c1)'''
        p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))
        #测试文档
        testEntry = ['love', 'my', 'dalmation']
        #将测试文档转换为0/1向量矩阵,并且转为数组的形式
        thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
        #对测试文档进行分类
        print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
        #第二个测试文档
        testEntry = ['stupid', 'garbage']
        #转换为0/1词条向量
        thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
        print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    
    if __name__ == '__main__':
        postingList,classVec = loadDataSet()
        print(postingList,classVec)
        vocaList = createVocabList(postingList)
        print(vocaList)  
        returnVec = setOfWords2Vec(vocaList,postingList[0])
        #trainMat 所有的文档矩阵
        trainMat = []
        for postDoc in postingList:
            trainMat.append(setOfWords2Vec(vocaList,postDoc))
        p0Vect, p1Vect, pAbusive = trainNB0(trainMat, classVec)
        testingNB()
    '''
        ['love', 'my', 'dalmation'] classified as:  0
        ['stupid', 'garbage'] classified as:  1
        '''
    

    8、优化特征的标准:从根据每个词是否出现为特征(文档词集模型)转为---》统计每个词出现的次数为特征

    #文档词袋模型,从根据每个词是否出现为特征(文档词集模型)转为---》统计每个词出现的次数为特征
    def bagOfWords2VecMN(vocabList, inputSet):
        #建立一个和词集列表等长的行向量,初始化每个词的出现次数为0
        returnVec = [0] * len(vocabList)
        #遍历输入集统计输入集中的单词在词列表中出现的次数
        for word in inputSet:
            if word in vocabList:
                #vocabList.index(word)返回word在vocablist的索引值
                returnVec[vocabList.index(word)] += 1
        return returnVec

    为此朴素贝叶斯分类器就完全实现了

    接下来利用我们构建的分类器过滤垃圾邮件

    9、准备数据,切分文本

    def textParse(bigString):  # input is big string, #output is word list
        import re
        #这里的r表示原生字符串 分隔规则:匹配任意字母数字下划线 进行分隔
        listOfTokens = re.split(r'W*', bigString)
        #选出长度大于2的字符串并且转化为小写
        return [tok.lower() for tok in listOfTokens if len(tok) > 2]

    10、垃圾邮件测试函数

    def spamTest():
        docList = [];
        classList = [];
        fullText = []
        for i in range(1, 26):
            #依次打开spam目录下的每个txt文件,并且读取内容
            #按照分隔规则,将文本进行切割,得到一个列表
            wordList = textParse(open('email/spam/%d.txt' % i).read())
            #将每个切割后的文本作为一个整体的列表添加到doclist列表里面
            docList.append(wordList)
            #将所有切割后的文本添加具体的元素内容到fullText
            fullText.extend(wordList)
            #标签列表添加标签1
            classList.append(1)
            #打开ham文件夹下的所有txt文件,读取内容
            #进行切割文本
            wordList = textParse(open('email/ham/%d.txt' % i).read())
            #将每个文本作为整体加入到wordlist
            docList.append(wordList)
            #将每个文本的内容加入到wordlist
            fullText.extend(wordList)
            #标签列表添加标签0
            classList.append(0)
        #处理数据集doclist,去除重复,并且将所有的文本单词向量添加到一个列表
        vocabList = createVocabList(docList)  # create vocabulary
        #0-49
        trainingSet = range(50);
        testSet = []  # create test set
        #0-9
        #从trainingset集合里面随机选择10个数添加到testset列表里面
        for i in range(10):
            #uniform() 方法将随机生成下一个实数,它在 [x, y) 范围内。
            #random.uniform()返回一个浮点数
            #获取随机下标
            randIndex = int(random.uniform(0, len(trainingSet)))
            testSet.append(trainingSet[randIndex])
            #删除选出的随机数,防止下次再次选出
            del (trainingSet[randIndex])
    
        trainMat = [];
        trainClasses = []
        #遍历剩下的trainingSet的40个整数
        for docIndex in trainingSet:  # train the classifier (get probs) trainNB0
            #遍历doclist的40个文本,统计他们在词典vocabList每个单词出现的次数
            #将每个文本对应的词典次数向量添加到trainmat
            trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
            #将40个文本对应标签也添加到trainClasses标签列表里面
            trainClasses.append(classList[docIndex])
        #将所有文本对应的词袋向量以及标签矩阵转为数组计算概率
        p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
        errorCount = 0
        #遍历选出的10个测试整数
        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
    

    到这里垃圾邮件的过滤也已经完成了

    下面展示贝叶斯另外一个功能根据个人广告获取区域倾向

    1、统计高频词

    #统计高频词
    def calcMostFreq(vocabList,fullText):
        import operator
        #创建一个空字典,用于装取词典里面每个单词在fulltext里面出现的次数
        freqDict = {}
        for token in vocabList:
            #直接使用count()函数统计文本fulltext中具体单词出现的次数
            freqDict[token]=fullText.count(token)
        #freqDict.iteritems()返回一个迭代器
        #key=operator.itemgetter(1)根据字典的第二个域排序:也就是单词出现的次数
        #参考博客:http://blog.csdn.net/dongtingzhizi/article/details/12068205
        '''orted(iterable[, cmp[, key[, reverse]]])
            参数解释:
            (1)iterable指定要排序的list或者iterable,不用多说;
            (2)cmp为函数,指定排序时进行比较的函数,可以指定一个函数或者lambda函数
            reverse参数就不用多说了,是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时'''
        sortedFreq = sorted(freqDict.iteritems(), key=operator.itemgetter(1), reverse=True)
        #返回前30个单词
        return sortedFreq[:30]
    欢迎关注我的公众号:小秋的博客 CSDN博客:https://blog.csdn.net/xiaoqiu_cr github:https://github.com/crr121 联系邮箱:rongchen633@gmail.com 有什么问题可以给我留言噢~
  • 相关阅读:
    你的C/C++程序为什么无法运行?揭秘Segmentation fault (2)
    亲,这就是遗传算法
    我们为什么需要Map-Reduce?
    搜索引擎-架构概述(2)
    搜索引擎-架构概述(1)
    单源最短路径-迪杰斯特拉算法(Dijkstra's algorithm)
    最小生成树-普利姆算法eager实现
    最小生成树-普利姆算法lazy实现
    最小生成树-克鲁斯卡尔算法(kruskal's algorithm)实现
    索引式优先队列(indexed priority queue)
  • 原文地址:https://www.cnblogs.com/flyingcr/p/10327073.html
Copyright © 2011-2022 走看看