《Machine Learning in Action》—— 白話貝葉斯,「恰瓜群眾」應該恰好瓜還是恰壞瓜

《Machine Learning in Action》—— 白話貝葉斯,「恰瓜群眾」應該恰好瓜還是恰壞瓜

概率論,可以說是在機器學習當中扮演了一個非常重要的角色了。Taoye對概率論知識的掌握目前也還僅僅只是停留在本科期間所接觸到的,而且還都已經忘了不少。快速的複習回顧一下之後,用來理解機器學習中的貝葉斯算法,還是足夠的。

手撕機器學習系列文章目前已經更新了支持向量機SVM、決策樹、K-近鄰(KNN),現在我們來玩玩貝葉斯算法,其他機器學習系列文章可根據自己需求來食用(持續更新中):

本篇文章主要包括以下幾個部分的內容:

  • 到底啥是貝葉斯,很厲害嘛???主要介紹了貝葉斯的一些基礎知識,然後寫了下筆者對貝葉斯的個人看法,這個部分的內容不難,認真閱讀應該都能理解
  • 介紹貝葉斯決策所涉及到的一些理論,其中包括條件概率、全概率、貝葉斯推斷等內容,並且通過幾個比較生動的案例或是題目幫助大家對理論的理解。這裡需要特別值得注意的是:最後一個案例(罐子和石頭)務必要理解清楚貝葉斯真正所要表達的實際意義。
  • 這第三部分的內容主要是通過一個西瓜的案例來給大夥進行一下貝葉斯實戰,Taoye命名為:「吃瓜群眾」應該恰好瓜還是壞瓜。此「吃瓜群眾」就單純字面上的意思,而非網絡用語之「梗」。另外,在這部分內容裏面,還會詳細介紹標稱型數據和數值型數據的具體處理方式,以及常用的「平滑」處理——拉普拉斯修正。當然了,這部分內容還是會照常給大家通過代碼的形式來實戰這個案例。

一、到底啥是貝葉斯,很厲害嘛???

貝葉斯定理由英國數學家貝葉斯 ( Thomas Bayes 1702-1761 ) 發展,用來描述兩個條件概率之間的關係,比如 P(A|B) 和 P(B|A)。按照乘法法則,可以立刻導出:P(A∩B) = P(A)P(B|A)=P(B)P(A|B)。如上公式也可變形為:P(A|B)=P(B|A)*P(A)/P(B)。

托馬斯·貝葉斯(Thomas Bayes,1702-1761),18世紀英國神學家、數學家、數理統計學家和哲學家,概率論理論創始人,貝葉斯統計的創立者,「歸納地」運用數學概率,「從特殊推論一般、從樣本推論全體」的第一人。

放一張大佬的圖片鎮文:

圖片來源網絡

對於貝葉斯算法來講,其優點是在簡單易懂,學習效率高,數據較少的情況下仍然有效,並且可以處理多分類問題。

至於缺點嘛,在貝葉斯算法中,假設屬性與屬性之間是有一定的相關性的,這個時候計算量相對會比較複雜,而且處理起來也不是那麼的容易。所以就衍生出了樸素貝葉斯來降低屬性與屬性之間的關係(一句話,沒有任何關係),也就是屬性之間是完全獨立的,但我們知道在實際問題中,屬性之間很難做到完全獨立。即使這樣,樸素貝葉斯依然會有廣的應用。

上面所提到的屬性之間是獨立的,這句話應該怎麼來理解呢???

獨立性可以說是概率論當中的常客了。就是說,兩者之間沒有什麼任何關係,我不管你,請你也不要管我,你過你的獨木橋,我走我的陽光道,單人做的事情都不會對他人造成任何的影響。(需要重點理解)

關於對上述的理解,如有些許疑惑也沒關係,Taoye會在下面通過案例來詳細介紹的,以幫助大家對獨立性的理解。

另外,在《機器學習實戰》這本書中,還提到了該算法的適用數據類型為標稱型數據。但從實際來講,除了適用標稱型數據來之外,還能適用數值型數據。

這裡稍微解釋下標稱型數據和數值型數據: 標稱型數據一般用來表示離散屬性,比如身高我們不對其做具體的多少cm,而是高和矮兩種結果。而數值型數據一般針對於連續屬性,比如身高我們可以具體到:170cm、175cm、180cm等等。

在貝葉斯算法中,不同類型的數據,我們會有不一樣的方式來處理。對於標稱型數據來講,可以直接通過頻率來處理,高的人有幾個???矮的人有幾個???而對於數值型數據或者說是連續性數據來講,我們一般考慮概率密度函數來處理,假設連續性數據滿足高斯分佈。(這裡不理解也沒關係,我們後面會詳細來摳摳高斯分佈在這裡的應用)

以上就是關於貝葉斯算法所涉及到的一些基礎概念了,Taoye儘可能做到白話了,對於有基礎的讀者來講應該不是很難理解。有些許疑問也沒關係,下面我們來具體看看貝葉斯究竟是何方神聖???

二、貝葉斯決策相關理論

條件概率:

關於條件概率,其實早在中學時期就有接觸過吧,我還猶新記得當時Taoye學這部分內容的時候賊起勁,上課總是與老師瘋狂互動。

先這樣,再那樣,最後再這樣搞一下不就解決了嘛,小意思啦

Taoye還特意給大家找出了這本書,就是數學 選修2-3。如果有機會再夕拾這本書的話,一定會非常的有意思,想想就有點刺激。總共分為A、B版兩本,喏,就是紫色皮皮和藍色皮皮的這本「神書」(有機會一定要夕拾一哈):

好了,好了,我們來快速回顧一下條件概率吧!

上圖的術語,我們可以把它叫做文氏圖、Venn圖、溫氏圖、維恩圖、范氏圖,都行。它主要用來幫助我們理解集合與集合之間的關係,根據上圖,我們可以很清楚的看到在B事件發生的情況下,事件A發生的概率\(P(A|B)\)(不明的話可以把它理解成面積,是不是秒懂???):

\[P(A|B) = \frac{P(A \cap B)}{P(B)} \tag{2-1}
\]


為了表達對「神書」的敬意,我們從其中抽一道題目來看看吧~~~

題目來源:人教版高中數學B版選修2-3

Q:拋擲紅、藍兩顆骰子,設事件B=「藍色骰子的點數為3或6」,事件A=「兩顆骰子的點數之和大於8」,那麼問題來了,在已知藍色骰子的點數為3或6的時候,事件A發生的概率是多少呢?

我們知道,每顆篩子有6種可能,拋擲兩顆篩子總共有36種可能(6×6=36),對吧?而事件A和事件B同時發生的可能有5種,即\(P(A \cap B)=\frac{5}{36}\),而事件A發生的可能有12種,所以\(P(A)=\frac{12}{36}\),所以我們可以得到\(P(A|B)\)的值如下:

\[\begin{aligned}
P(A|B) & = \frac{P(A \cap B)}{P(B)} \\
& = \frac{\frac{5}{36}}{\frac{12}{36}}=\frac{5}{12}
\end{aligned}
\]


怎麼樣,挺簡單的吧?上述的條件概率的表達形式是我們中學時所接觸到的,而在貝葉斯算法中的條件概率則稍微有點不同,只是做了小小的變動。

是醬紫的

前面我們不是得到了\(P(A|B) = \frac{P(A \cap B)}{P(B)}\)嘛,變動後即:

\[P(A \cap B) = P(A|B) * P(B) \tag{2-2}
\]

同理:

\[P(A \cap B) = P(B|A) * P(A) \tag{2-3}
\]

所以:

\[P(A|B) * P(B) = P(B|A) * P(A) \tag{2-4}
\]

即:

\[P(A|B) = \frac{P(B|A) * P(A)}{P(B)} \tag{2-5}
\]

其中,式2-5就是我們貝葉斯當中得所使用的到的條件概率公式。

全概率公式:

假設樣本空間S,是兩個事件A與A’的和,如下圖:

上圖中,事件A和事件A’共同構成了樣本空間S。

這種情況下,事件B能劃分成兩個部分。如下圖:

即:

\[P(B) = P(B \cap A) + P(B \cap A^{‘}) \tag{2-6}
\]

由上面的推導可知:

\[P(B \cap A) = P(B|A)P(A) \tag{2-7}
\]

所以

\[P(B) = P(B|A)P(A) + P(B|A^{‘})P(A^{‘}) \tag{2-8}
\]

這就是全概率公式。它的含義是,如果A和A’構成樣本空間的一個劃分,那麼事件B的概率,就等於A和A’的概率分別乘以B對這兩個事件的條件概率之和。

將這個全概率公式代入到上面的條件概率公式中,就可以得到條件概率的另一種寫法:

\[P(A|B) = \frac{P(B|A)P(A)}{P(B|A)P(A) + P(B|A^{‘})P(A^{‘})} \tag{2-9}
\]

貝葉斯推斷

對條件概率公式進行變形,可以得到如下形式:

\[P(A|B) = P(A)\frac{P(B|A)}{P(B)} \tag{2-10}
\]

我們把P(A)稱為“先驗概率”(Prior probability),即在B事件發生之前,我們對A事件概率的一個判斷,也就是說這個時候單純的考慮事件A,與事件B無關。
P(A|B)稱為“後驗概率”(Posterior probability),即在B事件發生之後,我們對A事件概率的重新評估,這個時候就要考慮事件B了。
P(B|A)/P(B)稱為“可能性函數”(Likelyhood),這是一個調整因子,使得預估概率更接近真實概率。

所以,條件概率可以理解成下面的式子:

\[後驗概率 = 先驗概率 x 調整因子
\]

這就是貝葉斯推斷的含義。我們先預估一個”先驗概率”,然後加入實驗結果,看這個實驗到底是增強還是削弱了”先驗概率”,由此得到更接近事實的”後驗概率”。

在這裡,如果”可能性函數”P(B|A)/P(B)>1,意味着”先驗概率”被增強,事件A的發生的可能性變大;如果”可能性函數”=1,意味着B事件無助於判斷事件A的可能性;如果”可能性函數”<1,意味着”先驗概率”被削弱,事件A的可能性變小。

上述內容,來自於阮一峰老師的網絡日誌: //www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.html

為了加深對貝葉斯推斷的理解,我們來看看下面的一個例子:


例子參考於:《機器學習實戰》

現在有完全一樣的1、2號兩個罐子,其中1號罐有4塊石頭,分別是2白2黑;2號罐子有3塊石頭,分別1白2黑。現在隨機選擇一個罐子,從中抓起1塊石頭,發現是白色的,請問這塊白色石頭更可能來自於哪一個罐子???

這道題目是Taoye根據《機器學習實戰》這本書上的例子進行改動的,主要是為了方便大家更容易理解貝葉斯在分類問題中的應用。注意這道題問的是:更可能來自於哪一個罐子?

總共就兩個罐子,不是1號罐子就是2號罐子,而更可能描述的是一個可能性,其實就相當於一個分類問題。來自哪一個罐子的可能性更大,我們最終就把這個白石頭歸類於哪一個罐子。

換句話講,我們可以把石頭的顏色表示為樣本的屬性特徵,而罐子的類別則表示為樣本所對應的標籤。(這種問題的轉化思維一定要引起重視)至此的話,我們就可以分別計算出來自於1、2號罐子的概率,哪一個更大,那麼就將該石頭歸類於那一個罐子。

我們不妨通過上述的條件概率公式來進行分析,條件概率重現如下:

\[P(A|B) = P(A)\frac{P(B|A)}{P(B)}
\]

對此,我們令事件A=「來自1號罐子」,事件B=「選中了白色石頭」。

則我們可以知道,因為2個罐子是完全一樣的,所以:

\[P(A) = \frac{1}{2}
\]

\(P(B|A)\)表示的是在1號罐子中選中白色石頭的概率,我們知道1號罐子中有四塊石頭,其中有兩塊是白色的,所以:

\[P(B|A) = \frac{2}{4}=\frac{1}{2}
\]

\(P(B)\)很簡單,就是在全局中選中白色石頭的概率,全局有7塊,其中白色石頭有3塊,所以:

\[P(B) = \frac{3}{7}
\]

綜上,我們就可以得到我們的條件概率結果,即在發現是白色的前提下,這塊石頭來自1號罐子的概率為:

\[\begin{aligned}
P(A|B) & = P(A)\frac{P(B|A)}{P(B)} \\
& = \frac{1}{2}\frac{\frac{1}{2}}{\frac{3}{7}}=\frac{7}{12}
\end{aligned}
\]

同理可知,假設事件C=「來自2號罐子」,我們可以計算出此時的條件概率:

\[\begin{aligned}
P(C|B) & = P(A)\frac{P(C|A)}{P(C)} \\
& = \frac{1}{2}\frac{\frac{1}{3}}{\frac{3}{7}}=\frac{7}{18}
\end{aligned}
\]

對於這道題來講,先驗概率\(P(A)=P(C)=\frac{1}{2}\),經過調整因子(可能性函數)處理之後得到的後驗概率\(P(A|B)=\frac{7}{12}\),而\(P(C|B)=\frac{7}{18}\)。也就是說在取出一個白石頭之前,事件A和事件C的可能性是相同的,而在取出一個白石頭之後,事件A的可能性得到了增強,而事件C的可能性得到了的減弱。且\(P(A|B) > P(C|B)\),為此,我們更情願將取到的這個白球歸類於1號罐子。

上面這段話,各位讀者一定要重點理解清楚,這個對於理解樸素貝葉斯的實際意義有着非常重要的作用。我們可以這樣說:在被檢測樣本的各個屬性已知的前提下,我們需要通過貝葉斯算法來計算該樣本分別為各類別的概率情況,以此來判斷該被檢測樣本的最終分類。

不知道各位讀者有沒有注意噢,對於上述問題,白球的歸類不是1號罐就是2號罐,按道理來講\(P(A|B)+P(C|B)=1\),然而我們發現,將計算出來的這兩個值相加之後並沒有等於1,這不是完全不講道理嘛,真的是討厭。。。

首先,值得肯定的是,存在這種疑問的讀者非常的棒,說明在閱讀的過程有認真的在思考。其實,有這疑問的讀者忽視了「域」的問題,我們所理解的\(P(A|B)+P(C|B)=1\)是在整體「域」當中,也就是7個石頭,而考慮條件概率的時候,「域」就已經發生了改變,此時的「域」就不再是一個整體了,而是被分割成了兩個子「域」,所以此時計算的兩個概率和並不會一定為1。(重點理解)

關於上述「域」的問題,為Taoye在學習過程中獨立思考所得,暫時沒有參考任何的權威資料,所以不能完全保證上述說法的正確性。如有問題,還請各位讀者不吝賜教,在下方留言或是私聊Taoye。

最後再提醒一句,將這道題的真正意義搞懂,對於理解貝葉斯算法真的尤為重要。


三、貝葉斯實戰之恰瓜群眾應該恰好瓜還是壞瓜

在本節內容中,我們主要用周志華老師的西瓜書上的一個例子來理解下貝葉斯的應用,隨後會通過代碼的形式來解決問題。總體上的過程與上述例子大致相同,主要在於讀者對於不同的問題要學會變通,要學會對問題的轉換。就像《周易》里說的那樣,窮則變,變則通,通則久。這一點還是挺重要的,尤其是對於我們學生來講。

下面我們開始進入到正題。

例子參考於:周志華-《機器學習》第四章

為了讓讀者在閱讀的過程中不是那麼的無趣,或是能更好的進入到這個案例,Taoye編個簡短的故事來作為引子吧。

注意:此「吃瓜群眾」就單純字面上的意思,而非網絡用語之「梗」。

從前有座山,山上有座廟,廟裡有位吃瓜群眾在吃瓜。(唱起來還挺順口的,hhhhhh)

同時,廟裏面也有成千上百的西瓜可供吃瓜群眾食用。一開始的時候,吃瓜群眾還是非常開心的,一口一個西瓜吞吞的下肚。但是這西瓜恰到一定數量的時候,他發現有的瓜是好瓜,而有的瓜是壞瓜,當時就困惑着:我滴乖乖,這壞瓜該不會是過期了吧???

那咋行呢?我要每次恰之前能夠挑選出壞瓜才行,至少說挑選出好瓜的概率要比壞瓜大才可以吧。為此,他收集了之前恰西瓜時候的一些屬性特徵以及對應標籤,以此來作為他判別好瓜還是壞瓜的依據。

吃瓜群眾收集到的西瓜數據如下所示:

這個數據樣本集總共有17個西瓜,其中好瓜有8個,壞瓜有9個。

這個吃瓜群眾的案例相較於前面罐子石頭的來講就複雜一點點,但也只是一點點而已。這裡涉及到了多個屬性特徵,而且除了標稱型數據之外,還有數值型數據,這些不同類型的數據我們需要怎麼處理呢???另外還有一點需要說的是,假如說我們的檢測西瓜中的特徵值在17個樣本裏面不存在,那麼這個時候又應該需要怎麼處理呢???

以上所提到的都是我們這節內容中所需要解決的問題。

  • 屬性問題的解決

在前面罐子石頭的案例中,我們的屬性特徵只有顏色一個,而在這個吃瓜群眾的案例裏面,屬性特徵卻有色澤、根蒂、敲聲、紋理、臍部、觸感、密度、含糖率8個。

通過前面幾篇手撕機器學習的文章來看,我們可以知道,當一個樣本數據中的屬性特徵有多個的時候,這個時候我們可以把這多個屬性特徵看做是一個整體,什麼整體呢???沒錯,就是一個特徵向量。

我們不妨將這裡的特徵向量表示為\(x=(色澤,根蒂,敲聲,紋理,臍部,觸感,密度,含糖率)\),而好瓜、壞瓜標籤表示為\(c\),則在已知一個被檢測西瓜樣本的屬性特徵的前提下,我們要來判斷這個瓜是好瓜還是壞瓜,則通過貝葉斯定理,我們有

\[P(c|x)=\frac{P(c)P(x|c)}{P(x)} \tag{3-1}
\]

通過上式,我們不不難發現,基於貝葉斯公式3-1來估計後驗概率\(P(c|x)\)的主要困難在於:類條件概率\(P(x|c)\)是所有屬性上的聯合概率,很難從有限的訓練樣本中直接估計而得。還有一點就是,這種形式的表示就相當於笛卡爾積,這個對計算也不是很友好。(關於笛卡爾積,讀者可自行了解,後期有機會的話Taoye也會來介紹)

為了避開上述這個問題,「樸素貝葉斯分類器」就採用了「屬性條件獨立性假設」:對已知類別,假設所有屬性相互獨立,互相不會產生任何的影響。這個時候,我們再來重新閱讀前面所說到的一句話: 我不管你,請你也不要管我,你過你的獨木橋,我走我的陽光道,單人做的事情都不會對他人造成任何的影響。

是不是獨立的理解有點感覺了???

基於屬性條件獨立性假設,式子3-1,我們可以重寫為:

\[P(c|x)=\frac{P(c)P(x|c)}{P(x)}=\frac{P(c)}{P(x)}\Pi_{i=1}^8P(x_i|c) \tag{3-2}
\]

而我們知道,對於一個檢測西瓜樣本來講,該樣本每一個屬性特徵值在每個樣本類別里計算得到的結果都是一樣的,比如說對於好瓜與壞瓜的判別來講,計算得到的\(P(x)\)都是相同的。換句話講,\(P(x)\)的計算結果並不會對不同標籤計算後驗概率結果產生任何的影響,也就是說,要想判別這個是好瓜還是壞瓜,我們沒有必要去計算\(P(x)\)的值,這是畫蛇添腳、多此一舉。對此,我們得到如下所示:

\[h_{nb}(x) = arg \ maxP(c)\Pi_{i=1}^8P(x_i|c) \tag{3-3}
\]

這也就是我們的樸素貝葉斯的表達式,表達的意思就是比較不同類別時候的\(P(c)\Pi_{i=1}^8P(x_i|c)\),值最大者所對應的標籤就是我們想要的分類結果。

不難吧???應該能看懂吧???也應該能理解吧???感覺解釋的已經很白話了 (ノへ ̄、)

  • 不同數據類型的處理方式

我們觀察數據,可以發現樣本的屬性特徵有兩類,一類是標稱型屬性數據:色澤、根蒂、敲聲、紋理、臍部、觸感,另一類是數值型數據:密度、含糖率。我們可以把標稱型數據理解成離散型的,而把數值型數據理解成連續型的,而在貝葉斯算法中,不同類型的數據會有不同的處理方式。

對於離散屬性來講,令\(D_{c,x_i}\)表示\(D_c\)中在第i屬性上取值為\(x_i\)的樣本組成的集合,則條件概率\(P(x_i|c)\)可以估計為:

\[P(x_i|c)=\frac{|D_{c,x_i}|}{|D_c|} \tag{3-4}
\]

換言之,就是頻率的一種計算。

而對於連續屬性來講,我們可以考慮概率密度函數,假定\(p(x_i|c)\)服從\(N(u_{c,i},\sigma_{c,i}^2)\),其中\(u_{c,i}\)\(\sigma_{c,i}^2\)分別是第c類樣本在第i個屬性上取值的均值和方差,則有:

\[p(x_i|c)=\frac{1}{\sqrt{2\pi}\sigma_{c,i}}exp(-\frac{(x_i-u_{c,i})^2}{2\sigma_{c,i}^2}) \tag{3-5}
\]

也就是說此時的\(p(x_i|c)\)就相當於把數據樣本集中所對應特徵的所有數值型數據服從高斯分佈,依次來計算\(p(x_i|c)\)的結果

ok,這兩個問題搞懂了之後,我們就可以來計算下吃瓜群眾所恰西瓜的好壞了

我們不妨假設此時恰瓜群眾此時拿到的一個西瓜所對應屬性特徵如下,我們通過貝葉斯來判斷該西瓜的好壞:

我們首先計算先驗概率\(P(c)\),由於總共有17個瓜,其中好瓜8個,壞瓜9個,所以有:

\[\begin{aligned}
& P(好瓜=是)=\frac{8}{17}=0.471 \\
& P(好瓜=否)=\frac{9}{17}=0.529
\end{aligned}
\]

之後,為每個屬性估計條件概率\(P(x_i|c)\)

\[\begin{aligned}
& P_{青綠|是}=P(色澤=青綠|好瓜=是)=\frac{3}{8}=0.375 \\
& P_{青綠|否}=P(色澤=青綠|好瓜=否)=\frac{3}{9}=0.333 \\
& P_{蜷縮|是}=P(根蒂=蜷縮|好瓜=是)=\frac{5}{8}=0.625 \\
& P_{蜷縮|否}=P(根蒂=蜷縮|好瓜=否)=\frac{3}{9}=0.333 \\
& P_{濁響|是}=P(敲聲=濁響|好瓜=是)=\frac{6}{8}=0.750 \\
& P_{濁響|否}=P(敲聲=濁響|好瓜=否)=\frac{4}{9}=0.444 \\
& P_{清晰|是}=P(紋理=清晰|好瓜=是)=\frac{7}{8}=0.875 \\
& P_{清晰|否}=P(紋理=清晰|好瓜=否)=\frac{2}{9}=0.222 \\
& P_{凹陷|是}=P(臍部=凹陷|好瓜=是)=\frac{6}{8}=0.625 \\
& P_{凹陷|否}=P(臍部=凹陷|好瓜=否)=\frac{2}{9}=0.222 \\
& P_{硬滑|是}=P(觸感=硬滑|好瓜=是)=\frac{6}{8}=0.750 \\
& P_{硬滑|否}=P(觸感=硬滑|好瓜=否)=\frac{6}{9}=0.667 \\
\end{aligned}
\]

\[\begin{aligned}
P_{密度:0.697|是} & =P(密度=0.697|好瓜=是) \\
& = \frac{1}{\sqrt{2\pi}0.129}exp(-\frac{(0.697-0.574)^2}{2*0.129^2}) \\
& = 1.962 \\
P_{密度:0.697|否} & =P(密度=0.697|好瓜=否) \\
& = \frac{1}{\sqrt{2\pi}0.195}exp(-\frac{(0.697-0.496)^2}{2*0.195^2}) \\
& = 1.194 \\
P_{含糖:0.460|是} & =P(含糖=0.460|好瓜=是) \\
& = \frac{1}{\sqrt{2\pi}0.101}exp(-\frac{(0.460-0.279)^2}{2*0.101^2}) \\
& = 0.669 \\
P_{含糖:0.460|否} & =P(含糖=0.460|好瓜=否) \\
& = \frac{1}{\sqrt{2\pi}0.108}exp(-\frac{(0.460-0.154)^2}{2*0.108^2}) \\
& = 0.42 \\
\end{aligned}
\]

這裡有必要說一點:在周志華西瓜書中\(P_{凹陷|是}\)計算結果是有錯誤的,實際結果應該是0.625,而非0.750,讀者可自行計算從而驗證

於是,我們可以計算得到該瓜是好瓜和壞瓜的可能性如下

\[\begin{aligned}
& P(好瓜=是)*P_{青綠|是}*P_{蜷縮|是}*P_{濁響|是}*P_{清晰|是} \\
& \quad \quad \quad \quad \quad \quad *P_{凹陷|是}*P_{硬滑|是}*P_{密度:0.697|是}*P_{含糖:0.460|是}=0.046 \\
& P(好瓜=否)*P_{青綠|否}*P_{蜷縮|否}*P_{濁響|否}*P_{清晰|否}\\
& \quad \quad \quad \quad \quad \quad*P_{凹陷|否}*P_{硬滑|否}*P_{密度:0.697|否}*P_{含糖:0.460|否}=4.36*10^{-5}
\end{aligned}
\]

由計算可以得到,\(0.046>4.36*10^{-5}\),所以我們理應將這個判別樣本歸類於「好瓜」

下面,我們不妨通過代碼來描述上述貝葉斯的判別過程。

首先,建立一個establish_data方法用於準備數據:

定義一個calc_label_countcalc_p_c方法,分別用於統計不同類別標籤的數量,以及計算各類別在數據樣本集中的頻率,即各類別的\(P(c)\)值:

程序運行結果如下,可見與我們前面手動計算的結果一致

根據上述貝葉斯分類的流程,還需定義一個calc_dispersed_p_xi_c方法以及calc_continuity_p_xi_c來分別計算\(P(x_i|c)\)的值,方法分別對應着離散型數據和連續型數據

不過有一點還需要說明的是,在計算連續型數據的\(P(x_i,c)\)的時候,我們還應該提前得知數據的均值以及方差,為此還需定義一個calc_mean_standard方法來實現這個功能,該三個核心方法的具體代碼如下(都挺簡單的):

運算結果如下圖所示:

可以看到,此時的貝葉斯算法判斷該瓜為好瓜,與我們實際的標籤一致,說明預測正確。當然了,這個代碼只是預測了一個西瓜樣本,讀者可自行根據程序代碼預測多個樣本西瓜,從而判斷該貝葉斯的正確率。

完整代碼:

import numpy as np

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 用於生成樣本的屬性特徵以及對應的標籤
    Return:
        x_data: 數據樣本的屬性,其中包括8個屬性
        y_label: 樣本屬性所對應的標籤
"""
def establish_data():
    x_data = [[1, 1, 1, 1, 1, 1, 0.697, 0.460],
             [2, 1, 2, 1, 1, 1, 0.774, 0.376],
             [2, 1, 1, 1, 1, 1, 0.634, 0.264],
             [1, 1, 2, 1, 1, 1, 0.608, 0.318],
             [3, 1, 1, 1, 1, 1, 0.556, 0.215],
             [1, 2, 1, 1, 2, 2, 0.403, 0.237],
             [2, 2, 1, 2, 2, 2, 0.481, 0.149],
             [2, 2, 1, 1, 2, 1, 0.437, 0.211],
             [2, 2, 2, 2, 2, 1, 0.666, 0.091],
             [1, 3, 3, 1, 3, 2, 0.243, 0.267],
             [3, 3, 3, 3, 3, 1, 0.245, 0.057],
             [3, 1, 1, 3, 3, 2, 0.343, 0.099],
             [1, 2, 1, 2, 1, 1, 0.639, 0.161],
             [3, 2, 2, 2, 1, 1, 0.657, 0.198],
             [2, 2, 1, 1, 2, 2, 0.360, 0.370],
             [3, 1, 1, 3, 3, 1, 0.593, 0.042],
             [1, 1, 2, 2, 2, 1, 0.719, 0.103]]
    y_label = [0, 0, 0, 0, 0, 0, 0, 0,
              1, 1, 1, 1, 1, 1, 1, 1, 1]
    return np.array(x_data), np.array(y_label)

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 用於統計不同標籤的樣本數量
    Parameters:
        y_label: 樣本屬性所對應的標籤
    Return:
        label_count: 不同樣本標籤的數量
"""
def calc_label_count(y_label):
    label_count_0, label_count_1 = 0, 0; data_number = y_label.shape[0]
    for label in y_label:    # 遍歷y_label,統計不同類別的數量
        if int(label) == 0: label_count_0 += 1
        if int(label) == 1: label_count_1 += 1
    return label_count_0, label_count_1

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 用於計算各類別在數據樣本集中的頻率,即各類別的$P(c)$值:
    Parameters:
        y_label: 樣本屬性所對應的標籤
    Return:
        pc: 指定對應標籤的頻率值
"""
def calc_p_c(y_label):
    data_number = y_label.shape[0]
    label_count_0, label_count_1 = calc_label_count(y_label)
    return label_count_0 / data_number, label_count_1 / data_number

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 用於計算各類別在數據樣本集中的頻率,即各類別的$P(c)$值,主要用於標稱型數據
    Parameters:
        y_label: 樣本屬性所對應的標籤
    Return:
        pc: 指定對應標籤的頻率值
"""
def calc_dispersed_p_xi_c(test_data, x_data, y_label, attribute_index):
    label_count_0, label_count_1  = calc_label_count(y_label)
    attribute_count_0, attribute_count_1 = 0, 0
    for item in x_data[:label_count_0]:
        if test_data[attribute_index] == item[attribute_index]:
            attribute_count_0 += 1
    for item in x_data[label_count_0:]:
        if test_data[attribute_index] == item[attribute_index]:
            attribute_count_1 += 1
    return attribute_count_0 / label_count_0, attribute_count_1 / label_count_1

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 用於計算均值和標準差
"""
def calc_mean_standard(x_data):
    mean_value_0, mean_value_1 = np.mean(x_data[:8, 6:8], axis = 0), np.mean(x_data[8:, 6:8], axis = 0)
    std_value_0, std_value_1 = np.std(x_data[:8, 6:8], axis = 0), np.std(x_data[8:, 6:8], axis = 0)
    return mean_value_0, mean_value_1, std_value_0, std_value_1

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 將數據進行高斯轉化
"""
def calc_gaussian(data, mean_value, std_value):
    return (1 / (np.sqrt(2*np.pi) * std_value)) * (np.e ** ((- (data - mean_value) ** 2) / (2 * (std_value) ** 2)))

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain: 計算數值型數據的p_xi_c
"""
def calc_continuity_p_xi_c(test_data, x_data):
    mean_value_0, mean_value_1, std_value_0, std_value_1 = calc_mean_standard(x_data)  
    pxi_density_0 = calc_gaussian(test_data[6], mean_value_0[0], std_value_0[0])
    pxi_density_1 = calc_gaussian(test_data[6], mean_value_1[0], std_value_1[0])
    pxi_sugar_0 = calc_gaussian(test_data[7], mean_value_0[1], std_value_0[1])
    pxi_sugar_1 = calc_gaussian(test_data[7], mean_value_1[1], std_value_1[1])
    return pxi_density_0, pxi_density_1, pxi_sugar_0, pxi_sugar_1

if __name__ == "__main__":
    test_data = [1, 1, 1, 1, 1, 1, 0.697, 0.460]
    x_data, y_label = establish_data()
    attr0 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 0)
    attr1 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 1)
    attr2 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 2)
    attr3 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 3)
    attr4 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 4)
    attr5 = calc_dispersed_p_xi_c(test_data, x_data, y_label, 5)
    print("標稱型數據的P_{(x_i,c)}:", attr0, attr1, attr2, attr3, attr4, attr5)
    pxi_density_0, pxi_density_1, pxi_sugar_0, pxi_sugar_1 = calc_continuity_p_xi_c(test_data, x_data)
    print("數值型數據的P_{(x_i,c)}:", pxi_density_0, pxi_density_1, pxi_sugar_0, pxi_sugar_1)
    p1, p2 = calc_p_c(y_label)
    print("數據樣集中的各類別的概率情況分別為:", p1, p2)
    p_good_melon = p1 * attr0[0] * attr1[0] * attr2[0] * attr3[0] * attr4[0] * attr5[0] * pxi_density_0 * pxi_sugar_0
    p_bad_melon = p2 * attr0[1] * attr1[1] * attr2[1] * attr3[1] * attr4[1] * attr5[1] * pxi_density_1 * pxi_sugar_1
    print("分類為好瓜和壞瓜的可能性分別為:", p_good_melon, p_bad_melon)
    print("恰瓜群眾拿到這個是好瓜") if p_good_melon >= p_bad_melon else print("恰瓜群眾拿到這個是好瓜")

在本節開始的時候,我們提出了三個問題,其中已經已經解決了兩個,現在我們來解決最後一個問題。

  • 假如說我們的檢測西瓜中的特徵值在17個樣本裏面都不存在,那麼這個時候又應該需要怎麼處理呢???

就是說,若某個屬性值在訓練集中沒有與某個類同時出現,則我們根據前面所提到的樸素貝葉斯進行判別將會出現問題。例如,在使用西瓜數據集訓練樸素貝葉斯的時候,對一個「敲聲=清脆」的測試樣例,有:

\[P_{清脆|是}=P(敲聲=清脆|好瓜=是)=\frac{0}{8}=0
\]

由連乘式計算出的概率值為0,因此,無論該樣本的其他屬性特徵是什麼,哪怕在其他屬性上明顯是好瓜,分類的結果都是「好瓜=否」,這個顯然不是很合理。

為了避免其他屬性攜帶的信息被訓練集中未出現的屬性值「抹去」,在估計概率值時通常要進行「平滑」操作,常用的是「拉普拉斯修正」。具體來說,令N表示訓練集D中可能的類別數,\(N_i\)表示第i個屬性可能的取值數,則此時的式子3-4和3-5分別修正為:

\[\hat{P}(c) = \frac{|D_c| + 1}{|D| + N} \\
\hat{P}(x_i|c)=\frac{|D_{c,x_i}|+1}{|D_c|+N_i}
\]

例如,在本節例子中,類先驗概率可估計為:

\[\hat{P}(好瓜=是)=\frac{8+1}{17+2},\hat{P}(好瓜=否)=\frac{9+1}{17+2}=0.526
\]

這個拉普拉斯修正沒什麼難點,主要是處理單個屬性在數據樣本中不存在的問題,讀者可根據拉普拉斯修正的方式來將上述完整代碼進行改進。

關於貝葉斯算法,其實後面還有些內容,限於篇幅原因,我們留着後面有機會再來肝。

如果這篇文章對您有所幫助,點個贊、分享下吧~~~

這篇文章就不嘮嗑了。

我是Taoye,愛專研,愛分享,熱衷於各種技術,學習之餘喜歡下象棋、聽音樂、聊動漫,希望藉此一畝三分地記錄自己的成長過程以及生活點滴,也希望能結實更多志同道合的圈內朋友,更多內容歡迎來訪微信公主號:玩世不恭的Coder。

我們下期再見,拜拜~~~

參考資料:

[1] 《機器學習實戰》:Peter Harrington 人民郵電出版社
[2] 《統計學習方法》:李航 第二版 清華大學出版社
[3] 《機器學習》:周志華 清華大學出版社
[4] 人教版高中數學B版選修2-3
[5] 貝葉斯推斷及其互聯網應用://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.html

推薦閱讀

《Machine Learning in Action》—— 女同學問Taoye,KNN應該怎麼玩才能通關
《Machine Learning in Action》—— 懂的都懂,不懂的也能懂。非線性支持向量機
《Machine Learning in Action》—— hao朋友,快來玩啊,決策樹呦
《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什麼「鬼」
《Machine Learning in Action》—— 剖析支持向量機,優化SMO
《Machine Learning in Action》—— 剖析支持向量機,單手狂撕線性SVM
print( “Hello,NumPy!” )
幹啥啥不行,吃飯第一名
Taoye滲透到一家黑平台總部,背後的真相細思極恐
《大話數據庫》-SQL語句執行時,底層究竟做了什麼小動作?