【機器學習實戰-python3】基於概率論的分類方法:樸素貝葉斯

實踐代碼和訓練測試數據可以參考這裏
https://github.com/stonycat/ML-in-Action
通過概率大小來判斷分類結果歸屬,涉及到概率論的條件概率。
p(ci | x,y)=p(x,y | ci)·p(ci)/p(x,y)
比較p(ci | x,y)的大小(i=1,2……)

1、從文本中構建詞向量

#coding=utf-8
from numpy import *
#文本轉化爲詞向量
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表示侮辱類,0表示不屬於
    return postingList,classVec #詞條切分後的分檔和類別標籤

運行結果:
這裏寫圖片描述

2、訓練算法
首先導入數據得到listOPosts所有文檔和類別;
然後創建一個myVocabList包含所有詞不重複的list;
創建一個訓練集,然後添加數據並轉化爲詞向量,計算概率。
由於概率都很小,那麼相乘之後就更小,會造成四捨五入之後爲0,解決這個問題的辦法是我們對概率取對數。一下輸出爲負數的結果是取對數後的值。

#包含所有文檔 不含重複詞的list
def createVocabList(dataSet):
    vocabSet=set([])#創建空集,set是返回不帶重複詞的list
    for document in dataSet:
        vocabSet=vocabSet|set(document) #創建兩個集合的並集
    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 Vocabulary!" % word)#返回文檔向量 表示某個詞是否在輸入文檔中出現過 1/0
    return returnVec

#樸素貝葉斯分類訓練函數
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs=len(trainMatrix) #文檔數目
    numWords=len(trainMatrix[0])
    pAbusive=sum(trainCategory)/float(numTrainDocs) #文檔中屬於侮辱類的概率,等於1才能算,0是非侮辱類
    #p0Num=zeros(numWords); p1Num=zeros(numWords)
    #p0Denom=0.0;p1Denom=0.0
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    for i in range(numTrainDocs):#遍歷每個文檔
        #if else潛在遍歷類別,共2個類別
        if trainCategory[i]==1: #一旦某個詞出現在某個文檔中出現(出現爲1,不出現爲0)
            p1Num+=trainMatrix[i]  #該詞數加1
            p1Denom+=sum(trainMatrix[i]) #文檔總詞數加1
        else: #另一個類別
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])
        # p1Vect = p1Num / p1Denom
        # p0Vect = p0Num / p0Denom
    p1Vec = log(p1Num / p1Denom)
    p0Vec = log(p0Num / p0Denom)
    return p0Vec, p1Vec, pAbusive  #返回p0Vec,p1Vec都是矩陣,對應每個詞在文檔總體中出現概率,pAb對應文檔屬於1的概率

這裏寫圖片描述

3、 測試樸素貝葉斯

#給定詞向量 判斷類別
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): #第一個參數爲0,1組合二分類矩陣,對應詞彙表各個詞是否出現
    p1=sum(vec2Classify*p1Vec)+log(pClass1)
    p0=sum(vec2Classify*p0Vec)+log(1.0-pClass1)
    if p1>p0:
        return 1
    else: return 0
#封裝的bayes測試函數
def testingNB():
    listOPosts,listClasses=loadDataSet() #導入數據,第一個存儲文檔,第二個存儲文檔標記類別
    myVocabList=createVocabList(listOPosts) #所有詞彙總list,不含重複的
    trainMat=[]
    for postinDoc in listOPosts:#生成文檔對應詞的矩陣 每個文檔一行,每行內容爲詞向量
        trainMat.append(setOfWords2Vec(myVocabList,postinDoc)) #每個詞在文檔中是否出現,生成10組合的詞向量
    p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses)) #根據現有數據輸出詞對應的類別判定和概率
    testEntry=['love','my','dalmation']
    thisDoc=array(setOfWords2Vec(myVocabList,testEntry)) #判斷測試詞條在詞彙list中是否出現,生成詞向量
    print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb)) #根據貝葉斯返回的概率,將測試向量與之乘,輸出結果
    testEntry=['stupid','garbage']
    thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
    print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))

這裏寫圖片描述

4、示例:分類垃圾郵件
高級詞袋模型:

#高級詞袋模型,判斷詞出現次數
def bagOfWords2VecMN(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)  # 返回文檔向量 表示某個詞是否在輸入文檔中出現過 1/0
    return returnVec
#示例:過濾垃圾郵件
#預處理
def textParse(bigString):
    import re
    listOfTokens=re.split('x*',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()) 書上這行代碼有些問題 unicode error
        #修改爲下面:
        wordList = textParse(open('email/spam/%d.txt' % i, "rb").read().decode('GBK', 'ignore'))
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        # wordList=textParse(open('email/ham/%d.txt' %i).read()) 同理上面一樣 修改爲下面一行
        wordList = textParse(open('email/ham/%d.txt' % i, "rb").read().decode('GBK', 'ignore'))
        docList.append(wordList) #不融合格式
        fullText.extend(wordList) #添加元素 去掉數組格式
        classList.append(0)
    vocabList=createVocabList(docList) #創建詞列表
    trainingSet = list(range(50))
    #trainingSet=range(50) python3 del不支持返回數組對象 而是range對象
    testSet=[] #spam+ham=50 eamils
    for i in range(10):#隨機選擇10封作爲測試集
        randIndex=int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex]) #報錯,python3 del不支持返回數組對象 而是range對象 修改上面108行
    trainMat=[]; trainClasses=[]
    for docIndex in trainingSet: #遍歷訓練集
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex])) #對每一封郵件創建詞向量並計算分類概率
        trainClasses.append(classList[docIndex]) #類別
    p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClasses)) #訓練出概率
    errorCount=0
    for docIndex in testSet:
        wordVector=setOfWords2Vec(vocabList,docList[docIndex])
        if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:
            errorCount+=1
    print('the error rate is:',float(errorCount)/len(testSet))

測試結果:
過程:收集數據,提供文本文件—準備數據,將文件解析成詞向量
—訓練算法—測試算法

Python自帶的切分文本函數:
Split()包含標點:
這裏寫圖片描述
正則表達式切分句子,包含空字符:
這裏寫圖片描述
去掉空字符:
這裏寫圖片描述
修改大寫字母統一爲小寫:
這裏寫圖片描述
測試郵件分類(兩次都恰好是0…):
這裏寫圖片描述

5、 使用樸素貝葉斯分類器從個人廣告中獲取區域傾向
首先介紹安裝RSS源:從https://github.com/kurtmckee/feedparser 上下載zip解壓到本地,cd到feedparser文件夾內,輸入python setup.py install 完成安裝
這裏可以import feedparser進行測試
這裏寫圖片描述

#從個人廣告中獲取區域傾向
#RSS源分類器及高頻詞去除函數
def calcMostFreq(vocabList,fullText):#對所有詞出現頻率進行排序,返回排序後出現頻率最高的前30個
    import operator
    freqDict={}
    for token in vocabList:
        freqDict[token]=fullText.count(token)
    sortedFreq=sorted(freqDict.items(),key=operator.itemgetter(1),reverse=True)#True=降序排列
    return sortedFreq[:30]

def localWords(feed1,feed0):#兩個RSS源作爲參數,與spamTest差別不大
    import feedparser
    docList=[];classList=[];fullText=[]
    minLen=min(len(feed1['entries']),len(feed0['entries']))
    for i in range(minLen):#訪問RSS源
        wordList = textParse(feed1['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)  # NY is class 1
        wordList = textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)
    top30Words=calcMostFreq(vocabList,fullText)
    for pairW in top30Words:#去掉出現頻數最高的錢30個詞
        if pairW[0] in vocabList: vocabList.remove(pairW[0])
    trainingSet = list(range(2*minLen)) #python3修改替換trainSet=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))
    errorCount = 0
    for docIndex in testSet:
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
        if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
    print('the error rate is: ', float(errorCount) / len(testSet))
    return vocabList, p0V, p1V

測試分類結果(誤差率相對較大)
這裏寫圖片描述
顯示地域相關的用詞:

#最具代表性的詞彙顯示函數
def getTopWords(ny,sf):
    import operator
    vocabList,p0V,p1V=localWords(ny,sf)
    topNY=[];topSF=[]
    for i in range(len(p0V)):
        if p0V[i]>-1.0: topSF.append((vocabList[i],p0V[i]))#這裏個人把閾值從-6.0調大了,打印的詞不會太多
        if p1V[i]>-1.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])

這裏覆蓋了之前的輸入 所以下面重新輸入供查看:
上面是分類結果。
這裏寫圖片描述

貝葉斯概率及貝葉斯準則提供一種利用已知值來估計概率的有效方法,可以通過特徵之間的條件獨立性假設,降低對數據量的要求。獨立性假設是一個詞的出現概率不依賴於其他詞,當然有時是不準確的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章