本文總體架構(gòu):
一思犁、樸素貝葉斯分類器的理論解釋和計算步驟
二逊笆、代碼框架展示
三拂苹、結(jié)合樸素貝葉斯理論解釋代碼
四谭羔、此次試驗總結(jié)
本次試驗主要解決對短信類型的分類問題华糖,其實也是解決文本分類問題。
一瘟裸、樸素貝葉斯分類器理論
樸素貝葉斯與貝葉斯的區(qū)別客叉,如圖
這里我們運用樸素貝葉斯分類器是因為這個算法的屬性我們假設是獨立的,而事實上一些文本中的詞與詞之間是存在聯(lián)系的话告,我們?yōu)榱撕啽憔图僭O詞與詞之間兼搏,即屬性間的關系是獨立的。所以我們的后驗概率表示為
我們要計算出先驗概率 P(c) 和每個屬性的條件概率P(xi|c)沙郭,P(X)為證據(jù)因子
下面解釋一下先驗概率佛呻、條件概率、證據(jù)因子之間的關系病线,以及為什么證據(jù)因子不變的
樸素貝葉斯計算步驟
第一步計算P(c)
為了避免某個屬性值在訓練集沒有出現(xiàn)吓著,導致該類情況出現(xiàn)概率為0,影響判斷送挑,我們引入拉普拉斯修正夜矗,N表示訓練集D中可能出現(xiàn)的類別
第二步計算P(xi | c)
同理引入拉普拉斯修正,Ni表示第i個屬性可能取值的個數(shù)
拉普拉斯修正解釋鏈接:https://www.cnblogs.com/misszhu-home/p/7693168.html
第三步計算出P(C|X)
第四步使用對數(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
cls:是每條短信所屬的類型,比如垃圾短信spam开伏、非垃圾短信ham膀跌。content:就是每條短信的內(nèi)容。 word_vect:詞條向量固灵。 如下圖
函數(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é)展示出來
然后我們這里返回出來的是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é)果展示
總結(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