1. 程式人生 > >機器學習實戰教程(四):樸素貝葉斯基礎篇之言論過濾器

機器學習實戰教程(四):樸素貝葉斯基礎篇之言論過濾器

word 最可 dog 一個 mac github上 開課 和數 基礎上

原文鏈接: Jack-Cui,https://cuijiahua.com/blog/2017/11/ml_4_bayes_1.html

技術分享圖片

一、前言

樸素貝葉斯算法是有監督的學習算法,解決的是分類問題,如客戶是否流失、是否值得投資、信用等級評定等多分類問題。該算法的優點在於簡單易懂、學習效率高、在某些領域的分類問題中能夠與決策樹、神經網絡相媲美。但由於該算法以自變量之間的獨立(條件特征獨立)性和連續變量的正態性假設為前提,就會導致算法精度在某種程度上受影響。

本篇文章將從樸素貝葉斯推斷原理開始講起,通過實例進行輔助講解。最後,使用Python3編程實現一個簡單的言論過濾器。

技術分享圖片

二、樸素貝葉斯理論

樸素貝葉斯是貝葉斯決策理論的一部分,所以在講述樸素貝葉斯之前有必要快速了解一下貝葉斯決策理論。

1、貝葉斯決策理論

假設現在我們有一個數據集,它由兩類數據組成,數據分布如下圖所示:

技術分享圖片

我們現在用p1(x,y)表示數據點(x,y)屬於類別1(圖中紅色圓點表示的類別)的概率,用p2(x,y)表示數據點(x,y)屬於類別2(圖中藍色三角形表示的類別)的概率,那麽對於一個新數據點(x,y),可以用下面的規則來判斷它的類別:

  • 如果p1(x,y)>p2(x,y),那麽類別為1
  • 如果p1(x,y)<p2(x,y),那麽類別為2

也就是說,我們會選擇高概率對應的類別。這就是貝葉斯決策理論的核心思想,即選擇具有最高概率的決策。已經了解了貝葉斯決策理論的核心思想,那麽接下來,就是學習如何計算p1和p2概率。

2、條件概率

在學習計算p1 和p2概率之前,我們需要了解什麽是條件概率(Condittional probability),就是指在事件B發生的情況下,事件A發生的概率,用P(A|B)來表示。

技術分享圖片

根據文氏圖,可以很清楚地看到在事件B發生的情況下,事件A發生的概率就是P(A∩B)除以P(B)。

技術分享圖片

因此,

技術分享圖片

同理可得,

技術分享圖片

所以,

技術分享圖片

技術分享圖片

這就是條件概率的計算公式。

3、全概率公式

除了條件概率以外,在計算p1和p2的時候,還要用到全概率公式,因此,這裏繼續推導全概率公式。

假定樣本空間S,是兩個事件A與A‘的和。

技術分享圖片

上圖中,紅色部分是事件A,綠色部分是事件A‘,它們共同構成了樣本空間S。

在這種情況下,事件B可以劃分成兩個部分。

技術分享圖片

技術分享圖片

在上一節的推導當中,我們已知

技術分享圖片

所以,

技術分享圖片

這就是全概率公式。它的含義是,如果A和A‘構成樣本空間的一個劃分,那麽事件B的概率,就等於A和A‘的概率分別乘以B對這兩個事件的條件概率之和。

將這個公式代入上一節的條件概率公式,就得到了條件概率的另一種寫法:

技術分享圖片

4、貝葉斯推斷

對條件概率公式進行變形,可以得到如下形式:

技術分享圖片

我們把P(A)稱為"先驗概率"(Prior probability),即在B事件發生之前,我們對A事件概率的一個判斷。

P(A|B)稱為"後驗概率"(Posterior probability),即在B事件發生之後,我們對A事件概率的重新評估。

P(B|A)/P(B)稱為"可能性函數"(Likelyhood),這是一個調整因子,使得預估概率更接近真實概率。

所以,條件概率可以理解成下面的式子:

後驗概率 = 先驗概率 x 調整因子

這就是貝葉斯推斷的含義。我們先預估一個"先驗概率",然後加入實驗結果,看這個實驗到底是增強還是削弱了"先驗概率",由此得到更接近事實的"後驗概率"

在這裏,如果"可能性函數"P(B|A)/P(B)>1,意味著"先驗概率"被增強,事件A的發生的可能性變大;如果"可能性函數"=1,意味著B事件無助於判斷事件A的可能性;如果"可能性函數"<1,意味著"先驗概率"被削弱,事件A的可能性變小。

為了加深對貝葉斯推斷的理解,我們舉一個例子。

技術分享圖片

兩個一模一樣的碗,一號碗有30顆水果糖和10顆巧克力糖,二號碗有水果糖和巧克力糖各20顆。現在隨機選擇一個碗,從中摸出一顆糖,發現是水果糖。請問這顆水果糖來自一號碗的概率有多大?

我們假定,H1表示一號碗,H2表示二號碗。由於這兩個碗是一樣的,所以P(H1)=P(H2),也就是說,在取出水果糖之前,這兩個碗被選中的概率相同。因此,P(H1)=0.5,我們把這個概率就叫做"先驗概率",即沒有做實驗之前,來自一號碗的概率是0.5。

再假定,E表示水果糖,所以問題就變成了在已知E的情況下,來自一號碗的概率有多大,即求P(H1|E)。我們把這個概率叫做"後驗概率",即在E事件發生之後,對P(H1)的修正。

根據條件概率公式,得到
技術分享圖片

已知,P(H1)等於0.5,P(E|H1)為一號碗中取出水果糖的概率,等於30÷(30+10)=0.75,那麽求出P(E)就可以得到答案。根據全概率公式,

技術分享圖片

所以,

技術分享圖片

將數字代入原方程,得到

技術分享圖片

這表明,來自一號碗的概率是0.6。也就是說,取出水果糖之後,H1事件的可能性得到了增強。

同時再思考一個問題,在使用該算法的時候,如果不需要知道具體的類別概率,即上面P(H1|E)=0.6,只需要知道所屬類別,即來自一號碗,我們有必要計算P(E)這個全概率嗎?要知道我們只需要比較 P(H1|E)和P(H2|E)的大小,找到那個最大的概率就可以。既然如此,兩者的分母都是相同的,那我們只需要比較分子即可。即比較P(E|H1)P(H1)和P(E|H2)P(H2)的大小,所以為了減少計算量,全概率公式在實際編程中可以不使用

5、樸素貝葉斯推斷

理解了貝葉斯推斷,那麽讓我們繼續看看樸素貝葉斯。貝葉斯和樸素貝葉斯的概念是不同的,區別就在於“樸素”二字,樸素貝葉斯對條件個概率分布做了條件獨立性的假設。 比如下面的公式,假設有n個特征:

技術分享圖片

由於每個特征都是獨立的,我們可以進一步拆分公式 :

技術分享圖片

這樣我們就可以進行計算了。如果有些迷糊,讓我們從一個例子開始講起,你會看到貝葉斯分類器很好懂,一點都不難。

某個醫院早上來了六個門診的病人,他們的情況如下表所示:

技術分享圖片

現在又來了第七個病人,是一個打噴嚏的建築工人。請問他患上感冒的概率有多大?

根據貝葉斯定理:

技術分享圖片

可得:

技術分享圖片

根據樸素貝葉斯條件獨立性的假設可知,"打噴嚏"和"建築工人"這兩個特征是獨立的,因此,上面的等式就變成了

技術分享圖片

這裏可以計算:

技術分享圖片

因此,這個打噴嚏的建築工人,有66%的概率是得了感冒。同理,可以計算這個病人患上過敏或腦震蕩的概率。比較這幾個概率,就可以知道他最可能得什麽病。

這就是貝葉斯分類器的基本方法:在統計資料的基礎上,依據某些特征,計算各個類別的概率,從而實現分類。

同樣,在編程的時候,如果不需要求出所屬類別的具體概率,P(打噴嚏) = 0.5和P(建築工人) = 0.33的概率是可以不用求的。

三、動手實戰

說了這麽多,沒點實踐編程怎麽行?

以在線社區留言為例。為了不影響社區的發展,我們要屏蔽侮辱性的言論,所以要構建一個快速過濾器,如果某條留言使用了負面或者侮辱性的語言,那麽就將該留言標誌為內容不當。過濾這類內容是一個很常見的需求。對此問題建立兩個類型:侮辱類和非侮辱類,使用1和0分別表示。

我們把文本看成單詞向量或者詞條向量,也就是說將句子轉換為向量。考慮出現所有文檔中的單詞,再決定將哪些單詞納入詞匯表或者說所要的詞匯集合,然後必須要將每一篇文檔轉換為詞匯表上的向量。簡單起見,我們先假設已經將本文切分完畢,存放到列表中,並對詞匯向量進行分類標註。編寫代碼如下:

# -*- coding: UTF-8 -*-
 
"""
函數說明:創建實驗樣本
 
Parameters:
    無
Returns:
    postingList - 實驗樣本切分的詞條
    classVec - 類別標簽向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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
 
if __name__ == ‘__main__‘:
    postingLIst, classVec = loadDataSet()
    for each in postingLIst:
        print(each)
    print(classVec)

從運行結果可以看出,我們已經將postingList是存放詞條列表中,classVec是存放每個詞條的所屬類別,1代表侮辱類 ,0代表非侮辱類。

技術分享圖片

繼續編寫代碼,前面我們已經說過我們要先創建一個詞匯表,並將切分好的詞條轉換為詞條向量。

# -*- coding: UTF-8 -*-
 
"""
函數說明:創建實驗樣本
 
Parameters:
    無
Returns:
    postingList - 實驗樣本切分的詞條
    classVec - 類別標簽向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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
 
"""
函數說明:根據vocabList詞匯表,將inputSet向量化,向量的每個元素為1或0
 
Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的詞條列表
Returns:
    returnVec - 文檔向量,詞集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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:
    dataSet - 整理的樣本數據集
Returns:
    vocabSet - 返回不重復的詞條列表,也就是詞匯表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #創建一個空的不重復列表
    for document in dataSet:               
        vocabSet = vocabSet | set(document) #取並集
    return list(vocabSet)
 
if __name__ == ‘__main__‘:
    postingList, classVec = loadDataSet()
    print(‘postingList:\n‘,postingList)
    myVocabList = createVocabList(postingList)
    print(‘myVocabList:\n‘,myVocabList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    print(‘trainMat:\n‘, trainMat)

從運行結果可以看出,postingList是原始的詞條列表,myVocabList是詞匯表。myVocabList是所有單詞出現的集合,沒有重復的元素。詞匯表是用來幹什麽的?沒錯,它是用來將詞條向量化的,一個單詞在詞匯表中出現過一次,那麽就在相應位置記作1,如果沒有出現就在相應位置記作0。trainMat是所有的詞條向量組成的列表。它裏面存放的是根據myVocabList向量化的詞條向量。

技術分享圖片

我們已經得到了詞條向量。接下來,我們就可以通過詞條向量訓練樸素貝葉斯分類器。

# -*- coding: UTF-8 -*-
import numpy as np
 
"""
函數說明:創建實驗樣本
 
Parameters:
    無
Returns:
    postingList - 實驗樣本切分的詞條
    classVec - 類別標簽向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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
 
"""
函數說明:根據vocabList詞匯表,將inputSet向量化,向量的每個元素為1或0
 
Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的詞條列表
Returns:
    returnVec - 文檔向量,詞集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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:
    dataSet - 整理的樣本數據集
Returns:
    vocabSet - 返回不重復的詞條列表,也就是詞匯表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #創建一個空的不重復列表
    for document in dataSet:
        vocabSet = vocabSet | set(document) #取並集
    return list(vocabSet)
 
"""
函數說明:樸素貝葉斯分類器訓練函數
 
Parameters:
    trainMatrix - 訓練文檔矩陣,即setOfWords2Vec返回的returnVec構成的矩陣
    trainCategory - 訓練類別標簽向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 非侮辱類的條件概率數組
    p1Vect - 侮辱類的條件概率數組
    pAbusive - 文檔屬於侮辱類的概率
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-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數組,詞條出現數初始化為0
    p0Denom = 0.0; p1Denom = 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                            #返回屬於侮辱類的條件概率數組,屬於非侮辱類的條件概率數組,文檔屬於侮辱類的概率
 
if __name__ == ‘__main__‘:
    postingList, classVec = loadDataSet()
    myVocabList = createVocabList(postingList)
    print(‘myVocabList:\n‘, myVocabList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(trainMat, classVec)
    print(‘p0V:\n‘, p0V)
    print(‘p1V:\n‘, p1V)
    print(‘classVec:\n‘, classVec)
    print(‘pAb:\n‘, pAb)

運行結果如下,p0V存放的是每個單詞屬於類別0,也就是非侮辱類詞匯的概率。比如p0V的倒數第6個概率,就是stupid這個單詞屬於非侮辱類的概率為0。同理,p1V的倒數第6個概率,就是stupid這個單詞屬於侮辱類的概率為0.15789474,也就是約等於15.79%的概率。我們知道stupid的中文意思是蠢貨,難聽點的叫法就是傻逼。顯而易見,這個單詞屬於侮辱類。pAb是所有侮辱類的樣本占所有樣本的概率,從classVec中可以看出,一用有3個侮辱類,3個非侮辱類。所以侮辱類的概率是0.5。因此p0V存放的就是P(非侮辱類 | him) = 0.0833,P(非侮辱類 | is) = 0.0417,一直到P(非侮辱類 | dog) = 0.0417,這些單詞的條件概率。同理,p1V存放的就是各個單詞屬於侮辱類的條件概率。pAb就是先驗概率。

技術分享圖片

已經訓練好分類器,接下來,使用分類器進行分類。

# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce
 
"""
函數說明:創建實驗樣本
 
Parameters:
    無
Returns:
    postingList - 實驗樣本切分的詞條
    classVec - 類別標簽向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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 - 返回不重復的詞條列表,也就是詞匯表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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 - 文檔向量,詞集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
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 - 文檔屬於侮辱類的概率
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-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 - 屬於侮辱類
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-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:
    無
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-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,‘屬於侮辱類‘)                                        #執行分類並打印分類結果
    else:
        print(testEntry,‘屬於非侮辱類‘)                                        #執行分類並打印分類結果
    testEntry = [‘stupid‘, ‘garbage‘]                                        #測試樣本2
 
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))                #測試樣本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,‘屬於侮辱類‘)                                        #執行分類並打印分類結果
    else:
        print(testEntry,‘屬於非侮辱類‘)                                        #執行分類並打印分類結果
 
if __name__ == ‘__main__‘:
    testingNB()

我們測試了兩個詞條,在使用分類器前,也需要對詞條向量化,然後使用classifyNB()函數,用樸素貝葉斯公式,計算詞條向量屬於侮辱類和非侮辱類的概率。運行結果如下:

技術分享圖片

你會發現,這樣寫的算法無法進行分類,p0和p1的計算結果都是0,顯然結果錯誤。這是為什麽呢?下一篇文章繼續講解~

四、總結

樸素貝葉斯推斷的一些優點

  • 生成式模型,通過計算概率來進行分類,可以用來處理多分類問題。
  • 對小規模的數據表現很好,適合多分類任務,適合增量式訓練,算法也比較簡單。

樸素貝葉斯推斷的一些缺點

  • 對輸入數據的表達形式很敏感。
  • 由於樸素貝葉斯的“樸素”特點,所以會帶來一些準確率上的損失。
  • 需要計算先驗概率,分類決策存在錯誤率。

其它

  • 本文中的編程實例,存在一定的問題,需要進行改進,下篇文章會講解改進方法;
  • 同時,本文中的編程實例,沒有進行前期的文本切分,下一篇文章會講解英文單詞和中文單詞的切分方法;
  • 下篇文章將使用sklearn進行中文實例練習;
  • 樸素貝葉斯的準確率,其實是比較依賴於訓練語料的,機器學習算法就和純潔的小孩一樣,取決於其成長(訓練)條件,"吃的是草擠的是奶",但"不是所有的牛奶,都叫特侖蘇"。
  • 參考文獻:http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html
  • 如有問題,請留言。如有錯誤,還望指正,謝謝!

PS: 如果覺得本篇本章對您有所幫助,歡迎關註、評論、贊!

本文出現的所有代碼和數據集,均可在我的github上下載,歡迎Follow、Star:https://github.com/Jack-Cherish/Machine-Learning


相關文章和視頻推薦

圓方圓學院匯集 Python + AI 名師,打造精品的 Python + AI 技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。
公開課地址:

機器學習實戰教程(四):樸素貝葉斯基礎篇之言論過濾器