機器學習|樸素貝葉斯演算法(一)-貝葉斯簡介及應用

kissjz發表於2018-01-29

偶然間聽說阿里云云棲社群這個“寒假換裝攻略活動”,還送鍵盤!
正好缺一個鍵盤(ノへ ̄、)
人窮志短,人窮志短,不開心的話說兩遍,回到現實,想想把樸素貝葉斯演算法再重新整理整理,來拿鍵盤( ̄︶ ̄)↗ 

tip:文章可能很長,希望看完了能有所收穫

機器學習|樸素貝葉斯演算法(一)-貝葉斯簡介及應用
機器學習|樸素貝葉斯演算法(二)-用sklearn實踐貝葉斯
機器學習|樸素貝葉斯演算法(三)-深入理解樸素貝葉斯原理


一、 貝葉斯

貝葉斯簡介:

貝葉斯(RE V Thomas Bayes),英國數學家。
貝葉斯演算法源於用來-解決一個“逆向概率”的問題。

貝葉斯要解決的問題:

  • 正向概率:假設袋子裡面有N個白球,M個黑球,閉著眼伸手去摸球,摸出白球的概率是多少
  • 逆向概率:如果我們事先並不知道袋子裡面黑白球的比例,而是閉著眼睛摸出一個(或好幾個)球,觀察這些取出來的球的顏色之後,那麼我們可以就此對袋子裡面的黑白球的比例作出什麼樣的推測

為什麼用貝葉斯:

  • 現實世界本身就是不確定的,人類的觀察能力是有侷限性的
  • 我們日常所觀察到的只是事物表面上的結果,因此我們需要提供一個猜測

下面舉一個簡單的概率問題來說明此事:

問題:男60%,女40%,男生總是穿長褲,女生則一半穿長褲一半穿裙子

正向概率:隨機選取一個學生,他(她)穿長褲的概率和穿裙子的概率是多大

逆向概率:迎面走來一個穿長褲的學生,你只看得見他(她)穿的是否長褲,而無法確定他(她)的性別,你能夠推斷出他(她)是女生的概率是多大嗎?

假設學校裡面人的總數是 $U $個

穿長褲的男生:$U imes P(Boy) imes P(Pants|Boy)$

$P(Boy)$ 是男生的概率 = 60%

$P(Pants|Boy)$ 是條件概率,即在 Boy 這個條件下穿長褲的概率是多大,這裡是 100% ,因為所有男生都穿長褲

穿長褲的女生:$ U imes P(Girl) imes P(Pants|Girl)$

求解:穿長褲的人裡面有多少女生?

穿長褲總數:$U imes P(Boy) imes P(Pants|Boy) + U imes P(Girl) imes P(Pants|Girl)$

結果: $frac{U imes P(Girl) imes P(Pants|Girl) } {U imes P(Boy) imes P(Pants|Boy) + U imes P(Girl) imes P(Pants|Girl)}$

與總人數有關嗎?

$frac{U imes P(Girl) imes P(Pants|Girl) }{U imes P(Boy) imes P(Pants|Boy) + U imes P(Girl) * P(Pants|Girl)}$

容易發現這裡校園內人的總數U是無關的,可以消去

$P(Girl|Pants) = frac{P(Girl) imes P(Pants|Girl) }{P(Boy) imes P(Pants|Boy) + P(Girl) imes P(Pants|Girl)}$

可以發現:

分母其實就是$ P(Pants)$

分子其實就是$ P(Pants, Girl)$

貝葉斯公式:$Pleft ( A|B
ight )= frac{Pleft ( B|A
ight )Pleft ( A
ight )}{Pleft ( B
ight )}$

二、貝葉斯應用:

拼寫糾正例項
垃圾郵件過濾例項

1.拼寫糾正例項

問題是我們看到使用者輸入了一個不在字典中的單詞,我們需要去猜測:這個傢伙到底真正想輸入的單詞是什麼呢?

P(我們猜測他想輸入的單詞 | 他實際輸入的單詞)

使用者實際輸入的單詞記為 D ( D 代表 Data ,即觀測資料)

猜測1:$P(h_1 | D)$,猜測2:$P(h_2 | D)$,猜測3:$P(h_3 | D)$……

統一為:$P(h | D)$

根據上文推匯出來的貝葉斯公式:$P(h | D) = frac{P(h) imes P(D | h)} { P(D)}$

對於不同的具體猜測 h1 h2 h3 .. ,P(D) 都是一樣(因為都是同一個觀測資料D嘛)的,所以在比較$ P(h_1 | D)$ 和 $P(h_2 | D)$ 的時候我們可以忽略這個常數

$P(h | D) ∝ P(h) * P(D | h)$

上式的意思表明了:對於給定觀測資料,一個猜測是好是壞,取決於“這個猜測本身獨立的可能性大小“(先驗概率,Prior )(即$P(h)$)和“這個猜測生成我們觀測到的資料的可能性大小”(即似然,$P(D | h)$)。

貝葉斯方法計算: $P(h) * P(D | h),P(h) $是特定猜測的先驗概率

比如使用者輸入tlp ,那到底是 top 還是 tip ?這個時候,當最大似然不能作出決定性的判斷時,先驗概率就可以插手進來給出指示—— “既然你無法決定,那麼我告訴你,一般來說 top 出現的程度要高許多,所以更可能他想打的是 top ”

模型比較理論

最大似然:最符合觀測資料的(即 $P(D | h)$ 最大的)最有優勢的

奧卡姆剃刀:$ P(h)$ 較大的模型有較大的優勢

擲一個硬幣,觀察到的是“正”,根據最大似然估計的精神,我們應該猜測這枚硬幣擲出“正”的概率是 1(也就是這枚硬幣是特製的,兩面都是正),因為這個才是能最大化 $P(D | h) $的那個猜測

如果平面上有 N 個點,近似構成一條直線,但絕不精確地位於一條直線上。這時我們既可以用直線來擬合(模型1),也可以用二階多項式(模型2)擬合,也可以用三階多項式(模型3),特別地,用 N-1 階多項式便能夠保證肯定能完美通過 N 個資料點。那麼,這些可能的模型之中到底哪個是最靠譜的呢?

根據奧卡姆剃刀:在兩個模型在訓練集訓練出來的結果一樣好時,選擇簡單的(防止過擬合,把模型中特有的引數作為特徵),也就是選擇階數最小的那個模型。

2.垃圾郵件過濾例項

問題:給定一封郵件,判定它是否屬於垃圾郵件

D 來表示這封郵件,注意 D 由 N 個單片語成。我們用 h+ 來表示垃圾郵件,h- 表示正常郵件

$P(h_+|D) = frac{P(h_+) imes P(D|h_+)} { P(D)}$

$P(h_- |D) =frac{P(h_- ) imes P(D|h_- )} { P(D)}$

先驗概率:$P(h_+)$ 和$ P(h_-)$ 這兩個先驗概率都是很容易求出來的,只需要計算一個郵件庫裡面垃圾郵件和正常郵件的比例就行了。

設D 裡面含有 N 個單詞 $d_1,d_2,d_3,P(D|h_+) = P(d_1,d_2,..,d_n|h_+)$

$P(d_1,d_2,..,d_n|h_+) $就是說在垃圾郵件當中出現跟我們目前這封郵件一模一樣的一封郵件的概率是多大!

顯然,如果要一模一樣也就是說要郵件中每個單詞都一樣,那真是很苛刻了,所以我們現實中要降低判斷垃圾郵件的標準,只要求和我們已知的垃圾郵件有一定的相似度就行了

$P(d_1,d_2,..,d_n|h_{+}) $由全概率公式擴充套件為: $P(d_1|h_+) imes P(d_2|d_1, h_+) imes P(d_3|d_2,d_1, h_+) imes ..$

這裡我們假設 $d_i$與 $d_{i-1} $是完全條件無關的(因為樸素貝葉斯(naïve bayes)假設特徵之間是獨立,互不影響

簡化為$ P(d_1|h_+) imes P(d_2|h_+) imes P(d_3|h_+) imes ..$,再次降低了評判的標準,而現在每一個概率又都是可計算的了

對於$P(d_1|h_+) imes P(d_2|h_+) imes P(d_3|h_+) imes ..$只要統計 $d_i$ 這個單詞在垃圾郵件中出現的頻率即可。
下面來看一下老師在課程中給出的貝葉斯-拼寫檢查器的程式碼。

求解:argmaxc P(c|w) -> argmaxc P(w|c) P(c) / P(w)

P(c):文章中出現一個正確拼寫詞 c 的概率, 也就是說, 在英語文章中, c 出現的概率有多大
P(w|c):在使用者想鍵入 c 的情況下敲成 w 的概率. 因為這個是代表使用者會以多大的概率把 c 敲錯成 w
argmaxc:用來列舉所有可能的 c 並且選取概率最大的


import re, collections
 ###貝葉斯-拼寫檢查器###
# 把語料中的單詞全部抽取出來, 轉成小寫, 並且去除單詞中間的特殊符號
def words(text): return re.findall(`[a-z]+`, text.lower()) 
 
#避免一個單詞未在語料庫中出現但卻出現了(那不就返回0了),為了避免,用很小的lambda代替
def train(features):
    model = collections.defaultdict(lambda: 1)
    for f in features:
        model[f] += 1
    return model
 
NWORDS = train(words(open(`big.txt`).read()))
 
alphabet = `abcdefghijklmnopqrstuvwxyz`
##編輯距離:兩個詞之間的編輯距離定義為使用了幾次插入(在詞中插入一個單字母), 刪除(刪除一個單字母), 交換(交換相鄰兩個字母), 替換(把一個字母換成另一個)的操作從一個詞變到另一個詞

#返回所有與單詞 w 編輯距離為 1 的集合
def edits1(word):
    n = len(word)
    return set([word[0:i]+word[i+1:] for i in range(n)] +                     # deletion
               [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition
               [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration
               [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet])  # insertion
 
#返回所有與單詞 w 編輯距離為 2 的集合
#在這些編輯距離小於2的詞中間, 只把那些正確的詞作為候選詞
def known_edits2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)
 

#正常來說把一個母音拼成另一個的概率要大於子音 (因為人常常把 hello 打成 hallo 這樣); 把單詞的第一個字母拼錯的概率會相對小, 等等.但是為了簡單起見, 選擇了一個簡單的方法: 編輯距離為1的正確單詞比編輯距離為2的優先順序高, 而編輯距離為0的正確單詞優先順序比編輯距離為1的高. 
def known(words): return set(w for w in words if w in NWORDS)

#如果known(set)非空, candidate 就會選取這個集合, 而不繼續計算後面的
def correct(word):
    candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
    return max(candidates, key=lambda w: NWORDS[w])

OK,如果看完這一篇對貝葉斯還是不瞭解的話,沒關係!
接下來我會用python機器學習的庫來簡單實踐一下,再深入的談一談對樸素貝葉斯演算法原理的理解。


相關文章