樸素貝葉斯從放棄到入門
理論基礎
聯合概率
聯合概率表示兩個事件共同發生的概率。A與B的聯合概率表示為$P(AB)$,$P(A,B)$或者$P(A \bigcap B)$。
聯合概率可以推廣到任意又窮多個事件出現的情況,設($A_1,A_2,\cdots,A_n$)為任意n個事件($n\ge2$),事件$A_1,A_2,\cdots,A_n$共同發生的概率記為$P(A_1A_2 \dots A_n)$,$P(A_1,A_2,\dots,A_n)$或者$P(A_1 \bigcap A_2 \bigcap \dots \bigcap A_n)$
條件概率
設A,B 是兩個事件,且$P(A) > 0$,則稱$P(B|A) = \frac{P(AB)}{P(A)}$為在事件A發生的條件下,事件B發生的條件概率。一般地,$P(B|A) \not= P(B)$ ,且它滿足以下三條件:(1)非負性;(2)規範性;(3)可列可加性。
設E為隨機試驗,Ω為樣本空間,A,B為任意兩個事件,設$P(A)>0$,稱$P(B|A) = \frac{P(AB)}{P(A)}$為在事件A發生的條件下事件B的條件概率。
上述乘法公式可推廣到任意有窮多個事件時的情況。
設($A_1,A_2,\cdots,A_n$)為任意n個事件($n\ge2$)且$P(A_1,A_2,\cdots,A_n)>0$,則$P(A_1A_2 \cdots A_n)=P(A_1)P(A_2|A_1) \cdots P(A_n|A_1A_2 \cdots A_{n-1}) = \prod_{i=1}^n P(A_i|A_1 \cdots A_{i-1})$。
對於一段文本序列$S=w_1,w_2,\cdots,w_n$,它的概率可表示為:
$$P(S) = P(w_1,w_2,\cdots,w_n) = \prod_{t=1}^n(w_t|w_1 \cdots w_{t-1}) = P(w_1) \cdot P(w_2|w_1) \cdot P(w_3|w_1w_2) \cdots P(w_n|w_1w_2 \cdots w_{n-1})$$
Ngram模型
1.Ngram模型
$$P(w_t|w_1w_2 \cdots w_{t-1}) \approx P(w_t|w_{t-n+1} \cdots w_{t-1})$$
2.bigram
$$P(w_t|w_1w_2 \cdots w_{t-1}) \approx P(w_t|w_{t-1})$$
3.trigram
$$P(w_t|w_1w_2 \cdots w_{t-1}) \approx P(w_t|w_{t-1}w_{t-2})$$
獨立性假設
$$P(S) = P(w_1,w_2,\cdots,w_n) \approx \prod_{t=1}^T P(w_t) = P(w_1)P(w_2) \cdots P(w_n)$$
其他
$$ 先驗概率 = P(原因);後驗概率 = P(原因|結果) $$
$$ P(a,b|c) = \frac{P(a,b,c)}{P(c)} = \frac{P(a,b,c)}{P(b,c)} \cdot \frac{P(b,c)}{P(c)} = P(a|b,c) \cdot P(b|c)$$
完備事件組/樣本空間的劃分
設($A_1,\cdots,A_i,\cdots,A_n$)是一組事件,若
- $\forall_{i\not=j} A_i \bigcap A_j = \emptyset; i,j\in(1,2,\cdots,n)$
- $\sum_{i=1}^n A_i = \Omega$
則稱($A_1,\cdots,A_i,\cdots,A_n$)是樣本空間Ω的一個劃分,或稱為樣本空間Ω 的一個完備事件組。
全概率公式
設($A_1,\cdots,A_i,\cdots,A_n$)施一個完備事件組,則有$P(B) = \sum_{i=1}^n P(A_i) \cdot P(B|A_i) = \sum_{i=1}^n P(A_iB)$
貝葉斯公式
設($A_1,\cdots,A_i,\cdots,A_n$)是一組完備事件組,則有
$$P(A_i|B) = \frac{PA_iB}{P(B)} = \frac{P(A_i)P(B|A_i)}{\sum_{j=1}^nP(A_j)P(B|A_j)}$$
根據條件概率和全概率公式,很容易得出貝葉斯公式。
貝葉斯定理的應用
貝葉斯定理在艾滋病檢測上的應用
假設艾滋病在人群中的發病率為萬分之一,艾滋病檢測假陰性的概率千分之一(假陰性的意思是本來有病應該呈現陽性,但是呈現了陰性);艾滋病檢測假陽性的概率為萬分之一(假陽性意思是本來沒病應該呈現陰性,但是呈現了陽性)。假設某人在某次檢測當中結果呈現陽性,那麼他真正感染艾滋病的概率是多少?
# 艾滋病在人群中的發病概率P(T)
p(T) = 0.0001
# 在有艾滋病的場景下,檢測為陽性的概率P(檢測為陽性|患病),即1-P(假陰性)
p(fT) = 0.999
# 在沒有艾滋病的場景下,檢測為陽性的概率P(檢測為陽性|不患病),即假陽性
p(f_T) = 0.0001
def bayes(pT, pfT, pf_T):
return (pfT pT) / (pfT pT + pf_T * (1 - pT))
print bayes(pT, pfT, pf_T)
將數據代入公式,計算得出P(患病|檢測為陽性)=49.977%,看起來還是不能確定該被試是否感染艾滋病(被試的感染艾滋病的幾率從萬分之一上升到近50%)。為了確定被試是否真正感染艾滋病,我們隻需再進行一次檢測,如果下一次檢測還呈陽性,再一次應用貝葉斯定理,則該被試感染艾滋病的幾率瞬間提升到99.99%,基本可以確定該被試感染艾滋病了。
pT = 49.977
print bayes(pT, pfT, pf_T)
# 0.999899817803
貝葉斯定理在垃圾郵件過濾上的應用
給定訓練集,垃圾郵件和正常郵件各5000封,假定詞$w_1$,$w_2$出現的頻率如下。
| 詞 | 郵件類別 | 詞在該郵件類別中的數量 |
| -- | ------ | ------------------- |
| $w_1$ | Spam | 250 |
| $w_1$ | Health | 5 |
| $w_2$ | Spam | 495 |
| $w_2$ | Health | 5 |
# P(垃圾郵件)
pS = 0.5
# P(正常郵件)
pH = 1 - pS # 0.5
# P(w1|垃圾郵件)
pw1S = 250.0 / 5000 # 0.05
# P(w1|正常郵件)
pw1H = 5.0 / 5000 # 0.001
根據貝葉斯定理,我們很容易計算$P(垃圾郵件|w_1)$的概率。
def bayes(pS, pwS, pwH):
return (pwS pS) / (pwS pS + pwH * (1 - pS))
# P(垃圾郵件|w1)
pSw1 = bayes(pS, pw1S, pw1H) # 0.980392156863
其實根據樣本分布,我們也很容易計算$P(垃圾郵件|w_1)$的概率。
$$ P(垃圾郵件|w_1) = \frac{P(w_1,垃圾郵件)}{P(w_1)} = \frac{250}{250 + 5} = 98.04% $$
我們可以看出樣本中包含$w_1$的郵件是垃圾郵件的概率超過98%,如果樣本的分布和總體的分布一致,可以看出$w_1$的推斷能力很強,盡管如此,我們依然不能根據單個詞來明確的判斷一封包含$w_1$的新郵件就是垃圾郵件。我們需要更多的證據。
一封郵件由多個詞組成,如果一封郵件不隻是包含$w_1$,還包含$w_2$,那麼這封郵件的是垃圾概率是多少呢。
$$ P(垃圾郵件|w_1,w_2) = \frac{P(垃圾郵件,w_1,w_2)}{P(w_1,w_2)} $$
$$ P(w_1,w_2) = P(w_1,w_2|垃圾郵件) \cdot P(垃圾郵件) + P(w_1,w_2|正常郵件) \cdot P(正常郵件) $$
$$ = P(w_1,w_2,垃圾郵件) + P(w_1,w_2,正常郵件) $$
也即:
$$ P(垃圾郵件|w_1,w_2) = \frac{P(垃圾郵件,w_1,w_2)}{P(w_1,w_2,垃圾郵件) + P(w_1,w_2,正常郵件)} $$
這裏涉及兩個聯合概率事件。
- 已知$w_1$,$w_2$的情況下,該郵件是垃圾郵件的概率,即$P(w_1,w_2,垃圾郵件)$,記為 $E_1$。
- 已知$w_1$,$w_2$的情況下,該郵件是正常郵件的概率,即$P(w_1,w_2,正常郵件)$,記為 $E_2$。
| 事件 | $w_1$ | $w_2$ | 垃圾郵件 |
| -- | ------ | ------ | ------ |
| $E_1$ | 出現 | 出現 | 是 |
| $E_2$ | 出現 | 出現 | 不是 |
$$ P(E_1) = P(w_1,w_2,垃圾郵件) = P(垃圾郵件) P(w_1|垃圾郵件) P(w_2|垃圾郵件,w_1) $$
然而$P(w_2|垃圾郵件,w_1)$該怎麼計算呢?現在是樸素貝葉斯出場的時候了,基於獨立性假設,$w_1$,$w_2$之間相互獨立。則有:
$$ P(w_2|垃圾郵件,w_1) = P(w_2|垃圾郵件) $$
# P(S,w1,w2) = P(S) P(w1|S) P(w2|S,w1)
# Independence hypothesis => P(S,w1,w2) = P(S) P(w1|S) P(w2|S)
def joint(pS, pw1S, pw2S):
return pS pw1S pw2S
$P(E_1) = P(垃圾郵件) P(w_1|垃圾郵件) P(w_2|垃圾郵件)$
$P(E_2) = P(正常郵件) P(w_1|正常郵件) P(w_2|正常郵件)$
目標概率:$P(垃圾郵件|w_1,w_2) = \frac{P(E_1)}{P(E_1) + P(E_2)}$
# P(w2|垃圾郵件)
ps2W = 495.0 / 5000 # 0.001
# P(w2|正常郵件)
pw2H = 5.0 / 5000 # 0.099
# P(E1)
pE1 = joint(pS, pw1S, pw2S) # 0.002475
pE2 = joint(pH, pw1H, pw2H) # 5e-07
# P(垃圾郵件|w_1,w_2)
print pE1 / (pE1 + pE2) # 0.999798020602
黑客與畫家中的疑問
Paul Graham在他的《黑客與畫家》當中,有舉過樸素貝葉斯的例子,他的做法是選出區分度最高的15個詞,並計算其聯合概率,並給出了最終公式。
$$ P_{spam|w_1,w_2,\cdots,w_{15}} = \frac{\prod_{i=1}^{15} P_{spam|w_i}}{\prod_{i=1}^{15} P_{spam|w_i} + \prod_{i=1}^{15} (1 - P_{spam|w_i})} $$
那麼這個公式是怎麼推導出來的呢?為了方便,我們取$w_1$,$w_2$兩個詞來嚐試推導出這個公式,簡化以後,公式變為:
$$ P_{spam|w_1,w_2} = \frac{P_{spam|w_1} \cdot P_{spam|w_2}}{P_{spam|w_1} \cdot P_{spam|w_2} + (1 - P_{spam|w_1}) \cdot (1 - P_{spam|w_2})} $$
下麵我們開始推導過程。
根據貝葉斯定理有:
$$ P_{spam|w_1,w_2} = \frac{P_{w_1,w_2|spam} \cdot P_{spam}}{P_{w_1,w_2}} = \frac{P_{w_1,w_2|spam} \cdot P_{spam}}{P_{w_1,w_2|spam} \cdot P_{spam} + P_{w_1,w_2|\overline{spam}} \cdot P_{\overline{spam}}} $$
根據獨立性假設$P_{w_1,w_2|spam} = P_{w_1|w_2,spam} \cdot P_{w_2|spam} = P_{w_1|spam} \cdot P_{w_2|spam}$,得到:
$$ P_{spam|w_1,w_2} \approx \frac{P_{w_1|spam} \cdot P_{w_2|spam} \cdot P_{spam}}{P_{w_1|spam} \cdot P_{w_2|spam} \cdot P_{spam} + P_{w_1|\overline{spam}} \cdot P_{w_2|\overline{spam}} \cdot P_{\overline{spam}}} $$
根據貝葉斯公式$P_{w|S} = \frac{P_{S|w} \cdot P_w}{P_S}$,得到:
$$ P_{spam|w_1,w_2} \approx \frac{P_{spam|w_1} \cdot P_{w_1} \cdot P_{spam|w_2} \cdot P_{w_2}}{P_{spam|w_1} \cdot P_{w_1} \cdot P_{spam|w_2} \cdot P_{w_2} + \frac{P_{\overline{spam}|w_1} \cdot P_{w_1} \cdot P_{\overline{spam}|w_2} \cdot P_{w_2} \cdot P_{spam}}{P_{\overline{spam}}}} $$
$$ = \frac{P_{spam|w_1} \cdot P_{spam|w_2}}{P_{spam|w_1} \cdot P_{spam|w_2} + \frac{P_{\overline{spam}|w_1} \cdot P_{\overline{spam}|w_2} \cdot P_{spam}}{P_{\overline{spam}}}} $$
取$P_{spam}=P_{\overline{spam}}=0.5$,得到:
$$ P_{spam|w_1,w_2} \approx \frac{P_{spam|w_1} \cdot P_{spam|w_2}}{P_{spam|w_1} \cdot P_{spam|w_2} + P_{\overline{spam}|w_1} \cdot P_{\overline{spam}|w_2}} $$
又因為:
$$P_{\overline{spam}|w} = \frac{P_{w|\overline{spam}} \cdot P_{\overline{spam}}}{P_w} = \frac{P_{w|\overline{spam}} \cdot P_{\overline{spam}}}{P_{w|\overline{spam}} \cdot P_{\overline{spam}} + P_{w|spam} \cdot P_{spam}} $$
$$ = 1 - \frac{P_{w|spam} \cdot P_{spam}}{P_{w|\overline{spam}} \cdot P_{\overline{spam}} + P_{w|spam} \cdot P_{spam}} = 1 - P_{spam|w} $$
最終可得:
$$ P_{spam|w_1,w_2} = \frac{P_{spam|w_1} \cdot P_{spam|w_2}}{P_{spam|w_1} \cdot P_{spam|w_2} + (1 - P_{spam|w_1}) \cdot (1 - P_{spam|w_2})} $$
pSw1 = bayes(pS, pw1S, pw1H)
pSw2 = bayes(pS, pw2S, pw2H)
e1 = pS pSw1 pSw2
e2 = pH (1 - pSw1) (1 - pSw2)
print e1 / (e1 + e2) # 0.999798020602
可見,在$P_{spam}=P_{\overline{spam}}=0.5$的情況下,結果和之前是一樣的。
推廣到15個詞,就得到:
$$ P_{spam|w_1,w_2,\cdots,w_{15}} = \frac{\prod_{i=1}^{15} P_{spam|w_i}}{\prod_{i=1}^{15} P_{spam|w_i} + \prod_{i=1}^{15} (1 - P_{spam|w_i})} $$
實戰垃圾郵件過濾
公式推導
給定一個郵件M,它由文本序列$S=w_1,w_2,\ldots,w_n$組成,則給定郵件為垃圾為垃圾郵件的概率為:
$$ P(spam|M) = P(spam|w_1,w_2,\cdots,w_n) = \frac{P(w_1,w_2,\cdots,w_n|spam) \cdot P(spam)}{P(w_1,w_2,\ldots,w_n|spam) \cdot P(spam) + P(w_1,w_2,\ldots,w_n|\overline{spam}) \cdot P(\overline{spam}) } $$
根據樸素貝葉斯的獨立性假設,則有:
$$ P(spam|M) \approx \frac{\prod_{i=1}^n P(w_i|spam) \cdot P(spam)}{\prod_{i=1}^n P(w_i|spam) \cdot P(spam) + \prod_{i=1}^n P(w_i|\overline{spam}) \cdot P(\overline{spam}) } $$
模型數據
category | count |
---|---|
spam | count1 |
$\overline{spam}$ | count2 |
word | category | count |
---|---|---|
$w_1$ | spam | $w_1c_1$ |
$w_1$ | $\overline{spam}$ | $w_1c_2$ |
$w_2$ | spam | $w_2c_1$ |
$w_2$ | $\overline{spam}$ | $w_2c_2$ |
... | ... | ... |
$w_n$ | spam | $w_nc_1$ |
$w_n$ | $\overline{spam}$ | $w_nc_1$ |
概率計算
- 垃圾郵件概率:$ P(spam) = \frac{count(spam)}{count(spam) + count(\overline{spam})}$
- 正常郵件概率:$P(\overline{spam}) = 1 - P(spam)$
- $w_i$在垃圾郵件中的概率:$P(w_i|spam) = \frac{count(w_i,spam)}{count(spam)}$,也就是 $\frac{w_i關聯的垃圾郵件數量}{垃圾郵件的數量}$
- $w_i$在正常郵件中的概率:$P(w_i|\overline{spam}) = \frac{count(w_i,\overline{spam})}{count(\overline{spam})}$,也就是 $\frac{w_i關聯的正常郵件數量}{正常郵件的數量}$
代碼演示
import numpy as np
class AntiSpam:
def __init__(self):
self.wc = {} # 記錄每個詞在垃圾郵件和正常郵件中出現的次數
self.mc = {} # 記錄垃圾郵件和正常郵件中出現的次數
def incw(self, word, category):
self.wc.setdefault(word, {})
self.wc[word].setdefault(category, 0)
self.wc[word][category] += 1
def incm(self, category):
self.mc.setdefault(category, 0)
self.mc[category] += 1
def train(self, words, category):
for w in words:
self.incw(w, category)
self.incm(category)
def show(self):
print "mc: %s \nwc:%s\n\n\n" % (self.mc, self.wc)
def wcount(self, word, category):
if word in self.wc and category in self.wc[word]:
return float(self.wc[word][category])
return 1.0
def wprob(self, word, category):
return self.wcount(word, category) / self.mc[category]
def cprob(self, category):
return float(self.mc[category]) / sum(self.mc.values())
# 利用對數來計算
def safe_prob(self, words):
s, h = 0.0, 0.0
for w in words:
s += np.log(self.wprob(w, 'spam'))
h += np.log(self.wprob(w, 'health'))
s += np.log(self.cprob('spam'))
h += np.log(self.cprob('health'))
return np.exp(s) / (np.exp(s) + np.exp(h))
# 利用乘法來計算
def prob(self, words):
sprob, hprob = 1.0, 1.0
for w in words:
sprob *= self.wprob(w, 'spam')
hprob *= self.wprob(w, 'health')
sprob *= self.cprob('spam')
hprob *= self.cprob('health')
return sprob / (sprob + hprob)
模擬樣本訓練
antiSpam = AntiSpam()
for i in range(4989):
antiSpam.train(['hello', 'world', 'todo'], 'health')
for k in range(4901):
antiSpam.train(['invoice', 'bill', 'todo'], 'spam')
antiSpam.train(['discount', 'promotion', 'cool'], 'health')
for k in range(10):
antiSpam.train(['spam', 'mail', 'attention'], 'health')
for k in range(9):
antiSpam.train(['discount', 'promotion', 'cool'], 'spam')
for k in range(90):
antiSpam.train(['spam', 'mail', 'attention'], 'spam')
antiSpam.show()
# mc: {'health': 5000, 'spam': 5000}
# wc: {'attention': {'health': 10, 'spam': 90}, 'spam': {'health': 10, 'spam': 90}, 'bill': {'spam': 4901}, 'discount': {'health': 1, 'spam': 9}, 'invoice': {'spam': 4901}, 'mail': {'health': 10, 'spam': 90}, 'world': {'health': 4989}, 'promotion': {'health': 1, 'spam': 9}, 'todo': {'health': 4989, 'spam': 4901}, 'hello': {'health': 4989}, 'cool': {'health': 1, 'spam': 9}}
垃圾郵件過濾
print antiSpam.prob(['discount', 'spam', 'todo']) # 0.987588626017
print antiSpam.safe_prob(['discount', 'spam', 'todo']) # 0.987588626017
print antiSpam.prob(['hello', 'mail', 'todo']) # 0.00176901392183
print antiSpam.safe_prob(['hello', 'mail', 'todo']) # 0.00176901392183
print antiSpam.prob(['hello', 'mail', 'todo']) # 0.999999995374
print antiSpam.safe_prob(['hello', 'mail', 'todo']) # 0.999999995374
最後更新:2017-11-06 14:05:05