徒手寫代碼之《機器學習實戰》---樸素貝葉斯算法(1)

很久沒寫博客了,最近忙着中期考覈。補寫一下樸素貝葉斯的代碼部分,重點和難點在於計算條件概率。計算條件概率和類概率時,代碼部分都使用了拉普拉斯平滑,主要是爲了避免其它屬性攜帶的信息被訓練集中未出現的屬性值“抹去”的現象。並且這裏使用了對數的表示法代替直接計算若干個條件概率的乘積值,是因爲如果連續的概率乘積會造成數值過小,python可能無法顯示出來這樣的數值。也叫做防止數值溢出。之所以對數表示法可行,因爲一個數在加對數爲底數以後,它的單調性並未改變,而這樣的得數不會過小(python能夠顯示)

1.詞表到向量的轉換函數

"""
第一個函數loadDataSet()創建了一些實驗樣本。該函數返回的第一個變量是進行詞條切分後的文檔集合,
這些文檔來自斑點犬愛好者留言板。這些留言文本被切分成一系列的詞條集合,標點符號從文本中去掉,後面會
探討文本處理的細節。loadDataSet()函數返回的第二個變量是一個類別標籤的集合。這裏有兩類,侮辱性和非
侮辱性。這些文本的類別由人工標註,這些標註信息用於訓練程序以便自動檢測侮辱性留言
"""
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



postingList, classVec = loadDataSet()
for each in postingList:
    print(each)
print(classVec)
print(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']
[0, 1, 0, 1, 0, 1]
[['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']]
"""
下一個函數 createVocabList()會創建一個包含在所有文檔中出現的不重複詞的列表,爲此使用了python的set數據類型。
將詞條列表輸給set構造函數,set就會返回一個不重複詞表。首先,創建一個空集合, 然後將每篇文檔返回的新詞集合添
加到該集合中.操作符丨用於求兩個集合的並集
"""
#dataSet--整理的樣本數據集;  vocabSet--返回不重複的詞條列表,也就是詞彙表
def createVocabList(dataSet):
    vocabSet = set([])                      #創建一個空的不重複列表
    for document in dataSet:
        vocabSet = vocabSet | set(document) #取並集
    return list(vocabSet)

import numpy as np 

#vocabList--createVocabList返回的列表; inputSet--切分的詞條列表; returnVec--文檔向量,詞集模型
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                                                    #返回文檔向量


myVocabList=createVocabList(postingList)
myVocabList

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:
 [['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']]
myVocabList:
 ['food', 'licks', 'dalmation', 'help', 'cute', 'is', 'stupid', 'ate', 'mr', 'not', 'park', 'buying', 'I', 'love', 'to', 'problems', 'flea', 'worthless', 'my', 'please', 'take', 'him', 'stop', 'so', 'dog', 'how', 'posting', 'garbage', 'maybe', 'quit', 'has', 'steak']
trainMat:
 [[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0]]

2.建立樸素貝葉斯分類器,並進行測試

"""
函數說明:樸素貝葉斯分類器訓練函數
trainMatrix--訓練文檔矩陣,即setOfWords2Vec返回的returnVec構成的矩陣;trainCategory--訓練類別標籤向量,即loadDataSet返回的classVec
p0Vect--侮辱類的條件概率數組;p1Vect--非侮辱類的條件概率數組;pAbusive--文檔屬於侮辱類的概率
"""
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)             #計算訓練的文檔數目
    numWords = len(trainMatrix[0])                  #計算每篇文檔的詞條數
    pAbusive = sum(trainCategory)/float(numTrainDocs)        #文檔屬於侮辱類的概率
    p0Num = np.ones(numWords); p1Num = np.ones(numWords)    #創建numpy.zeros數組,詞條出現數初始化爲0
    p0Denom = 2.0; p1Denom = 2.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 = np.log(p1Num/p1Denom)                                    
    p0Vect = np.log(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)

myVocabList:
 ['food', 'licks', 'dalmation', 'help', 'cute', 'is', 'stupid', 'ate', 'mr', 'not', 'park', 'buying', 'I', 'love', 'to', 'problems', 'flea', 'worthless', 'my', 'please', 'take', 'him', 'stop', 'so', 'dog', 'how', 'posting', 'garbage', 'maybe', 'quit', 'has', 'steak']
p0V:
 [-3.25809654 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936
 -3.25809654 -2.56494936 -2.56494936 -3.25809654 -3.25809654 -3.25809654
 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -3.25809654
 -1.87180218 -2.56494936 -3.25809654 -2.15948425 -2.56494936 -2.56494936
 -2.56494936 -2.56494936 -3.25809654 -3.25809654 -3.25809654 -3.25809654
 -2.56494936 -2.56494936]
p1V:
 [-2.35137526 -3.04452244 -3.04452244 -3.04452244 -3.04452244 -3.04452244
 -1.65822808 -3.04452244 -3.04452244 -2.35137526 -2.35137526 -2.35137526
 -3.04452244 -3.04452244 -2.35137526 -3.04452244 -3.04452244 -1.94591015
 -3.04452244 -3.04452244 -2.35137526 -2.35137526 -2.35137526 -3.04452244
 -1.94591015 -3.04452244 -2.35137526 -2.35137526 -2.35137526 -2.35137526
 -3.04452244 -3.04452244]
classVec:
 [0, 1, 0, 1, 0, 1]
pAb:
 0.5

#函數說明:樸素貝葉斯分類器分類函數
#vec2Classify--待分類的詞條數組; p0Vec--侮辱類的條件概率數組; p1Vec--非侮辱類的條件概率數組; pClass1--文檔屬於侮辱類的概率
#0--屬於非侮辱類; 1--屬於侮辱類
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)        #對應元素相乘。logA * B = logA + logB,所以這裏加上log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + np.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(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()

['love', 'my', 'dalmation'] 屬於非侮辱類
['stupid', 'garbage'] 屬於侮辱類

參考文檔:
1.《西瓜書》樸素貝葉斯理論部分
2.《機器學習實戰》代碼部分

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章