眷恋你的方圆

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

贝叶斯的数学基础和理论就不写了,很基础,网上博客也一大堆。这里只写实现的具体过程

(代码复制可以直接使用,没有缺少,里面会有一些测试性的语句)

总的来说实现的过程分成四个步骤

第一部分:一些基础函数的实现

loadDataSet()函数创建了一些实验样本,这个是我们自己写的,用来对代码编写后进行简单的测试,其中postingList是样本,classVec是样本的标签(1 是侮辱性留言,0是非侮辱性留言)

createVocaList()函数会创建一个包含在所有文档中出现的不重复的列表,得到基于数据集的一个词集

setOfWord2Vec()函数 输入参数为词汇表及某个文档,输出的是文档向量,向量的每个元素为1或者0,代表单词是否在文档中出现。这个称为词集模型

bagOfWord2Vec()函数 和setOfWord2Vec()函数差不多,不过这个是统计词在文档中出现的次数,因为一个词可能在文档中出现不止一次,这个称为词袋模型

最后一些语句就是一些测试性代码,看看函数有没有编写成功。

import numpy as np
import re
import feedparser as fp
import operator as op
def loadDataSet():
    postingList = [['my','dog','has','flea','problems','help','please','haha'],
                   ['maybe','not','take','him','to','dog','park','stupid'],
                   ['my','dalmation','is','so','cute','I','love','him','haha'],
                   ['stop','posting','stupid','worthless','garbage'],
                   ['mr','licks','ate','my','steak','how','to','stop','him','haha'],
                   ['quit','buying','worthless','dog','food','stupid']
                   ]
    classVec = [0,1,0,1,0,1]
    return postingList,classVec
#创建一个包含在所有文档中不重复的词的列表,为Python的 set数据类型
def createVocaList(dataSet):
    vocabSet = set([])
    for docunment in dataSet:
        vocabSet = vocabSet | set(docunment)
    return list(vocabSet)

#词集模型,将每个词的出现与否作为一个特征,创建一个词向量
def setOfWords2Vec(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 vocabList!" % word)
    return returnVec

#如果一个词在文档中出现不止一次,称为词袋模型
def bagOfWords2Vec(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 vocabList!" % word)
    return returnVec

'''listOPosts,listClasses = loadDataSet()
vocaSet = createVocaList(listOPosts)
print (vocaSet)
#listOPosts[0].append('hhh')
print (setOfWords2Vec(vocaSet,listOPosts[0]))'''

第二部分:是贝叶斯算法的代码实现:

trainNB0()函数是贝叶斯训练函数,通过样本得到一些条件概率,输出结果就是p0Vect代表是p(W(i) | 0)也就是p(某词 | 非侮辱性)的概率,p1Vect代表是p(W(i) | 0)也就是     p(某词 | 侮辱性)概率,pAbusive代表是训练集中侮辱性的样本数目的概率。其中会对得到的初步概率进行对数处理,原因代码里简单写了一下。

classifyNB()函数通过上面的函数得到的概率,输入要输入的预测的样本得到分类

testingNB()函数是用来测试用的,简单封装到了一个函数里

 

#朴素贝叶斯训练函数
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])#这里直接用第一个例子的长度作为词的长度会不会不合适
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    #p0Num = np.zeros(numWords)
    #p1Num = np.zeros(numWords)
    #p0Denom = 0.0
    #p1Denom = 0.0
    #其中一个概率值为0,那么最后的乘积也为0,所以在初始化的时候把所有的词出现的次数初始化为1
    #并将分母初始化为2(为什么要初始化为2呢?,可能要考虑极端情况吧,比如如果训练集中的数据标签
     #全是0 或者全是 1 ,可能也不一定是2 ,也可能是其他更好的数组)
    p0Num = np.ones(numWords)
    p1Num = np.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 = np.log(p1Num/p1Denom)
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect , p1Vect , pAbusive
    
'''listOPosts,listClasses = loadDataSet()
myVocabList = createVocaList(listOPosts)
#print (myVocabList)
trainMat = []
for postinDoc in listOPosts:
    trainMat.append(setOfWords2Vec(myVocabList,postinDoc))

p0V, p1V, pAb = trainNB0(trainMat, listClasses)

print ("the p0V is :",p0V)
print ("the p1V is :",p1V)
print ("the pAb is :",pAb)'''

#要理解为什么要对p0V, p1V取对数,以及ln(a*b) = lna + lnb,是为了解决下溢出问题,由于太多很小
#的数相乘。
#p0V就是p(w(i)|c0)的概率,p1V就是p(w(i)|c1)的概率,pClass1就是训练数据中p(c1)的概率
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
    #ln(a*b) = lna + lnb
    #p(w)的概率是一定的,只需要比较分子的大小
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)     
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0-pClass1) 
    if p1 > p0:
        return  1
    else:
        return 0
    

def testingNB():
    listOPosts,listClasses = loadDataSet()
    myVocabList = createVocaList(listOPosts)
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
    
    p0V, p1V, pAb = trainNB0(trainMat, listClasses)
    testEntry = ['love','my','dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList,testEntry))
    print (testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))
    testEntry = ['stupid','garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList,testEntry))
    print (testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))

#testingNB()

第三部分:使用朴素贝叶斯过滤垃圾邮件

textParse()函数是将每一个样本解析成一个字符串列表,好进行使用

spamTest()函数是用来测试的函数,将样本全部读取出来,然后抽取一部分样本用来测试算法,然后调用第一部分和第二部分的代码进行测试

#垃圾邮件检测实例
#将邮件内容解析成字符串列表。//将一个大字符串解析成字符串列表
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): #spam是垃圾邮件 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 = createVocaList(docList) trainingSet = list(range(50)) testSet = [] #选出来十条数据作为测试数据集 for i in range(10): randIndex = int(np.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(np.array(trainMat),np.array(trainClasses)) #print ("diyige:",p0V,"dierge:",p1V,"disange",pSpam) errorCount = 0 for docIndex in testSet: wordVector = setOfWords2Vec(vocabList,docList[docIndex]) if classifyNB(wordVector,p0V,p1V,pSpam) != classList[docIndex]:

第四部分:使用贝叶斯分类器从个人广告找那个获取区域倾向

这部分数据从网上获取,需要收集数据:导入RSS源,了解一下RSS源的用法,有一个feedparser包可以用来处理RSS源

calcMostFreq()函数统计文本中的高频词汇,然后用来提出,以为例如英文中的 is ,as, the等词出现的频率很高,但是对于用来分类毫无用处,甚至会降低分类的准确性。

localWords()函数用来获取数据和测试样本,和spamTest()函数功能上基本一样,就是获取数据的方式不一样(读懂代码的话需要了解一下RSS的编写规则)

#实例:从个人广告中获取区域倾向

#遍历词汇表中的每个词并统计它在文本中出现的次数,然后从高到低排序
def calcMostFreq(vocabList,fullText):
    freqDict = {}
    for token in vocabList:
        freqDict[token] = fullText.count(token)
    sortedFreq = sorted(freqDict.items(),key = op.itemgetter(1),reverse = True)
    return sortedFreq[:30]

def localWords(feed1,feed0):
    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)     
    vocabList = createVocaList(docList)
    top30Words = calcMostFreq(vocabList,fullText)
    for pairW in top30Words:
        if pairW in vocabList:
            vocabList.remove(pairW)
    trainingSet = list(range(2*minLen))
    testSet = []
    for i in range(20):
        randIndex = int(np.random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])   
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(bagOfWords2Vec(vocabList,docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses))
    errorCount = 0
    for docIndex in testSet:
        wordVector = bagOfWords2Vec(vocabList,docList[docIndex])
        if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount +=1
    print ('the error rate is :',float(errorCount)/len(testSet))
    return vocabList,p0V,p1V
        
        
ny = fp.parse('http://newyork.craigslist.org/stp/index.rss')
sf = fp.parse('http://sfbay.craigslist.org/stp/index.rss')
#print (ny['entries'])

vocabList, psf,pny = localWords(ny,sf)

总结:使用概率有时要比使用硬规则更有效,这里假设每个词(特征)之间独立,但是现实中往往不是这样的,每个词的出现往往和周围的词有关,但是这里我们只进行了简单的实现。可以优化的方面还有很多。

 

posted on 2017-10-14 20:42  眷恋你的方圆  阅读(2583)  评论(0编辑  收藏  举报