1、一些數(shù)學(xué)知識(shí)
-
先驗(yàn)概率:指的是事件發(fā)生前的預(yù)判概率,可以根據(jù)歷史數(shù)據(jù)/經(jīng)驗(yàn)估算得到。例如,當(dāng)我們需要判斷西瓜是不是好瓜的時(shí)候,對(duì)紋理、根蒂等特征都不了解,只是平常我們買西瓜的時(shí)候買到好瓜的概率是70%,那么這個(gè)西瓜是好瓜的概率我們也可以認(rèn)為是70%。這個(gè)概率70%就是先驗(yàn)概率。
-
后驗(yàn)概率:指的是事件發(fā)生后求的反向條件概率。例如,當(dāng)我們買西瓜的時(shí)候,已經(jīng)知道當(dāng)西瓜臍部凹陷的時(shí)候,是好瓜的概率是80%。如果把臍部凹陷當(dāng)作一種結(jié)果,然后去推測(cè)好瓜的概率,那么這個(gè)概率P(好瓜|紋理清晰)就是后驗(yàn)概率。
-
聯(lián)合概率:指的是包含多個(gè)條件,且所有條件同時(shí)成立的概率,記作P(A,B)。
-
相互獨(dú)立:如果P(A,B)=P(A)·P(B),則稱事件A和事件B相互獨(dú)立
-
條件概率:指的是一個(gè)事件發(fā)生的條件下,另一個(gè)事件發(fā)生的概率,記作P(A|B)。公式:
2、貝葉斯公式
- P(A)是先驗(yàn)概率
- P(B)是先驗(yàn)概率
- P(B|A)是條件概率,也叫似然概率
- P(A|B)是后驗(yàn)概率,一般是求解的目標(biāo)
3、樸素貝葉斯算法
(1)介紹
樸素貝葉斯算法是以貝葉斯定理為基礎(chǔ)的一種分類方法,之所以稱為 “樸素” ,是假定所有輸入事件之間是相互獨(dú)立的。這是為了簡(jiǎn)便計(jì)算,獨(dú)立事件間的概率計(jì)算更加簡(jiǎn)單。
(2)核心思想
對(duì)于給出的待分類樣本,求解在此樣本出現(xiàn)的條件下各個(gè)類別出現(xiàn)的概率,哪個(gè)概率最大,就認(rèn)為此待分類樣本屬于哪個(gè)類別。
例子:當(dāng)我們挑選西瓜的時(shí)候,通過西瓜的敲擊聲來判斷西瓜的好壞,假設(shè)敲擊聲是“濁響”的西瓜是好瓜的概率是70%,敲擊聲是“沉悶”的概率是50%,敲擊聲是“清脆”的概率是10%,那么這個(gè)西瓜的敲擊聲是“濁響”,我們就認(rèn)為這個(gè)西瓜是個(gè)好瓜。
(3)樸素貝葉斯算法
(4)拉普拉斯修正
(5)防溢出策略
(6)一般過程
- 收集數(shù)據(jù):可以使用任何方法。
- 準(zhǔn)備數(shù)據(jù):需要數(shù)值型或者布爾型數(shù)據(jù)。
- 分析數(shù)據(jù):有大量特征時(shí),繪制特征作用不大,此時(shí)使用直方圖效果最好。
- 訓(xùn)練算法:計(jì)算不同的獨(dú)立特征的條件概率。
- 測(cè)試算法:計(jì)算錯(cuò)誤率。
- 使用算法:可以在任意的分類場(chǎng)景中使用樸素貝葉斯分類器,一個(gè)常見的樸素貝葉斯應(yīng)用是文檔分類。
(7)優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):
· 在數(shù)據(jù)較少的情況下仍然有效,可以處理多類別問題;
· 有穩(wěn)定的分類效率;
· 對(duì)缺失數(shù)據(jù)不太敏感,算法比較簡(jiǎn)單;
· 分類準(zhǔn)確率高,速度快; - 缺點(diǎn):
· 由于使用了樣本屬性獨(dú)立性的假設(shè),所以如果特征屬性有關(guān)聯(lián)時(shí)效果不太好;
· 需要計(jì)算先驗(yàn)概率,而先驗(yàn)概率很多時(shí)候取決于假設(shè),假設(shè)的模型可以有很多種,因此在某些時(shí)候會(huì)由于假設(shè)的先驗(yàn)?zāi)P偷脑驅(qū)е骂A(yù)測(cè)效果不佳; - 適用數(shù)據(jù)類型:標(biāo)稱型數(shù)據(jù)
4、例子實(shí)現(xiàn)——垃圾郵件分類
數(shù)據(jù)集說明:非垃圾郵件ham和垃圾郵件spam各25封,測(cè)試郵件5封,其中把spam中的1、2和ham中的22、23、24拿出來當(dāng)測(cè)試集
ham:
ham的其中一封:
spam:
spam中的其中一封:
test:
導(dǎo)包:
import os
import re
import string
import math
import numpy as np
數(shù)據(jù)預(yù)處理:
def get_filtered_str(category):
email_list = []
translator = re.compile('[%s]' % re.escape(string.punctuation))
for curDir, dirs, files in os.walk(f'./email/{category}'):
for file in files:
file_name = os.path.join(curDir, file)
with open(file_name, 'r', encoding='utf-8') as f:
txt_str = f.read()
# 全部小寫
txt_str = txt_str.lower()
# 過濾掉所有符號(hào)
txt_str = translator.sub(' ', txt_str)
# 過濾掉全部數(shù)字
txt_str = replace_num(txt_str)
# 把全體的郵件文本 根據(jù)換行符把string劃分成列表
txt_str_list = txt_str.splitlines()
# 把獲取的全體單詞句子列表轉(zhuǎn)成字符串
txt_str = ''.join(txt_str_list)
# print(txt_str)
email_list.append(txt_str)
return email_list
數(shù)據(jù)處理階段:
def get_dict_spam_dict_w(spam_email_list):
'''
:param email_list: 每個(gè)郵件過濾后形成字符串,存入email_list
:param all_email_words: 列表。把所有的郵件內(nèi)容,分詞。一個(gè)郵件的詞 是它的一個(gè)列表元素
:return:
'''
all_email_words = []
# 用set集合去重
word_set = set()
for email_str in spam_email_list:
# 把每個(gè)郵件的文本 變成單詞
email_words = email_str.split(' ')
# 把每個(gè)郵件去重后的列表 存入列表
all_email_words.append(email_words)
for word in email_words:
if(word!=''):
word_set.add(word)
# 計(jì)算每個(gè)垃圾詞出現(xiàn)的次數(shù)
word_dict = {}
for word in word_set:
# 創(chuàng)建字典元素 并讓它的值為1
word_dict[word] = 0
# print(f'word={word}')
# 遍歷每個(gè)郵件,看文本里面是否有該單詞,匹配方法不能用正則.郵件里面也必須是分詞去重后的!?。?否則 比如出現(xiàn)re是特征, 那么remind 也會(huì)被匹配成re
for email_words in all_email_words:
for email_word in email_words:
# print(f'spam_email={email_word}')
# 把從set中取出的word 和 每個(gè)email分詞后的word對(duì)比看是否相等
if(word==email_word):
word_dict[word] += 1
# 找到一個(gè)就行了
break
# 計(jì)算垃圾詞的概率
# spam_len = len(os.listdir(f'./email/spam'))
# print(f'spam_len={spam_len}')
# for word in word_dict:
# word_dict[word] = word_dict[word] / spam_len
return word_dict
def get_dict_ham_dict_w(spam_email_list,ham_email_list):
'''
:param email_list: 每個(gè)郵件過濾后形成字符串,存入email_list
:param all_email_words: 列表。把所有的郵件內(nèi)容,分詞。一個(gè)郵件的詞 是它的一個(gè)列表元素
:return:
'''
all_ham_email_words = []
# 用set集合去重 得到垃圾郵件的特征w
word_set = set()
#獲取垃圾郵件特征
for email_str in spam_email_list:
# 把每個(gè)郵件的文本 變成單詞
email_words = email_str.split(' ')
for word in email_words:
if (word != ''):
word_set.add(word)
for ham_email_str in ham_email_list:
# 把每個(gè)郵件的文本 變成單詞
ham_email_words = ham_email_str.split(' ')
# print(f'ham_email_words={ham_email_words}')
# 把每個(gè)郵件分割成單詞的 的列表 存入列表
all_ham_email_words.append(ham_email_words)
# print(f'all_ham_email_words={all_ham_email_words}')
# 計(jì)算每個(gè)垃圾詞出現(xiàn)的次數(shù)
word_dict = {}
for word in word_set:
# 創(chuàng)建字典元素 并讓它的值為1
word_dict[word] = 0
# print(f'word={word}')
# 遍歷每個(gè)郵件,看文本里面是否有該單詞,匹配方法不能用正則.郵件里面也必須是分詞去重后的?。?! 否則 比如出現(xiàn)re是特征, 那么remind 也會(huì)被匹配成re
for email_words in all_ham_email_words:
# print(f'ham_email_words={email_words}')
for email_word in email_words:
# 把從set中取出的word 和 每個(gè)email分詞后的word對(duì)比看是否相等
# print(f'email_word={email_word}')
if(word==email_word):
word_dict[word] += 1
# 找到一個(gè)就行了
break
return word_dict
# 獲取測(cè)試郵件中出現(xiàn)的 垃圾郵件特征
def get_X_c1(spam_w_dict,file_name):
# 獲取測(cè)試郵件
# file_name = './email/spam/25.txt'
# 過濾文本
translator = re.compile('[%s]' % re.escape(string.punctuation))
with open(file_name, 'r', encoding='utf-8') as f:
txt_str = f.read()
# 全部小寫
txt_str = txt_str.lower()
# 過濾掉所有符號(hào)
txt_str = translator.sub(' ', txt_str)
# 過濾掉全部數(shù)字
txt_str = replace_num(txt_str)
# 把全體的郵件文本 根據(jù)換行符把string劃分成列表
txt_str_list = txt_str.splitlines()
# 把獲取的全體單詞句子列表轉(zhuǎn)成字符串
txt_str = ''.join(txt_str_list)
# 把句子分成詞
email_words = txt_str.split(' ')
# 去重
x_set = set()
for word in email_words:
if word!='':
x_set.add(word)
# print(f'\ntest_x_set={x_set}')
spam_len = len(os.listdir(f'./email/spam'))
# 判斷測(cè)試郵件的詞有哪些是垃圾郵件的特征
spam_X_num = []
for xi in x_set:
for wi in spam_w_dict:
if xi == wi:
spam_X_num.append(spam_w_dict[wi])
# print(f'\nspam_X_num={spam_X_num}')
w_appear_sum_num = 1
for num in spam_X_num:
w_appear_sum_num += num
# print(f'\nham_w_appear_sum_num={w_appear_sum_num}')
# 求概率
w_c1_p = w_appear_sum_num / (spam_len + 2)
return w_c1_p
# 獲取測(cè)試郵件中出現(xiàn)的非垃圾郵件特征
def get_X_c2(ham_w_dict,file_name):
# 過濾文本
translator = re.compile('[%s]' % re.escape(string.punctuation))
with open(file_name, 'r', encoding='utf-8') as f:
txt_str = f.read()
# 全部小寫
txt_str = txt_str.lower()
# 過濾掉所有符號(hào)
txt_str = translator.sub(' ', txt_str)
# 過濾掉全部數(shù)字
txt_str = replace_num(txt_str)
# 把全體的郵件文本 根據(jù)換行符把string劃分成列表
txt_str_list = txt_str.splitlines()
# 把獲取的全體單詞句子列表轉(zhuǎn)成字符串
txt_str = ''.join(txt_str_list)
# 把句子分成詞
email_words = txt_str.split(' ')
# 去重
x_set = set()
for word in email_words:
if word!='':
x_set.add(word)
# print(f'\ntest_x_set={x_set}')
# 判斷測(cè)試郵件的詞有哪些是垃圾郵件的特征
ham_X_num = []
for xi in x_set:
for wi in ham_w_dict:
if xi == wi:
ham_X_num.append(ham_w_dict[wi])
# print(f'\nham_X_num={ham_X_num}')
# 先求分子 所有詞出現(xiàn)的總和
ham_len = len(os.listdir(f'./email/ham'))
w_appear_sum_num = 1
for num in ham_X_num:
w_appear_sum_num += num
# print(f'\nspam_w_appear_sum_num={w_appear_sum_num}')
# 求概率
w_c2_p = w_appear_sum_num / (ham_len+2)
return w_c2_p
測(cè)試階段:
def email_test(spam_w_dict,ham_w_dict):
for curDir, dirs, files in os.walk(f'./email/test'):
for file in files:
file_name = os.path.join(curDir, file)
print('---------------------------------------------------------------')
print(f'測(cè)試郵件: {file}')
# 獲取條件概率 p(X|c1)
p_X_c1 = get_X_c1(spam_w_dict,file_name)
# 獲取條件概率 p(X|c2)
p_X_c2 = get_X_c2(ham_w_dict,file_name)
# print(f'\nX_c1={p_X_c1}')
# print(f'\nX_c2={p_X_c2}')
# #注意:Log之后全部變?yōu)樨?fù)數(shù)
A = np.log(p_X_c1) + np.log(1 / 2)
B = np.log(p_X_c2) + np.log(1 / 2)
# 除法會(huì)出現(xiàn)問題,-1 / 負(fù)分母 結(jié)果 < -2/同一個(gè)分母
print(f'p1={A},p2={B}')
# 因?yàn)榉帜敢恢?,所以只比較 分子即可
if A > B:
print('p1>p2,所以是垃圾郵件.')
if A <= B:
print('p1<p2,所以是正常郵件.')
完整代碼:
import os
import re
import string
import math
import numpy as np
# 過濾數(shù)字
def replace_num(txt_str):
txt_str = txt_str.replace(r'0', '')
txt_str = txt_str.replace(r'1', '')
txt_str = txt_str.replace(r'2', '')
txt_str = txt_str.replace(r'3', '')
txt_str = txt_str.replace(r'4', '')
txt_str = txt_str.replace(r'5', '')
txt_str = txt_str.replace(r'6', '')
txt_str = txt_str.replace(r'7', '')
txt_str = txt_str.replace(r'8', '')
txt_str = txt_str.replace(r'9', '')
return txt_str
def get_filtered_str(category):
email_list = []
translator = re.compile('[%s]' % re.escape(string.punctuation))
for curDir, dirs, files in os.walk(f'./email/{category}'):
for file in files:
file_name = os.path.join(curDir, file)
with open(file_name, 'r', encoding='utf-8') as f:
txt_str = f.read()
# 全部小寫
txt_str = txt_str.lower()
# 過濾掉所有符號(hào)
txt_str = translator.sub(' ', txt_str)
# 過濾掉全部數(shù)字
txt_str = replace_num(txt_str)
# 把全體的郵件文本 根據(jù)換行符把string劃分成列表
txt_str_list = txt_str.splitlines()
# 把獲取的全體單詞句子列表轉(zhuǎn)成字符串
txt_str = ''.join(txt_str_list)
# print(txt_str)
email_list.append(txt_str)
return email_list
def get_dict_spam_dict_w(spam_email_list):
'''
:param email_list: 每個(gè)郵件過濾后形成字符串,存入email_list
:param all_email_words: 列表。把所有的郵件內(nèi)容,分詞。一個(gè)郵件的詞 是它的一個(gè)列表元素
:return:
'''
all_email_words = []
# 用set集合去重
word_set = set()
for email_str in spam_email_list:
# 把每個(gè)郵件的文本 變成單詞
email_words = email_str.split(' ')
# 把每個(gè)郵件去重后的列表 存入列表
all_email_words.append(email_words)
for word in email_words:
if(word!=''):
word_set.add(word)
# 計(jì)算每個(gè)垃圾詞出現(xiàn)的次數(shù)
word_dict = {}
for word in word_set:
# 創(chuàng)建字典元素 并讓它的值為1
word_dict[word] = 0
# print(f'word={word}')
# 遍歷每個(gè)郵件,看文本里面是否有該單詞,匹配方法不能用正則.郵件里面也必須是分詞去重后的?。?! 否則 比如出現(xiàn)re是特征, 那么remind 也會(huì)被匹配成re
for email_words in all_email_words:
for email_word in email_words:
# print(f'spam_email={email_word}')
# 把從set中取出的word 和 每個(gè)email分詞后的word對(duì)比看是否相等
if(word==email_word):
word_dict[word] += 1
# 找到一個(gè)就行了
break
# 計(jì)算垃圾詞的概率
# spam_len = len(os.listdir(f'./email/spam'))
# print(f'spam_len={spam_len}')
# for word in word_dict:
# word_dict[word] = word_dict[word] / spam_len
return word_dict
def get_dict_ham_dict_w(spam_email_list,ham_email_list):
'''
:param email_list: 每個(gè)郵件過濾后形成字符串,存入email_list
:param all_email_words: 列表。把所有的郵件內(nèi)容,分詞。一個(gè)郵件的詞 是它的一個(gè)列表元素
:return:
'''
all_ham_email_words = []
# 用set集合去重 得到垃圾郵件的特征w
word_set = set()
#獲取垃圾郵件特征
for email_str in spam_email_list:
# 把每個(gè)郵件的文本 變成單詞
email_words = email_str.split(' ')
for word in email_words:
if (word != ''):
word_set.add(word)
for ham_email_str in ham_email_list:
# 把每個(gè)郵件的文本 變成單詞
ham_email_words = ham_email_str.split(' ')
# print(f'ham_email_words={ham_email_words}')
# 把每個(gè)郵件分割成單詞的 的列表 存入列表
all_ham_email_words.append(ham_email_words)
# print(f'all_ham_email_words={all_ham_email_words}')
# 計(jì)算每個(gè)垃圾詞出現(xiàn)的次數(shù)
word_dict = {}
for word in word_set:
# 創(chuàng)建字典元素 并讓它的值為1
word_dict[word] = 0
# print(f'word={word}')
# 遍歷每個(gè)郵件,看文本里面是否有該單詞,匹配方法不能用正則.郵件里面也必須是分詞去重后的?。?! 否則 比如出現(xiàn)re是特征, 那么remind 也會(huì)被匹配成re
for email_words in all_ham_email_words:
# print(f'ham_email_words={email_words}')
for email_word in email_words:
# 把從set中取出的word 和 每個(gè)email分詞后的word對(duì)比看是否相等
# print(f'email_word={email_word}')
if(word==email_word):
word_dict[word] += 1
# 找到一個(gè)就行了
break
return word_dict
# 獲取測(cè)試郵件中出現(xiàn)的 垃圾郵件特征
def get_X_c1(spam_w_dict,file_name):
# 獲取測(cè)試郵件
# file_name = './email/spam/25.txt'
# 過濾文本
translator = re.compile('[%s]' % re.escape(string.punctuation))
with open(file_name, 'r', encoding='utf-8') as f:
txt_str = f.read()
# 全部小寫
txt_str = txt_str.lower()
# 過濾掉所有符號(hào)
txt_str = translator.sub(' ', txt_str)
# 過濾掉全部數(shù)字
txt_str = replace_num(txt_str)
# 把全體的郵件文本 根據(jù)換行符把string劃分成列表
txt_str_list = txt_str.splitlines()
# 把獲取的全體單詞句子列表轉(zhuǎn)成字符串
txt_str = ''.join(txt_str_list)
# 把句子分成詞
email_words = txt_str.split(' ')
# 去重
x_set = set()
for word in email_words:
if word!='':
x_set.add(word)
# print(f'\ntest_x_set={x_set}')
spam_len = len(os.listdir(f'./email/spam'))
# 判斷測(cè)試郵件的詞有哪些是垃圾郵件的特征
spam_X_num = []
for xi in x_set:
for wi in spam_w_dict:
if xi == wi:
spam_X_num.append(spam_w_dict[wi])
# print(f'\nspam_X_num={spam_X_num}')
w_appear_sum_num = 1
for num in spam_X_num:
w_appear_sum_num += num
# print(f'\nham_w_appear_sum_num={w_appear_sum_num}')
# 求概率
w_c1_p = w_appear_sum_num / (spam_len + 2)
return w_c1_p
# 獲取測(cè)試郵件中出現(xiàn)的 垃圾郵件特征
def get_X_c2(ham_w_dict,file_name):
# 過濾文本
translator = re.compile('[%s]' % re.escape(string.punctuation))
with open(file_name, 'r', encoding='utf-8') as f:
txt_str = f.read()
# 全部小寫
txt_str = txt_str.lower()
# 過濾掉所有符號(hào)
txt_str = translator.sub(' ', txt_str)
# 過濾掉全部數(shù)字
txt_str = replace_num(txt_str)
# 把全體的郵件文本 根據(jù)換行符把string劃分成列表
txt_str_list = txt_str.splitlines()
# 把獲取的全體單詞句子列表轉(zhuǎn)成字符串
txt_str = ''.join(txt_str_list)
# 把句子分成詞
email_words = txt_str.split(' ')
# 去重
x_set = set()
for word in email_words:
if word!='':
x_set.add(word)
# print(f'\ntest_x_set={x_set}')
# 判斷測(cè)試郵件的詞有哪些是垃圾郵件的特征
ham_X_num = []
for xi in x_set:
for wi in ham_w_dict:
if xi == wi:
ham_X_num.append(ham_w_dict[wi])
# print(f'\nham_X_num={ham_X_num}')
# 先求分子 所有詞出現(xiàn)的總和
ham_len = len(os.listdir(f'./email/ham'))
w_appear_sum_num = 1
for num in ham_X_num:
w_appear_sum_num += num
# print(f'\nspam_w_appear_sum_num={w_appear_sum_num}')
# 求概率
w_c2_p = w_appear_sum_num / (ham_len+2)
return w_c2_p
def email_test(spam_w_dict,ham_w_dict):
for curDir, dirs, files in os.walk(f'./email/test'):
for file in files:
file_name = os.path.join(curDir, file)
print('---------------------------------------------------------------')
print(f'測(cè)試郵件: {file}')
# 獲取條件概率 p(X|c1)
p_X_c1 = get_X_c1(spam_w_dict,file_name)
# 獲取條件概率 p(X|c2)
p_X_c2 = get_X_c2(ham_w_dict,file_name)
# print(f'\nX_c1={p_X_c1}')
# print(f'\nX_c2={p_X_c2}')
# #注意:Log之后全部變?yōu)樨?fù)數(shù)
A = np.log(p_X_c1) + np.log(1 / 2)
B = np.log(p_X_c2) + np.log(1 / 2)
# 除法會(huì)出現(xiàn)問題,-1 / 負(fù)分母 結(jié)果 < -2/同一個(gè)分母
print(f'p1={A},p2={B}')
# 因?yàn)榉帜敢恢?,所以只比較 分子即可
if A > B:
print('p1>p2,所以是垃圾郵件.')
if A <= B:
print('p1<p2,所以是正常郵件.')
if __name__=='__main__':
spam_email_list = get_filtered_str('spam')
ham_email_list = get_filtered_str('ham')
spam_w_dict = get_dict_spam_dict_w(spam_email_list)
ham_w_dict = get_dict_ham_dict_w(spam_email_list,ham_email_list)
# print(f'\n從垃圾郵件中提取的特征及每個(gè)特征出現(xiàn)的郵件數(shù):')
# print(f'spam_w_dict={spam_w_dict}')
# print(f'\n普通郵件中垃圾郵件特征出現(xiàn)的郵件數(shù)為:')
# print(f'ham_w_dict={ham_w_dict}')
email_test(spam_w_dict, ham_w_dict)
測(cè)試結(jié)果:文章來源:http://www.zghlxwxcb.cn/news/detail-771694.html
數(shù)據(jù)集以及代碼:
鏈接: https://pan.baidu.com/s/1cHBDd_M_B0vgUA6la6AK9Q?pwd=psk9
提取碼:psk9文章來源地址http://www.zghlxwxcb.cn/news/detail-771694.html
到了這里,關(guān)于機(jī)器學(xué)習(xí)——樸素貝葉斯算法(垃圾郵件分類)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!