當前位置:
首頁 > 知識 > 機器學習演算法實踐:樸素貝葉斯 (Naive Bayes)

機器學習演算法實踐:樸素貝葉斯 (Naive Bayes)

(點擊

上方藍字

,快速關注我們)




來源:伯樂在線專欄作者 - iPytLab


如有好文章投稿,請點擊 → 這裡了解詳情




前言




上一篇《機器學習演算法實踐:決策樹 (Decision Tree)》總結了決策樹的實現,本文中我將一步步實現一個樸素貝葉斯分類器,並採用SMS垃圾簡訊語料庫中的數據進行模型訓練,對垃圾簡訊進行過濾,在最後對分類的錯誤率進行了計算。



正文




與決策樹分類和k近鄰分類演算法不同,貝葉斯分類主要藉助概率論的知識來通過比較提供的數據屬於每個類型的條件概率, 將他們分別計算出來然後預測具有最大條件概率的那個類別是最後的類別。當然樣本越多我們統計的不同類型的特徵值分布就越準確,使用此分布進行預測則會更加準確。




貝葉斯準則




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






此公式表示兩個互換的條件概率之間的關係,他們通過聯合概率關聯起來,這樣使得我們知道p(B|A) 的情況下去計算p(A|B) 成為了可能,而我們的貝葉斯模型便是通過貝葉斯準則去計算某個樣本在不同類別條件下的條件概率並取具有最大條件概率的那個類型作為分類的預測結果。




使用條件概率來進行分類




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



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






  1. p(男生|ω) ,ω 等於 [0,1,1,0 的條件下此人是男生的概率



  2. p(女生|ω)) ,ω 等於 [0,1,1,0] 的條件下此人是女生的概率




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




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




根據貝葉斯準則,






寫成好理解些的便是:







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






這樣我們就能計算當前這個背影屬於男生和屬於女生的條件概率了。




實現自己的貝葉斯分類器




貝葉斯分類器實現起來非常的簡單, 下面我以進行文本分類為目的使用Python實現一個樸素貝葉斯文本分類器.




為了計算條件概率,我們需要計算各個特徵的在不同類別下的條件概率以及類型的邊際概率,這就需要我們通過大量的訓練數據進行統計獲取近似值了,這也就是我們訓練我們樸素貝葉斯模型的過程.



針對不同的文本,我們可以將所有出現的單詞作為數據特徵向量,統計每個文本中出現詞條的數目(或者是否出現某個詞條)作為數據向量。這樣一個文本就可以處理成一個整數列表,並且長度是所有詞條的數目,這個向量也許會很長,用於本文的數據集中的簡訊詞條大概一共3000多個單詞。





def

get_doc_vector

(

words

,

vocabulary

)

:


    

""" 根據辭彙表將文檔中的詞條轉換成文檔向量


 

    :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

)


 


    

for

word

in

words

:


        

if

word

in

vocabulary

:


            

idx

=

vocabulary

.

index

(

word

)


            

doc_vect

[

idx

]

=

1


 


    

return

doc_vect




統計訓練的過程的代碼實現如下:





def

train

(

self

,

dataset

,

classes

)

:


    

""" 訓練樸素貝葉斯模型


 


    :param dataset: 所有的文檔數據向量


    :type dataset: MxN matrix containing all doc vectors.


 


    :param classes: 所有文檔的類型


    :type classes: 1xN list


 


    :return cond_probs: 訓練得到的條件概率矩陣


    :type cond_probs: dict


 


    :return cls_probs: 各種類型的概率


    :type cls_probs: dict


    """


    

# 按照不同類型記性分類


    

sub_datasets

=

defaultdict

(

lambda

:

[])


    

cls_cnt

=

defaultdict

(

lambda

:

0

)


 


    

for

doc_vect

,

cls

in

zip

(

dataset

,

classes

)

:


        

sub_datasets

[

cls

].

append

(

doc_vect

)


        

cls_cnt

[

cls

]

+=

1


 


    

# 計算類型概率


    

cls_probs

=

{

k

:

v

/

len

(

classes

)

for

k

,

v

in

cls_cnt

.

items

()}


 


    

# 計算不同類型的條件概率


    

cond_probs

=

{}


    

dataset

=

np

.

array

(

dataset

)


    

for

cls

,

sub_dataset

in

sub_datasets

.

items

()

:


        

sub_dataset

=

np

.

array

(

sub_dataset

)


        

# Improve the classifier.


        

cond_prob_vect

=

np

.

log

((

np

.

sum

(

sub_dataset

,

axis

=

0

)

+

1

)

/

(

np

.

sum

(

dataset

)

+

2

))


        

cond_probs

[

cls

]

=

cond_prob_vect


 


    

return

cond_probs

,

cls_probs




注意這裡對於基本的條件概率直接相乘有兩處改進:






  1. 各個特徵的概率初始值為1,分母上統計的某一類型的樣本總數的初始值是1,這是為了避免如果有一個特徵統計的概率為0,則聯合概率也為零那自然沒有什麼意義了, 如果訓練樣本足夠大時,並不會對比較結果產生影響.



  2. 由於各個獨立特徵的概率都是小於1的數,累積起來必然會是個更小的書,這會遇到浮點數下溢的問題,因此在這裡我們對所有的概率都取了對數處理,這樣在保證不會有損失的情況下避免了下溢的問題。




獲取了統計概率信息後,我們便可以通過貝葉斯準則預測我們數據的類型了,這裡我並沒有直接計算每種情況的概率,而是通過統計得到的向量與數據向量進行內積獲取條件概率的相對值並進行相對比較做出決策的。





def

classify

(

self

,

doc_vect

,

cond_probs

,

cls_probs

)

:


    

""" 使用樸素貝葉斯將doc_vect進行分類.


    """


    

pred_probs

=

{}


    

for

cls

,

cls_prob

in

cls_probs

.

items

()

:


        

cond_prob_vect

=

cond_probs

[

cls

]


        

pred_probs

[

cls

]

=

np

.

sum

(

cond_prob_vect

*

doc_vect

)

+

np

.

log

(

cls_prob

)


    

return

max

(

pred_probs

,

key

=

pred_probs

.

get

)




進行簡訊分類




已經構建好了樸素貝葉斯模型,我們就可以使用此模型來統計數據並用來預測了。這裡我使用了SMS垃圾簡訊語料庫中的垃圾簡訊數據, 並隨機抽取90%的數據作為訓練數據,剩下10%的數據作為測試數據來測試我們的貝葉斯模型預測的準確性。




當然在建立模型前我們需要將數據處理成模型能夠處理的格式:





ENCODING

=

"ISO-8859-1"


TRAIN_PERCENTAGE

=

0.9


 


def

get_doc_vector

(

words

,

vocabulary

)

:


    

""" 根據辭彙表將文檔中的詞條轉換成文檔向量


 


    :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

)


 


    

for

word

in

words

:


        

if

word

in

vocabulary

:


            

idx

=

vocabulary

.

index

(

word

)


            

doc_vect

[

idx

]

=

1


 


    

return

doc_vect


 


def

parse_line

(

line

)

:


    

""" 解析數據集中的每一行返回詞條向量和簡訊類型.


    """


    

cls

=

line

.

split

(

","

)[

-

1

].

strip

()


    

content

=

","

.

join

(

line

.

split

(

","

)[

: -

1

])


    

word_vect

=

[

word

.

lower

()

for

word

in

re

.

split

(

r

"W+"

,

content

)

if

word

]


    

return

word_vect

,

cls


 


def

parse_file

(

filename

)

:


    

""" 解析文件中的數據


    """


    

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

)


                

word_vects

.

append

(

word_vect

)


                

classes

.

append

(

cls

)


    

vocabulary

=

list

(

set

(

vocabulary

))


 


    

return

vocabulary

,

word_vects

,

classes




有了上面三個函數我們就可以直接將我們的文本轉換成模型需要的數據向量,之後我們就可以劃分數據集並將訓練數據集給貝葉斯模型進行統計。





# 訓練數據 & 測試數據


ntest

=

int

(

len

(

classes

)

*

(

1

-

TRAIN_PERCENTAGE

))


 


test_word_vects

=

[]


test_classes

=

[]


for

i

in

range

(

ntest

)

:


    

idx

=

random

.

randint

(

0

,

len

(

word_vects

)

-

1

)


    

test_word_vects

.

append

(

word_vects

.

pop

(

idx

))


    

test_classes

.

append

(

classes

.

pop

(

idx

))


 


train_word_vects

=

word_vects


train_classes

=

classes


 


train_dataset

=

[

get_doc_vector

(

words

,

vocabulary

)

for

words

in

train_word_vects

]




訓練模型:





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




剩下我們用測試數據來測試我們貝葉斯模型的預測準確度:





# 測試模型


error

=

0


for

test_word_vect

,

test_cls

in

zip

(

test_word_vects

,

test_classes

)

:


    

test_data

=

get_doc_vector

(

test_word_vect

,

vocabulary

)


    

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

)))




隨機測了四組,錯誤率分別為:0, 0.037, 0.015, 0. 平均錯誤率為1.3%




測完了我們嘗試下看看不同類型簡訊各個詞條的概率分布是怎樣的吧:





# 繪製不同類型的概率分布曲線


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.3

)


    

ax

.

legend

()


 


plt

.

show

()







試試決策樹




上一篇我們基於ID3演算法實現了決策樹,同樣是分類問題,我們同樣可以使用我們的文本數據來構建用於分類簡訊的決策樹,當然唯一比較麻煩的地方在於如果按照與貝葉斯相同的向量作為數據,則屬性可能會非常多,我們在構建決策樹的時候每層樹結構都是遞歸通過遍歷屬性根據信息增益來選取最佳屬性進行樹分裂的,這樣很多的屬性可能會對構建決策樹這一過程來說會比較耗時.那我們就試試吧…





# 生成決策樹


if

not

os.path

.

exists

(

"sms_tree.pkl"

)

:


    

clf

.

create_tree

(

train_dataset

,

train_classes

,

vocabulary

)


    

clf

.

dump_tree

(

"sms_tree.pkl"

)


else

:


    

clf

.

load_tree

(

"sms_tree.pkl"

)


 


# 測試模型


error

=

0


for

test_word_vect

,

test_cls

in

zip

(

test_word_vects

,

test_classes

)

:


    

test_data

=

get_doc_vector

(

test_word_vect

,

vocabulary

)


    

pred_cls

=

clf

.

classify

(

test_data

,

feat_names

=

vocabulary

)


    

if

test_cls

!=

pred_cls

:


        

print

(

"Predict: {} -- Actual: {}"

.

format

(

pred_cls

,

test_cls

))


        

error

+=

1


 


print

(

"Error Rate: {}"

.

format

(

error

/

len

(

test_classes

)))




隨機測了兩次,錯誤率分別為:0.09, 0.0




效果還算不錯




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










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




總結




本文我們使用Python一步步實現了樸素貝葉斯分類器,並對簡訊進行了垃圾簡訊過濾,同樣的數據我們同決策樹的分類效果進行了簡單的比較。本文相關代碼實現:https://github.com/PytLab/MLBox/tree/master/naive_bayes 。決策樹過濾垃圾簡訊的腳本在https://github.com/PytLab/MLBox/tree/master/decision_tree




參考






  • 《Machine Learning in Action》



  • 實例詳解貝葉斯推理的原理



  • 大道至簡:樸素貝葉斯分類器




相關閱讀






  • 機器學習演算法實踐-決策樹(Decision Tree)




看完本文有收穫?請轉發分享給更多人


關注「大數據與機器學習文摘」,成為Top 1%


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

這2個套路走完, 你就成了 Facebook 認證的數據分析師
動手實現推薦系統,挑戰高薪!
5 分鐘掌握智聯招聘網站爬取並保存到 MongoDB 資料庫
全棧開發者都應該關注這些
如果老婆和女朋友她們是程序……

TAG:Python開發者 |

您可能感興趣

Machine Learning:十大機器學習演算法
ClickHouse如何結合自家的GNDT演算法庫CatBoost來做機器學習
Python實現樸素貝葉斯演算法
dijkstra演算法O(n2) 堆優化O(nlogn)
演算法:Sums In A Triangle
Train the Trainer:利用強化學習優化基於模型的強化學習演算法
K-means演算法優化
書評:《演算法之美( Algorithms to Live By )》
AI機器學習-決策樹-python實現CART演算法
Adaboost演算法及python實戰
Bayesian Personalized Ranking 演算法解析及Python實現
一文讀懂進化演算法Evolutionary Algorithm簡介
利用搖滾樂隊學習TensorFlow,Word2Vec模型和TSNE演算法
人工智慧–Autoencoder演算法
用Python 實現的機器人演算法示例集合——PythonRobotics
Tile-based Optical Flow 演算法流程與基本思想
Tamar Charney:重視演算法的力量
谷歌和UC伯克利的新式Actor-Critic演算法快速在真實世界訓練機器人
超越MnasNet、Proxyless:小米開源全新神經架構搜索演算法FairNAS
OpenAI Baselines 更新,新增 HER 強化學習演算法