基于概率論的分類方法: 樸素貝葉斯
我的微信公眾號: s406205391; 歡迎大家一起學習,一起進步!W⒉尽女阀!
????????k-近鄰算法和決策樹會給出“該數(shù)據(jù)屬于哪一類”的明確回答宅荤。不過,分類器有時會產(chǎn)生錯誤結果浸策,這是可以要求分類器給出一個最優(yōu)的類別的猜測結果冯键,同事給出這個猜測的概率估計值。
????????樸素貝葉斯就是一個概率分類器庸汗。我們稱之為“樸素”惫确,是因為整個形式化的過程只做最原始、最簡單的假設。樸素貝葉斯的優(yōu)點:在數(shù)據(jù)較少的情況下仍然有效改化,可以處理多類別問題昧诱。缺點:對于輸入數(shù)據(jù)的準備方式較為敏感。適用數(shù)據(jù)類型:標稱型數(shù)據(jù).貝葉斯決策理論的核心思想就是所袁,選擇高概率對應的類別盏档。這里會應用到一種有效計算條件概率的方法,稱為貝葉斯準則燥爷。
數(shù)據(jù)準備:構建詞向量
????????在應用貝葉斯分類算法之前蜈亩,構建輸入向量是很重要的一項。
????????機器學習的一個重要應用就是文檔的自動分類前翎。在文檔分類中稚配,整個文檔(如一封電子郵件)是實例,而電子郵件中的某些原色則構成特征港华。雖然電子郵件是一種會不斷增加的文本道川,但我們同樣也可以對新聞報道、用戶留言立宜、政府公文等其他任意類型的文本進行分類冒萄。我們可以觀察文檔中出現(xiàn)的詞,并把每個詞的出現(xiàn)或者不出現(xiàn)作為一個特征橙数,這樣得到的特征數(shù)目就會跟詞匯表中的詞數(shù)目一樣多尊流。
樸素貝葉斯的一般過程:
收集數(shù)據(jù):可以使用任何方法。
準備數(shù)據(jù):需要數(shù)值型或者布爾型數(shù)據(jù)
分析數(shù)據(jù):有大量特征時灯帮,繪制特征作用不大崖技,此時使用直方圖效果更好
訓練算法:計算不同的獨立特征的條件概率
測試算法:計算錯誤率
使用算法:一個常見的樸素貝葉斯應用是文檔分類排嫌≈莨簦可以在任意的分類場景中使用樸素貝葉斯分類器,不一定非要是文本值朋。
????????樸素貝葉斯有一個很重要的假設腻贰,每個特征同等重要吁恍。
???????? 現(xiàn)在我們通過如下函數(shù)去構建詞向量。load_data_set()函數(shù)創(chuàng)建了一些實驗樣本银受,其中posting_list表示不同的文本践盼,class_vec表示該文本的分類,0表示正常宾巍,1表示侮辱性文本咕幻。我們在處理真實文本時,首先也會把文本拆分成一個個單詞組成的列表顶霞。create_vocab_list()函數(shù)會將詞向量中的所有詞肄程,整合為不含重復詞的列表锣吼。set_of _words_2_vec()函數(shù)會統(tǒng)計不含重復詞的詞向量在我們待統(tǒng)計的詞向量中出現(xiàn)的次數(shù)。即最后我們會得到一個長度等于不含重復詞的詞向量的一個數(shù)值向量蓝厌,其中的數(shù)值表示該詞出現(xiàn)的次數(shù)玄叠。
def load_data_set():
"""構建模擬的詞列表和標簽"""
posting_list = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'pleas'],
['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']]
class_vec = [0, 1, 0, 1, 0, 1] # 1表示屬于侮辱性詞匯
return posting_list, class_vec
def create_vocab_list(data_set):
"""將詞矩陣中的所有詞整合為不含重復詞的列表"""
vocab_set = reduce(lambda x, y: set(x) | set(y), data_set)
return list(vocab_set)
def set_of_words_2_vec(vocab_list, input_set):
""" 統(tǒng)計vocab_list中的詞在input_set中出現(xiàn)的次數(shù)。
:param vocab_list: 需要統(tǒng)計的詞的列表
:param input_set: 生成的不帶重復詞的詞向量
:return: 返回一個數(shù)字矩陣拓提,0表示該詞在vocab_list中未出現(xiàn)读恃,3表示該次在vocab_list中出現(xiàn)了三次
"""
return_vec = [0] * len(vocab_list)
for word in input_set:
if word in vocab_list:
return_vec[vocab_list.index(word)] += 1
return return_vec
訓練算法:從詞向量計算概率
????????我們可以根據(jù)貝葉斯準則來計算文本屬于侮辱性文本的概率。
w表示所有詞的一個向量代态。
????????p(ci)表示文檔屬于侮辱性文檔的概率寺惫,我們可以通過侮辱性文檔的數(shù)量,除以文檔總數(shù)蹦疑,很方便的求的西雀。p(w|ci)表示每一個詞在侮辱性文檔中出現(xiàn)的概率,根據(jù)樸素貝葉斯假設歉摧,我們可以將w展開為一個個獨立的特征,那么我們就可以將上述概率寫作p(w0|ci)p(w1|ci)p(w2|ci)...p(wn|ci)來計算上述概率艇肴。
基本過程如下:
計算每個類別中的文檔數(shù)目
對每篇訓練文檔:
????????對每個類別:
????????????????如果詞條出現(xiàn)雜文檔中,那么增加該詞條的計數(shù)值
????????????????增加所有詞條的計數(shù)值
對每個類別:
????????對每個詞條:
?????????????????將該詞條的數(shù)目除以總詞條數(shù)目得到條件概率
返回每個類別的條件概率
????????我們在利用貝葉斯分類器對文檔進行分類時叁温,要計算多個概率的乘積再悼,如果其中一個概率為0,那么最后的乘積也為0券盅。為降低這種影響帮哈,我們將所有詞的出現(xiàn)數(shù)初始化為1,并將分母初始化為2锰镀。另外一個問題是下溢,當很多很小的概率相乘時咖刃,程序會下溢泳炉,得不到正確答案。因此這里把概率轉換成自然對數(shù)嚎杨。
def train_nb(train_matrix, train_category):
""" 計算訓練矩陣中花鹅,每一個詞屬于不同分類的概率
:param train_matrix: 待計算的詞矩陣
:param train_category: 每一個文檔的分類標簽
:return: 返回每個文檔中每個詞屬于不同分類文檔的概率
"""
# 計算訓練數(shù)據(jù)集中,侮辱性文本的概率
num_train_docs = len(train_matrix)
num_words = len(train_matrix[0])
p_abusive = sum(train_category) / num_train_docs
# 計算每個詞屬于不同分類文本的概率
p0_num = np.ones(num_words)
p1_num = np.ones(num_words)
p0_denom = 2
p1_denom = 2 # 分母為2枫浙,初始分子為0刨肃,為了防止概率過小導致概率為0
for i in range(num_train_docs):
if train_category[i] == 1:
p1_num += train_matrix[i]
p1_denom += sum(train_matrix[i])
else:
p0_num += train_matrix[i]
p0_denom += sum(train_matrix[i])
p1_vect = log(p1_num / p1_denom)
p0_vect = log(p0_num / p0_denom)
return p0_vect, p1_vect, p_abusive
構建樸素貝葉斯分類器
????????在準備好上述函數(shù)后,就可以進行貝葉斯分類了箩帚。
def classify_nb(vec_2_classify, p0_vec, p1_vec, p_class):
p1 = sum(vec_2_classify * p1_vec) + np.log(p_class)
p0 = sum(vec_2_classify * p0_vec) + np.log(1 - p_class)
if p1 > p0:
return 1
else:
return 0
示例:使用樸素貝葉斯過濾垃圾郵件
????????在email(提取碼:lael)文件夾中真友,有兩個子文件,分別表示垃圾郵件和正常郵件紧帕。我們首先將所有文件都整合成詞向量矩陣盔然,然后再隨機取40封郵件作為訓練數(shù)據(jù)集桅打,10封郵件作為測試數(shù)據(jù)集。
#!/usr/bin/env python3
# coding: utf-8
# Author:Shen Yi
# Date :2020/2/16 1:05
"""機器學習實戰(zhàn) 樸素貝葉斯"""
from functools import reduce
from glob import glob
import numpy as np
import re
def load_data_set():
"""構建模擬的詞列表和標簽"""
posting_list = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'pleas'],
['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']]
class_vec = [0, 1, 0, 1, 0, 1]
return posting_list, class_vec
def create_vocab_list(data_set):
"""將詞矩陣中的所有詞整合為不含重復詞的列表"""
vocab_set = reduce(lambda x, y: set(x) | set(y), data_set)
return list(vocab_set)
def set_of_words_2_vec(vocab_list, input_set):
""" 統(tǒng)計vocab_list中的詞在input_set中出現(xiàn)的次數(shù)愈案。
:param vocab_list: 需要統(tǒng)計的詞的列表
:param input_set: 生成的不帶重復詞的詞向量
:return: 返回一個數(shù)字矩陣挺尾,0表示該詞在vocab_list中未出現(xiàn),3表示該次在vocab_list中出現(xiàn)了三次
"""
return_vec = [0] * len(vocab_list)
for word in input_set:
if word in vocab_list:
return_vec[vocab_list.index(word)] += 1
return return_vec
def train_nb(train_matrix, train_category):
""" 計算訓練矩陣中站绪,每一個詞屬于不同分類的概率
:param train_matrix: 待計算的詞矩陣
:param train_category: 每一個文檔的分類標簽
:return: 返回每個文檔中每個詞屬于不同分類文檔的概率
"""
num_train_docs = len(train_matrix)
num_words = len(train_matrix[0])
p_abusive = sum(train_category) / num_train_docs
p0_num = np.ones(num_words)
p1_num = np.ones(num_words)
p0_denom = 2
p1_denom = 2
for i in range(num_train_docs):
if train_category[i] == 1:
p1_num += train_matrix[i]
p1_denom += sum(train_matrix[i])
else:
p0_num += train_matrix[i]
p0_denom += sum(train_matrix[i])
p1_vect = np.log(p1_num / p1_denom)
p0_vect = np.log(p0_num / p0_denom)
return p0_vect, p1_vect, p_abusive
def classify_nb(vec_2_classify, p0_vec, p1_vec, p_class):
p1 = sum(vec_2_classify * p1_vec) + np.log(p_class)
p0 = sum(vec_2_classify * p0_vec) + np.log(1 - p_class)
if p1 > p0:
return 1
else:
return 0
def demo_filter_email():
"""示例:樸素貝葉斯過濾垃圾郵件"""
# 生成詞向量矩陣
doc_list = []
class_list = []
full_text = []
emails = glob('data\\Ch04\\email\\*\\*.txt')
for email in emails:
word_list = re.split(r'\W+', open(email).read())
word_list = [word.lower() for word in word_list if len(word) > 2]
doc_list.append(word_list)
full_text.extend(word_list)
if 'spam' in email:
class_list.append(1)
else:
class_list.append(0)
# 得到不重復的詞向量
vocab_list = create_vocab_list(doc_list)
# 隨機選取10封作為測試數(shù)據(jù)集
training_set = list(range(50))
test_set = []
for i in range(10):
rand_index = int(np.random.uniform(0, len(training_set)))
test_set.append(training_set[rand_index])
del(training_set[rand_index])
# 對訓練數(shù)據(jù)集進行訓練
train_mat = []
train_classes = []
for doc_index in training_set:
train_mat.append(set_of_words_2_vec(vocab_list, doc_list[doc_index]))
train_classes.append(class_list[doc_index])
p0_vect, p1_vect, p_spam = train_nb(np.array(train_mat), np.array(train_classes))
# 利用測試數(shù)據(jù)集遭铺,計算錯誤率
error_count = 0
for doc_index in test_set:
word_vector = set_of_words_2_vec(vocab_list, doc_list[doc_index])
if classify_nb(np.array(word_vector), p0_vect, p1_vect, p_spam) != class_list[doc_index]:
error_count += 1
print(f'the error rate is: {error_count / len(test_set)}')
if __name__ == '__main__':
demo_filter_email()