Python学习-机器学习实战-ch04 Bayes

毕业论文写不下去,就逃避来学这个

万事开头难,要勇敢迈出第一步

加油!

========================================================================================

贝叶斯的原理不赘述啦,网上还是有很多资料的


创建一个数据集,书中是以文档分类的例子来讲

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

上面这个函数就创建了一个小数据集,包含六篇文档,每篇文档有各自的分类(此例仅有0和1两类)

def createVocabList(dataset):
    vocabSet=set([])
    for document in dataset:
        vocabSet=vocabSet|set(document)
    #循环对数据集内的每个文件提取word,set用于去重
    #求并集
    return list(vocabSet)

该函数将文档集转换为一个词汇库(vocabulary),里面包含在文档集内的所有word

贝叶斯的文档分类都是基于词汇库将文档转换成(特征)向量的,值就0和1表示存在或不存在

def setOfWords2Vec(vocabList,inputSet):
    returnVec=[0]*len(vocabList)
    #创建一个所含元素都是0的向量
    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])
    #由第一行的个数获得vocabulary的长度
    pAbusive=sum(trainCategory)/float(numTrainDocs)
    #表示类别的概率,此例中仅限类别为0和1的状况
    p0Num=zeros(numWords)
    p1Num=zeros(numWords)
    #pXNum是一个与Vocabulary等长的向量,用于统计对应word出现的次数
    p0Denom=0.0
    p1Denom=0.0
    #pXDenom表示第X类内单词的总数
    for i in range(numTrainDocs):
        if trainCategory[i]==1:
            p1Num+=trainMatrix[i]
            p1Denom+=sum(trainMatrix[i])
        else:
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])

    p1Vec=p1Num/p1Denom
    p0Vec=p0Num/p0Denom
    #vocabulary中的某个词在某类别里头出现的频率
    return p0Vec,p1Vec,pAbusive

#首先搞清楚参数的意思
#结合前几个函数:postingList表示文档的集合,每一行表示一篇文档,行数即文档数
#classVec向量内值的个数与文档数相同,表示各文档的分类
#createVocabList函数把这些文档整合起来求得不含重复word的vocabulary
#setOfWords2Vec函数把一篇文档的word对应到vocabulary中,变成一个向量
#本函数的第一个参数表示每篇转化到vocabulary对应的向量,为n*m,n是文档数,m是vocabulary的长度
#trainCategory是一个向量,是每篇文档对应的类别


测试用的代码:

from numpy import *
import bayes

listPost,listClass=bayes.loadDataSet()
myVoc=bayes.createVocabList(listPost)

trainMat=[]
for postinDoc in listPost:
    trainMat.append(bayes.setOfWords2Vec(myVoc,postinDoc))

p0V,p1V,pAb=bayes.trainNB0(trainMat,listClass)
print(myVoc)
print(p0V)
print(p1V)
print(pAb)

这里朴素贝叶斯分类器训练函数的输出:

vocabulary里的word在个类别中出现的概率(先验概率)

每个类别出现的概率(先验概率)

此例中pAb结果为0.5,表示0和1两类是等概率出现的


根据现实情况修改:

1.初始化问题

贝叶斯进行文档分类时,需要多个概率的乘积以获得文档属于某个类别的概率

即:分别在每个类内对文档内的每个WORD的概率相乘,以获得整个文档对应该类别的概率

但是如果某个概率值为0,则整个概率值也为0。所以书中将所有单词出现数初始化为1,分母初始化为2

    p0Num=ones(numWords)
    p1Num=ones(numWords)
    #pXNum的个数被初始化为1
    p0Denom=2.0
    p1Denom=2.0

2.下溢出

由于有很多个很小的数相乘,容易造成下溢出,最后会四舍五入得0.

解决的方法是:对乘积取对数

ln(a*b)=ln(a)+ln(b)

具体代码中为:

    p1Vec=log(p1Num/p1Denom)
    p0Vec=log(p0Num/p0Denom)


最后是整合上面的步骤,用于进行分类

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():
    listPost,listClass=loadDataSet()
    myVoc=createVocabList(listPost)
    trainMat=[]
    for postinDoc in listPost:
        trainMat.append(setOfWords2Vec(myVoc,postinDoc))
    p0V,p1V,pAb=trainNB0(trainMat,listClass)

    testEntry=['love','my','dalmation']
    thisDoc=array(setOfWords2Vec(myVoc,testEntry))
    print(testEntry,' classified as ',classifyNB(thisDoc,p0V,p1V,pAb))

    testEntry=['stupid','garbage']
    thisDoc=array(setOfWords2Vec(myVoc,testEntry))
    print(testEntry,' classified as ',classifyNB(thisDoc,p0V,p1V,pAb))

整合上述步骤,同时用了两个测试用例

检测结果:



使用朴素贝叶斯过滤垃圾邮件

def textParse(bigString):
    import re
    listOfTokens=re.split(r'\W*',bigString)
    #使用中正则表达式提取
    return [token.lower() for token in listOfTokens if len(token) >2]
此处,我犯了个错,就是正则表达式那一块,打的小写的w,所以结果错误。怎么犯这么愚蠢的错误

def spamTest():
    docList=[];classList=[];fullText=[]
    for i in range(1,26):
        wordList=textParse(open('email\spam\%d.txt' %i).read())
        docList.append(wordList)
        fullText.append(wordList)
        classList.append(1)
        #正例
        wordList=textParse(open('email\ham\%d.txt' %i).read())
        docList.append(wordList)
        fullText.append(wordList)
        classList.append(0)
        #反例
    vocabulary=createVocabList(docList)
    trainingSet=list(range(50))
    testSet=[]
    for i in range(10):
        randIndex=int(random.uniform(0,len(trainingSet)))
        #random模块用于生成随机数
        #random.uniform(a,b)用于生成制定范围内的随机浮点数
        testSet.append(trainingSet[randIndex])
        del trainingSet[randIndex]
        #随机选择10个文档作为测试集,其余作为训练集
    trainMat=[];trainClasses=[]
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabulary,docList[docIndex]))
        trainClasses.append(classList[docIndex])
        #将选中的训练集逐个整合在一起
    p0V,p1V,pSpam=trainNB0(trainMat,trainClasses)
    errorCount=0
    for docIndex in testSet:
        wordVector=setOfWords2Vec(vocabulary,docList[docIndex])
        if(classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]):
            errorCount+=1
        #如果分类结果与原类别不一致,错误数加1
    print('the error rate is:',float(errorCount)/len(testSet))
修改了一个地方:按照原文的话抱一个错就是在trainingSet的地方

del(trainingSet[randIndex])TypeError: 'range' object doesn't support item deletion

于是,我在初始化的时候,把它改成了List型

def calcMostFreq(vocabulary,fulltext):
    import operator
    freqDict={}
    for token in vocabulary:
        freqDict[token]=fulltext.count(token)
    sortedFreq=sorted(freqDict.items(),key=operator.itemgetter(1),reverse=True)
    return sortedFreq[:30]
    #出现频率前30的词

def localWords(feed1,feed0):
    import feedparser
    docList=[];classList=[];fullText=[]
    minlen=min(len(feed1['entries']),len(feed0['entries']))
    for i in range(minlen):
        wordList=textParse(feed1['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList=textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    #两个RSS源作为正反例
    vocabulary=createVocabList(docList)
    #创建词汇库
    top30Words=calcMostFreq(vocabulary,fullText)
    #获得出现频率最高的30个
    for pairW in top30Words:
        if pairW[0] in vocabulary:vocabulary.remove(pairW[0])
    #去除前30的单词

    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])
    #随机选择训练和测试集;测试集为20个
    trainMat=[];trainClass=[]
    for docIndex in trainingSet:
        trainMat.append(bagOfWords2VecMN(vocabulary,docList[docIndex]))
        trainClass.append(classList[docIndex])
    #将训练集内的文档转换成频数特征
    p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClass))
    errorCount=0
    for docIndex in testSet:
        wordVector=bagOfWords2VecMN(vocabulary,docList[docIndex])
        if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:
            errorCount+=1
    print('the error rate is: ',float(errorCount)/len(testSet))
    return vocabulary,p0V,p1V


其中还是修改了

 trainingSet=list(range(2*minlen))

不知道其他学习的同学们有没有遇到这个问题,这么处理对不对?

测试用的代码:

import feedparser
ny=feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
sf=feedparser.parse('http://sfbay.craigslist.org/stp/index.rss')
vocabulary,pSF,pNY=bayes.localWords(ny,sf)

结果是因随机抽取的测试集和训练集不一样会发生变化。

最具表征性词汇显示:

def getTopWord(ny,sf):
    import operator
    vocabulary,p0V,p1V=localWords(ny,sf)
    topNY=[];topSF=[]
    for i in range(len(p0V)):
        if p0V[i]>-6.0:topSF.append((vocabulary[i],p0V[i]))
        if p1V[i]>-6.0:topNY.append((vocabulary[i],p1V[i]))
    #按照排序选择
    sortedSF=sorted(topSF,key=lambda pair:pair[1],reverse=True)
    #pair:pair[1]表示按每个元素的第二个参数排序
    print("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")
    for item in sortedNY:
        print(item[0])

Python学习-机器学习实战-ch04 Bayes_第1张图片

=========================================================================================

下载安装feedsparse

下载地址:点击打开链接

安装方法:首先将路径转换到该文件夹下

    然后输入指令python setup.py install

你可能感兴趣的:(python学习)