機(jī)器學(xué)習(xí)實(shí)戰(zhàn)-03-樸素貝葉斯

一、樸素貝葉斯介紹

??前兩章我們要求分類器做出艱難決策,給出“該數(shù)據(jù)實(shí)例屬于哪一類”這類問題的明確答案。不過,分類器有時(shí)會(huì)產(chǎn)生錯(cuò)誤結(jié)果,這時(shí)可以要求分類器給出一個(gè)最優(yōu)的類別猜測結(jié)果,同時(shí)給出這個(gè)猜測的概率估計(jì)值。
??樸素貝葉斯(Naive Bayesian)算法能夠根據(jù)數(shù)據(jù)加先驗(yàn)概率來估計(jì)后驗(yàn)概率,在垃圾郵件分類、文本分類、信用等級評定等多分類問題中得到廣泛應(yīng)用。樸素貝葉斯算法以自變量之間的獨(dú)立性和連續(xù)變量的正態(tài)性假設(shè)為前提,會(huì)導(dǎo)致算法精度在一定程度上受到影響。



介紹

二、樸素貝葉斯理論

1、貝葉斯決策理論
假設(shè)現(xiàn)在我們有一個(gè)數(shù)據(jù)集,它由兩類數(shù)據(jù)組成,數(shù)據(jù)分布如下圖所示:

??我們現(xiàn)在用 p1(x,y) 表示數(shù)據(jù)點(diǎn)(x,y)屬于類別1(圖中用圓點(diǎn)表示的類別)的概率,用 p2(x,y) 表示數(shù)據(jù)點(diǎn)(x,y)屬于類別2(圖中用三角形表示的類別)的概率,那么對于一個(gè)新數(shù)據(jù)點(diǎn)(x,y),可以用下面的規(guī)則來判斷它的類別:
??如果 p1(x,y) > p2(x,y) ,那么類別為1。
??如果 p2(x,y) > p1(x,y) ,那么類別為2。
??也就是說,我們會(huì)選擇高概率對應(yīng)的類別。這就是貝葉斯決策理論的核心思想,即選擇具有最高概率的決策。

2、條件概率
??條件概率(Condittional probability)是指在事件B發(fā)生的情況下,事件A發(fā)生的概率,用P(A|B)來表示。

條件概率計(jì)算公式

3、全概率公式
??假定樣本空間S,是兩個(gè)事件A與A'的和。紅色部分是事件A,綠色部分是事件A',它們共同構(gòu)成了樣本空間S。

??在這種情況下,事件B可以劃分成兩個(gè)部分。

??得到了條件概率的另一種寫法:

4、貝葉斯推斷
??對條件概率公式進(jìn)行變形,可以得到如下形式:

??我們把P(A)稱為"先驗(yàn)概率"(Prior probability),即在B事件發(fā)生之前,我們對A事件概率的一個(gè)判斷。
??P(A|B)稱為"后驗(yàn)概率"(Posterior probability),即在B事件發(fā)生之后,我們對A事件概率的重新評估。
??P(B|A)/P(B)稱為"可能性函數(shù)"(Likelyhood),這是一個(gè)調(diào)整因子,使得預(yù)估概率更接近真實(shí)概率。
??所以,條件概率可以理解成: 后驗(yàn)概率=先驗(yàn)概率x調(diào)整因子,這就是貝葉斯推斷的含義。
??我們先預(yù)估一個(gè)"先驗(yàn)概率",然后加入實(shí)驗(yàn)結(jié)果,看這個(gè)實(shí)驗(yàn)到底是增強(qiáng)還是削弱了"先驗(yàn)概率",由此得到更接近事實(shí)的"后驗(yàn)概率"。如果"可能性函數(shù)"P(B|A)/P(B)>1,意味著"先驗(yàn)概率"被增強(qiáng),事件A的發(fā)生的可能性變大;如果"可能性函數(shù)"=1,意味著B事件無助于判斷事件A的可能性;如果"可能性函數(shù)"<1,意味著"先驗(yàn)概率"被削弱,事件A的可能性變小。
??舉個(gè)例子,從下圖中計(jì)算從A桶中取到黑色石頭的概率。

??H1表示A桶,H2表示B桶。由于這兩個(gè)桶是一樣的,被選中的概率都一樣,所以P(H1)=P(H2)=0.5,我們把這個(gè)概率就叫做"先驗(yàn)概率",即沒有做實(shí)驗(yàn)之前,來自A桶的概率是0.5。假定,E表示黑色石頭,所以問題變成了在已知E的情況下,來自A桶的概率有多大,即求P(H1|E)。我們把這個(gè)概率叫做"后驗(yàn)概率",即在E事件發(fā)生之后,對P(H1)的修正。

??這表明,來自A的概率是3/5。也就是說,取出黑色石頭之后,H1事件的可能性得到了增強(qiáng)。

5、樸素貝葉斯推斷


貝葉斯算法的一個(gè)實(shí)例

??貝葉斯和樸素貝葉斯的概念是不同的,區(qū)別就在于“樸素”二字,樸素貝葉斯對條件個(gè)概率分布做了條件獨(dú)立性的假設(shè)。 比如下面的公式,假設(shè)有n個(gè)特征:

??由于每個(gè)特征都是獨(dú)立的,我們可以進(jìn)一步拆分公式 :

三、樸素貝葉斯完成文檔分類

貼上第三、第四節(jié)的全部代碼:
# -*- coding: UTF-8 -*-
from numpy import *
import re
import feedparser


def loadDataSet():
    """
        Function:
            創(chuàng)建實(shí)驗(yàn)樣本
        Parameters:
            無
        Returns:
            postingList - 實(shí)驗(yàn)樣本切分的原始詞條列表,列表每一行代表一個(gè)文檔
            classVec - 類別標(biāo)簽向量
        Modify:
            2018-08-11
        """
    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'],
                   ['my', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0, 1, 0, 1, 0, 1]
    return postingList, classVec


# 統(tǒng)計(jì)所有文檔中出現(xiàn)的詞條列表,即沒有重復(fù)的單詞
def createVocabList(dataSet):
    """
        Function:
            創(chuàng)建一個(gè)包含在所有文檔中出現(xiàn)的不重復(fù)詞的列表
        Parameters:
            dataSet - 樣本切分詞條數(shù)據(jù)集
        Returns:
            vocabSet - 返回不重復(fù)的詞條列表,也就是詞匯表
        Modify:
            2018-08-11
        """
    vocabSet = set([])
    for document in dataSet:
        # 將文檔列表轉(zhuǎn)為集合的形式,保證每個(gè)詞條的唯一性
        # 然后與vocabSet取并集,向vocabSet中添加沒有出現(xiàn)新的詞條
        vocabSet = vocabSet | set(document)
    return list(vocabSet)


def setOfWords2Vec(vocabList, inputSet):
    """
        Function:
            根據(jù)詞條列表中的詞條是否在文檔中出現(xiàn)(出現(xiàn)1,未出現(xiàn)0),將文檔轉(zhuǎn)化為詞條向量
        Parameters:
            vocabList - createVocabList返回的列表
            inputSet - 切分的詞條列表
        Returns:
            returnVec - 文檔向量,詞集模型
        Modify:
            2018-08-11
        """
    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


# 樸素貝葉斯分類器的訓(xùn)練函數(shù)
def trainNB0(trainMatrix, trainCategory):
    """
        Function:
            樸素貝葉斯分類器的訓(xùn)練函數(shù)
        Parameters:
            trainMatrix - 訓(xùn)練文檔矩陣,即setOfWords2Vec返回的returnVec構(gòu)成的矩陣
            trainCategory - 訓(xùn)練類別標(biāo)簽向量,即loadDataSet返回的classVec
        Returns:
            p0Vect - 非侮辱類的條件概率數(shù)組
            p1Vect - 侮辱類的條件概率數(shù)組
            pAbusive - 文檔屬于侮辱類的概率
        Modify:
            2018-08-11
        """
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory) / float(numTrainDocs)
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    # 初始化所有詞出現(xiàn)數(shù)為1,并將分母初始化為2,避免某一個(gè)概率值為0
    p0Denom = 2.0
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            # 統(tǒng)計(jì)屬于侮辱類的條件概率所需的數(shù)據(jù),即P(w0|1),P(w1|1),P(w2|1)...
            p1Num += trainMatrix[i]
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            # 統(tǒng)計(jì)屬于非侮辱類的條件概率所需的數(shù)據(jù),即P(w0|0),P(w1|0),P(w2|0)···
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    # 將結(jié)果取自然對數(shù),避免下溢出,即太多很小的數(shù)相乘造成的影響
    p1Vect = log(p1Num / p1Denom)
    p0Vect = log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive


# 樸素貝葉斯分類函數(shù)
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    """
        Function:
            樸素貝葉斯分類函數(shù)
        Parameters:
            vec2Classify - 待分類的詞條數(shù)組
            p0Vec - 侮辱類的條件概率數(shù)組
            p1Vec -非侮辱類的條件概率數(shù)組
            pClass1 - 文檔屬于侮辱類的概率
        Returns:
            0 - 屬于非侮辱類
            1 - 屬于侮辱類
        Modify:
            2018-08-15
        """
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0


def testingNB():
    """
        Function:
            樸素貝葉斯分類測試函數(shù)
        Parameters:
            無
        Returns:
            無
        Modify:
            2018-08-15
        """
    listOPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(trainMat, array(listClasses))
    # 測試文檔
    testEntry = ['love', 'my', 'dalmation']
    # 將測試文檔轉(zhuǎn)為詞條向量,并轉(zhuǎn)為NumPy數(shù)組的形式
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    # 利用貝葉斯分類函數(shù)對測試文檔進(jìn)行分類并打印
    print(testEntry, 'classified as:', classifyNB(thisDoc, p0V, p1V, pAb))
    # 第二個(gè)測試文檔
    testEntry1 = ['stupid', 'garbage']
    # 同樣轉(zhuǎn)為詞條向量,并轉(zhuǎn)為NumPy數(shù)組的形式
    thisDoc1 = array(setOfWords2Vec(myVocabList, testEntry1))
    print(testEntry1, 'classified as:', classifyNB(thisDoc1, p0V, p1V, pAb))


def bagOfWords2VecMN(vocabList, inputSet):
    """
        Function:
            樸素貝葉斯詞袋模型,根據(jù)詞條列表中的詞條是否在文檔中出現(xiàn)(出現(xiàn)1,未出現(xiàn)0),將文檔轉(zhuǎn)化為詞條向量
        Parameters:
            vocabList - createVocabList返回的列表
            inputSet - 切分的詞條列表
        Returns:
            returnVec - 文檔向量,詞集模型
        Modify:
            2018-08-15
        """
    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 textParse(bigString):
    """
        Function:
            將大字符串其解析為字符串列表
        Parameters:
            bigString - 郵件字符串
        Returns:
            tok - 字符串列表
        Modify:
            2018-08-15
        """
    # 對長字符串進(jìn)行分割,分隔符為除單詞和數(shù)字之外的任意符號串
    listOfTokens = re.split(r'\W*', bigString)
    # 將分割后的字符串中所有的大些字母變成小寫lower(), 并且只保留單詞長度大于3的單詞
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]


def spamTest():
    """
        Function:
            使用樸素貝葉斯過濾垃圾郵件測試函數(shù)
        Parameters:
            無
        Returns:
            無
        Modify:
            2018-08-15
        """
    docList = []
    classList = []
    fullText = []
    for i in range(1, 26):
        wordList = textParse(
            open('D:/PycharmProjects/Machine/machinelearninginaction/Ch04/email/spam/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(
            open('D:/PycharmProjects/Machine/machinelearninginaction/Ch04/email/ham/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)
    trainingSet = list(range(50))
    testSet = []
    # 選10組做測試集,根據(jù)隨機(jī)產(chǎn)生索引值獲取
    for i in range(10):
        randIndex = int(random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del (trainingSet[randIndex])
    trainMat = []
    trainClasses = []
    # 生成訓(xùn)練矩陣及標(biāo)簽
    for docIndex in trainingSet:
        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
    errorCount = 0
    # 測試并計(jì)算錯(cuò)誤率
    for docIndex in testSet:
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
        if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
            print("分類錯(cuò)誤的測試集:", docList[docIndex])
    print('the error rate is: ', float(errorCount) / len(testSet))


if __name__ == '__main__':
    # postingList, classVec = loadDataSet()
    # for each in postingList:
    #     print(each)
    # print(classVec)

    # myVocabList = createVocabList(postingList)
    # print('詞匯表:', myVocabList)
    # trainMat = []
    # for postinDoc in postingList:
    #     trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    # print('詞條向量:', trainMat)
    #
    # p0Vect, p1Vect, pAbusive = trainNB0(trainMat, classVec)
    # print('p0V:\n', p0Vect)
    # print('p1V:\n', p1Vect)
    # print('classVec:\n', classVec)
    # print('pAb:\n', pAbusive)

    # testingNB()

    spamTest()
    spamTest()

??以在線社區(qū)的留言板為例。為了不影響社區(qū)的發(fā)展,我們要屏蔽侮辱性的言論,所以要構(gòu)建一個(gè)快速過濾器,如果某條留言使用了負(fù)面或者侮辱性的語言,那么就將該留言標(biāo)識為內(nèi)容不當(dāng)。過濾這類內(nèi)容是一個(gè)很常見的需求。對此問題建立兩個(gè)類別:侮辱類和非侮辱類,使用1和0分別表示。

(1)創(chuàng)建實(shí)驗(yàn)樣本

創(chuàng)建實(shí)驗(yàn)樣本

(2)構(gòu)建詞條向量

構(gòu)建詞條向量

(3)分類器訓(xùn)練

分類器訓(xùn)練結(jié)果

??利用貝葉斯分類器對文檔進(jìn)行分類時(shí),要計(jì)算多個(gè)概率的乘積以獲得文檔屬于某個(gè)類別的概
率,即計(jì)算 p(w 0 |1)p(w 1 |1)p(w 2 |1) 。如果其中一個(gè)概率值為0,那么最后的乘積也為0。為降低這種影響,可以將所有詞的出現(xiàn)數(shù)初始化為1,并將分母初始化為2。
??另一個(gè)遇到的問題是下溢出,這是由于太多很小的數(shù)相乘造成的。當(dāng)計(jì)算乘積
p(w 0 |c i )p(w 1 |c i )p(w 2 |c i )...p(w N |c i ) 時(shí),由于大部分因子都非常小,所以程序會(huì)下溢出或者得到不正確的答案。一種解決辦法是對乘積取自然對數(shù)。在代數(shù)中有 ln(a*b) = ln(a)+ln(b) ,于是通過求對數(shù)可以避免下溢出或者浮點(diǎn)數(shù)舍入導(dǎo)致的錯(cuò)誤。同時(shí),采用自然對數(shù)進(jìn)行處理不會(huì)有任何損失。

(4)樸素貝葉斯分類函數(shù)測試

樸素貝葉斯分類測試函數(shù)測試結(jié)果

(5)使用文檔詞袋模型完善樸素貝葉斯分類器

??前面我們將每個(gè)詞的出現(xiàn)與否作為一個(gè)特征,這可以被描述為詞集模型(set-of-words model)。如果一個(gè)詞在文檔中出現(xiàn)不止一次,這可能意味著包含該詞是否出現(xiàn)在文檔中所不能表達(dá)的某種信息,這種方法被稱為詞袋模型(bag-of-words model)。在詞袋中,每個(gè)單詞可以出現(xiàn)多次,而在詞集中,每個(gè)詞只能出現(xiàn)一次。為適應(yīng)詞袋模型,需要對函數(shù) setOfWords2Vec()稍加修改,修改后的函數(shù)稱為bagOfWords2VecMN() 。

四、示例:使用樸素貝葉斯過濾垃圾郵件

使用樸素貝葉斯過濾垃圾郵件測試結(jié)果

??函數(shù) spamTest() 會(huì)輸出在10封隨機(jī)選擇的電子郵件上的分類錯(cuò)誤率。既然這些電子郵件是隨機(jī)選擇的,所以每次的輸出結(jié)果可能有些差別。如果發(fā)現(xiàn)錯(cuò)誤的話,函數(shù)會(huì)輸出錯(cuò)分文檔的詞表,這樣就可以了解到底是哪篇文檔發(fā)生了錯(cuò)誤。如果想要更好地估計(jì)錯(cuò)誤率,那么就應(yīng)該將上述過程重復(fù)多次,比如說10次,然后求平均值。

五、應(yīng)用scikit-learn實(shí)現(xiàn)新浪新聞分類

??1、中文語句切分
??英文的語句可以通過非字母和非數(shù)字進(jìn)行切分,同樣地,漢語句子也可以直接使用第三方分詞組件jieba進(jìn)行切分。可以在cmd命令行下用pip install jieba命令進(jìn)行安裝即可。新浪新聞數(shù)據(jù)集已經(jīng)做好分類,分文件夾保存,分類結(jié)果如下:

數(shù)據(jù)集目錄結(jié)構(gòu)
新浪新聞數(shù)據(jù)集分類
切分結(jié)果

??紅色框上邊還有很多沒顯示出來。

??2、文本特征選擇
??將所有文本分成訓(xùn)練集和測試集,并對訓(xùn)練集中的所有單詞進(jìn)行詞頻統(tǒng)計(jì),并按降序排序。也就是將出現(xiàn)次數(shù)多的詞語在前,出現(xiàn)次數(shù)少的詞語在后進(jìn)行排序。

??觀察一下打印結(jié)果,不難發(fā)現(xiàn),里包含了很多標(biāo)點(diǎn)符號,很顯然這些標(biāo)點(diǎn)符號是不能作為新聞分類的特征的。為了降低這些高頻的符號對分類結(jié)果的影響,應(yīng)該要?jiǎng)h除他們。
??除了這些,還有"在","了"等這樣無關(guān)痛癢的詞,還有一些數(shù)字,數(shù)字顯然也不能作為分類新聞的特征。所以要消除它們對分類結(jié)果的影響,我們可以定制一個(gè)規(guī)則:首先去掉高頻詞,至于去掉多少個(gè)高頻詞,我們可以通過觀察去掉高頻詞個(gè)數(shù)和最終檢測準(zhǔn)確率的關(guān)系來確定。除此之外,去除數(shù)字,不把數(shù)字作為分類特征。同時(shí),去除一些特定的詞語,比如:"的","一","在","不","當(dāng)然","怎么"這類的對新聞分類無影響的介詞、代詞、連詞。

??現(xiàn)在看到已經(jīng)濾除了那些沒有用的詞組了。這個(gè)featureWords就是我們最終選出的用于新聞分類的特征。

??3、使用Sklearn構(gòu)建樸素貝葉斯分類器
??相對于決策樹,KNN之類的算法,樸素貝葉斯需要關(guān)注的參數(shù)是比較少的,是一類比較簡單的算法。在scikit-learn中,一共有3個(gè)樸素貝葉斯的分類算法類,分別是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB是先驗(yàn)為高斯分布的樸素貝葉斯,MultinomialNB是先驗(yàn)為多項(xiàng)式分布的樸素貝葉斯,BernoulliNB是先驗(yàn)為伯努利分布的樸素貝葉斯。
??對于新聞分類,屬于多分類問題??梢允褂肕ultinamialNB()完成我們的新聞分類問題。
??貼上代碼:

import os
import jieba
import random
from sklearn.naive_bayes import MultinomialNB
import matplotlib.pyplot as plt


def textProcessing(folderPath, testSize=0.2):
    """
        Function:
            中文文本處理
        Parameters:
            folderPath - 文本存放的路徑
            testSize - 測試集占比,默認(rèn)占所有數(shù)據(jù)集的百分之20
        Returns:
            allWordsList - 按詞頻降序排序的訓(xùn)練集列表
            trainDataList - 訓(xùn)練集列表
            testDataList - 測試集列表
            trainClassList - 訓(xùn)練集標(biāo)簽列表
            testClassList - 測試集標(biāo)簽列表
        Modify:
            2018-08-22
        """
    folderList = os.listdir(folderPath)
    dataList = []
    classList = []
    # for folder in folderList[0:1]:
    for folder in folderList:
        newFolderList = os.path.join(folderPath, folder)
        files = os.listdir(newFolderList)
        # print(files)
        j = 1
        for file in files:
            if j > 100:
                break
            with open(os.path.join(newFolderList, file), 'r', encoding='utf-8') as f:
                raw = f.read()
            # 精簡模式,返回一個(gè)可迭代的generator
            wordCut = jieba.cut(raw, cut_all=False)
            # generator轉(zhuǎn)換為list
            wordList = list(wordCut)
            # 添加數(shù)據(jù)集數(shù)據(jù)
            dataList.append(wordList)
            # 添加數(shù)據(jù)集類別
            classList.append(folder)
            j += 1
    # zip()將對象中對應(yīng)的元素打包成一個(gè)個(gè)元組
    dataClassList = list(zip(dataList, classList))
    # 將data_class_list亂序
    random.shuffle(dataClassList)
    # 訓(xùn)練集和測試集切分的索引值
    index = int(len(dataClassList) * testSize) + 1
    trainList = dataClassList[index:]
    testList = dataClassList[:index]
    # 與 zip 相反,*zipped 可理解為解壓,返回二維矩陣式
    trainDataList, trainClassList = zip(*trainList)
    testDataList, testClassList = zip(*testList)

    allWordsDict = {}
    for wordList in trainDataList:
        for word in wordList:
            if word in allWordsDict.keys():
                allWordsDict[word] += 1
            else:
                allWordsDict[word] = 1

    # dict.items()函數(shù)以列表返回可遍歷的(鍵, 值)元組數(shù)組
    # 根據(jù)鍵的值倒序排序
    allWordsTupleList = sorted(allWordsDict.items(), key=lambda f: f[1], reverse=True)
    allWordsList, allWordsNums = zip(*allWordsTupleList)
    allWordsList = list(allWordsList)
    return allWordsList, trainDataList, testDataList, trainClassList, testClassList


def makeWordsSet(wordsPath):
    """
        Function:
            讀取文件里的內(nèi)容,并去重
        Parameters:
            folderPath - 文件路徑
        Returns:
            wordsSet - 讀取的內(nèi)容的set集合
        Modify:
            2018-08-22
        """
    wordsSet = set()
    with open(wordsPath, 'r', encoding='utf-8') as f:
        for line in f.readlines():
            word = line.strip()
            if len(word) > 0:
                wordsSet.add(word)
    return wordsSet


def wordsDict(allWordsList, deleteN, stopWordsSet=set()):
    """
        Function:
            文本特征選取
        Parameters:
            allWordsList - 按詞頻降序排序的訓(xùn)練集所有文本列表
            deleteN - 刪除詞頻最高的deleteN個(gè)詞
        Returns:
            featureWords - 特征集
        Modify:
            2018-08-22
        """
    featureWords = []
    n = 1
    for t in range(deleteN, len(allWordsList), 1):
        if n > 1000:
            break
        if not allWordsList[t].isdigit() and allWordsList[t] not in stopWordsSet and 1 < len(allWordsList[t]) < 5:
            featureWords.append(allWordsList[t])
        n += 1
    return featureWords


def matrixFeatures(trainDataList, testDataList, featureWords):
    """
        Function:
            根據(jù)feature_words將文本向量化
        Parameters:
            trainDataList - 訓(xùn)練集
            testDataList - 測試集
            featureWords - 特征集
        Returns:
            trainFeatureList - 訓(xùn)練集向量化列表
            testFeatureList - 測試集向量化列表
        Modify:
            2018-08-22
        """

    def matrixFeature(text, featureWords):
        textWords = set(text)
        # 出現(xiàn)在特征集中,則置1
        features = [1 if word in textWords else 0 for word in featureWords]
        return features

    trainFeatureList = [matrixFeature(text, featureWords) for text in trainDataList]
    testFeatureList = [matrixFeature(text, featureWords) for text in testDataList]
    return trainFeatureList, testFeatureList


def sinaNewsClassifier(trainFeatureList, testFeatureList, trainClassList, testClassList):
    classifier = MultinomialNB().fit(trainFeatureList, trainClassList)
    testAccuracy = classifier.score(testFeatureList, testClassList)
    return testAccuracy


if __name__ == '__main__':
    folderPath = './machinelearninginaction/Ch04/SogouC/Sample'
    # textProcessing(folderPath)

    allWordsList, trainDataList, testDataList, trainClassList, testClassList = textProcessing(folderPath)
    stopWordsFile = './machinelearninginaction/Ch04/SogouC/stopwords_cn.txt'
    stopWordsSet = makeWordsSet(stopWordsFile)
    # featureWords = wordsDict(allWordsList, 100, stopWordsSet)
    # print(featureWords)

    testAccuracyList = []
    deleteNs = range(0, 1000, 20)
    for deleteN in deleteNs:
        featureWords = wordsDict(allWordsList, deleteN, stopWordsSet)
        trainFeatureList, testFeatureList = matrixFeatures(trainDataList, testDataList, featureWords)
        testAccuracy = sinaNewsClassifier(trainFeatureList, testFeatureList, trainClassList, testClassList)
        testAccuracyList.append(testAccuracy)

    plt.figure()
    plt.plot(deleteNs, testAccuracyList)
    plt.title('Relationship of deleteNs and test_accuracy')
    plt.xlabel('deleteNs')
    plt.ylabel('test_accuracy')
    plt.show()
運(yùn)行結(jié)果

??上面繪制出了deleteNs和testAccuracy的關(guān)系,這樣可以大致確定去掉前多少的高頻詞匯了。每次運(yùn)行程序,繪制的圖形可能不一定相同。

六、小結(jié)

??樸素貝葉斯算法是建立在每一個(gè)特征值之間時(shí)獨(dú)立的基礎(chǔ)上的監(jiān)督學(xué)習(xí)分類算法,而這也是稱他為 “樸素”貝葉斯的緣由,在現(xiàn)實(shí)環(huán)境中,很難達(dá)到兩個(gè)特征值之間絕對的相互獨(dú)立。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容