zoukankan      html  css  js  c++  java
  • 机器学习实战-朴素贝叶斯

    使用python进行文本分类

    # -*- coding: utf-8 -*-
    """
    Created on Mon Aug 21 17:17:19 2017
    
    @author: rocky
    """
    
    #机器学习实战第四章
    #朴素bayes
    #使用python进行文本分类
    
    #从文本中构建词向量
    from numpy import *
    def loadDataSet():#创建一个实验样本
        postingList=[['my','dog','has','flea','problem','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 createVocalList(dataSet):
        vocabSet=set([])#set返回一个不含重复词的列表
        for document in dataSet:
            vocabSet=vocabSet|set(document)#创建两个集合的并集
        return list(vocabSet)
    
    def setOfWord2Vec(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
    #以上的三个程序的作用:
    #程序1返回几篇文档和文本类别,程序2对程序1进行非重复处理并拼接,返回一篇文档
    #程序3的输入分别对应程序2的结果和程序1中的第i篇文档,返回的结果是2的结果的每一个词
    #是否出现在第i篇文章中。出现则是1,没出现则是0.
    #这样我们就把一篇文章(在第i篇文档环境下)转换成了一组数字
    

    我们可以做这样的实验,在命令窗口输入以下:

    listOPosts,listClasses=loadDataSet()
    data=[['my','dog'],['is','rain']]
    
    lis=createVocalList(data)
    lis
    Out[22]: ['rain', 'my', 'is', 'dog']
    setOfWord2Vec(lis,listOPosts[5])
    the word: quit is not in my Vocabulary!
    the word: buying is not in my Vocabulary!
    the word: worthless is not in my Vocabulary!
    the word: food is not in my Vocabulary!
    the word: stupid is not in my Vocabulary!
    Out[23]: [0, 0, 0, 1]
    

    我们可以判断第六行的每个词是否出现在data中,从而得到data的数字表示,为后面对data进行概率计算(好坏属性判断)准备。

    训练算法:从词向量计算概率

    #训练算法:从词向量计算概率(假设二类分布,即只有两个属性)
    def trainNB0(trainMatrix,trainCategory):
        numTrainDocs=len(trainMatrix)#统计矩阵行数,即文档数
        numWords=len(trainMatrix[0])#统计列数,即每个文档的字数
        pAbusive=sum(trainCategory)/float(numTrainDocs)#计算文档属性的概率(P1,另一个就是P0)
        p0Num=zeros(numWords)#建立零向量,长度为文档字数
        p1Num=zeros(numWords)
        p0Denom=0.0
        p1Denom=0.0#以上是对概率的初始化
        for i in range(numTrainDocs):#循环每个文档
            if trainCategory[i]==1:#如果这个文档的属性是1
                p1Num+=trainMatrix[i]#把第i行数据(或者第i个文档)赋给p1Num
                p1Denom+=sum(trainMatrix[i])#求该文档中1的和,赋给。。。
            else:
                p0Num+=trainMatrix[i]
                p0Denom+=sum(trainMatrix[i])
        p1Vect=p1Num/p1Denom
        p0Vect=p0Num/p0Denom
        return p0Vect,p1Vect,pAbusive
    

    在命令行输入以下命令检验:

    #建立自己的数据集和类别,不使用上面的。
    data=[['to','my','worthless'],['dog','take','ate'],['stop','to','him']]
    
    listclass=[1,1,0]
    
    mylist=createVocalList(data)
    #mylist就是待检验的文档
    mylist
    Out[17]: ['to', 'worthless', 'take', 'ate', 'my', 'him', 'dog', 'stop']
    
    trainmat=[]
    #生成一个文本矩阵,代表着这个待检验文本在每个样本下的存在与否
    for postindoc in data:
        trainmat.append(setOfWord2Vec(mylist,postindoc))
      
    trainmat
    Out[21]: [[1, 1, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 1, 0], [1, 0, 0, 0, 0, 1, 0, 1]]
    #计算各个概率
    p0V,p1V,pAb=trainNB0(trainmat,listclass)
    
    pAb
    Out[23]: 0.66666666666666663
    
    p0V
    Out[24]: 
    array([ 0.33333333,  0.        ,  0.        ,  0.        ,  0.        ,
            0.33333333,  0.        ,  0.33333333])
    
    p1V
    Out[25]: 
    array([ 0.16666667,  0.16666667,  0.16666667,  0.16666667,  0.16666667,
            0.        ,  0.16666667,  0.        ])
       
    

    在trainNB0的循环中,循环参数是行数,也就是类别数,也就是3,而不是列数(8),这一点要注意。在上面自己的数据中,前两行类别都是1,那么经过i=0和i=1后,p1Num=(11111010)(两个向量相加),p1Denom=6(即3+3),经i=2后,p0Num=(10000101)(第三个向量),p0Denom=3。这样就可以计算概率了。
    以上计算的是文本中每个词在各个类别下出现的概率。

    测试算法,修改分类器

    上面计算了各个词在每个类别下的概率,下面就要计算多个概率的乘积来获得文档属于某个类别的概率。那么如果一个词的概率为0,总结果就是0,这显然不行。所以进行简单处理,所有词的初始出现数为1,分母初始化2,修改上述程序。修改p0Nump0Denomp1Nump1Denom的初值,以及最后的概率计算,改为对数运算。

    def trainNB0(trainMatrix,trainCategory):
        numTrainDocs=len(trainMatrix)#统计矩阵行数,即文档数
        numWords=len(trainMatrix[0])#统计列数,即每个文档的字数
        pAbusive=sum(trainCategory)/float(numTrainDocs)#计算文档属性的概率(P1,另一个就是P0)
        p0Num=ones(numWords)#建立o向量,长度为文档字数
        p1Num=ones(numWords)
        p0Denom=2.0
        p1Denom=2.0#以上是对概率的初始化
        for i in range(numTrainDocs):#循环每个文档
            if trainCategory[i]==1:#如果这个文档的属性是1
                p1Num+=trainMatrix[i]#把第i行数据(或者第i个文档)赋给p1Num
                p1Denom+=sum(trainMatrix[i])#求该文档中1的和,赋给。。。
            else:
                p0Num+=trainMatrix[i]
                p0Denom+=sum(trainMatrix[i])
        p1Vect=log(p1Num/p1Denom)
        p0Vect=log(p0Num/p0Denom)
        return p0Vect,p1Vect,pAbusive
        
    

    下面构建完整的分类器。

    #创建朴素贝叶斯分类函数
    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=createVocalList(listOPosts)
        trainMat=[]
        for postinDoc in listOPosts:
            trainMat.append(setOfWord2Vec(myVocabList,postinDoc))
        p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses))
        testEntry=['love','my','dalmation']
        thisDoc1=array(setOfWord2Vec(myVocabList,testEntry))
        #上面这一句的意思是检查myVocabList中的文字是否出现在testEntry中
        print(testEntry,'classified as: ',classifyNB(thisDoc1,p0V,p1V,pAb))
        
        testEntry=['stupid','garbage']
        thisDoc2=array(setOfWord2Vec(myVocabList,testEntry))
        print(testEntry,'classified as: ',classifyNB(thisDoc2,p0V,p1V,pAb))
        return thisDoc1,thisDoc2
    
        
    

    最后的return thisDoc1,thisDoc2是自己加的,检查结果。后一个函数是封装了一些操作。命令窗口输入以下:

    testingNB()
    ['love', 'my', 'dalmation'] classified as:  0
    ['stupid', 'garbage'] classified as:  1
    Out[31]: (array([0, 0, 0, ..., 1, 0, 0]), array([0, 0, 0, ..., 0, 1, 0]))
    

    我们可以看到,thisDoc1结果是([0, 0, 0, ..., 1, 0, 0]),中间有一些省略号,看不到中间是什么数据,这时我们可以在开头定义numpy的下面,输入一行'np.set_printoptions(threshold=np.inf)',可以保证数据全部显示,另外我也将myVocabList输出(return稍作修改即可):

    testingNB()
    ['love', 'my', 'dalmation'] classified as:  0
    ['stupid', 'garbage'] classified as:  1
    Out[42]: 
    (array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
            0, 0, 0, 0, 0, 0, 1, 0, 0]),
     array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 1, 0, 0, 0, 0, 1, 0]),
    ['to',
      'worthless',
      'ate',
      'quit',
      'stop',
      'steak',
      'take',
      'has',
      'I',
      'problem',
      'park',
      'dog',
      'so',
      'love',
      'is',
      'posting',
      'flea',
      'not',
      'help',
      'maybe',
      'my',
      'licks',
      'food',
      'cute',
      'buying',
      'stupid',
      'how',
      'please',
      'him',
      'dalmation',
      'garbage',
      'mr'])
    

    以上我们可以看到['love', 'my', 'dalmation']的出现,用1表示,而之前我们得到了一个概率分布,这样thisDoc1(thisDoc2)表示待测文本在当前环境下的表示(仍保持thisDoc1的意义),这样再利用函数classifyNB就可以计算thisDoc1是否属于侮辱性文档的概率。

    注:处理垃圾邮件尚未学习,等回顾上述思路待后续。

  • 相关阅读:
    Github国内mirror加速
    通过node-inspector或VSCode调试服务器上代码
    node nvm 常见命令
    HBuilderX 修改默认的终端
    LLVM 工具使用
    LLVM 获取Value Type
    LLVM Constant Value to c++ value
    Bison 命名位置
    llvm block
    vscode use cl.exe build C/C++
  • 原文地址:https://www.cnblogs.com/rayshaw/p/8531487.html
Copyright © 2011-2022 走看看