1. 程式人生 > >機器學習實戰筆記4(樸素貝葉斯)

機器學習實戰筆記4(樸素貝葉斯)

前面介紹的kNN和決策樹都給出了“該資料例項屬於哪一類”這類問題的明確答案,而有時候的分類並不能給出明確的答案,本節講解使用概率論進行分類的方法。

1:簡單概念描述

概念比較簡單,這裡我摘抄自百度百科,很容易理解。

樸素貝葉斯模型(NaiveBayesian classification))

----

Vmap=arg max P( Vj | a1,a2...an)

Vj屬於V集合

其中Vmap是給定一個example,得到的最可能的目標值.

其中a1...an是這個example裡面的屬性.

這裡面,Vmap目標值,就是後面計算得出的概率最大的一個.所以用max來表示

----

貝葉斯公式應用到

P(Vj | a1,a2...an).

可得到Vmap= arg max P(a1,a2...an | Vj ) P( Vj ) / P (a1,a2...an)

又因為樸素貝葉斯分類器預設a1...an他們互相獨立的.

所以P(a1,a2...an)對於結果沒有用處. [因為所有的概率都要除同一個東西之後再比較大小,最後結果也似乎影響不大]

可得到Vmap=arg max P(a1,a2...an | Vj ) P( Vj )

然後

"樸素貝葉斯分類器基於一個簡單的假定:給定目標值時屬性之間相互條件獨立。換言之。該假定說明給定例項的目標值情況下。觀察到聯合的a1,a2...an的概率正好是對每個單獨屬性的概率乘積:

P(a1,a2...an | Vj ) =Πi P( ai| Vj )

....

樸素貝葉斯分類器:Vnb=arg max P( Vj ) Π i P ( ai | Vj )

其中a1,a2…an為特徵值,Vj為分類的結果。這也體現了貝葉斯決策理論的核心思想,即選擇具有最高概率的決策。它是文件分類的常用演算法。

2:python程式碼的實現—過濾網站惡意留言

情景描述:


(1)   詞表到詞向量的轉換函式

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

# 建立一個包含在所有文件中出現的不重複詞的列表
def createVocabList(dataSet):
    vocabSet = set([])      #建立一個空集
    for document in dataSet:
        vocabSet = vocabSet | set(document)   #建立兩個集合的並集
    return list(vocabSet)

#將文件詞條轉換成詞向量
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0]*len(vocabList)        #建立一個其中所含元素都為0的向量
    for word in inputSet:
        if word in vocabList:
            #returnVec[vocabList.index(word)] = 1     #index函式在字串裡找到字元第一次出現的位置  詞集模型
            returnVec[vocabList.index(word)] += 1      #文件的詞袋模型    每個單詞可以出現多次
        else: print "the word: %s is not in my Vocabulary!" % word
    return returnVec


(2)   從詞向量計算概率

#樸素貝葉斯分類器訓練函式   從詞向量計算概率
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
   # p0Num = zeros(numWords); p1Num = zeros(numWords)
    #p0Denom = 0.0; p1Denom = 0.0
    p0Num = ones(numWords); p1Num = ones(numWords)         #避免一個概率值為0,最後的乘積也為0
    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 = p1Num / p1Denom
    #p0Vect = p0Num / p0Denom
    p1Vect = log(p1Num / p1Denom)
    p0Vect = log(p0Num / p0Denom)      #避免下溢位或者浮點數舍入導致的錯誤   下溢位是由太多很小的數相乘得到的
    return p0Vect, p1Vect, pAbusive


(3)   根據現實情況修改分類器

#樸素貝葉斯分類器
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 = createVocabList(listOPosts)
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))
    testEntry = ['love','my','dalmation']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    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)


注意:主要從以下兩點對分類器進行修改

<1>貝葉斯概率需要計算多個概率的乘積以獲得文件屬於某個類別的概率,即計算p(w0|1)p(w1|1)p(w2|1)。如果其中一個概率值為0,那麼最後的乘積也為0

<2>第二個問題就是下溢位,這是由於太多過小的數相乘造成的。由於大部分因子都非常小,所以程式會下溢位或者得不到正確的答案。解決辦法是對乘積取自然對數這樣可以避免下溢位或者浮點數舍入導致的錯誤。

<3>每個單詞的出現與否作為一個特徵,被稱為詞集模型;在詞袋模型中,每個單詞可以出現多次。

3:案例—過濾垃圾電子郵件

#過濾垃圾郵件
def textParse(bigString):      #正則表示式進行文字解析
    import re
    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)
        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 = createVocabList(docList)
    trainingSet = range(50);testSet = []
    for i in range(10):                         #隨機構建訓練集
        randIndex = int(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(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)

注意:這裡訓練測試的方法是從總的資料集中隨機選擇數字,將其新增到測試集中,同時將其從訓練集中剔除。這種隨機選擇資料的一部分作為訓練集,而剩餘部分作為測試集的過程為留存交叉驗證(hold-out cross validation)。有時為了更精確地估計分類器的錯誤率,就應該進行多次迭代後求出平均錯誤率。

註明:1:本筆記來源於書籍<機器學習實戰>

2:bayes.py檔案及筆記所用資料在這下載(http://download.csdn.net/detail/lu597203933/7732153).

 歡迎轉載或分享,但請務必宣告文章出處。 (新浪微博:小村長zack, 歡迎交流!)