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

Naive Bayes

樸素貝葉斯網絡是貝葉斯分類器的一種,貝葉斯分類算法是統計學的一種分類方法,利用概率論和統計知識進行分類。其原理是利用貝葉斯公式根據樣本的先驗概率來計算其後驗概率(即樣本屬於某一類的概率),然後選擇具有最大後驗概率的類作爲該對象所屬的類別。樸素貝葉斯分類以概率論爲基礎,有堅實的數學基礎,以及穩定的分類效率,其優點是算法簡單,在數據較少的情況下仍然準確。理論上樸素貝葉斯分類有最小的誤差率,但實際貝葉斯假設樣本之間的各個特徵相互獨立往往不成立,從而影響分類正確性。

1.貝葉斯理論

在貝葉斯理論中,假設X,Y兩個事件:
P(X)X先驗概率
P(Y|X) 是已知X 發生後Y 發生的條件概率,也稱之爲Y後驗概率
貝葉斯理論中,常用公式:
乘法公式:P(XYZ)=P(Z|XY)P(Y|X)P(X)
全概率公式:P(X)=kP(X|Yi)P(Yi)
貝葉斯公式:P(Yi|X)=P(X|Yi)P(X)

2.樸素貝葉斯法的學習與分類

設輸入空間XRnn 維特徵向量的集合,輸出空間爲分類標記集合Y={c1,c2,,ck} 。 樸素貝葉斯分類器的輸入爲特徵向量xX ,輸出爲預測類標記yY , 學習過程就是通過訓練數據集T={(x1,y1),(x2,y2),,(xN,yN)} 得到聯合分佈概率P(X,Y) , 並以此來預測未知樣本的標記。
具體地:
後驗概率:

P(Y=ck|X=x)=P(X=x|Y=ck)P(Y=ck)P(X=x)

其中,P(Y=ck) 爲類別標籤的先驗概率,P(X=x|Y=ck) 爲條件概率分佈,且在樸素貝葉斯中假設各個特徵具有相同地位且各自出現的概率相互獨立,因此:
P(X=x|Y=ck)=P(X(1)=x(1),X(2)=x(2),,X(n)=x(n)|Y=ck)=j=1nP(X(j)=x(j)|Y=ck)

P(X=x)=kP(Y=ck)P(X=x|Y=ck)=kP(Y=ck)j=1nP(X(j)=x(j)|Y=ck)

所以最終樸素貝葉斯分類器可以表示爲:
y=f(x)=argmaxkP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)kP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)

即在所有的後驗概率中找到最大的概率作爲該特徵預測的類別,而且我們注意到最終的分母是隊所有的類別都是一樣的,因此可以簡化爲:
y=f(x)=argmaxkP(Y=ck)j=1nP(X(j)=x(j)|Y=ck)

我們以兩個類別來簡要說明貝葉斯的分類準則:假設未知類別樣本特徵爲x ,分類標籤爲c1,c2 ,則樣本爲類別1的概率爲:
P(c1|x)=P(x|c1)P(c1)P(x)

樣本爲類別2的概率爲:
P(c2|x)=P(x|c2)P(c2)P(x)

其中P(c1) 爲所有訓練集中類別c1 出現的概率,P(x|c1) 爲所有c1 類別中特徵x 出現的概率。
  • 如果P(x|c1)P(c1)>P(x|c2)P(c2) ,那麼該測試樣本屬於c1
  • 如果P(x|c1)P(c1)<P(x|c2)P(c2) ,那麼該測試樣本屬於c2

3.樸素貝葉斯學習算法

損失函數:
通過上述介紹,我們知道樸素貝葉斯方法的分類規則就是後驗概率最大化,這與經驗風險最小化是相匹配的。我們定義樸素貝葉斯分類的損失函數爲:

L(Y,f(X))={1,0,if Yf(X)if Y=f(X)

其中,Y 爲樣本實際類別,f(X) 爲分類決策函數得到類別。這時,期望風險函數爲:
Rexp(f)=E[L(Y,f(X))]=Exk=1K[L(ck,f(X))]P(ck|X)

其中,P(ck|X) 爲各個預測的概率。爲了使期望風險最小化,只需要使所有各個樣本的誤差極小即可,即:
f(x)=argminyYk=1K[L(ck,y)]P(ck|X=x)=argminyYk=1KP(yck|X=x)=argminyY(1P(y=ck|X=x))=argmaxyYP(y=ck|X=x)

由此可以看出,後驗概率最大化準則符合損失函數最小化的要求。
參數估計:
通過上述介紹,我們知道樸素貝葉斯的學習過程就是估計先驗概率P(Y=ck) 和條件概率P(Xj=xj|Y=ck) 。在實際學習中,我們假設訓練樣本總數爲N,由極大似然估計可知:
P(Y=ck)=Nl=1I(yl=ck)N

然後在所有標記爲ck 的樣本中計算條件概率:
P(X(j)=ajl|Y=ck)=Nl=1I(X(j)=ajl,Y=ck)Nl=1I(yl=ck)

其中,X^{(j)}是樣本的第j 個特徵,本式計算在類別ck 中,不同特徵所在比例,及其分佈概率。
算法實現:
在本處,我們應用樸素貝葉斯方法來進行垃圾郵件分類。
- 準備數據:首先準備一定量垃圾郵件和正常郵件並給予一定標記
- 數據處理:由於本處文本量少,文本較爲簡單,直接統計文本中各個單詞的數量來作爲文本特徵
- 參數估計:首先統計訓練樣本中正負樣本各佔多少比例,即P(Y=ck) ,再統計每個類別中各個特徵所佔的比例,即P(X=x|Y=ck)
- 模型測試:統計給定訓練樣本的特徵,然後通過先驗概率計算出屬於各類的標籤
- 模型應用:可以編輯任意文本進行測試,看未標記文本是否分類正確
以下是程序實現源碼:
# Project: Machine learning-Naive Bayes
# Author: Lyndon
# date: 2015/10/20

from numpy import *
import re,string,os

# create the vocabulary
# input: traing_data text
# output: vocabulary list
def creatVocabulary(dataset):
    vocabulary = set([])
    for document in dataset:
        vocabulary = vocabulary | set(document)
    return list(vocabulary) 

# describe document using the bag-of-words model
# input: vocabulary list, input document
# output: descriptor of input document
def document2Vec(vocabulary,inputData):
    vector = [0]*len(vocabulary)
    for word in inputData:
        if word in vocabulary:
            vector[vocabulary.index(word)]+=1
        else:
            print "the word: %s is not in the vocabulary!" %word
    return vector 

# split the document to word list
# input: document
# output: word list
def text2word(textString):
    textString.translate(None, string.punctuation)
    # split the document
    wordsplit = re.split(r'\s+',textString)
    #  retain the alphabetic characters
    #for i in range(len(wordsplit)):
        #wordsplit[i] = filter(str.isalpha,wordsplit[i])
    # return the lower case word 
    return [tok.lower() for tok in wordsplit if len(tok) >2]


# process the dataset
# input: NULL
# output: document list, class label list, full text
def proDocument():
    documentlist = []
    classlist = []
    fulltext = []
    path1 = "F:/Program/Python/Machine_Learning/Bayes/email/spam"
    for root, dirs, files in os.walk(path1):
        #print files
        numDocument1 = len(files)  
    for i in range(numDocument1):
        wordList = text2word(open(path1+'/%d.txt' %(i+1)).read())
        documentlist.append(wordList)
        fulltext.extend(wordList)
        classlist.append(1)
    path2 = "F:/Program/Python/Machine_Learning/Bayes/email/ham"
    for root, dirs, files in os.walk(path2):
        #print files
        numDocument2 = len(files)
    for i in range(numDocument2):
        wordList = text2word(open(path2+'/%d.txt' %(i+1)).read())
        documentlist.append(wordList)
        fulltext.extend(wordList)
        classlist.append(0)
    return documentlist,classlist,fulltext

# training
# input: training_vector, training_label
# output: posterior probability P(Y=1), P(X=x|Y=1),P(X=x|y=0)
def trainNB(trainMatrix,trainlabel):
    numTrain = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainlabel)/float(numTrain)
    p0Num = ones(numWords); p1Num = ones(numWords)
    p0Denom = 2.0; p1Denom =2.0
    for i in range(numTrain):
        if trainlabel[i]==1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vector = log(p1Num/p1Denom)
    p0Vector = log(p0Num/p0Denom)
    return p0Vector,p1Vector,pAbusive

# testing 
# input: testing_vector, posterior probability P(Y=1), P(X=x|Y=1),P(X=x|y=0)
# output: testing_label
def testNB(testVector,p0Vector,p1Vector,pAbusive):
    p1 = sum(testVector*p1Vector)+log(pAbusive)
    p0 = sum(testVector*p0Vector)+log(1-pAbusive)
    if p1>p0:
        return 1
    else:
        return 0

# main function
if __name__=="__main__":
    # dataset processing
    documentlist = []
    classlist = []
    fulltext = []
    documentlist,classlist,fulltext=proDocument()
    vocabulary = creatVocabulary(documentlist)
    # randomly select training_set and testing_set
    trainSet=range(len(documentlist));testSet=[]
    for i in range(int(len(documentlist)*0.2)):
        randIndex = int (random.uniform(0,len(documentlist)-i))
        testSet.append(trainSet[randIndex])
        del(trainSet[randIndex])
    trainMat=[];trainlabel=[]
    for docIndex in trainSet:
        trainMat.append(document2Vec(vocabulary, documentlist[docIndex]))
        trainlabel.append(classlist[docIndex])
    p0Vector,p1Vector,pAbusive = trainNB(trainMat,trainlabel)
    error = 0
    for docIndex in testSet:
        doc2Vector = document2Vec(vocabulary, documentlist[docIndex])
        result=testNB(doc2Vector,p0Vector,p1Vector,pAbusive)
        if result!=classlist[docIndex]:
            error +=1
            print "classification error:"+str(documentlist[docIndex])+"is "+str(classlist[docIndex])+" but classify to "+str(result)
    print "the error rate is:",float(error)/len(testSet)

程序測試結果:
這裏寫圖片描述

在實際處理過程中,可能會出現某個條件概率P(X=x|Y=ck) 爲0的情況,這樣會使得獨立分佈相乘之後的結果爲0,影響正常的後驗概率,因此定義瞭如下的貝葉斯估計:

P(X(j)=ajl|Y=ck)=Nl=1I(X(j)=ajl,Y=ck)+λNl=1I(yl=ck)+Sjλ

λ=0 時就是極大似然估計,在λ=1 時,成爲拉普拉斯平滑。在本文的垃圾郵件處理中,我們初始化特徵數都是1。並且,爲了避免因爲多數特徵概率因子較小導致乘積下溢得不到正確的結果,我們將nj=1P(X(j)=x(j)|Y=ck) 取對數,變成各特徵的和。
PS:
本文爲機器學習(4)總結筆記,主要通過Python編程實現了樸素貝葉斯方法實現垃圾郵件分類系統。理論主要參考李航《統計學習方法》,
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章