機器學習實戰--樸素貝葉斯

使用Python進行文本分類

要從文本中獲取特徵,需要先拆分文本,具體如何做呢?這裏的特徵是來自文本的詞條,一個詞條是字符的任意組合。可以把詞條想象爲單詞,也可以使用非單詞詞條,如URL、IP地址或者任意其他字符串。然後將每一個文本片段表示爲一個詞條向量,其中值爲1表示詞條出現在文檔中,0表示詞條未出現。

此處以社區留言爲例,爲了過濾侮辱性的言論,我們使用1 和 0 來代表是侮辱類和非侮辱類。

首先會給出將文本轉換爲數字向量的過程,然後介紹如何基於這些向量來計算條件概率,並在此基礎上構建分類器,最後介紹利用Python實現樸素貝葉斯過程中需要考慮的問題。

在進行樸素貝葉斯實戰前,建議先理解樸素貝葉斯的原理,我在自己的博客中介紹過 機器學習初涉–貝葉斯分類

1.準備數據,從文本中構建詞向量

我們將文本看成詞向量或者詞條向量,也就是說將橘子轉換爲向量。考慮出現在所有文檔中的所有單詞,再決定將哪些詞納入詞彙表或者說所要的詞彙集合,然後必須要將每一篇文檔轉換爲詞彙表上的向量。接下來我們正式開始。

from numpy import *


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 is 侮辱性文字, 0 not
    return postingList, classVec


def createVocabList(dataSet):
    vocabSet = set([])  # create empty set
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 並集
    return list(vocabSet)


def setOfWords2Vec(vocabList, inputSet):
    """
    :param vocabList:  詞彙表
    :param inputSet:  某個文檔
    :return:  文檔向量,向量的元素是0/1,分別表示詞彙表中的單詞在輸入文檔中是否出現。
    """
    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 Vocabulary!" % word
    return returnVec

已經完成了將一組單詞轉換爲一組數字,那麼如何使用這組數字來計算概率。現在已經知道了一個字是否出現在一篇文檔中,也知道文檔所屬的類別。那麼一個留言屬於侮辱性的還是非侮辱性的這兩個概率如何計算?

首先,通過類別i(侮辱性和非侮辱性留言)中文檔數除以總的文檔數來計算概率P(Ci),接下來計算P(W|Ci),這裏需要用到樸素貝葉斯假設,如果將W展開爲一個個獨立的特徵,那麼就可以將上述概率寫作:P(W0,W1,W2..Wn|Ci)。這裏假設所有詞都相互獨立,也稱爲條件獨立性假設,它意味着可以使用P(W0|Ci)P(W1|Ci)P(W2|Ci)..P(Wn|Ci)來計算上述概率。

函數僞代碼:

計算每個類別中的文檔數目
對每篇訓練文檔:
    對每個類別:
        如果詞條出現在文檔中——>增加該詞條的計數值
        增加所有詞條的計數值
    對每個類別:
        對每個詞條:
            將該詞條的數目除以總詞條數得到條件概率(也就是求P(Wn|Ci))
    返回每個類別的條件概率

說明:這裏是將每個詞條視爲獨立的一個特徵,因此,需要計算出每一個詞條在Ci中出現的概率。

這部分的代碼如下:

def trainNB0(trainMatrix, trainCategory):
    """
    :param trainMatrix:  文檔矩陣
    :param trainCategory:   由每篇文檔類別所構成的向量
    :return:
    """
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory) / float(numTrainDocs) # P(Ci)
    # 初始化概率.因爲計算P(W0|Ci)*P(W1|Ci)...P(W2|Ci)時會出現其中一個概率爲0,導致最後的結果也爲0,
    # 因此,將初始化的出現次數設置爲1,並將默認的詞條總數設置爲2
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0

    # 對於每一篇文章
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i] # 將出現的詞條對應計數加1
            p1Denom += sum(trainMatrix[i])  # 詞條出現的總數加1
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])

    # 計算出現概率時,由於大部分因子非常小,會導致程序下溢或者得到不正確的答案,因此,解決辦法是通過求對數
    p1Vect = log(p1Num / p1Denom)    # 計算第一個類別中的P(W/Ci),即在Ci中每個詞條的出現概率
    p0Vect = log(p0Num / p0Denom)    # 另一個類別
    return p0Vect, p1Vect, pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    """
    :param vec2Classify: 待分類文檔的詞向量數組
    :param p0Vec: 第一個類別的各個特徵出現的概率
    :param p1Vec: 第二個類別的各個特徵出現的概率
    :param pClass1: P(Ci)
    :return:
    """
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)  # element-wise mult
    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))
    # 得到每個特徵在每個類別中出現的概率數組,以及P(Ci)
    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)

運行的結果如下:

['love', 'my', 'dalmation'] classified as:  0
['stupid', 'garbage'] classified as:  1

截止到目前爲止,我們將每個詞的出現與否視爲一個特徵,這可以描述爲詞集模型。如果一個詞在文檔中出現不止一次,這可能意味着僅僅通過詞出現與否表述爲特徵值是不夠的,需要幾記錄下詞的出現次數,這種方法被稱爲詞袋模型。若詞袋模型,可將setOfWordsVec()進行如下修改:

def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章