1. 程式人生 > >《機器學習實戰》學習筆記:樸素貝葉斯分類演算法

《機器學習實戰》學習筆記:樸素貝葉斯分類演算法

貝葉斯決策理論

選擇高概率對應的類別是貝葉斯決策理論的核心思想,即選擇具有最高概率的決策。

樸素貝葉斯

樸素貝葉斯法是基於貝葉斯定理特徵條件獨立假設的分類方法  。最為廣泛的兩種分類模型是決策樹模型(Decision Tree Model)和樸素貝葉斯模型(Naive Bayesian Model,NBM),本文主要討論樸素貝葉斯模型。我們稱之為“樸素”,是因為整個形式化過程只做最原始、最簡單的假設。

和決策樹模型相比,樸素貝葉斯分類器(Naive Bayes Classifier,或 NBC)發源於古典數學理論,有著堅實的數學基礎,以及穩定的分類效率。同時,NBC模型所需估計的引數很少,對缺失資料不太敏感,演算法也比較簡單。理論上,NBC模型與其他分類方法相比具有最小的誤差率。但是實際上並非總是如此,這是因為NBC模型假設屬性之間相互獨立,這個假設在實際應用中往往是不成立的,這給NBC模型的正確分類帶來了一定影響。

它也是基於貝葉斯定律的,如下圖,一般的貝葉斯估計就是基於條件概率。

傳送到

事件c發生的概率為P(c),即先驗概率;事件x發生的概率為P(x);在事件c發生的條件下事件x發生的概率為P(x|c);在事件x發生的條件下事件c發生的概率為P(c|x),其中P(x|c)P(c)= P(cx),即事件c、x同時發生的概率。

那麼根據貝葉斯定律:在事件x發生的條件下事件c發生的概率為P(c|x),即後驗概率,等於在事件x發生的條件下事件c、x同時發生的概率。

而樸素貝葉斯演算法則針對多元分類問題,假設在事件x1、x2…xn均發生條件下事件c的概率,這裡假設x1、x2…xn相互獨立,那麼P(x|c)的概率就可以計算為

:P(x|c)= P(x1|c) P(x2|c)* …P(x3|c)。

主要步驟如下:

1、建立資料集和標籤表
2、準備資料:從文字中構建詞向量(無重複的詞向量,通過set集合,和list來實現)
3、根據第二步構建詞向量,將原始資料集向量化,向量的每個元素為1或0
4、樸素貝葉斯分類器訓練函式
5、樸素貝葉斯分類器分類函式

# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce

"""
函式說明:建立實驗樣本

Parameters:
    無
Returns:
    postingList - 實驗樣本切分的詞條
    classVec - 類別標籤向量
Modify:
    2018-10-12
"""
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                                                             #返回實驗樣本切分的詞條和類別標籤向量

"""
函式說明:將切分的實驗樣本詞條整理成不重複的詞條列表,也就是詞彙表

Parameters:
    dataSet - 整理的樣本資料集
Returns:
    vocabSet - 返回不重複的詞條列表,也就是詞彙表
Modify:
    2018-10-12
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #建立一個空的不重複列表
    for document in dataSet:
        vocabSet = vocabSet | set(document) #取並集
    return list(vocabSet)

"""
函式說明:根據vocabList詞彙表,將inputSet向量化,向量的每個元素為1或0

Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的詞條列表
Returns:
    returnVec - 文件向量,詞集模型
Modify:
    2018-10-12
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)                                    #建立一個其中所含元素都為0的向量
    for word in inputSet:                                               #遍歷每個詞條
        if word in vocabList:                                           #如果詞條存在於詞彙表中,則置1
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec                                                    #返回文件向量


"""
函式說明:樸素貝葉斯分類器訓練函式

Parameters:
    trainMatrix - 訓練文件矩陣,即setOfWords2Vec返回的returnVec構成的矩陣
    trainCategory - 訓練類別標籤向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 侮辱類的條件概率陣列
    p1Vect - 非侮辱類的條件概率陣列
    pAbusive - 文件屬於侮辱類的概率
Modify:
    2018-10-12
"""
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)                         #計算訓練的文件數目
    numWords = len(trainMatrix[0])                          #計算每篇文件的詞條數
    pAbusive = sum(trainCategory)/float(numTrainDocs)       #文件屬於侮辱類的概率
    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)  #建立numpy.zeros陣列,
    p0Denom = 0.0; p1Denom = 0.0                            #分母初始化為0.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:                           #統計屬於侮辱類的條件概率所需的資料,即P(w0|1),P(w1|1),P(w2|1)···
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:                                               #統計屬於非侮辱類的條件概率所需的資料,即P(w0|0),P(w1|0),P(w2|0)···
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = p1Num/p1Denom                                  #相除
    p0Vect = p0Num/p0Denom
    return p0Vect,p1Vect,pAbusive                           #返回屬於侮辱類的條件概率陣列,屬於非侮辱類的條件概率陣列,文件屬於侮辱類的概率

"""
函式說明:樸素貝葉斯分類器分類函式

Parameters:
    vec2Classify - 待分類的詞條陣列
    p0Vec - 侮辱類的條件概率陣列
    p1Vec -非侮辱類的條件概率陣列
    pClass1 - 文件屬於侮辱類的概率
Returns:
    0 - 屬於非侮辱類
    1 - 屬於侮辱類
Modify:
    2018-10-12
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = reduce(lambda x,y:x*y, vec2Classify * p1Vec) * pClass1             #對應元素相乘
    p0 = reduce(lambda x,y:x*y, vec2Classify * p0Vec) * (1.0 - pClass1)
    print('p0:',p0)
    print('p1:',p1)
    if p1 > p0:
        return 1
    else:
        return 0

"""
函式說明:測試樸素貝葉斯分類器

Parameters:
    無
Returns:
    無
Modify:
    2018-10-12
"""
def testingNB():
    listOPosts,listClasses = loadDataSet()                                  #建立實驗樣本
    myVocabList = createVocabList(listOPosts)                               #建立詞彙表
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))             #將實驗樣本向量化
    p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))        #訓練樸素貝葉斯分類器
    testEntry = ['love', 'my', 'dalmation']                                 #測試樣本1
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))              #測試樣本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'wuru')                                        #執行分類並列印分類結果
    else:
        print(testEntry,'not belong insult')                                       #執行分類並列印分類結果
    testEntry = ['stupid', 'garbage']                                       #測試樣本2

    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))              #測試樣本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'is belong insult')                                        #執行分類並列印分類結果
    else:
        print(testEntry,'not belong insult')                                       #執行分類並列印分類結果

if __name__ == '__main__':
    testingNB()

執行結果:

應用場景

  • 垃圾郵件分類
  • 大量廣告中學習分類器
  • 文字分類、垃圾文字過濾、情感預測、多分類預測、推薦系統

總結:

優點:

  1. 需估計的引數很少
  2. 缺失資料不太敏感
  3. 演算法邏輯簡單,易於實現(演算法思路很簡單,只要使用貝葉斯公式轉化醫學即可!
  4. 在資料較少的情況下仍然有效,可以處理多類別問題
  5. 分類過程中時空開銷小(假設特徵相互獨立,只會涉及到二維儲存)

缺點:

  1. NBC模型假設屬性之間相互獨立,這個假設在實際應用中往往是不成立的
  2. 需要一個比較容易解釋,而且不同維度之間相關性較小的模型的時候

  3. 可以高效處理高維資料,雖然結果可能不盡如人意

  4. 對於輸入資料的準備方式較為敏感