機器學習 學習筆記(11) 貝葉斯分類器
貝葉斯決策論是在概率框架下實施決策的基本方法。對分類任務來說,在所有相關概率都已知的理想情形下,貝葉斯決策論考慮如何基於這些概率和誤判損失來選擇最優的類別標記,
假設有N種可能的類別標記,即,是將一個真實標記為的樣本誤分類為所產生的損失,則基於後驗概率可獲得將樣本x分類為所產生的期望損失,記在樣本x上的條件風險
希望找到一個判定準則h以最小化總體風險
顯然,對於每個樣本x,若h能最小化條件風險,則總體風險也將被最小化,這就產生了貝葉斯判定準則:為最小化總體風險,只需在每個樣本上選擇那個能使條件風險最小的類別標記,即,此時,稱為貝葉斯最優分類器,與之對應的總體風險稱為貝葉斯風險。反映了分類器所能達到的最好效能,即通過機器學習所能產生的模型精度的理論上限。
若誤判損失用0/1損失來表示,則條件風險為,於是,最小化分類錯誤率的貝葉斯最優分類器為,即對每個樣本x,選擇能使後驗概率P(c|x)最大的類別標記。
要求解,主要有兩種策略:給定x,可通過直接建模來預測c,這樣得到的是判別式模型,也可先對聯合概率分佈建模,然後在由此獲得,這樣得到的是生成式模型。
生成式模型必然考慮:,基於貝葉斯定理:
其中是類“先驗”概率,是樣本x相對與類標記c的類條件概率,或稱為似然。是用於歸一化的證據因子。對給定樣本x,證據因子與類標記無關,因此估計的問題就轉化為如何基於訓練資料D來估計先驗和。
估計類條件概率的一種常用策略是先假定其具有某種確定的概率分佈形式,再基於訓練樣本對概率分佈的引數進行估計。具體的,記關於類別c的類條件概率為
概率模型的訓練過程就是引數估計過程。
極大似然法(Maximum Likelihood Estimation,MLE),根據資料取樣來估計概率分佈。
令表示訓練集D中第c類樣本組合的集合,假設這些樣本是獨立同分布的,則引數對於資料集的似然是:
對進行極大似然估計,就是去尋找能最大化似然的引數值。連乘容易造成下溢,通常使用對數似然:
利用引數化的方法雖能使類條件概率估計變得相對簡單,但估計結果的準確性嚴重依賴於所假設的概率分佈形式是否符合潛在的正式資料分佈。
樸素貝葉斯分類器
樸素貝葉斯分類器採用了屬性條件獨立假設:對已知類別,假設所有屬性相互獨立,換言之,假設每個屬性獨立地對分類結果產生影響。
基於屬性條件獨立假設:
d為屬性數目,為x在第i個屬性上的取值,基於貝葉斯判定準則有:
,這就是樸素貝葉斯分類器的表示式。
顯然,樸素貝葉斯分類器的訓練過程就是基於訓練集D來估計類先驗概率P(c),併為每個屬性估計P(xi|c)。
令Dc表示訓練集的中第c類樣本組成的集合,若有充足的獨立同分布樣本,則可容易地估計出類先驗概率,
對離散屬性而言,令表示Dc中在第i個屬性上取值為xi的樣本組成的集合,則條件概率P(xi|c)可估計為:
對連續屬性可考慮概率密度函式,假定,其中和分別是第c類樣本在第i個屬性上取值的均值和方差,則有:
但是某個屬性值在訓練集中沒有與某個類同時出現過,直接進行概率計算時會導致0概率的出現。
為了避免其他屬性攜帶的資訊被訓練集中未出現的屬性值“抹去”,在估計概率值時通常要進行“平滑”,常用“拉普拉斯修正”,具體來說,令N表示訓練集D中可能的類別,表示第i個屬性可能的取值數,則:
拉普拉斯修正避免了因訓練集樣本不充分而導致概率估值0的問題,並且在訓練集變大時,修正過程所引入的先驗的影響也會逐漸變得可忽略,使得估值漸趨向於實際概率值。
半樸素貝葉斯分類器
對屬性條件獨立性假設進行一定程度的放鬆,產生了半樸素貝葉斯分類器。半樸素貝葉斯分類器的基本想法是適當考慮一部分屬性間的相互依賴資訊,從而既不需要進行完全聯合概率計算,又不至於徹底忽略了比較強的屬性依賴關係。獨依賴估計(One-Dependent Estimator,ODE)是半樸素貝葉斯分類器最常用的一種策略,顧名思義,獨依賴就是假設每個屬性在類別之外最多僅依賴於一個其他屬性。,為屬性所依賴的屬性,稱為的父屬性,若副屬性已知,就可以可以概率值。問題的關鍵轉化為如何確定每個屬性的父屬性,不同的做法產生不同的獨依賴分類器。
最直接的做法是假設所有屬性都依賴於同一個屬性,稱為“超父”,然後通過交叉驗證等方法來確定超父屬性,由此形成了SPODE(Super-Parent ODE方法)。在下圖(b)中,x1是超父屬性。
TAN則是在最大帶權生成樹演算法的基礎上通過以下步驟將屬性之間的依賴關係約簡為如(c)所示的樹形結構:
(1)計算任意兩個屬性之間的條件互資訊,
(2)以屬性為結點構建完全圖,任意兩個結點之間邊的權重設為
(3)構建此完全圖的最大帶權生成樹,挑選根變數,將邊置為有向
(4)加入類別結點y,增加從y到每個屬性的有向邊
條件互資訊刻畫了屬性和在已知類別情況下的相關性,因此,通過最大生成樹演算法,TAN僅僅保留了強相關屬性之間的依賴性。
AODE(Averaged One-Dependent Estimator)是一種基於整合的學習機制,更為強大的獨依賴分類器,與SPODE通過模型選擇確定超父屬性不同,AODE嘗試將每個屬性作為超父來構建SPODE,然後將那些具有足夠訓練資料支撐的SPODE整合起來作為最終結果,即:
其中是在第i個屬性上取值為的樣本集合,為閾值常數,顯然,AODE需估計和。
N是D中可能的類別數,是第i個屬性可能的取值數,是類別為c且在第i個屬性上取值為的樣本集合,是類別為c且在第i個和第j個屬性上取值分別為和的樣本集合。
與樸素貝葉斯分類器類似,AODE的訓練過程也是計數,即在訓練資料集上對符合條件的樣本進行計數的過程。與樸素貝葉斯分類器相似,AODE無需模型選擇,既能通過預計計算節省預測時間,也能採取懶惰學習方式在預測時再進行計數,並且易於實現增量學習。
貝葉斯網
貝葉斯網亦稱為信念網(belief network),它藉助有向無環圖(Directed Acyclic Graph,DAG)來刻畫屬性之間依賴關係,並使用條件概率表(Conditional Probability Table, CPT)來描述屬性的聯合概率分佈。
一個貝葉斯網B由結構G和引數兩部分構成,即。網路結構G是一個有向無環圖,其每個結點對應一個屬性,若兩個屬性有直接依賴關係,則它們由一條邊連線起來,引數定量描述這種依賴關係,假設屬性在G中父結點集為,則包含了每個屬性的條件概率表,
貝葉斯網結構有效地表達了屬性間的條件獨立性,給定父結點集,貝葉斯網假設每個屬性與它的非後裔屬性獨立,於是將屬性集的聯合概率分佈定義為
貝葉斯網中三個變數之間的典型依賴關係:
在同父結構中,給定父結點的取值,則與條件獨立。在順序結構中,給定x的值,則y與z條件獨立。V型結構也稱為衝撞結構,給定的值,與必不獨立,若取值完全未知,則V型結構下,與相互獨立。稱為邊際獨立性。
為了分析有向圖中變數間的條件獨立性,可以使用有向分離,先把有向圖轉變為一個無向圖:
- 找出有向圖中的所有V型結構,在V型結構的兩個父結點之間加上一條無向邊
- 將所有有向邊改為無向邊
由此產生的無向圖稱為道德圖,令父結點相連的過程稱為道德化。
基於道德圖能直觀、迅速地找到變數間的條件獨立性。假定道德圖中有變數x,y和變數集合,若變數x和y能在圖上被z分開,即從道德圖中將變數集合z去除後,x和y分屬兩個連通分支,則稱變數x和y被z有向分離。
貝葉斯網路學習的首要任務是根據訓練資料集來找出結構最恰當的貝葉斯網。“評分搜尋”是求解這一問題的常永方法。具體來說,我們定義一個評分函式,以此來估計貝葉斯我那個與訓練資料的契合程度,然後基於這個評分函式來尋找結構最優的貝葉斯網。
常用評分函式通常基於資訊理論準則,此類準則將學習問題看做一個數據壓縮任務,學習的目標是找到一個能以最短編碼長度描述訓練資料的模型,此時編碼的長度包括了描述模型自身所需的位元組長度和使用該模型描述資料所需的位元組長度。對貝葉斯網學習而言,模型就是一個貝葉斯網,同時每個貝葉斯網描述了一個在訓練資料集上的概率分佈,自有一套編碼機制能使哪些經常出現的樣本有更短的編碼。於是,我們應該選擇那個綜合編碼長度(包括描述網路和編碼資料)最短的貝葉斯網,這就是“最小描述長度”(Minimal Description Length,MDL).
給定資料集,貝葉斯網在D上的評分函式可寫為:,|B|是貝葉斯網引數個數,表示描述每個引數所需要的位元組數,而是貝葉斯網B的對數似然。第一項計算編碼貝葉斯網B所需的位元組數,第二項是計算B所對應的概率分佈對D描述得多好。學習任務轉為一個優化任務,尋找一個貝葉斯網B使得評分函式最小。
若,即每個引數用1位元組描述,則得到AIC評分函式。若,即每個引數用位元組描述,則得到BIC評分函式。
從所有可能的網路結構空間中搜索最優貝葉斯網路結構是一個NP難問題,難以快速求解。有兩種常用的策略能在有限時間內求得近似解:第一種是貪心法,例如從某個網路結構除法,每次調整一條邊(增加、刪除或調整方向),直到評分函式值不再降低為止,第二種是通過給網路結構施加約束來削減搜尋空間,例如將網路結構限定為樹形結構等。
通過已知變數觀測值來推測待查詢變數的過程稱為推斷,一直變數觀測值稱為證據。最理想的是直接根據貝葉斯網定義的聯合概率分佈來精確計算後驗概率,但是這被證明是NP難的。現實應用中,貝葉斯網的近似推斷經常使用吉布斯取樣來完成,這是一種隨機取樣方法。
令表示待查詢變數,為證據變數,已知其取值為。目標是計算後驗概率,其中是待查詢變數的一組取值。吉布斯取樣演算法先隨機產生一個與證據E=e一致的樣本作為初始點,然後每步從當前樣本出發產生下一個樣本。具體來說,在第t次取樣中,演算法先假設,然後對非證據變數逐個進行取樣改變其取值,取樣概率根據貝葉斯網B和其他變數的當前取值(Z=z)計算獲得。假定經過T次取樣得到的與q一致的樣本共有個,則可近似估算出後驗概率:。
事實上,吉布斯取樣是在貝葉斯網所有變數的聯合狀態空間與證據E=e一致的子空間中進行隨機漫步,每一步僅依賴於前一步的狀態,這是一個馬爾科夫鏈。在一定條件下,無論從什麼初始狀態開始,馬爾科夫鏈的第t步的狀態分佈在t趨於無窮時必收斂於一個平穩分佈。對於吉布斯取樣而言,這個分佈恰好是,因此在T很大時,吉布斯取樣相當於根據取樣,從而保證了收斂到。
由於馬爾科夫鏈通常需要很長時間才能趨於平穩分佈,因此吉布斯取樣演算法的收斂速度較慢。此外,若貝葉斯網中存在計算概率0或1,則不能保證馬爾科夫鏈存在平穩分佈,此時吉布斯取樣會給出錯誤的估計結果。
以下程式碼為樸素貝葉斯分類器程式碼:
# 程式碼和資料集來源於機器學習實戰,https://github.com/AnnDWang/MachineLearning/blob/master/thirdbook/ch4/bayes.py
from numpy import *
import feedparser
# 詞表到向量的轉換函式
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
# 建立一個包含在所有文件中出現的不重複詞的列表,為此使用了python的set資料型別
# 將詞條列表輸給set建構函式,set就會返回一個不重複詞表
# 首先建立一個空集合,然後將每篇文件返回的新詞集合新增到該集合中。
# 操作符|用於求兩個集合的並集,這也是一個按位或(OR)操作符
# 在數學符號表示上,按位或操作與集合求並操作使用相同記號
def createVocabList(dataSet):
vocabSet=set([])# 建立一個空集
for document in dataSet:
vocabSet=vocabSet|set(document) # 建立兩個集合的並集
return list(vocabSet)
# 輸入引數為詞彙表及某個文件
# 輸出時文件向量
# 向量的每個元素為1或0,分別表示詞彙表的單詞在輸入文件中是否出現
# 函式首先建立一個和詞彙表等長的向量,並將其元素都設定為0
# 接著,遍歷文件中的所有單詞,如果出現了詞彙表中的單詞,則將輸出的文件向量中的對應值設為1
# 一切都順利,就不需要檢查某個詞是否還在vocabList中
def setOfWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList) # 建立一個其中所含元素都為0的向量
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]=1
else:
print("the word: %s is not in my Voabulary!" % word)
return returnVec
# 樸素貝葉斯詞袋模型
def bagOfWords2VecMN(vocabList,inputSet):
returnVec=[0]* len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]+=1
return returnVec
listOPosts,listClasses=loadDataSet()
myVocabList=createVocabList(listOPosts)
myreturnVec=setOfWords2Vec(myVocabList,listOPosts[0])
# 樸素貝葉斯分類器訓練函式
def trainNB0(trainMatrix,trainCategory):
numTrainDocs=len(trainMatrix)
numWords=len(trainMatrix[0])
pAbusive=sum(trainCategory)/float(numTrainDocs)
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])
p1Vect=log(p1Num/p1Denom) # change to log()
p0Vect=log(p0Num/p0Denom) # change to log()
return p0Vect,p1Vect,pAbusive
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
p0v,p1v,pAb=trainNB0(trainMat,listClasses)
# 樸素貝葉斯分類函式
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))
testingNB()
參考:
- 《機器學習》
- 《統計學習方法》
- 《機器學習實戰》