1. 程式人生 > >深入理解樸素貝葉斯(Naive Bayes)

深入理解樸素貝葉斯(Naive Bayes)

之間 計算 else 不同 樸素 foo 防止 zeros 甜美

https://blog.csdn.net/li8zi8fa/article/details/76176597

樸素貝葉斯是經典的機器學習算法之一,也是為數不多的基於概率論的分類算法。樸素貝葉斯原理簡單,也很容易實現,多用於文本分類,比如垃圾郵件過濾。該算法雖然簡單,但是由於筆者不常用,總是看過即忘,這是寫這篇博文的初衷。當然,更大的動力來在於跟大家交流,有論述不妥的地方歡迎指正。

1.算法思想——基於概率的預測

邏輯回歸通過擬合曲線(或者學習超平面)實現分類,決策樹通過尋找最佳劃分特征進而學習樣本路徑實現分類,支持向量機通過尋找分類超平面進而最大化類別間隔實現分類。相比之下,樸素貝葉斯獨辟蹊徑,通過考慮特征概率來預測分類。舉個可能不太恰當的例子:眼前有100個人,好人和壞人個數差不多,現在要用他們來訓練一個“壞蛋識別器”。怎麽辦呢?咱們不管他們幹過啥事,只看他們長啥樣(這確實不是個恰當的例子)。也就是說,我們在區分好壞人時,只考慮他們的樣貌特征。比如說“笑”這個特征,它的取值可能是“甜美的笑”、“儒雅的笑”、“憨厚的笑”、“沒心沒肺的笑”、“微微一笑”,等等——這都是“好人的笑”;也可以是“陰險的笑”、“不屑的笑”、“色瞇瞇的笑”、“任我行似的笑”、“冷笑”、“皮笑肉不笑”,等等——這很可能是“壞人的笑”。單就“笑”這個特征來說,一個好人發出“好人的笑”的概率更大,而且頻率更高;而壞人則發出“壞人的笑”的概率更大,頻率更高(電視上總能看見作奸犯科的人在暗地裏發出挨千刀的笑)。當然,好人也有發出壞笑的時候(那種偶像劇裏面男豬腳“壞壞的笑”),壞人也有發出好人的笑的時候(想想《不要和陌生人說話》裏面的馮遠征),這些就都是噪聲了。

除了笑之外,這裏可用的特征還有紋身,性別等可以考慮。樸素貝葉斯把類似“笑”這樣的特征概率化,構成一個“人的樣貌向量”以及對應的“好人/壞人標簽”,訓練出一個標準的“好人模型”和“壞人模型”,這些模型都是各個樣貌特征概率構成的。這樣,當一個品行未知的人來以後,我們迅速獲取ta的樣貌特征向量,分布輸入“好人模型”和“壞人模型”,得到兩個概率值。如果“壞人模型”輸出的概率值大一些,那這個人很有可能就是個大壞蛋了。

決策樹是怎麽辦的呢?決策樹可能先看性別,因為它發現給定的帶標簽人群裏面男的壞蛋特別多,這個特征眼下最能區分壞蛋和好人,然後按性別把一撥人分成兩撥;接著看“笑”這個特征,因為它是接下來最有區分度的特征,然後把兩撥人分成四撥;接下來看紋身,,,,最後發現好人要麽在田裏種地,要麽在山上砍柴,要麽在學堂讀書。而壞人呢,要麽在大街上溜達,要麽在地下買賣白粉,要麽在海裏當海盜。這些個有次序的特征就像路上的一個個墊腳石(樹的節點)一樣,構成通往不同地方的路徑(樹的枝丫),這些不同路徑的目的地(葉子)就是一個類別容器,包含了一類人。一個品行未知的人來了,按照其樣貌特征順序及其對應的特征值,不斷走啊走,最後走到了農田或山上,那就是好人;走到了地下或大海,那就是大壞蛋。(這是個看臉的例子,但重點不是“臉”,是“例子”,這真的只是個沒有任何偏見的例子)。可以看出來,兩種分類模型的原理是很不相同。

2.理論基礎——條件概率,詞集模型、詞袋模型

條件概率:樸素貝葉斯最核心的部分是貝葉斯法則,而貝葉斯法則的基石是條件概率。貝葉斯法則如下:

這裏的C表示類別,輸入待判斷數據,式子給出要求解的某一類的概率。我們的最終目的是比較各類別的概率值大小,
而上面式子的分母是不變的,因此只要計算分子即可。仍以“壞蛋識別器”為例。我們用C0表示好人,C1表示壞人,
現在100個人中有60個好人,則P(C0)=0.6,那麽P(x,y|C0)怎麽求呢?註意,這裏的(x,y)是多維的,因為有
60個好人,每個人又有“性別”、“笑”、“紋身”等多個特征,這些構成X,y是標簽向量,有60個0和40個1構成。
這裏我們假設X的特征之間是獨立的,互相不影響,這就是樸素貝葉斯中“樸素”的由來。在假設特征間獨立的假設下
,很容易得到P(x,y|C0)=P(x0,y0|C0)P(x1,y1|C0)...P(xn,yn|C0)。然而,P(xn,yn|C0),n=0,1,
...,n如何求呢?有兩種情況,涉及到詞集模型和詞袋模型。接下來我們舉個更合適的例子,那就是文本分類。我們
的訓練集由正常的文檔和侮辱性的文檔組成,能反映侮辱性文檔的是侮辱性詞匯的出現與否以及出現頻率。

詞集模型:對於給定文檔,只統計某個侮辱性詞匯(準確說是詞條)是否在本文檔出現
詞袋模型:對於給定文檔,統計某個侮辱性詞匯在本文當中出現的頻率,除此之外,往往還需要剔除重要性極低的高頻詞和停用詞。因此,詞袋模型更精煉,也更有效。

需要解釋的是,為了高效計算,求解P(x,y|C0)時是向量化操作的,因此不會一個個的求解P(xn,yn|C0)。

3.數據預處理——向量化

向量化、矩陣化操作是機器學習的追求。從數學表達式上看,向量化、矩陣化表示更加簡潔;在實際操作中,矩陣化(向量是特殊的矩陣)更高效。仍然以侮辱性文檔識別為例:

首先,我們需要一張詞典,該詞典囊括了訓練文檔集中的所有必要詞匯(無用高頻詞和停用詞除外),還需要把每個文檔剔除高頻詞和停用詞;

其次,根據詞典向量化每個處理後的文檔。具體的,每個文檔都定義為詞典大小,分別遍歷某類(侮辱性和非侮辱性)文檔中的每個詞匯並統計出現次數;最後,得到一個個跟詞典一樣大小的向量,這些向量有一個個整數組成,每個整數代表了詞典上一個對應位置的詞在當下文檔中的出現頻率。

最後,統計每一類處理過的文檔中詞匯總個數,某一個文檔的詞頻向量除以相應類別的詞匯總個數,即得到相應的條件概率,如P(x,y|C0)。有了P(x,y|C0)和P(C0),P(C0|x,y)就得到了,用完全一樣的方法可以獲得

P(C1|x,y)。比較它們的大小,即可知道某人是不是大壞蛋,某篇文檔是不是侮辱性文檔了。

4.Python代碼解讀

def loadDataSet():
postingList=[[‘my‘,‘dog‘,‘has‘,‘flea‘,‘problem‘,‘help‘,‘please‘],
[‘maybe‘,‘not‘,‘take‘,‘him‘,‘to‘,‘dog‘,‘park‘,‘stupid‘],
[‘my‘,‘dalmation‘,‘is‘,‘so‘,‘cute‘,‘I‘,‘love‘,‘him‘],
[‘stop‘,‘posting‘,‘ate‘,‘my‘,‘steak‘,‘how‘,‘to‘,‘stop‘,‘him‘],
[‘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
#定義一個簡單的文本數據集,由6個簡單的文本以及對應的標簽構成。1表示侮辱性文檔,0表示正常文檔。
def createVocabList(dataSet):
vocabSet=set([])
for document in dataSet:
vocabSet=vocabSet|set(document)
return list(vocabSet)
def setOfWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList) #每個文檔的大小與詞典保持一致,此時returnVec是空表
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]=1 #當前文檔中有某個詞條,則根據詞典獲取其位置並賦值1
else:print "the word :%s is not in my vocabulary" %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 vocabulary" %word
return returnVec
#### 文檔向量化,這裏是詞袋模型,不知關心某個詞條出現與否,還考慮該詞條在本文檔中的出現頻率

def trainNB(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)
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])
#p1Vec=p1Num/float(p1Denom)
#p0Vec=p0Num/float(p0Denom)
p1Vec=log(p1Num/p1Denom) #統計詞典中所有詞條在侮辱性文檔中出現的概率
p0Vec=log(p0Num/p0Denom) #統計詞典中所有詞條在正常文檔中出現的概率
return pAbusive,p1Vec,p0Vec
#### 訓練生成樸素貝葉斯模型,實質上相當於是計算P(x,y|Ci)P(Ci)的權重。
### 註意:被註釋掉的代碼代表不太好的初始化方式,在那種情況下某些詞條的概率值可能會非常非常小,甚至約
###等於0,那麽在不同詞條的概率在相乘時結果就近似於0
def classifyNB(vec2classify,p0Vec,p1Vec,pClass1):  # 參數1是測試文檔向量,參數2和參數3是詞條在各個
#類別中出現的概率,參數4是P(C1)
p1=sum(vec2classify*p1Vec)+log(pClass1)   # 這裏沒有直接計算P(x,y|C1)P(C1),而是取其對數
#這樣做也是防止概率之積太小,以至於為0
p0=sum(vec2classify*p0Vec)+log(1.0-pClass1) #取對數後雖然P(C1|x,y)和P(C0|x,y)的值變了,但是
#不影響它們的大小關系。
if p1>p0:
return 1
else:
return 0



5.總結

不同於其它分類器,樸素貝葉斯是一種基於概率理論的分類算法;
特征之間的條件獨立性假設,顯然這種假設顯得“粗魯”而不符合實際,這也是名稱中“樸素”的由來。然而事實證明,樸素貝葉斯在有些領域很有用,比如垃圾郵件過濾;
在具體的算法實施中,要考慮很多實際問題。比如因為“下溢”問題,需要對概率乘積取對數;再比如詞集模型和詞袋模型,還有停用詞和無意義的高頻詞的剔除,以及大量的數據預處理問題,等等;
總體上來說,樸素貝葉斯原理和實現都比較簡單,學習和預測的效率都很高,是一種經典而常用的分類算法。
---------------------
作者:千君一發
來源:CSDN
原文:https://blog.csdn.net/li8zi8fa/article/details/76176597
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

深入理解樸素貝葉斯(Naive Bayes)