自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

機(jī)器學(xué)習(xí)算法實(shí)踐:樸素貝葉斯 (Naive Bayes)

開發(fā) 后端 算法
本文中我將一步步實(shí)現(xiàn)一個(gè)樸素貝葉斯分類器,并采用SMS垃圾短信語料庫中的數(shù)據(jù)進(jìn)行模型訓(xùn)練,對(duì)垃圾短信進(jìn)行過濾,在最后對(duì)分類的錯(cuò)誤率進(jìn)行了計(jì)算。

[[197761]]

前言

上一篇《機(jī)器學(xué)習(xí)算法實(shí)踐:決策樹 (Decision Tree)》總結(jié)了決策樹的實(shí)現(xiàn),本文中我將一步步實(shí)現(xiàn)一個(gè)樸素貝葉斯分類器,并采用SMS垃圾短信語料庫中的數(shù)據(jù)進(jìn)行模型訓(xùn)練,對(duì)垃圾短信進(jìn)行過濾,在***對(duì)分類的錯(cuò)誤率進(jìn)行了計(jì)算。

正文

與決策樹分類和k近鄰分類算法不同,貝葉斯分類主要借助概率論的知識(shí)來通過比較提供的數(shù)據(jù)屬于每個(gè)類型的條件概率, 將他們分別計(jì)算出來然后預(yù)測(cè)具有***條件概率的那個(gè)類別是***的類別。當(dāng)然樣本越多我們統(tǒng)計(jì)的不同類型的特征值分布就越準(zhǔn)確,使用此分布進(jìn)行預(yù)測(cè)則會(huì)更加準(zhǔn)確。

貝葉斯準(zhǔn)則

樸素貝葉斯分類器中最核心的便是貝葉斯準(zhǔn)則,他用如下的公式表示:

 

此公式表示兩個(gè)互換的條件概率之間的關(guān)系,他們通過聯(lián)合概率關(guān)聯(lián)起來,這樣使得我們知道p(B|A) 的情況下去計(jì)算p(A|B) 成為了可能,而我們的貝葉斯模型便是通過貝葉斯準(zhǔn)則去計(jì)算某個(gè)樣本在不同類別條件下的條件概率并取具有***條件概率的那個(gè)類型作為分類的預(yù)測(cè)結(jié)果。

使用條件概率來進(jìn)行分類

這里我通俗的介紹下如何通過條件概率來進(jìn)行分類,假設(shè)我們看到了一個(gè)人的背影,想通過他背影的一些特征(數(shù)據(jù))來判斷這個(gè)人的性別(類別),假設(shè)其中涉及到的特征有: 是否是長(zhǎng)發(fā), 身高是否在170以上,腿細(xì),是否穿裙子。當(dāng)我們看到一個(gè)背影便會(huì)得到一個(gè)特征向量用來描述上述特征(1表示是,0表示否): ω=[0,1,1,0]

貝葉斯分類便是比較如下兩個(gè)條件概率:

  • p(男生|ω) ,ω 等于 [0,1,1,0 的條件下此人是男生的概率
  • p(女生|ω)) ,ω 等于 [0,1,1,0] 的條件下此人是女生的概率

若p(男生|ω)>p(女生|ω) , 則判定此人為男生, 否則為女生

那么p(男生|ω) 怎么求呢? 這就要上貝葉斯準(zhǔn)則了

根據(jù)貝葉斯準(zhǔn)則,

 

寫成好理解些的便是:

 

如果特征之間都是相互獨(dú)立的(條件獨(dú)立性假設(shè)),那么便可以將上述條件概率改寫成:

 

這樣我們就能計(jì)算當(dāng)前這個(gè)背影屬于男生和屬于女生的條件概率了。

實(shí)現(xiàn)自己的貝葉斯分類器

貝葉斯分類器實(shí)現(xiàn)起來非常的簡(jiǎn)單, 下面我以進(jìn)行文本分類為目的使用Python實(shí)現(xiàn)一個(gè)樸素貝葉斯文本分類器.

為了計(jì)算條件概率,我們需要計(jì)算各個(gè)特征的在不同類別下的條件概率以及類型的邊際概率,這就需要我們通過大量的訓(xùn)練數(shù)據(jù)進(jìn)行統(tǒng)計(jì)獲取近似值了,這也就是我們訓(xùn)練我們樸素貝葉斯模型的過程.

針對(duì)不同的文本,我們可以將所有出現(xiàn)的單詞作為數(shù)據(jù)特征向量,統(tǒng)計(jì)每個(gè)文本中出現(xiàn)詞條的數(shù)目(或者是否出現(xiàn)某個(gè)詞條)作為數(shù)據(jù)向量。這樣一個(gè)文本就可以處理成一個(gè)整數(shù)列表,并且長(zhǎng)度是所有詞條的數(shù)目,這個(gè)向量也許會(huì)很長(zhǎng),用于本文的數(shù)據(jù)集中的短信詞條大概一共3000多個(gè)單詞。

  1. def get_doc_vector(words, vocabulary): 
  2.  
  3.     ''' 根據(jù)詞匯表將文檔中的詞條轉(zhuǎn)換成文檔向量 
  4.  
  5.   
  6.  
  7.     :param words: 文檔中的詞條列表 
  8.  
  9.     :type words: list of str 
  10.  
  11.   
  12.  
  13.     :param vocabulary: 總的詞匯列表 
  14.  
  15.     :type vocabulary: list of str 
  16.  
  17.   
  18.  
  19.     :return doc_vect: 用于貝葉斯分析的文檔向量 
  20.  
  21.     :type doc_vect: list of int 
  22.  
  23.     ''
  24.  
  25.     doc_vect = [0]*len(vocabulary) 
  26.  
  27.   
  28.  
  29.     for word in words: 
  30.  
  31.         if word in vocabulary: 
  32.  
  33.             idx = vocabulary.index(word) 
  34.  
  35.             doc_vect[idx] = 1 
  36.  
  37.   
  38.  
  39.     return doc_vect  

統(tǒng)計(jì)訓(xùn)練的過程的代碼實(shí)現(xiàn)如下:

  1. def train(self, dataset, classes): 
  2.  
  3.     ''' 訓(xùn)練樸素貝葉斯模型 
  4.  
  5.   
  6.  
  7.     :param dataset: 所有的文檔數(shù)據(jù)向量 
  8.  
  9.     :type dataset: MxN matrix containing all doc vectors. 
  10.  
  11.   
  12.  
  13.     :param classes: 所有文檔的類型 
  14.  
  15.     :type classes: 1xN list 
  16.  
  17.   
  18.  
  19.     :return cond_probs: 訓(xùn)練得到的條件概率矩陣 
  20.  
  21.     :type cond_probs: dict 
  22.  
  23.   
  24.  
  25.     :return cls_probs: 各種類型的概率 
  26.  
  27.     :type cls_probs: dict 
  28.  
  29.     ''
  30.  
  31.     # 按照不同類型記性分類 
  32.  
  33.     sub_datasets = defaultdict(lambda: []) 
  34.  
  35.     cls_cnt = defaultdict(lambda: 0) 
  36.  
  37.   
  38.  
  39.     for doc_vect, cls in zip(dataset, classes): 
  40.  
  41.         sub_datasets[cls].append(doc_vect) 
  42.  
  43.         cls_cnt[cls] += 1 
  44.  
  45.   
  46.  
  47.     # 計(jì)算類型概率 
  48.  
  49.     cls_probs = {k: v/len(classes) for k, v in cls_cnt.items()} 
  50.  
  51.   
  52.  
  53.     # 計(jì)算不同類型的條件概率 
  54.  
  55.     cond_probs = {} 
  56.  
  57.     dataset = np.array(dataset) 
  58.  
  59.     for cls, sub_dataset in sub_datasets.items(): 
  60.  
  61.         sub_dataset = np.array(sub_dataset) 
  62.  
  63.         # Improve the classifier. 
  64.  
  65.         cond_prob_vect = np.log((np.sum(sub_dataset, axis=0) + 1)/(np.sum(dataset) + 2)) 
  66.  
  67.         cond_probs[cls] = cond_prob_vect 
  68.  
  69.   
  70.  
  71.     return cond_probs, cls_probs  

注意這里對(duì)于基本的條件概率直接相乘有兩處改進(jìn):

  1. 各個(gè)特征的概率初始值為1,分母上統(tǒng)計(jì)的某一類型的樣本總數(shù)的初始值是1,這是為了避免如果有一個(gè)特征統(tǒng)計(jì)的概率為0,則聯(lián)合概率也為零那自然沒有什么意義了, 如果訓(xùn)練樣本足夠大時(shí),并不會(huì)對(duì)比較結(jié)果產(chǎn)生影響.
  2. 由于各個(gè)獨(dú)立特征的概率都是小于1的數(shù),累積起來必然會(huì)是個(gè)更小的書,這會(huì)遇到浮點(diǎn)數(shù)下溢的問題,因此在這里我們對(duì)所有的概率都取了對(duì)數(shù)處理,這樣在保證不會(huì)有損失的情況下避免了下溢的問題。

獲取了統(tǒng)計(jì)概率信息后,我們便可以通過貝葉斯準(zhǔn)則預(yù)測(cè)我們數(shù)據(jù)的類型了,這里我并沒有直接計(jì)算每種情況的概率,而是通過統(tǒng)計(jì)得到的向量與數(shù)據(jù)向量進(jìn)行內(nèi)積獲取條件概率的相對(duì)值并進(jìn)行相對(duì)比較做出決策的。

  1. def classify(self, doc_vect, cond_probs, cls_probs): 
  2.  
  3.     ''' 使用樸素貝葉斯將doc_vect進(jìn)行分類. 
  4.  
  5.     ''
  6.  
  7.     pred_probs = {} 
  8.  
  9.     for cls, cls_prob in cls_probs.items(): 
  10.  
  11.         cond_prob_vect = cond_probs[cls] 
  12.  
  13.         pred_probs[cls] = np.sum(cond_prob_vect*doc_vect) + np.log(cls_prob) 
  14.  
  15.     return max(pred_probs, key=pred_probs.get)  

進(jìn)行短信分類

已經(jīng)構(gòu)建好了樸素貝葉斯模型,我們就可以使用此模型來統(tǒng)計(jì)數(shù)據(jù)并用來預(yù)測(cè)了。這里我使用了SMS垃圾短信語料庫中的垃圾短信數(shù)據(jù), 并隨機(jī)抽取90%的數(shù)據(jù)作為訓(xùn)練數(shù)據(jù),剩下10%的數(shù)據(jù)作為測(cè)試數(shù)據(jù)來測(cè)試我們的貝葉斯模型預(yù)測(cè)的準(zhǔn)確性。

當(dāng)然在建立模型前我們需要將數(shù)據(jù)處理成模型能夠處理的格式:

  1. ENCODING = 'ISO-8859-1' 
  2.  
  3. TRAIN_PERCENTAGE = 0.9 
  4.  
  5.   
  6.  
  7. def get_doc_vector(words, vocabulary): 
  8.  
  9.     ''' 根據(jù)詞匯表將文檔中的詞條轉(zhuǎn)換成文檔向量 
  10.  
  11.   
  12.  
  13.     :param words: 文檔中的詞條列表 
  14.  
  15.     :type words: list of str 
  16.  
  17.   
  18.  
  19.     :param vocabulary: 總的詞匯列表 
  20.  
  21.     :type vocabulary: list of str 
  22.  
  23.   
  24.  
  25.     :return doc_vect: 用于貝葉斯分析的文檔向量 
  26.  
  27.     :type doc_vect: list of int 
  28.  
  29.     ''
  30.  
  31.     doc_vect = [0]*len(vocabulary) 
  32.  
  33.   
  34.  
  35.     for word in words: 
  36.  
  37.         if word in vocabulary: 
  38.  
  39.             idx = vocabulary.index(word) 
  40.  
  41.             doc_vect[idx] = 1 
  42.  
  43.   
  44.  
  45.     return doc_vect 
  46.  
  47.   
  48.  
  49. def parse_line(line): 
  50.  
  51.     ''' 解析數(shù)據(jù)集中的每一行返回詞條向量和短信類型. 
  52.  
  53.     ''
  54.  
  55.     cls = line.split(',')[-1].strip() 
  56.  
  57.     content = ','.join(line.split(',')[: -1]) 
  58.  
  59.     word_vect = [word.lower() for word in re.split(r'\W+', content) if word] 
  60.  
  61.     return word_vect, cls 
  62.  
  63.   
  64.  
  65. def parse_file(filename): 
  66.  
  67.     ''' 解析文件中的數(shù)據(jù) 
  68.  
  69.     ''
  70.  
  71.     vocabulary, word_vects, classes = [], [], [] 
  72.  
  73.     with open(filename, 'r', encoding=ENCODING) as f: 
  74.  
  75.         for line in f: 
  76.  
  77.             if line: 
  78.  
  79.                 word_vect, cls = parse_line(line) 
  80.  
  81.                 vocabulary.extend(word_vect) 
  82.  
  83.                 word_vects.append(word_vect) 
  84.  
  85.                 classes.append(cls) 
  86.  
  87.     vocabulary = list(set(vocabulary)) 
  88.  
  89.   
  90.  
  91.     return vocabulary, word_vects, classes  

有了上面三個(gè)函數(shù)我們就可以直接將我們的文本轉(zhuǎn)換成模型需要的數(shù)據(jù)向量,之后我們就可以劃分?jǐn)?shù)據(jù)集并將訓(xùn)練數(shù)據(jù)集給貝葉斯模型進(jìn)行統(tǒng)計(jì)。

  1. # 訓(xùn)練數(shù)據(jù) & 測(cè)試數(shù)據(jù) 
  2.  
  3. ntest = int(len(classes)*(1-TRAIN_PERCENTAGE)) 
  4.  
  5.   
  6.  
  7. test_word_vects = [] 
  8.  
  9. test_classes = [] 
  10.  
  11. for i in range(ntest): 
  12.  
  13.     idx = random.randint(0, len(word_vects)-1) 
  14.  
  15.     test_word_vects.append(word_vects.pop(idx)) 
  16.  
  17.     test_classes.append(classes.pop(idx)) 
  18.  
  19.   
  20.  
  21. train_word_vects = word_vects 
  22.  
  23. train_classes = classes 
  24.  
  25.   
  26.  
  27. train_dataset = [get_doc_vector(words, vocabulary) for words in train_word_vects]  

訓(xùn)練模型:

  1. cond_probs, cls_probs = clf.train(train_dataset, train_classes) 

剩下我們用測(cè)試數(shù)據(jù)來測(cè)試我們貝葉斯模型的預(yù)測(cè)準(zhǔn)確度:

  1. # 測(cè)試模型 
  2.  
  3. error = 0 
  4.  
  5. for test_word_vect, test_cls in zip(test_word_vects, test_classes): 
  6.  
  7.     test_data = get_doc_vector(test_word_vect, vocabulary) 
  8.  
  9.     pred_cls = clf.classify(test_data, cond_probs, cls_probs) 
  10.  
  11.     if test_cls != pred_cls: 
  12.  
  13.         print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls)) 
  14.  
  15.         error += 1 
  16.  
  17.   
  18.  
  19. print('Error Rate: {}'.format(error/len(test_classes)))  

隨機(jī)測(cè)了四組,錯(cuò)誤率分別為:0, 0.037, 0.015, 0. 平均錯(cuò)誤率為1.3%

測(cè)完了我們嘗試下看看不同類型短信各個(gè)詞條的概率分布是怎樣的吧:

  1. # 繪制不同類型的概率分布曲線 
  2.  
  3. fig = plt.figure() 
  4.  
  5. ax = fig.add_subplot(111) 
  6.  
  7. for cls, probs in cond_probs.items(): 
  8.  
  9.     ax.scatter(np.arange(0, len(probs)), 
  10.  
  11.                probs*cls_probs[cls], 
  12.  
  13.                label=cls, 
  14.  
  15.                alpha=0.3) 
  16.  
  17.     ax.legend() 
  18.  
  19.   
  20.  
  21. plt.show() 

 

試試決策樹

上一篇我們基于ID3算法實(shí)現(xiàn)了決策樹,同樣是分類問題,我們同樣可以使用我們的文本數(shù)據(jù)來構(gòu)建用于分類短信的決策樹,當(dāng)然唯一比較麻煩的地方在于如果按照與貝葉斯相同的向量作為數(shù)據(jù),則屬性可能會(huì)非常多,我們?cè)跇?gòu)建決策樹的時(shí)候每層樹結(jié)構(gòu)都是遞歸通過遍歷屬性根據(jù)信息增益來選取***屬性進(jìn)行樹分裂的,這樣很多的屬性可能會(huì)對(duì)構(gòu)建決策樹這一過程來說會(huì)比較耗時(shí).那我們就試試吧…

  1. # 生成決策樹 
  2.  
  3. if not os.path.exists('sms_tree.pkl'): 
  4.  
  5.     clf.create_tree(train_dataset, train_classes, vocabulary) 
  6.  
  7.     clf.dump_tree('sms_tree.pkl'
  8.  
  9. else
  10.  
  11.     clf.load_tree('sms_tree.pkl'
  12.  
  13.   
  14.  
  15. # 測(cè)試模型 
  16.  
  17. error = 0 
  18.  
  19. for test_word_vect, test_cls in zip(test_word_vects, test_classes): 
  20.  
  21.     test_data = get_doc_vector(test_word_vect, vocabulary) 
  22.  
  23.     pred_cls = clf.classify(test_data, feat_names=vocabulary) 
  24.  
  25.     if test_cls != pred_cls: 
  26.  
  27.         print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls)) 
  28.  
  29.         error += 1 
  30.  
  31.   
  32.  
  33. print('Error Rate: {}'.format(error/len(test_classes)))  

隨機(jī)測(cè)了兩次,錯(cuò)誤率分別為:0.09, 0.0

效果還算不錯(cuò)

我們還是用Graphviz可視化看一下決策樹都選取了那些詞條作為判別標(biāo)準(zhǔn)(這時(shí)候決策樹的好處就體現(xiàn)出來了)。

   

 

可見決策樹的深度并不是很深,如果分類類型一多,估計(jì)深度增加上去決策樹可能會(huì)有些麻煩。

總結(jié)

本文我們使用Python一步步實(shí)現(xiàn)了樸素貝葉斯分類器,并對(duì)短信進(jìn)行了垃圾短信過濾,同樣的數(shù)據(jù)我們同決策樹的分類效果進(jìn)行了簡(jiǎn)單的比較。本文相關(guān)代碼實(shí)現(xiàn):https://github.com/PytLab/MLBox/tree/master/naive_bayes 。決策樹過濾垃圾短信的腳本在https://github.com/PytLab/MLBox/tree/master/decision_tree

參考

  • 《Machine Learning in Action》
  • 實(shí)例詳解貝葉斯推理的原理
  • 大道至簡(jiǎn):樸素貝葉斯分類器

相關(guān)閱讀

 

責(zé)任編輯:龐桂玉 來源: Python開發(fā)者
相關(guān)推薦

2012-09-24 10:13:35

貝葉斯

2017-03-29 14:50:18

2023-01-31 15:49:51

機(jī)器學(xué)習(xí)函數(shù)評(píng)分函數(shù)

2021-08-30 11:53:36

機(jī)器學(xué)習(xí)人工智能計(jì)算機(jī)

2020-05-21 14:50:37

算法深度學(xué)習(xí)人工智能

2021-07-29 13:06:29

Python機(jī)器學(xué)習(xí)編程語言

2021-04-18 09:57:45

Java樸素貝葉斯貝葉斯定理

2018-09-13 12:51:58

數(shù)據(jù)挖掘算法樸素貝葉斯

2017-07-12 11:27:05

樸素貝葉斯情感分析Python

2019-03-20 07:50:47

機(jī)器學(xué)習(xí)算法線性回歸

2022-01-03 20:18:37

定理應(yīng)用貝葉斯

2023-11-28 12:08:56

機(jī)器學(xué)習(xí)算法人工智能

2020-10-19 12:55:59

機(jī)器學(xué)習(xí)技術(shù)人工智能

2017-08-07 13:02:32

全棧必備貝葉斯

2017-11-07 11:17:40

樸素貝葉斯畫像數(shù)據(jù)數(shù)據(jù)挖掘

2017-06-22 09:53:01

機(jī)器學(xué)習(xí)Python樸素貝葉斯

2020-10-09 12:41:04

算法優(yōu)化場(chǎng)景

2018-10-18 09:00:00

機(jī)器學(xué)習(xí)機(jī)器學(xué)習(xí)算法數(shù)據(jù)科學(xué)家

2012-02-14 10:55:24

2018-07-03 15:26:35

算法機(jī)器學(xué)習(xí)數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)