樸素貝葉斯 | 垃圾郵件識別

01 起

大數(shù)據(jù)時代朵诫,我們的“隱私”早已不再是隱私辛友,一個特別直接的證據(jù)是什么呢?

我們的郵箱也好剪返、手機也好废累,經(jīng)常收到惱人的垃圾郵件、垃圾短信

被這些東西煩的不行脱盲,怎么辦呢邑滨?網(wǎng)上有很多垃圾郵件過濾軟件,可以拿來直接用的钱反,其中的原理是什么呢掖看?

今天我們自己造個輪子來過濾郵箱里的垃圾郵件吧!

系好安全帶面哥,我要開車了哎壳!

02 過濾原理

垃圾郵件過濾的原理其實很簡單:樸素貝葉斯(Naive Bayes)

NB的詳細原理我們在這一文中講過:統(tǒng)計學習方法 | 樸素貝葉斯法,大家可以點擊學習

簡單點尚卫,利用樸素貝葉斯原理過濾垃圾郵件归榕,內(nèi)在邏輯是這樣的:

  • 樸素貝葉斯公式:P(Y|X)=P(X|Y)*P(Y)/P(X)

  • 我們把Y看作郵件分類結果(0不是垃圾郵件、1是垃圾郵件)吱涉,X看作郵件中的各個詞語

  • 于是刹泄,P(Y)表示訓練集中各類郵件出現(xiàn)的概率(條件概率)(大數(shù)定理,即各類郵件出現(xiàn)次數(shù)/總郵件數(shù))怎爵,若訓練樣本較少循签,大數(shù)定理不適用,本文旨在深刻理解NB原理疙咸,因此即使本文中訓練樣本較少县匠,但仍然使用了大數(shù)定理。

  • P(X)表示郵件某個詞語出現(xiàn)的概率,P(X|Y)表示某類郵件中詞語X出現(xiàn)的概率(先驗概率)

  • P(Y|X)表示郵件包含X詞語時乞旦,該郵件為Y類的概率(后驗概率)贼穆,是垃圾郵件過濾的結果,我們會將測試樣本X歸類到使得P(Y|X)最大的那個Y類

這就是利用NB進行垃圾郵件分類的原理

03 造輪子

知道了過濾原理兰粉,我們就來寫個算法過濾垃圾郵件吧故痊!

本次郵件過濾使用的樣本為我qq郵箱中的74條郵件,人工標注了是否為垃圾郵件玖姑,其中有26條垃圾郵件愕秫,樣本數(shù)據(jù)長這樣

可以看到,樣本數(shù)據(jù)中有較多符號等非文字內(nèi)容焰络,于是我們需要對樣本進行清洗戴甩,總得來說,造輪子主要是一下幾個步驟:

  1. 讀取樣本數(shù)據(jù)(自己qq郵箱74條郵件)
  2. 數(shù)據(jù)清洗(去除樣本中的符號等非文字內(nèi)容)
  3. 數(shù)據(jù)處理(訓練集測試集分割闪彼、文本向量化)
  4. 調(diào)用sklearn中的NB模型訓練甜孤,評估,優(yōu)化模型(本文重點不在此畏腕,略去)
  5. 自己寫個NB模型分辨垃圾郵件

數(shù)據(jù)清洗

剛才我們已經(jīng)讀取了樣本數(shù)據(jù)缴川,下面我們進行數(shù)據(jù)清洗,清洗思路如下:

  1. 去除待處理文本每條文本中的符號描馅、數(shù)字把夸、字母,僅剩中文字符(即以符號铭污、數(shù)字恋日、字母為分隔符,隔開每條文本作文item况凉,構成一個list)
  2. 去除文本list中長度小于等于1的item
  3. 將文本list(text[i])每個item分詞(可加載自定義詞典),并放入新的對應index的文本list中(text_word[i])
  4. 將新的文本list(text_word[i])用空格連接起來并返回

基于此思路各拷,給出以下清洗函數(shù)

def text_format():
    import jieba
    import re
    import pandas as pd
    
    print ("待處理文本格式要求:utf-8編碼格式刁绒,僅包含待處理文本,每行為一條文本")
    text_path=input("請輸入待清洗文本路徑+名字:")
    
    #加載用戶自定義詞典用于分詞
    userdict_path=input("請輸入自定義分詞詞典路徑+名字(可不輸入):")
    if userdict_path !="":
        jieba.load_userdict(userdict_path)
        
    #根據(jù)用戶輸入地址烤黍,讀取文件
    with open(text_path,"r",encoding="utf-8") as file:
        text=file.readlines()
    for i in range(len(text)):
        text[i]=text[i].strip()
    
    #定義一個空列表知市,用于存放分詞后的文本,長度和text一致
    text_word=[[] for i in range(len(text))]
    
    splitter=re.compile(r"\W+|\d+|[a-z]+") #正則匹配速蕊,去除文本中的符號嫂丙、數(shù)字、字母等非中文字符的元素
    for i in range(len(text)):
        text[i]=splitter.split(text[i].lower())
        text[i]=[word for word in text[i] if len(word)>1] #每條文本已經(jīng)被分為一段一段的句子规哲,每條文本此時是一個list跟啤,先去除其中字段長度小于等于1的單詞
        for word in text[i]:
            text_word[i].extend(jieba.lcut(word))
        text_word[i]=" ".join(text_word[i]) #為了便于TfidfVectorizer等文本向量化處理,將每條標題用元素用空格連起來
    
    return text_word

清洗前后效果如下
清洗前

清洗后

數(shù)據(jù)處理

接下來就是數(shù)據(jù)處理,包括以下步驟:

  1. 將每條郵件對應的標簽提取出來成為一個list
  2. 利用train_test_split將清洗后的數(shù)據(jù)與其對應的標簽切分為訓練集隅肥、測試集(這個分割函數(shù)也可以自己寫竿奏,本文略去)
  3. 構成詞袋模型,記錄各個詞出現(xiàn)的頻率
    • CountVectorizer(stop_words=).fit_transform(訓練集)
    • CountVectorizer(stop_words=).transform(測試集)
#建立訓練集腥放、測試集
label=emails.type.tolist()
X_train,X_test,Y_train,Y_test=train_test_split(emails_format,label,test_size=0.15,random_state=5)
#加載并處理停用詞典
with open(r"E:\python\data\stopwords.txt","r",encoding="utf-8") as file:
    stop_words=file.readlines()
for i in range(len(stop_words)):
    stop_words[i]=stop_words[i].strip("\n")
    
#構成詞袋模型泛啸,記錄各個詞出現(xiàn)的頻率
cv=CountVectorizer(stop_words=stop_words)
X_train_count=cv.fit_transform(X_train)
X_test_count=cv.transform(X_test)

處理后的結果如下,可以看到秃症,訓練集62條郵件候址、測試集12條郵件,共提取出386個文本特征

輪子來了:樸素貝葉斯模型

接下來就是重頭戲了种柑,自定義NB模型岗仑,思路如下:

  1. 利用各詞出現(xiàn)的頻率和對應標簽概率,訓練NB模型各概率參數(shù)
  2. 將測試集各特征在訓練集對應的先驗概率帶入計算莹规,得到測試集各樣本后驗概率赔蒲,取后驗概率最大的標簽類別為該測試樣本類別
1. 利用訓練集,訓練概率參數(shù)(拉普拉斯平滑)[類似mnb.fit()]
#先將訓練集的內(nèi)容和標簽合為一個dataframe
d={"content":X_train_count.toarray().tolist(),"label":Y_train}
emails_train=pd.DataFrame(data=d)

#將正常郵件(Y=0)和垃圾郵件(Y=1)分為兩個子集
normal=emails_train[emails_train.label==0]
normal.reset_index(inplace=True,drop=True) #重置normal索引良漱,作用于原表舞虱,丟棄之前的索引
spam=emails_train[emails_train.label==1]
spam.reset_index(inplace=True,drop=True) #重置spam索引,作用于原表母市,丟棄之前的索引

"""計算Y_train=0矾兜、1的條件概率(拉普拉斯平滑)"""
Py0=(len(normal)+1)/(len(emails_train)+2)
Py1=(len(spam)+1)/(len(emails_train)+2)

"""計算X_train各特征向量取各特征值時的先驗概率(拉普拉斯平滑)"""
"""計算垃圾郵件中,各特征向量的先驗概率"""
vd=len(spam.content[0]) #特征向量的維度
spam_count_dict={} #用于保存content特征向量按列累加的結果
spam_count_prob={} #用于保存垃圾郵件中各特征向量出現(xiàn)的概率

#求content各特征向量按列累加的結果,用于計算各向量在訓練集中出現(xiàn)的概率
for i in range(len(spam)):
    for j in range(vd):
        spam_count_dict[j]=spam_count_dict.get(j,0)+spam.content[i][j] #計算垃圾郵件中各特征向量出現(xiàn)的次數(shù)患久,即椅寺,求content各特征向量count按列累加的結果

for j in range(vd):
    spam_count_prob[j]=(spam_count_dict.get(j,0)+1)/(len(spam)+2)#計算垃圾郵件中各特征向量出現(xiàn)的概率(拉普拉斯平滑)

"""計算正常郵件中,各特征向量的先驗概率"""
normal_count_dict={} #用于保存content特征向量按列累加的結果
normal_count_prob={} #用于保存正常郵件中各特征向量出現(xiàn)的概率

#求content各特征向量按列累加的結果,用于計算各向量在訓練集中出現(xiàn)的概率
for i in range(len(normal)):
    for j in range(vd):
        normal_count_dict[j]=normal_count_dict.get(j,0)+normal.content[i][j] #計算垃圾郵件中各特征向量出現(xiàn)的次數(shù)蒋失,即返帕,求content各特征向量count按列累加的結果

for j in range(vd):
    normal_count_prob[j]=(normal_count_dict.get(j,0)+1)/(len(normal)+2)#計算垃圾郵件中各特征向量出現(xiàn)的概率(拉普拉斯平滑)
2. 將測試集各特征向量值帶入訓練的概率參數(shù)中,計算后驗概率篙挽,取使后驗概率最大的Y=ck為測試樣本的分類[類似mnb.predict(), mnb.predict_proba()]
"""計算各測試樣本的后驗概率"""
test_classify={} #用于保存測試集各樣本的后驗概率 P(Y|X)=P(Y)*P(X|Y)/P(X)
Px_spam={} #用于保存測試集各樣本在垃圾郵件下的先驗概率 P(X|Y)
Px_normal={} #用于保存測試集各樣本在正常郵件下的先驗概率 P(X|Y)

for i in range(X_test_array.shape[0]):
    for j in range(X_test_array.shape[1]):
        if X_test_array[i][j]!=0:
            Px_spam[i]=Px_spam.get(i,1)*spam_count_prob.get(j)#計算垃圾郵件下荆萤,各測試樣本的后驗概率
            Px_normal[i]=Px_normal.get(i,1)*normal_count_prob.get(j)#計算正常郵件下,各測試樣本的后驗概率

    test_classify[i]=Py0*Px_normal.get(i,0),Py1*Px_spam.get(i,0) #后驗概率P(Y|X)=P(Y)*P(X|Y)/P(X)

#比較各樣本屬于不同分類時(正常/垃圾)的后驗概率铣卡,去后驗概率大的為樣本分類結果
results={} #用于存放郵件判定結果
for key,value in test_classify.items():
    if value[0]<=value[1]:
        results[key]=1
    else:
        results[key]=0

測試集12條郵件的分類結果如下链韭,其中第二列為該郵件不是垃圾郵件的概率,第三列為是垃圾郵件的概率:

因此得到分類結果:
3. 計算分類準確率 [類似mnb.score()]
#計算分類準確率
count=0 #計數(shù)煮落,統(tǒng)計被正確分類的郵件數(shù)量
for key,value in results.items():
    if value==Y_test[key]:
        count+=1
score=count/len(Y_test)
print ("NB模型分類準確率為:{0}%".format(score*100))

輸出結果為:

04 總結

本文從樸素貝葉斯原理出發(fā)敞峭,自己寫了一個NB模型,用于濾除個人郵箱中的垃圾郵件蝉仇,拋磚引玉旋讹,希望可以啟發(fā)大家殖蚕。

另外,本文設計的NB模型不足之處在于骗村,訓練樣本太少嫌褪,分類不夠客觀準確。解法其實很簡單:增加樣本量胚股。

當然笼痛,本文重點在于理解NB算法原理,下期實操預告:決策樹實現(xiàn)琅拌。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缨伊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子进宝,更是在濱河造成了極大的恐慌刻坊,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件党晋,死亡現(xiàn)場離奇詭異谭胚,居然都是意外死亡,警方通過查閱死者的電腦和手機未玻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門灾而,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扳剿,你說我怎么就攤上這事旁趟。” “怎么了庇绽?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵锡搜,是天一觀的道長。 經(jīng)常有香客問我瞧掺,道長耕餐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任辟狈,我火速辦了婚禮肠缔,結果婚禮上,老公的妹妹穿的比我還像新娘上陕。我一直安慰自己桩砰,他們只是感情好拓春,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布释簿。 她就那樣靜靜地躺著,像睡著了一般硼莽。 火紅的嫁衣襯著肌膚如雪庶溶。 梳的紋絲不亂的頭發(fā)上煮纵,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音偏螺,去河邊找鬼行疏。 笑死,一個胖子當著我的面吹牛套像,可吹牛的內(nèi)容都是我干的酿联。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼夺巩,長吁一口氣:“原來是場噩夢啊……” “哼贞让!你這毒婦竟也來了?” 一聲冷哼從身側響起柳譬,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤喳张,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后美澳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體销部,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年制跟,在試婚紗的時候發(fā)現(xiàn)自己被綠了舅桩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡凫岖,死狀恐怖江咳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哥放,我是刑警寧澤歼指,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站甥雕,受9級特大地震影響踩身,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜社露,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一挟阻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧峭弟,春花似錦附鸽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至情臭,卻和暖如春省撑,著一層夾襖步出監(jiān)牢的瞬間赌蔑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工竟秫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娃惯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓肥败,卻偏偏與公主長得像趾浅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子馒稍,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

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