一步步教你輕鬆學樸素貝葉斯模型算法Sklearn深度篇3

一步步教你輕鬆學樸素貝葉斯深度篇3
( 白寧超   2018年9月4日14:18:14)

導讀:樸素貝葉斯模型是機器學習經常使用的模型算法之一,其在文本分類方面簡單易行,且取得不錯的分類效果。因此很受歡迎,對於樸素貝葉斯的學習,本文首先介紹理論知識即樸素貝葉斯相關概念和公式推導,爲了加深理解,採用一個維基百科上面性別分類例子進行形式化描述。而後經過編程實現樸素貝葉斯分類算法,並在屏蔽社區言論、垃圾郵件、我的廣告中獲取區域傾向等幾個方面進行應用,包括建立數據集、數據預處理、詞集模型和詞袋模型、樸素貝葉斯模型訓練和優化等。而後結合復旦大學新聞語料進行樸素貝葉斯的應用。最後,你們熟悉其原理和實現以後,採用機器學習sklearn包進行實現和優化。因爲篇幅較長,採用理論理解、案例實現、sklearn優化三個部分進行學習。(本文原創,轉載必須註明出處: 一步步教你輕鬆學樸素貝葉斯模型算法Sklearn深度篇3php

目錄

機器學習:一步步教你輕鬆學KNN模型算法

機器學習:一步步教你輕鬆學決策樹算法

機器學習:一步步教你輕鬆學樸素貝葉斯模型算法理論篇1 

機器學習:一步步教你輕鬆學樸素貝葉斯模型實現篇2 

機器學習:一步步教你輕鬆學樸素貝葉斯模型算法Sklearn深度篇3

機器學習:一步步教你輕鬆學邏輯迴歸模型算法

機器學習:一步步教你輕鬆學K-means聚類算法

機器學習:一步步教你輕鬆學關聯規則Apriori算法

機器學習: 一步步教你輕鬆學支持向量機SVM算法之理論篇1

10 機器學習: 一步步教你輕鬆學支持向量機SVM算法之案例篇2

11 機器學習: 一步步教你輕鬆學主成分分析PCA降維算法

12 機器學習: 一步步教你輕鬆學支持向量機SVM降維算法

更多文章請點擊這裏>>

復旦新聞語料:樸素貝葉斯中文文本分類

項目概述

本節介紹樸素貝葉斯分類算法模型在中文領域中的應用。咱們對新聞語料進行多文本分類操做,本文選擇藝術、文學、教育、哲學、歷史五個類別的訓練文本,而後採用新的測試語料進行分類預測。html

收集數據

數據集是從復旦新聞語料庫中抽取出來的,考慮學習使用,樣本選擇並不大。主要抽選藝術、文學、教育、哲學、歷史五個類別各10篇文章。所有數據文檔50篇。具體狀況不一樣對收集數據要求不一樣,你也能夠選擇網絡爬取,數據庫導出等。這文檔讀取時候可能會遇到gbk,utf-8等格式共存的狀況,這裏建議採用BatUTF8Conv.exe點擊下載)工具,進行utf-8格式批量轉化。git

準備數據

建立數據集代碼以下:github

'''建立數據集和類標籤'''
def loadDataSet():
    docList = [];classList = [] # 文檔列表、類別列表
    dirlist = ['C3-Art','C4-Literature','C5-Education','C6-Philosophy','C7-History']
    for j in range(5):
        for i in range(1, 11): # 總共10個文檔
            # 切分,解析數據,並歸類爲 1 類別
            wordList = textParse(open('./fudan/%s/%d.txt' % (dirlist[j],i),encoding='UTF-8').read())
            docList.append(wordList)
            classList.append(j)
            # print(i,'\t','./fudan/%s/%d.txt' % (dirlist[j],i),'\t',j)
    return docList,classList

''' 利用jieba對文本進行分詞,返回切詞後的list '''
def textParse(str_doc):
    # 正則過濾掉特殊符號、標點、英文、數字等。
    import re
    r1 = '[a-zA-Z0-9’!"#$%&\'()*+,-./:;<=>?@,。?★、…【】《》?「」‘’![\\]^_`{|}~]+'
    str_doc=re.sub(r1, '', str_doc)

    # 建立停用詞列表
    stwlist = set([line.strip() for line in open('./stopwords.txt', 'r', encoding='utf-8').readlines()])
    sent_list = str_doc.split('\n')
    # word_2dlist = [rm_tokens(jieba.cut(part), stwlist) for part in sent_list]  # 分詞並去停用詞
    word_2dlist = [rm_tokens([word+"/"+flag+" " for word, flag in pseg.cut(part) if flag in ['n','v','a','ns','nr','nt']], stwlist) for part in sent_list] # 帶詞性分詞並去停用詞
    word_list = list(itertools.chain(*word_2dlist)) # 合併列表
    return word_list



''' 去掉一些停用詞、數字、特殊符號 '''
def rm_tokens(words, stwlist):
    words_list = list(words)
    for i in range(words_list.__len__())[::-1]:
        word = words_list[i]
        if word in stwlist:  # 去除停用詞
            words_list.pop(i)
        elif len(word) == 1:  # 去除單個字符
            words_list.pop(i)
        elif word == " ":  # 去除空字符
            words_list.pop(i)
    return words_list

代碼分析:loadDataSet()方法是遍歷讀取文件夾,並對每篇文檔進行處理,最後返回所有文檔集的列表和類標籤。textParse()方法是對每篇文檔字符串進行數據預處理,咱們首選使用正則方法保留文本數據,而後進行帶有詞性的中文分詞和詞性選擇,rm_tokens()是去掉一些停用詞、數字、特殊符號。最終返回相對乾淨的數據集和標籤集。算法

分析數據

前面兩篇文章都介紹了,咱們須要把文檔進行向量化表示,首先構建所有文章的單詞集合,實現代碼以下:數據庫

'''獲取全部文檔單詞的集合'''
def createVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 操做符 | 用於求兩個集合的並集
    # print(len(vocabSet),len(set(vocabSet)))
    return list(vocabSet)

基於文檔模型的基礎上,咱們將特徵向量轉化爲數據矩陣向量,這裏使用的詞袋模型,構造與實現方法以下:apache

'''文檔詞袋模型,建立矩陣數據'''
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

對矩陣數據能夠採用可視化分析方法或者結合NLTK進行數據分析,檢查數據分佈狀況和特徵向量構成狀況及其特徵選擇做爲參考。編程

訓練算法

咱們在前面兩篇文章介紹了樸素貝葉斯模型訓練方法,咱們在該方法下稍微改動就獲得以下實現:緩存

'''樸素貝葉斯模型訓練數據優化'''
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix) # 總文件數
    numWords = len(trainMatrix[0]) # 總單詞數

    p1Num=p2Num=p3Num=p4Num=p5Num = ones(numWords) # 各種爲1的矩陣
    p1Denom=p2Denom=p3Denom=p4Denom=p5Denom = 2.0 # 各種特徵和
    num1=num2=num3=num4=num5 = 0 # 各種文檔數目

    pNumlist=[p1Num,p2Num,p3Num,p4Num,p5Num]
    pDenomlist =[p1Denom,p2Denom,p3Denom,p4Denom,p5Denom]
    Numlist = [num1,num2,num3,num4,num5]

    for i in range(numTrainDocs): # 遍歷每篇訓練文檔
        for j in range(5): # 遍歷每一個類別
            if trainCategory[i] == j: # 若是在類別下的文檔
                pNumlist[j] += trainMatrix[i] # 增長詞條計數值
                pDenomlist[j] += sum(trainMatrix[i]) # 增長該類下全部詞條計數值
                Numlist[j] +=1 # 該類文檔數目加1

    pVect,pi = [],[]
    for index in range(5):
        pVect.append(log(pNumlist[index] / pDenomlist[index]))
        pi.append(Numlist[index] / float(numTrainDocs))
    return pVect, pi

構建分類函數,其優化後的代碼實現以下:網絡

'''樸素貝葉斯分類函數,將乘法轉換爲加法'''
def classifyNB(vec2Classify, pVect,pi):
    # 計算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
    bnpi = [] # 文檔分類到各種的機率值列表
    for x in range(5):
        bnpi.append(sum(vec2Classify * pVect[x]) + log(pi[x]))
    # print([bnp for bnp in bnpi])
    # 分類集合
    reslist = ['Art','Literature','Education','Philosophy','History']
    # 根據最大機率,選擇索引值
    index = [bnpi.index(res) for res in bnpi if res==max(bnpi)]
    return reslist[index[0]] # 返回分類值

測試算法

咱們加載構建的數據集方法,而後建立單詞集合,集合詞袋模型進行特徵向量化,構建訓練模型和分類方法,最終咱們從復旦新聞語料中選擇一篇未加入訓練集的教育類文檔,進行開放測試,具體代碼以下:

'''樸素貝葉斯新聞分類應用'''
def testingNB():
    # 1. 加載數據集
    dataSet,Classlabels = loadDataSet()
    # 2. 建立單詞集合
    myVocabList = createVocabList(dataSet)

    # 3. 計算單詞是否出現並建立數據矩陣
    trainMat = []
    for postinDoc in dataSet:
        trainMat.append(bagOfWords2VecMN(myVocabList, postinDoc))
    with open('./word-bag.txt','w') as f:
        for i in trainMat:
            f.write(str(i)+'\r\n')
    # 4. 訓練數據
    pVect,pi= trainNB0(array(trainMat), array(Classlabels))
    # 5. 測試數據
    testEntry = textParse(open('./fudan/test/C5-1.txt',encoding='UTF-8').read())
    thisDoc = array(bagOfWords2VecMN(myVocabList, testEntry))
    print(testEntry[:10], '分類結果是: ', classifyNB(thisDoc, pVect,pi))

實現結果以下:

Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.892 seconds.
Prefix dict has been built succesfully.
['全國/n ', '舉辦/v ', '電影/n ', '新華社/nt ', '北京/ns ', '國家教委/nt ', '廣播電影電視部/nt ', '文化部/n ', '聯合/v ', '決定/v '] 分類結果是:  Literature
耗時:29.4882 s

結果分析:咱們運行分類器得出結果易知,預測結果是文化類,且運行時間爲29s。首先分析爲何預測錯誤,這裏面主要是訓練集樣本比較少和特徵選擇的緣由。運行時間是因爲將特徵矩陣存儲本地後,後面直接讀取文本,至關於加載緩存,大大縮短運行時間。可是這裏還有值得優化的地方,好比每次運行都會加載訓練模型,大大消耗時間,咱們能不能訓練模型加載一次,屢次調用呢?固然是能夠的,這個問題下文繼續優化。咱們重點關注下特徵選擇問題

特徵選擇問題討論

  • 作文本分類的時候,遇到特徵矩陣1.5w。在測試篇幅小的文章老是分類錯誤?這個時候如何作特徵選擇?是否是說去掉特徵集中頻率極高和極低的一部分,對結果有所提高?
    答:你說的這個狀況是很廣泛的現象,篇幅小的文章,特徵小,因此模型更容易判斷出錯!去掉高頻和低頻一般是可使得訓練的模型泛化能力變強

  • 好比:藝術,文化,歷史,教育。界限原本就不明顯,好比測試數據「我愛藝術,藝術是個人所有」。結果會分類爲文化。其實這個裏面還有就是不一樣特徵詞的權重問題,採用tf-idf優化下應該會好一些?

    答:我我的以爲作文本特徵提取,仍是須要本身去分析文本自己內容的文字特色,你能夠把每一類的文本的實體提取出來,而後統計一下每一個詞在每一類上的數量,看看數量分佈,也許能夠發現一些數據特色

  • 我就是按照這個思路作的,還有改進時候的停用詞,其實能夠分析特徵文本,針對不一樣業務,使用自定義的停用詞要比通用的好
    還有提早各種見最具表徵性的詞彙加權,凸顯本類的權重是吧?
    答:好比,藝術類文章中,哪些詞出現較多,哪些詞出現少,再觀察這些詞的詞性主要是哪些,這樣可能會對你制定提取特徵規則方式的時候提供必定的思路參考,我能夠告訴你的是,有些詞絕對會某一類文章出出現多,而後在其餘類文章出現不多,這一類的詞就是文章的特徵詞

  • 那樣的思路能夠是:對某類文章單獨構建類內的詞彙表再進行選擇。最後對類間詞彙表疊加就ok了。
    答:詞彙表有個缺點就是,不能很好的適應新詞
  • 改進思路呢
    答:我給你一個改進思路:你只提取每一個文本中的名詞、動詞、形容詞、地名,用這些詞的做爲文本的特徵來訓練試一試,用文本分類用主題模型(LDA)來向量化文本,再訓練模型試一試。若是效果仍是不夠好,再將文本向量用PCA進行一次特徵降維,而後再訓練模型試一試,按常理來講,效果應該會有提升
  • 還有我以前我的寫的程序分類效果不理想,後來改用sklearn內置BN運行依舊不理想。適當改進了特徵提取,仍是不理想。估計每類10篇文章的訓練數據太少了
    答:文本自己特徵提取就相對難一些,再加上訓練數據少,訓練出來的模型效果可想而已,正常的

sklearn:樸素貝葉斯分類調用

數據準備和數據預處理

加載文檔數據集和分類集

數據準備和數據預處理上文已經介紹了,本節增長了一個全局變量存儲詞彙表,目的是寫入到本地文本里,本地讀取詞聚集,避免每次都作特徵向量時加載訓練集,提升運行時間。

myVocabList = [] # 設置詞彙表的全局變量

'''建立數據集和類標籤'''
def loadDataSet():
    docList = [];classList = []  # 文檔列表、類別列表、文本特徵
    dirlist = ['C3-Art','C4-Literature','C5-Education','C6-Philosophy','C7-History']
    for j in range(5):
        for i in range(1, 11): # 總共10個文檔
            # 切分,解析數據,並歸類爲 1 類別
            wordList = textParse(open('./fudan/%s/%d.txt' % (dirlist[j],i),encoding='UTF-8').read())
            docList.append(wordList)
            classList.append(j)
            # print(i,'\t','./fudan/%s/%d.txt' % (dirlist[j],i),'\t',j)
    # print(len(docList),len(classList),len(fullText))
    global myVocabList
    myVocabList = createVocabList(docList)  # 建立單詞集合
    return docList,classList,myVocabList



''' 利用jieba對文本進行分詞,返回切詞後的list '''
def textParse(str_doc): #與上文方法一致


''' 去掉一些停用詞、數字、特殊符號 '''
def rm_tokens(words, stwlist):  #與上文方法一致

文檔數據集和分類集在本地讀寫操做

# 本地存儲數據集和標籤
def storedata():
    # 3. 計算單詞是否出現並建立數據矩陣
    # trainMat =[[0,1,2,3],[2,3,1,5],[0,1,4,2]] # 訓練集
    # classList = [0,1,2] #類標籤
    docList,classList,myVocabList = loadDataSet()
    # 計算單詞是否出現並建立數據矩陣
    trainMat = []
    for postinDoc in docList:
        trainMat.append(bagOfWords2VecMN(myVocabList, postinDoc))
    res = ""
    for i in range(len(trainMat)):
        res +=' '.join([str(x) for x in trainMat[i]])+' '+str(classList[i])+'\n'
    # print(res[:-1]) # 刪除最後一個換行符
    with open('./word-bag.txt','w') as fw:
        fw.write(res[:-1])
    with open('./wordset.txt','w') as fw:
        fw.write(' '.join([str(v) for v in myVocabList]))


# 讀取本地數據集和標籤
    def grabdata():
        f = open('./word-bag.txt') # 讀取本地文件
        arrayLines = f.readlines() # 行向量
        tzsize = len(arrayLines[0].split(' '))-1 # 列向量,特徵個數減1即數據集
        returnMat = zeros((len(arrayLines),tzsize))    # 0矩陣數據集
        classLabelVactor = []                     # 標籤集,特徵最後一列

        index = 0
        for line in arrayLines: # 逐行讀取
            listFromLine = line.strip().split(' ')    # 分析數據,空格處理
            # print(listFromLine)
            returnMat[index,:] = listFromLine[0:tzsize] # 數據集
            classLabelVactor.append(int(listFromLine[-1])) # 類別標籤集
            index +=1
        # print(returnMat,classLabelVactor)
        myVocabList=writewordset()
        return returnMat,classLabelVactor,myVocabList

    def writewordset():
        f1 = open('./wordset.txt')
        myVocabList =f1.readline().split(' ')
        for w in myVocabList:
            if w=='':
                myVocabList.remove(w)
        return myVocabList

獲取文檔集合和構建詞袋模型

'''獲取全部文檔單詞的集合'''
def createVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 操做符 | 用於求兩個集合的並集
    # print(len(vocabSet),len(set(vocabSet)))
    return list(vocabSet)



'''文檔詞袋模型,建立矩陣數據'''
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

高斯樸素貝葉斯

GaussianNB 實現了運用於分類的高斯樸素貝葉斯算法。特徵的可能性(即機率)假設爲高斯分佈:

參數使用最大似然法估計。

高斯樸素貝葉斯實現方法代碼:

'''高斯樸素貝葉斯'''
def MyGaussianNB(trainMat='',Classlabels='',testDoc=''):
    # -----sklearn GaussianNB-------
    # 訓練數據
    X = np.array(trainMat)
    Y = np.array(Classlabels)
    # 高斯分佈
    clf = GaussianNB()
    clf.fit(X, Y)
    # 測試預測結果
    index = clf.predict(testDoc) # 返回索引
    reslist = ['Art','Literature','Education','Philosophy','History']
    print(reslist[index[0]])

多項樸素貝葉斯

MultinomialNB 實現了服從多項分佈數據的樸素貝葉斯算法,也是用於文本分類(這個領域中數據每每以詞向量表示,儘管在實踐中 tf-idf 向量在預測時表現良好)的兩大經典樸素貝葉斯算法之一。 分佈參數由每類 y 的 向量決定, 式中 n 是特徵的數量(對於文本分類,是詞彙量的大小) 是樣本中屬於類 y 中特徵 i 機率

參數 使用平滑過的最大似然估計法來估計,即相對頻率計數:

式中 是 訓練集 T 中 特徵 i 在類 y 中出現的次數,
是類 y 中出現全部特徵的計數總和。
先驗平滑因子應用於在學習樣本中沒有出現的特徵,以防在未來的計算中出現0機率輸出。 把 被稱爲拉普拉斯平滑(Lapalce smoothing),而 被稱爲利德斯通(Lidstone smoothing)。

多項樸素貝葉斯實現方法代碼:

'''多項樸素貝葉斯'''
def MyMultinomialNB(trainMat='',Classlabels='',testDoc=''):
    # -----sklearn MultinomialNB-------
    # 訓練數據
    X = np.array(trainMat)
    Y = np.array(Classlabels)
    # 多項樸素貝葉斯
    clf = MultinomialNB()
    clf.fit(X, Y)
    # 測試預測結果
    index = clf.predict(testDoc) # 返回索引
    reslist = ['Art','Literature','Education','Philosophy','History']
    print(reslist[index[0]])

伯努利樸素貝葉斯

BernoulliNB 實現了用於多重伯努利分佈數據的樸素貝葉斯訓練和分類算法,即有多個特徵,但每一個特徵 都假設是一個二元 (Bernoulli, boolean) 變量。 所以,這類算法要求樣本以二元值特徵向量表示;若是樣本含有其餘類型的數據, 一個 BernoulliNB 實例會將其二值化(取決於 binarize 參數)。伯努利樸素貝葉斯的決策規則基於

與多項分佈樸素貝葉斯的規則不一樣 伯努利樸素貝葉斯明確地懲罰類 y 中沒有出現做爲預測因子的特徵 i ,而多項分佈分佈樸素貝葉斯只是簡單地忽略沒出現的特徵。

在文本分類的例子中,詞頻向量(word occurrence vectors)(而非詞數向量(word count vectors))可能用於訓練和用於這個分類器。 BernoulliNB 可能在一些數據集上可能表現得更好,特別是那些更短的文檔。 若是時間容許,建議對兩個模型都進行評估。

伯努利樸素貝葉斯代碼實現以下:

'''伯努利樸素貝葉斯'''
def MyBernoulliNB(trainMat='',Classlabels='',testDoc=''):
    # -----sklearn BernoulliNB-------
    # 訓練數據
    X = np.array(trainMat)
    Y = np.array(Classlabels)
    # 多項樸素貝葉斯
    clf = BernoulliNB()
    clf.fit(X, Y)
    # 測試預測結果
    index = clf.predict(testDoc) # 返回索引
    reslist = ['Art','Literature','Education','Philosophy','History']
    print(reslist[index[0]])

各類貝葉斯模型分類測試

代碼實現以下:
def testingNB():

# 加載數據集和單詞集合
trainMat,Classlabels,myVocabList = grabdata() # 讀取訓練結果
# 測試數據
testEntry = textParse(open('./fudan/test/C6-2.txt',encoding='UTF-8').read())
testDoc = np.array(bagOfWords2VecMN(myVocabList, testEntry)) # 測試數據
# 測試預測結果
MyGaussianNB(trainMat,Classlabels,testDoc)
MyMultinomialNB(trainMat,Classlabels,testDoc)
MyBernoulliNB(trainMat,Classlabels,testDoc)

運行結果:

Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 1.014 seconds.
Prefix dict has been built succesfully.
高斯樸素貝葉斯:Education
多項樸素貝葉斯分類結果:Art
伯努利樸素貝葉斯分類結果:Literature
耗時:2.3996 s

參考文獻

  1. scikit中文社區:http://sklearn.apachecn.org/cn/0.19.0/
  2. 中文維基百科:https://zh.wikipedia.org/wiki/
  3. 文本分類特徵選擇:https://www.cnblogs.com/june0507/p/7601001.html
  4. GitHub:https://github.com/BaiNingchao/MachineLearning-1
  5. 圖書:《機器學習實戰》
  6. 圖書:《天然語言處理理論與實戰》

完整代碼下載

源碼請進【機器學習和天然語言QQ羣:436303759】文件下載:


做者聲明

本文版權歸做者全部,旨在技術交流使用。未經做者贊成禁止轉載,轉載後需在文章頁面明顯位置給出原文鏈接,不然相關責任自行承擔。

相關文章
相關標籤/搜索