樸素貝葉斯分類算法對垃圾短信分類實現(xiàn)總結(jié)

本文總體架構(gòu):

一思犁、樸素貝葉斯分類器的理論解釋和計算步驟

二逊笆、代碼框架展示

三拂苹、結(jié)合樸素貝葉斯理論解釋代碼

四谭羔、此次試驗總結(jié)

本次試驗主要解決對短信類型的分類問題华糖,其實也是解決文本分類問題。

一瘟裸、樸素貝葉斯分類器理論

樸素貝葉斯與貝葉斯的區(qū)別客叉,如圖

image

這里我們運用樸素貝葉斯分類器是因為這個算法的屬性我們假設是獨立的,而事實上一些文本中的詞與詞之間是存在聯(lián)系的话告,我們?yōu)榱撕啽憔图僭O詞與詞之間兼搏,即屬性間的關系是獨立的。所以我們的后驗概率表示為

image

我們要計算出先驗概率 P(c) 和每個屬性的條件概率P(xi|c)沙郭,P(X)為證據(jù)因子

下面解釋一下先驗概率佛呻、條件概率、證據(jù)因子之間的關系病线,以及為什么證據(jù)因子不變的

樸素貝葉斯計算步驟

第一步計算P(c)
image

為了避免某個屬性值在訓練集沒有出現(xiàn)吓著,導致該類情況出現(xiàn)概率為0,影響判斷送挑,我們引入拉普拉斯修正夜矗,N表示訓練集D中可能出現(xiàn)的類別
image

第二步計算P(xi | c)
image

同理引入拉普拉斯修正,Ni表示第i個屬性可能取值的個數(shù)

拉普拉斯修正解釋鏈接:https://www.cnblogs.com/misszhu-home/p/7693168.html

image

第三步計算出P(C|X)
image

第四步使用對數(shù)似然 log p(c|x),取最大值

我們根據(jù)上面的計算步驟让虐,直接上代碼解釋

二、代碼步驟

思路先定義各個非主函數(shù)罢荡,然后我們在定義主函數(shù)數(shù)的時候赡突,往各個函數(shù)中傳入?yún)?shù)对扶,然后利用這些函數(shù)處理傳入的參數(shù),返回出我們要的數(shù)據(jù)惭缰。比如我們需要將詞條轉(zhuǎn)換成向量浪南,我們就將詞條傳入之前定義好的函數(shù),然后返回出向量形式的參數(shù)漱受。

第一步先定義函數(shù)模塊

1络凿、根據(jù)詞匯表將文檔中的詞條轉(zhuǎn)換成文檔向量函數(shù)

2、解析數(shù)據(jù)集中的每一行返回詞條向量和短信類型函數(shù)

3昂羡、解析文件中的數(shù)據(jù)函數(shù)

第二步絮记、定義樸素貝葉斯分類器

1、樸素貝葉斯模型訓練函數(shù)

2虐先、樸素貝葉斯分類函數(shù)

第三步怨愤、定義主函數(shù)

三、結(jié)合樸素貝葉斯理論解釋代碼

首先蛹批,導入下面幾個庫和定義編碼格式撰洗、訓練數(shù)據(jù)的比例

import random
import numpy as np
import matplotlib.pyplot as plt
from bayes import  NaiveBayesClassifier

ENCODING = 'ISO-8859-1'#編碼
TRAIN_PERCENTAGE = 0.9#訓練數(shù)據(jù)所占的百分率

函數(shù)1、根據(jù)詞匯表將短信文檔中的詞條轉(zhuǎn)換成文檔向量腐芍,因為計算機無法識別文字差导,所以我們將每條短信中的文字變成向量

words: 文檔中的詞條列表(一個文檔) vocabulary: 總的詞匯列表(里面的詞是用數(shù)據(jù)集中所有的文檔里面的不重復值) doc_vect: 用于貝葉斯分析的文檔向量

    ''' 根據(jù)詞匯表將文檔中的詞條轉(zhuǎn)換成文檔向量
    :param words: 文檔中的詞條列表
    :type words: list of str
    :param vocabulary: 總的詞匯列表
    :type vocabulary: list of str
    :return doc_vect: 用于貝葉斯分析的文檔向量
    :type doc_vect: list of int
    '''
    doc_vect = [0]*len(vocabulary)#產(chǎn)生一個列表[0,0......]vocabulery里有幾個詞就有幾個0
    #print(doc_vect)
    #print(len(vocabulary))
    for word in words:
        if word in vocabulary:
            idx = vocabulary.index(word) #index:從vocabulary 中找到word位置
            doc_vect[idx] = 1 #本來列表doc_vect一開始就是全部0,然后如果word在vocabulary中猪勇,
                              # 則找到一個word在vocabulary中的位置设褐,就把那個位置的0替換成1.
            #print(doc_vect)
    return doc_vect#返回一個列表里面可能是[0,1,1,1,1,0,1,1,1,0,1,]

函數(shù)2、解析數(shù)據(jù)集中的每一行返回詞條向量和短信類型.

    ''' 解析數(shù)據(jù)集中的每一行返回詞條向量和短信類型.
    '''
    cls = line.split(',')[-1].strip()#Python strip() 方法用于移除字符串頭尾指定的字符(默認為空格或換行符)或字符序列埠对。
                                     #注意:該方法只能刪除開頭或是結(jié)尾的字符络断,不能刪除中間部分的字符。
    #print("cls:",cls)
    content = ','.join(line.split(',')[: -1])
    #print("content:",content)
    # join():    連接字符串數(shù)組项玛。將字符串貌笨、元組、列表中的元素以指定的字符(分隔符)連接生成一個新的字符串
    word_vect = [word.lower() for word in re.split(r'\W+', content) if word]
    #print("word_vect:",word_vect)
    #re.split(r'\W+', content)content是一個字符串襟沮,分割content锥惋,得到每個含有word字符的一個新content列表
    return word_vect, cls
image
cls:是每條短信所屬的類型,比如垃圾短信spam开伏、非垃圾短信ham膀跌。content:就是每條短信的內(nèi)容。 word_vect:詞條向量固灵。 如下圖
image

函數(shù)3捅伤、解析文件中的數(shù)據(jù)

    ''' 解析文件中的數(shù)據(jù)
    '''
    vocabulary, word_vects, classes = [], [], []
    with open(filename, 'r', encoding=ENCODING) as f:
        for line in f:
            if line:
                word_vect, cls = parse_line(line)
                vocabulary.extend(word_vect)#extend:在vocabulary列表后面增加word_vect
                word_vects.append(word_vect)#把將word_vect寫進列表word_vects
                classes.append(cls)#cls就是每行詞條最后的那個ham和spam
    vocabulary = list(set(vocabulary))# vocabulary是一個詞庫(不重復詞的)
                                      # set()函數(shù)創(chuàng)建一個無序不重復元素集,可進行關系測試巫玻,刪除重復數(shù)據(jù)丛忆,
                                      # 還可以計算交集祠汇、差集、并集等熄诡。(s1-s2)差集
                                      # (s1&s2)#交集
                                      #(s1|s2)#并集
    return vocabulary, word_vects, classes

這里我們對函數(shù)parse_line(line)進行了傳參可很,傳入的參數(shù)正是我們讀取到的文檔數(shù)據(jù),然后函數(shù)parse_line(line)返回每條短信的詞條向量和類型凰浮,將詞條向量word_vect寫入列表vocabulary和word_vects我抠,將類別寫入classes列表。最后列表word_vects和vocabulary中里面有文檔所有詞匯袜茧,將列表vocabulary中重復的詞除去菜拓,制作一個詞庫vocabulary。

定義樸素貝葉斯分類器

導入庫

from collections import defaultdict
import numpy as np

函數(shù)4惫周、訓練函數(shù)

    def train(self, dataset, classes):
        ''' 訓練樸素貝葉斯模型'''

        # 按照不同類型記性分類
        sub_datasets = defaultdict(lambda: [])
        #相當建一個詞典尘惧,但是key是待定的,而value值是空列表递递,即{“  ”:[]}
        cls_cnt = defaultdict(lambda: 0)
        #相當建一個詞典喷橙,但是key是待定的,而value值是0登舞,即{“  ”:0}
        #defaultdict解釋鏈接:https://blog.csdn.net/weixin_42160653/article/details/80297894
        for doc_vect, cls in zip(dataset, classes):
            sub_datasets[cls].append(doc_vect)
            #sub_datasets即{spam:[[文檔向量1],[文檔向量1]]  ham:[[文檔向量3],[文檔向量4]]   }字典格式
            cls_cnt[cls] += 1#cls_cnt是即{spam:數(shù)目,ham:數(shù)目}字典格式
            #相當于統(tǒng)計classes中每個詞出現(xiàn)的數(shù)量贰逾,即spam和ham的數(shù)量
        # 計算類型概率        #cls_cnt是即{spam:數(shù)目,ham:數(shù)目}字典格式
        cls_probs = {k: v/len(classes) for k, v in cls_cnt.items()}
        #cls_cnt是即{spam:數(shù)目,ham:數(shù)目}字典格式
        #用k,v去遍歷cls_cnt,k即key=spam和ham,v即value=spam和ham對應的個數(shù),然后k: v/len(classes)算出各類的占比即概率
        #items()函數(shù)a = {'first':'hello','second':'world'}-->a.items()-->dict_items([(‘first’, ‘hello’), (‘second’, ‘world’)])
        # 計算不同類型的條件概率
        cond_probs = {}
        dataset = np.array(dataset)#array函數(shù):生成矩陣
    

defaultdict解釋鏈接:https://blog.csdn.net/weixin_42160653/article/details/80297894

根據(jù)樸素貝葉斯的計算步驟

第一步:計算P(C)即cls_probs

第二步:計算P(Xi |C),P(Xi |C)=(np.sum(sub_dataset, axis=0) +1)/(np.sum(dataset) +2)菠秒,運用了拉普拉斯修正

第三步:取對數(shù)疙剑,log p(Xi | C)

    for cls, sub_dataset in sub_datasets.items():
            #將key和value分開,循環(huán)第一遍得到cls=["spam"],sub_dataset=[[文檔向量1],[文檔向量2],[文檔向量3].....,[文檔向量n]]
            #循環(huán)第二遍得到cls = ["ham"], sub_dataset = [[文檔向量5], [文檔向量6], [文檔向量7]....., [文檔向量n-1]]
            #sub_datasets即{spam:[[文檔向量1],[文檔向量2]]  ham:[[文檔向量3],[文檔向量4]]   }字典格式
            sub_dataset = np.array(sub_dataset)
            #sub_dataset=['[文檔向量1]'  '[文檔向量2]'  '[文檔向量3]'.....'[文檔向量n]']
            # Improve the classifier.
            cond_prob_vect = np.log((np.sum(sub_dataset, axis=0) + 1)/(np.sum(dataset) + 2))
            #運用了拉普拉斯修正践叠,計算每個一個詞在不同類型的短信中占該詞在所有短信的比率言缤,然后對這個比率取對數(shù),將這個結(jié)果賦予cond_prob_vect
            cond_probs[cls] = cond_prob_vect
            #將上面的計算結(jié)果禁灼,分類管挟,比如ham中yes這個詞出現(xiàn)的概率和spam出現(xiàn)yes的概率
        return cond_probs, cls_probs

我將上圖的計算細節(jié)展示出來

image

然后我們這里返回出來的是P(C):cls_probs, log p(Xi | C)的累加:cond_prob_vect = np.log((np.sum(sub_dataset, axis=0) +1)/(np.sum(dataset) +2))

函數(shù)5弄捕、分類函數(shù)

分類函數(shù)就是計算出log P(C | X)通過前面計算出來的所有詞條件概率的對數(shù)似然值僻孝,與每條短信向量相乘得出每條短信中的詞對應的條件概率的對數(shù)似然值,最后乘上P(C),將計算結(jié)果最大的那個類別返回出來守谓,因為P(x)都是一樣的穿铆,所以不做處理。

        ''' 使用樸素貝葉斯將doc_vect進行分類.
        '''
        pred_probs = {}

        for cls, cls_prob in cls_probs.items():#cls_probs類似{ham:cond_prob_vect,spam:cond_prob_vect}
            cond_prob_vect = cond_probs[cls]
            pred_probs[cls] = np.sum(cond_prob_vect*doc_vect) + np.log(cls_prob)
            #計算出短信的屬于spam和ham的一個似然值斋荞,返回大的那個即為我們的預測答案
        return max(pred_probs, key=pred_probs.get)
上面代碼的計算原理

第三步荞雏、主函數(shù)

主函數(shù)這里我認為

第一步:定義測試集和訓練集的數(shù)據(jù)量

第二步:往各個函數(shù)里傳參

第三步:拿預測類型與實際類型比較看否預測正確

第四步:計算錯誤率

第五步:畫圖

(在注釋里基本已經(jīng)解釋了)

if '__main__' == __name__:
    clf = NaiveBayesClassifier()
    vocabulary, word_vects, classes = parse_file('english_big.txt')
    # 訓練數(shù)據(jù) & 測試數(shù)據(jù)
    ntest = int(len(classes)*(1-TRAIN_PERCENTAGE)) #測試數(shù)據(jù)的數(shù)量
    test_word_vects = []
    test_classes = []
    for i in range(ntest):
        idx = random.randint(0, len(word_vects)-1)#在(0, len(word_vects)-1)之間生成隨機數(shù)
        test_word_vects.append(word_vects.pop(idx))#在word_vects中移除一個idx,將移除的那個詞寫入test_woed_vects中
        test_classes.append(classes.pop(idx))#同理
    train_word_vects = word_vects#此時的word_vects已經(jīng)移除了測試集的那部分了
    train_classes = classes
    #下面就是往上面各個定義的函數(shù)傳參數(shù),返回我們想要的值
    train_dataset = [get_doc_vector(words, vocabulary) for words in train_word_vects]
    # 訓練貝葉斯模型
    cond_probs, cls_probs = clf.train(train_dataset, train_classes)
    #計算出cond_probs, cls_probs=條件概率的似然值讯檐,兩種類型的概率
    # 測試模型
    error = 0
    for test_word_vect, test_cls in zip(test_word_vects, test_classes):

        test_data = get_doc_vector(test_word_vect, vocabulary)#傳入數(shù)據(jù)羡疗,轉(zhuǎn)化成向量
        pred_cls = clf.classify(test_data, cond_probs, cls_probs)#傳入向量,預測類型
        if test_cls != pred_cls:#預測出來的類型與實際類型做判斷
            print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls))
            error += 1
    print('Error Rate: {}'.format(error/len(test_classes)))
    # 繪制不同類型的概率分布曲線
    fig = plt.figure()
    ax = fig.add_subplot(111)#把畫布分成一行一列别洪,然后畫在從左往右從上往下的第一個
    for cls, probs in cond_probs.items():
        ax.scatter(np.arange(0, len(probs)),
                   probs*cls_probs[cls],
                   label=cls,
                   alpha=0.2)
        ax.legend()
    plt.show()

第六、結(jié)果展示

image
image

總結(jié):

樸素貝葉斯除了解決文本分類問題柳刮,還能解決拼寫糾錯挖垛、新聞分類等等諸如此類分類問題。我們從第一二章好瓜壞瓜正例反例的二分類問題秉颗,到我們學習線性模型痢毒、決策樹、神經(jīng)網(wǎng)絡蚕甥、支持向量機哪替、貝葉斯,我們一直在解決這個分類問題菇怀。從簡單的一維到二維凭舶、三維我們不斷在優(yōu)化這個分類的方法和角度。

我們知道算法之間很難說分個高下爱沟,不是說學了神經(jīng)網(wǎng)絡帅霜,決策樹這個算法就沒神經(jīng)網(wǎng)絡那么好。每個算法在不同的應用場景的作用不一樣呼伸,就好像我現(xiàn)在有自行車身冀、汽車、飛機括享,可能我從北苑到南苑搂根,自行車比飛機更好使。但是我要出國的話铃辖,自行車就沒有飛機好使剩愧。決策樹的超強的學習能力和泛化能力,訓練速度快澳叉,適用場景在搜索排序隙咸。神經(jīng)網(wǎng)絡適用于圖像處理(手寫數(shù)字識別)、支持向量機適用在高維分類問題上成洗、樸素貝葉斯則適用在文本分類(垃圾短信分類)五督。各個算法各有長處。

拉普拉斯修正解釋鏈接:https://www.cnblogs.com/misszhu-home/p/7693168.html

defaultdict解釋鏈接:https://blog.csdn.net/weixin_42160653/article/details/80297894

代碼和數(shù)據(jù)文檔:https://gitee.com/liu_ji_duan/DuanGe/tree/master/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E6%9C%B4%E7%B4%A0%E8%B4%9D%E5%8F%B6%E6%96%AF%E5%9E%83%E5%9C%BE%E7%9F%AD%E4%BF%A1%E5%88%86%E7%B1%BB%E5%AE%9E%E7%8E%B0

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓶殃,一起剝皮案震驚了整個濱河市充包,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖基矮,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淆储,死亡現(xiàn)場離奇詭異,居然都是意外死亡家浇,警方通過查閱死者的電腦和手機本砰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钢悲,“玉大人点额,你說我怎么就攤上這事≥毫眨” “怎么了还棱?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惭等。 經(jīng)常有香客問我珍手,道長,這世上最難降的妖魔是什么辞做? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任琳要,我火速辦了婚禮,結(jié)果婚禮上凭豪,老公的妹妹穿的比我還像新娘焙蹭。我一直安慰自己,他們只是感情好嫂伞,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布孔厉。 她就那樣靜靜地躺著,像睡著了一般帖努。 火紅的嫁衣襯著肌膚如雪撰豺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天拼余,我揣著相機與錄音污桦,去河邊找鬼。 笑死匙监,一個胖子當著我的面吹牛凡橱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亭姥,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼稼钩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了达罗?” 一聲冷哼從身側(cè)響起坝撑,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤静秆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后巡李,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抚笔,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年侨拦,在試婚紗的時候發(fā)現(xiàn)自己被綠了殊橙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡狱从,死狀恐怖蛀柴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情矫夯,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布吊洼,位于F島的核電站训貌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冒窍。R本人自食惡果不足惜递沪,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望综液。 院中可真熱鬧款慨,春花似錦、人聲如沸谬莹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽附帽。三九已至埠戳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蕉扮,已是汗流浹背整胃。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喳钟,地道東北人屁使。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像奔则,于是被迫代替她去往敵國和親蛮寂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容