当前位置:   article > 正文

机器学习笔记(三)之朴素贝叶斯_编写 textparse()函数的代码并且编译运行,该函数用于接收一个大字符串并将其解析

编写 textparse()函数的代码并且编译运行,该函数用于接收一个大字符串并将其解析

朴素贝叶斯

朴素贝叶斯算法仍然是流行的十大挖掘算法之一,该算法是有监督的学习算法,解决的是分类问题,如客户是否流失、是否值得投资、信用等级评定等多分类问题。该算法的优点在于简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。但由于该算法以自变量之间的独立(条件特征独立)性和连续变量的正态性假设为前提,就会导致算法精度在某种程度上受影响。接下来我们就详细介绍该算法的知识点及实际应用。

数学模型

贝叶斯公式

贝叶斯定理由英国数学家贝叶斯 ( Thomas Bayes 1702-1761 ) 发展,用来描述两个条件概率之间的关系,比如 P(A|B) 和 P(B|A)。按照乘法法则,可以立刻导出:P(A∩B) = P(A)*P(B|A)=P(B)*P(A|B)。如上公式也可变形为:
P(A|B) = P(B|A)*P(A)/P(B)
这里写图片描述

朴素贝叶斯

与贝叶斯的区别:基于贝叶斯,但是各个特征条件都是独立的

朴素贝叶斯得思想

朴素贝叶斯分类是一种十分简单的分类算法,叫它朴素贝叶斯分类是因为这种方法的思想真的很朴素,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。

朴素贝叶斯正式定义
  1. 设x-{a1,a2,a3…..am}为一个待分类项
  2. 有类别集合C={y1,y2,y3,y4…yn}
  3. 计算P(y1|x),P(y2|x),P(y3|x)…..
  4. 如果P(yk|x)=max{P(y1|x),P(y2|x),P(y3|x)…..P(yn|x)}
    关键在于如何计算第三步中的概率,我们可以进行如下操作
  5. 找到已知分类的待分类集合
  6. 统计各个类别下各个属性出现的概率,即
    这里写图片描述
  7. 如果各个属性是相互独立的,那么根据贝叶斯定理有如下推导
    这里写图片描述
  8. 因为分母对于所有类别为常数,所以我们只需要将分子最大化即可,又因为各属性是相互独立的,所以有
    这里写图片描述

上面讲完了朴素贝叶斯的数学原理,下面结合分档分类进行具体的分析

使用朴素贝叶斯进行文档分析

机器学习中一个重要的分类是文档的自动分类,在分档分类中整个文档是实类,而文档中的某些元素构成特征。虽然电子邮件是一种不断增加的文本,但是我们可以根据文本中出现的高频词汇对文章进行分类

朴素贝叶斯的一般过程
  1. 收集数据:可以使用任何方法。我们是用rss。
  2. 准备数据:需要数值型或者bool型数据。
  3. 分析数据:有大量特征是,使用直方图
  4. 训练数据:计算不同数据特征的条件概率。
  5. 测试算法:计算错误率。

使用Python对文本进行分类

要获取文本中的特征,首先要拆分文本。这里的特征来自文本的词条,一个词条是字符的任意组合。可以把词条想象为单词,也可以使用非单词词条,如ip地址,url或者其他字符。然后将每一个文本表示为一个词条向量,其中词条在文本中出现记为1,否则记为0。
下面以在线社区留言板为例,为了不影响社区发展,我们要屏蔽掉侮辱性的言论。

准备数据:从文本中构建词向量

将文本转换成单词向量,考虑出现的单词再考虑将那些哪些词纳入词汇表。

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]
return postingList,classVec

def createVocablist(dataset):#创建一个包含所有文档中不重复单词的列表
vocaset=set([])#创建一个空集
for document in dataset:
    vocaset = vocaset | set(document)#创建两个集合合并的并集
return list(vocaset)

def setOfWord2Vec(vocablist,inputset):#第一个参数为词汇表,第二个参数为文档或者文件
returnVec = [0]*len(vocablist)#创建一个所有向量为0的元素
for word in inputset:#将文档中对应出现的单词记为1
    if word in vocablist:
        returnVec[vocablist.index(word)] = 1#如果在词汇表中出现,将对应的词汇向量记为1
    else:
        print("the word %s is not in my vocabulary!"%word)
return returnVec
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
训练算法

前面介绍了如何将一组单词转换成一组数字,接下来介绍如何使用这些数字计算概率。现在我们已经知道一个词会出现在一篇文档当中,也知道文档所属的类别。现在我们重写贝叶斯公式。w表示这是一个向量,即他有多个数值组成,在这个例子中数据值和词汇表中的单词个数相同。
这里写图片描述

我们是用上述公式对每个类计算该值,然后比较两个概率值的大小。具体做法:首先通过类别i(侮辱性和非侮辱性留言)中文档数除以总的文档数来计算概率P(ci),接下来计算P(w|ci),这里需要用到朴素贝叶斯假设。
该函数的伪代码如下:

计算每个类别中的文档数目
对每篇训练文档:
    对每一个类别训练文档:
        如果词条出现在该文档中->增加该词条的计数值
        增加所有词条的计数值
对每个类别:
    对每个词条:
        将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
朴素贝叶斯分类器训练函数
def trainNB0(trainMaxtrix,trainCategory):#trainMaxtrix为文档矩阵, 第二个参数为每类文档构成的向量
    numTrainDocs = len(trainMaxtrix)#求文档的个数
    numWords =len(trainMaxtrix[0])#求第一个文档中文字的个数
    pAbusive = sum(trainCategory)/float(numTrainDocs)#计算所有文档中属于1的比例p(c=1)
    p0Num = ones(numWords)#生成一个1*n的一矩阵
    p1Num = ones(numWords)#
    p0Denom = 2.0
    p1Denom = 2.0
    #遍历每一篇文档的词条向量
    for i in range(numTrainDocs):
        if trainCategory[i] ==1:#如果该词条向量对应的标签为1,统计词条向量中个词条出现的次数
            p1Num+=trainMaxtrix[i]#统计标签为1的词条向量中各个词条出现的次数
            p1Denom+=sum(trainMaxtrix[i])#统计标签为1的词条向量出现的单词总数
        else:
            p0Denom+=sum(trainMaxtrix[i])
            p0Num+=trainMaxtrix[i]
    p1Vect = log(p1Num/p1Denom)#计算p(wi|c1)
    p0Vect = log(p0Num/p0Denom)#计算p(wi|c0)
    return p0Vect,p1Vect,pAbusive
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

1. 计算概率时,需要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0|ci)p(w1|ci)…p(wN|ci),然后当其中任意一项的值为0,那么最后的乘积也为0.为降低这种影响,采用拉普拉斯平滑,在分子上添加a(一般为1),分母上添加ka(k表示类别总数),即在这里将所有词的出现数初始化为1,并将分母初始化为2*1=2
2. 解决下溢出问题
  正如上面所述,由于有太多很小的数相乘。计算概率时,由于大部分因子都非常小,最后相乘的结果四舍五入为0,造成下溢出或者得不到准确的结果,所以,我们可以对成绩取自然对数,即求解对数似然概率。这样,可以避免下溢出或者浮点数舍入导致的错误。同时采用自然对数处理不会有任何损失。

朴素贝叶斯分类
#vec2Classify:待测试分类的词条向量
#p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0)
#p0Vec:类别1所有文档中各个词条出现的频数p(wi|c1)
#pClass1:类别为1的文档占文档总数比例
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass):
    #分别计算出待测文档输入类0和类1的概率
    p1 = sum(vec2Classify*p1Vec)+log(pClass)
    p0 = sum(vec2Classify*p0Vec)+log(1-pClass)
    if p1>p0:
        return 1
    else:
        return 0

def testNB():
    listOPosts,listClasses = loadDataSet()
    myVocabList = createVocablist(listOPosts)
    train_mat = []
    for postinDoc in listOPosts:
        train_mat.append(setOfWord2Vec(myVocabList,postinDoc))
    p0V,p1V,pAb = trainNB0(array(train_mat),array(listClasses))
    testEntry = ['love','my','dalmation']
    this_doc = array(setOfWord2Vec(myVocabList,testEntry))
    print(testEntry,"classified as: ",classifyNB(this_doc,p0V,p1V,pAb))
    testEntry = ['stupid','garbage']
    this_doc = array(setOfWord2Vec(myVocabList,testEntry))
    print(testEntry,'classified as: ',classifyNB(this_doc,p0V,p1V,pAb))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
准备数据:文档词袋模型

如果我们将每个词的出现与否作为一个特征,这可以描述为词集模型(set-of-words)。如果一个词可能不止出现一次,这时候词集模型就不能统计到出现次数的特征,这是我们就需要词袋模型

#朴素贝叶斯词袋模型  
def bagOfWords2VecMN(vocabList,inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)]+=1
    return returnVec
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

实例1:利用朴素贝叶斯过滤垃圾邮件

切分文本

对于一个文本字符串,我们可以使用string.split()方法将其切分。但是切分时,标点也会被单做词的一部分。我们可以使用正则表达式来切分句子,其中分割词是除单词、数字外的任意字符串此时,我们可以使用正则表达式来切分句子,其中分割符是除单词和数字之外的其他任意字符串,即

import re
re.compile('\\W*')
  • 1
  • 2

这样就得到了一系列词组成的词表,但是里面的空字符串还是需要去掉,此时我们可以通过字符的长度,去掉长度等于0的字符。并且,由于我们是统计某一词是否出现,不考虑其大小写,所有还可以将所有词转为小写字符,即lower(),相应的,转为大写字符为upper()

 #朴素贝叶斯过滤垃圾邮件
 #使用朴素贝叶斯进行交叉认证
 #textParse()接受一个大写字符串并将其解析为字符串列表
 #该函数去掉长度少于两个的字符串
def textParse(bigString):
    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):
        wordList = textParse(open('email/spam/%d.txt'%i).read())#到指定文件中将字符读取出来
        docList.append(wordList)#将列表添加到docList中
        fullText.extend(wordList)#将列表中的元素添加到fulltext中
        classList.append(1)#类标签添加为1
        wordList = textParse(open('email/ham/%d.txt'%i).read())#处理另一个标签为0的文件
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)

    vocabList = createVocablist(docList)#创建一个所有单词的集合
    trainingSet = list(range(50))#构建一个大小为50的整数列表和空列表
    testSet = []
    for i in range(10):#随机从中选出10个数作为引索,构建测试集
        randIndex = int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])#将选出数的列表值添加到testSet中
        del(trainingSet[randIndex])#删掉选择的值,避免重复选择
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:#遍历训练集中的每一个字符串列表
        # 将字符串列表转换成词条向量,添加到训练矩阵中
        trainMat.append(bag
        OfWords2VecMN(vocabList,docList[docIndex]))
        #将该邮件的类标签加入训练列表中
        trainClasses.append(classList[docIndex])
    #计算出贝叶斯需要的概率值并且返回
    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))
    errorcount = 0
    for docIndex in testSet:#遍历测试集中的字符串列表
        #将字符串列表转换成词条向量
        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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

利用随机方法从数据集中选择训练集,剩下的作为测试集,这样就能够得到不同的数据集和训练集,这样就能够得到不同的错误率,通过多次迭代求误差率,求其平均错误率,这样能够得到更精确的错误率,称为存留交叉验证法。

朴素贝叶斯从个人广告中获取区域倾向

  在本例中,我们通过从不同的城市的RSS源中获得的同类型广告信息,比较两个城市人们在广告用词上是否不同。如果不同,那么各自的常用词是哪些?而从人们的用词当中,我们能否对不同城市的人所关心的内容有所了解?如果能得到这些信息,分析过后,相信对于广告商而言具有不小的帮助。

利用RSS源得到文本数据
#RSS源分类及高频词去出函数
def calcMostFreq(vocabList,fullText):
    import operator
    freqDict = {}#创建一个空字典
    for token in vocabList:#遍历词条中的每一个词
        freqDict[token] = fullText.count(token)#将单词出现的次数作为键值插入字典
    sortedFreq = sorted(freqDict.items(),key = operator.itemgetter(1),reverse=True)#python3中舍弃了iteritems
    return sortedFreq[:30]#返回次数最多的30个单词
def localWords(feed1,feed0):
    docList = []
    classList = []
    fullText = []
    #获取rss源最小的条目数
    minLen = min(len(feed1['entries']),len(feed0['entries']))
    for i in range(minLen):#遍历每一个条目

        wordList = textParse(feed1['entries'][i]['summary'])#解析和处理相应的数据
        docList.append(wordList)#添加词条同列表到docList
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocablist(docList)
    top30Words = calcMostFreq(vocabList,fullText)#获取出现频率最高的30个单词
    for pairW in top30Words:
        if pairW[0] in vocabList:
            vocabList.remove(pairW[0])
    trainingSet = list(range(2*minLen))
    testSet = []
    for i in range(20):
        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))
    error_count = 0
    for docIndex in testSet:
        word_vector = bagOfWords2VecMN(vocabList,docList[docIndex])
        if classifyNB(array(word_vector),p0V,p1V,pSpam) != classList[docIndex]:
            error_count += 1
    print('the error rate is: ',float(error_count)/len(testSet))
    return vocabList,p0V,p1V
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

对得到的数据进行分析

#显示地域相关用词
def getTopWords(ny,sf):
# 利用rss获取所有出现的词条列表,以及每个种类中单词出现的概率
    vocabList,p0V,p1V = localWords(ny,sf)
    topNY = []
    topSF = []
    for i in range(len(p0V)):#遍历每个类各个单词出现的概率
        #往相应的元组列表中概率值大于阈值的单词及其概率
        if p0V[i] > -6.0:
            topSF.append((vocabList[i],p0V[i]))
        if p1V[i] > -6.0:
            topNY.append((vocabList[i],p1V[i]))
            sortedSF = sorted(topSF,key=lambda pair:pair[1],reverse=True)#按照概率由大到小进行排序
    print("SF**SF**SF**SF**SF*
    *SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF")
    for item in sortedSF:
        print(item[0])
    sortedNY = sorted(topNY,key= lambda pair:pair[1],reverse=True)
    print("NY**NY**NY**NY**NY*
*NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY")
    for item in sortedNY:
        print(item[0])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

源代码

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号