貝葉斯分類器本不是一個復雜的東西,但是博主在網上幾翻查找,並未找到有哪一篇博文將其寫得易懂。硬着頭皮去看書《模式分類》,而書上公式一大堆,實在讓人頭疼。幾番痛苦的學習下,終於明白其中原理。現寫出此文,獻給各位同志。如果大家覺得這文章寫得還不錯,日后我可以將此文的pdf共享給大家。
這篇博文總共有4節內容,如果你對貝葉斯分類已經熟悉,只想看看它在圖像分類中的應用,請直接跳到第4節。
1、概率論的一些基礎知識
考慮到一些小伙伴的概率論的知識有點忘了,這是有必要貼心地幫助大家簡單地回顧一下一些基本的概率論的知識。
-
我們記一個事件\(A\)發的概率為\(P(A)\),那么它是\(0 \le P(A) \le 1\)。
-
我們記事件\((A,\ B)\)發生的概率為\(P(A,\ B)\),有時也記為\(P(AB)\),這表示\(A, \ B\)同時發生的概率;或者記成\(P(A\cap B)\),這幾種寫法是等價的(如果你覺得不對,可以在下面留言討論,歡迎拍磚😃)。
-
我們記事件A或者B發生的概率為\(P(A\cup B)\),這里需要注意一下的是,如果\(A,\ B\)不是相互獨立的,那么\(P(A\cup B)\ne P(A)+P(B)\)。
說明:\(A, \ B\)獨立的意思是:它們兩個互不相干,就像"博主長得帥"跟“你一看就有錢"這倆件事沒有任何關系一樣。
- 如果\(A,\ B,\ C\)是相互獨立的,那么有\(P(ABC)=P(A)P(B)P(C)\)。這可推廣到\(n\)種情況,如果事件\(A_1,\ A_2,\cdots,\ A_n\)是相互獨立的,有如下關系(這個公式我們下面會用到,記住哦😎)
- 條件概率: 已知事件\(B\)發生的情況下,事件\(A\)發生的概率記為\(P(A|B)\)
注:條件概率也是一個概率,它只是不同於前的記號,所以概率有的性質,條件概率也有。比如,已知事件\(C\)發生的情況下,\(A, \ B\) 發生的概率記為\(P((A,B)|C)\),如果事件\(A,\ B\)是相互獨立的,那么有\(P((A,B)|C)=P(A|C)P(B|C)\)。
- 高斯分布,或者叫作
正態分布
:假設隨機變量\(X\)的均值(更專業一點,稱之為期望值)和方差分別為\((\mu,\ \sigma^2)\),且服從高斯分布,那么它的概率密度為
好了,我們需要回憶的基本的概率論的知識點就是這么多了。
2、貝葉斯公式
在前面的條件概率中我們回憶到,已知事件\(B\)發生的情況下,事件\(A\)發生的概率為
根據這個公式,我們可以得一個概率的乘法公式
如果我們想求\(P(B|A)\)怎么辦呢,同樣利用條件概率和乘法公式,有如下推論:
式(2-1)就是我們大名鼎鼎的貝葉斯公式
了,這來得非常容易,並不困難😉。
接下來看看在這個貝葉斯問題是如何應用要我們的分類問題中的。我們現在有\(n\)個類別
,記為\(c_1,c_2,\cdots,c_n\),每個類別都有\(m\)個特征
\(x_1.x_2,\cdots,x_m\),我們可以把這些特征記為\(\boldsymbol{x}=(x_1.x_2,\cdots,x_m)\),就是把這些特征用一個向量\(\boldsymbol{x}\)來表示。分類問題可以描述為:已知特征\(\boldsymbol{x}=(x_1.x_2,\cdots,x_m)\)的情況下,判斷出它是屬於哪個類別\(c_i\)。很明顯,這里要求\(c_i \in (c_1,c_2,\cdots,c_n)\)。
說明:為了更好地描述上面的分類問題,多哆嗦幾句,我在這里舉個例子。假設我們現在有一群人,我們要把他們分為3個類:老人(\(c_1\))、年輕人(\(c_2\))、小孩(\(c_3\));用這樣4個特征來描述出這些人:身高(\(x_1\))、體重(\(x_2\))、力量(\(x_3\))、是否有白頭發(\(x_4\))。需要做的分類問題就是,如果已知一個人身高180cm、體重160kg、力量200kg、無白發,即已知\(\boldsymbol{x}=({x_1=180, \ x_2=160,\ x_3 = 200, \ x_4 = 0})\),求\(P(c_1|\boldsymbol{x}),\ P(c_2|\boldsymbol{x}),\ P(c_3|\boldsymbol{x})\)。很明顯,求出哪個概率大,我們就可以判斷出這個人是老人,年輕人還是小孩。
據式(2-1)我們可以將上述問題描述為如下貝葉斯公式:
其中\(\boldsymbol{x}\)就是我們上面所講特征
的向量記法,表示為\(\boldsymbol{x}=(x_1.x_2,\cdots,x_m)\),其中\(m\)表示特征個數。為了顯示專業性,我們還得給上面的幾個概率取幾個聽起來很厲害的名字。\(P(c_i|\boldsymbol{x})\)稱為后驗概率(posterior),\(P(\boldsymbol{x}|c_{i})\)稱為似然概率(likelihood),\(P(c_i)\)稱為先驗概率(prior),\(P(\boldsymbol{x})\)稱為證據因子(evidence)。嗯,我們把它們記下來,以后聽到別的大佬討論的時候,或許還可以弱弱地插上幾句話,我們不裝X,我們只是知識的搬運工😜。
接下來,我們的工作就是要求解出上面的式子,其中\(P(c_i)\)和\(P(\boldsymbol{x})\),並不難求(為什么不難求,我們記着,后面再來解釋),而求\(P(\boldsymbol{x}|c_i)\)是件比較麻煩的事情。在這里面,為了簡化它的計算,我們假設特征向量\(\boldsymbol{x}\)中的所有特征是相互獨立的。如果你還記得式(1-1)的話,我們可得到
這樣的話,我們只需計算出已知為類別\(c_i\)的情況下,每一個特征\(x_k\)的條件概率,然后將它們乘起來。式(2-3)也是我們常說的朴素貝葉斯(Naive Bayes,NB)了,為何朴素,因為簡單!!。至於\(P(x_k|c_i)\)如何求,這成了我們分類問題的關鍵,這也是我們后面要繼續探討。去泡杯茶,我們繼續💪!!
3、貝葉斯的兩個簡單的例子
為了說明如何利用上面的貝葉斯公式,有必要舉2個典型的例子,應用我們的貝葉斯公式實戰一下,這樣才有點成就感。
3.1 據天氣情況,我們是否出去玩
如下是一張根據天氣情況是否出去玩的表格,據這張表,判斷當有如下天氣情況時,我們是否出去玩。現在提出這樣一個問題,當天氣狀況為:<Outlook = sunny, Temperature = cool, Humidity = high, Wind = strong>,我們應出去玩嗎?。
Day | Outlook | Temperature | Humidity | Wind | Play? |
---|---|---|---|---|---|
D1 | Sunny | Hot | High | Weak | No |
D2 | Sunny | Hot | High | Strong | No |
D3 | Overcast | Hot | High | Weak | Yes |
D4 | Rain | Mild | High | Weak | Yes |
D5 | Rain | Cool | Normal | Weak | Yes |
D6 | Rain | Cool | Normal | Strong | No |
D7 | Overcast | Cool | Normal | Strong | Yes |
D8 | Sunny | Mild | High | Weak | No |
D9 | Sunny | Cool | Normal | Weak | Yes |
D10 | Rain | Mild | Normal | Weak | Yes |
D11 | Sunny | Mild | Normal | Strong | Yes |
D12 | Overcast | Mild | High | Strong | Yes |
D13 | Overcast | Hot | Normal | Weak | Yes |
D14 | Rain | Mild | High | Strong | No |
根據第2節討論的貝葉斯,可以將上面的問題抽象成貝葉斯問題,列出如下表3.1。將各種天氣的情況寫成一個特征向量;是否出去玩,僅有兩類,可以記為:\(c_1=\text{Yes},\ c_2=\text{No}\)。
Day | Outlook | Temperature | Humidity | Wind | Play? |
---|---|---|---|---|---|
特征:\(\boldsymbol{x}\) | \(x_1\) | \(x_2\) | \(x_3\) | \(x_4\) | 類別:\(\boldsymbol{c}=(c_1,\ c_2)\) |
D1 | Sunny | Hot | High | Weak | No |
D2 | Sunny | Hot | High | Strong | No |
D3 | Overcast | Hot | High | Weak | Yes |
D4 | Rain | Mild | High | Weak | Yes |
D5 | Rain | Cool | Normal | Weak | Yes |
D6 | Rain | Cool | Normal | Strong | No |
D7 | Overcast | Cool | Normal | Strong | Yes |
D8 | Sunny | Mild | High | Weak | No |
D9 | Sunny | Cool | Normal | Weak | Yes |
D10 | Rain | Mild | Normal | Weak | Yes |
D11 | Sunny | Mild | Normal | Strong | Yes |
D12 | Overcast | Mild | High | Strong | Yes |
D13 | Overcast | Hot | Normal | Weak | Yes |
D14 | Rain | Mild | High | Strong | No |
因此,我們需要求解的問題是兩個概率問題:
前面我提到過\(P(c_i)\)的計算很簡單,但是沒有給出理由,在這個例子里面,我們可以先計算\(P(c_1)\)和\(P(c_2)\),看看我是否騙了你?
注:看到了吧, 就是這么容易,我們從上面的表中,數出Yes的個數,然后除以總的情況,這就是“出去玩的概率(Play)”,求No的概率同樣是如此,不必我廢話了🤐。
我們再求\(P(\boldsymbol{x})=P(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong})\)。同樣我在前面也說過\(P(\boldsymbol{x})\)也不難求,但是這個是比上面的難一點點。首先我們作出第一個假設:各個天氣特征是相互獨立的,因此我們有
那么,\(P(\text{sunny})\)又如何求呢,查看上面的表3.1,我們知道Outlook總共才3種天氣情況。天晴(Sunny)出現的次數為5次,因此有
同理,我們得到其他概率為\(P(\text{cool})=\dfrac{4}{14},\quad P(\text{high})=\dfrac{7}{14}, \quad P(\text{strong})=\dfrac{9}{14}\)。
好了,我們只剩下最后\(P(\boldsymbol{x}|c_{1}),\ P(\boldsymbol{x}|c_{2})\)概率需要求了。我們先求\(P(\boldsymbol{x}|c_1)\),從數學的字面上理解它:已知\(c_1\)發生的情況下,\(\boldsymbol{x}\)發生的概率,這里,把它翻譯成人話就是:我們想要去玩(\(c_1=\text{Yes}\)),而天氣情況為\(x_1=\text{sunny},x_2=\text{cool},x_3=\text{high},x_4=\text{strong}\),這時候我們出去玩的可能性多大呢(概率多大)?我們得用到前面的假設了:\(x_1.x_2.x_3,x_4\)是相互獨立的。也就是說我們認為,天氣晴不晴,溫度低不低,溫度高不高…,這些因素都是相互獨立(你肯定會覺得,溫度跟天氣晴不晴肯定相關,但為了簡單起見,就得認為它們不相關,否則你可能就算不出來了)。據這個假設,我們有
接下來,我們把已知為\(c_1=\text{Yes}\)的情況表列出來,如下表,共有9條出去玩的記錄。
Day | Outlook | Temperature | Humidity | Wind | Play? |
---|---|---|---|---|---|
特征:\(\boldsymbol{x}\) | \(x_1\) | \(x_2\) | \(x_3\) | \(x_4\) | 類別:\(\boldsymbol{c}=(c_1)\) |
D3 | Overcast | Hot | High | Weak | Yes |
D4 | Rain | Mild | High | Weak | Yes |
D5 | Rain | Cool | Normal | Weak | Yes |
D7 | Overcast | Cool | Normal | Strong | Yes |
D9 | Sunny | Cool | Normal | Weak | Yes |
D10 | Rain | Mild | Normal | Weak | Yes |
D11 | Sunny | Mild | Normal | Strong | Yes |
D12 | Overcast | Mild | High | Strong | Yes |
D13 | Overcast | Hot | Normal | Weak | Yes |
這時候,計算式(3-4)就很簡單了,就是數數,這個我們應該很拿手吧。據數數的結果,我們計算出式(3-4)的概率如下:
式(3-1)里的該求的終於都求完了,接下來把它計算出來如下:
\(P(c_2|\boldsymbol{x})\)可以根據\(P(c_1|\boldsymbol{x})\)的求法求出來,當然,我們也有\(P(c_2|\boldsymbol{x})=1-P(c_1|\boldsymbol{x})=0.516\),所以有\(P(c_2|\boldsymbol{x})>P(c_1|\boldsymbol{x})\),這個時候我們應該選擇不出去玩。
注:建議你可以試一試利用求\(P(c_1|\boldsymbol{x})\)的方法來求一下\(P(c_2|\boldsymbol{x})\),紙上得來終覺淺,絕知此事要躬行,動動手,印象會更加深刻的。
我們在這里,思考一個問題,我們是不是不需將具體的\(P(c_1|\boldsymbol{x}),\ P(c_2|\boldsymbol{x})\)計算出來,因為我們只需比較它們的大小,就決定哪個選擇。我們回過頭來看看式(3-1)和(3-2)
相信你不難看出,它們倆分母是相同的,所以我們只需要計算出哪個分子大,我們就選哪個。因此我們可以把它記成如下形式
其中\(C_{NB}\)為我們最終的決策。更一般地,我們可以將上式推廣一下,假設現在有\(m\)個類\(c=\{c_1,c_2,\cdots,c_m\}\),這些類有\(n\)個特征\(\boldsymbol{x}=\{x_1,x_2,\cdots,x_n\}\),因此朴素貝葉斯分類器(Naïve Bayes Classifier)可以記為如下公式
注:通過上面的例子,我們推論出了朴素貝葉斯分類器的公式,是不是有點小成就感😎,但是不急,這里面還有不少問題需要我們去解決,且下一個例子。
3.2 這是一朵什么樣的花
我們現在有一些鳶尾花的數據,其數據如下表3.3所示,共有15條數據,它有4個屬性,萼片長(septal length)、萼片寬(sepal width)、花瓣長(petal length)、花瓣寬(petal width),它共有3個種類,分別標號為0、1、2(對應實際的類名為:'setosa' 、'versicolor'、 'virginica'),每個類別有5條件數據。為了方便說明,我們還是像上面一樣,將這些花的特征(屬性)記為\(\boldsymbol{x}=(x_1,x_2,x_3,x_4)\),類別記為\(\boldsymbol{c}=(c_1,c_2,c_3)\)。
注:你有可能會問,'setosa' 、'versicolor'、 'virginica'這都是些什么花,額…,我也不知道,但這並不影響我們做下面的分類問題,它們具體的介紹可以參考IRIS數據集介紹。這個數據集共有150條數據,為了把我們的問題簡化,我只取了其中15條。
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | class_no | |
---|---|---|---|---|---|
特征:x | x_1 | x_2 | x_3 | x_4 | 類別:c |
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | 0 |
4 | 5.0 | 3.6 | 1.4 | 0.2 | 0 |
5 | 7.0 | 3.2 | 4.7 | 1.4 | 1 |
6 | 6.4 | 3.2 | 4.5 | 1.5 | 1 |
7 | 6.9 | 3.1 | 4.9 | 1.5 | 1 |
8 | 5.5 | 2.3 | 4.0 | 1.3 | 1 |
9 | 6.5 | 2.8 | 4.6 | 1.5 | 1 |
10 | 6.3 | 3.3 | 6.0 | 2.5 | 2 |
11 | 5.8 | 2.7 | 5.1 | 1.9 | 2 |
12 | 7.1 | 3.0 | 5.9 | 2.1 | 2 |
13 | 6.3 | 2.9 | 5.6 | 1.8 | 2 |
14 | 6.5 | 3.0 | 5.8 | 2.2 | 2 |
接下來,我提出一個分類問題,我們有下面這樣一組數據,需要判斷它是屬於哪種鳶尾花。
sepal length (cm) 5.4
sepal width (cm) 3.7
petal length (cm) 1.5
petal width (cm) 0.2
這個問題我覺得肯定難不倒你,因為我們在上面的3.1小節中詳細地講述了這類問題如何用貝葉斯求解,按照上面的方法我們需要求出如下條件概率
同樣有在上面的3.1小節中說過,我們只需要比較這幾個概率的大小,因此無需計算出\(P(\boldsymbol{x})\),\(P(c_1),P(c_2),P(c_3)\)的計算相信也難不倒機智的我們,因為每個類有5條數據,因此有
到這里一切都OK,因為\(P(c_1)=P(c_2)=P(c_3)\),所以我們只需計算出上面的似然
概率。即,我們需要分別計算\(P(\boldsymbol{x}|c_1),\ P(\boldsymbol{x}|c_2),\ P(\boldsymbol{x}|c_3)\),然后比較它們的大小,即可判斷出這條數據是屬於哪種類別的鳶尾花。好,接下我們就按此思路,我們同樣認為這是一個朴素貝葉斯問題,即花的各個屬性不相關。我們先計算第一個概率
你可以去試一試,努力把它計算出來……停在此處,拿起你的紙和筆開始計算吧……,一分鍾……5分鍾……1小時……過去了,你可能還是沒有算出來,也許你在第一分鍾的時候就放棄了。因為你發現上面式子中的第一個概率\(P(x_1=5.4|c_1)\)你就沒法算,其中\(x_1=5.4\)根本沒有在表3.3中出現過,可能你會覺得,沒有出現過,那么它的概率不就是0?但是你覺得這樣子合理嗎?\(x_1\)它是一個連續的值,假如我們的\(x_1=5.099\),你也會覺得\(P(x_1=5.099|c_1)=0\ ?\),它只和\(x_1=5.1\)相差了\(0.001\),你就說它為0,這樣是不是有點不公平。這就是我們這個例子與上面3.1小節的例子不同的地方,在上面的3.1小節中,天氣狀況的屬性只有那幾個確定的值(在數學上,稱之為離散
的),而在本節的例子中,這些屬性都是一個連續
的值。
在此,我們碰了問題,那我們該怎么解決?我們可以去請教一下大佬——高斯(Gauss),高斯大佬說,你可以將\(P(x_1|c_1)\)的概率分布看成一個高斯分布(也叫正態分布,參考我們第一小節中的高斯分布),高斯分布有2個參數\((\mu,\sigma^2)\),分別叫均值和方差。Okay,我們就聽高斯大佬的,求出\(P(x_1|c_1)\)的均值和方差。我們將類別0的數據單獨拿出來,如下表3.4所示。
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | class_no | |
---|---|---|---|---|---|
特征:x | x_1 | x_2 | x_3 | x_4 | 類別:c |
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | 0 |
4 | 5.0 | 3.6 | 1.4 | 0.2 | 0 |
根據表3.4,求出\(P(x_1|c_1)\)的均值\(\mu = 4.86\),方差\(\sigma ^2= 0.21^2\),因此我們可以認為\(P(x_1|c_1)\)的概率分布為
注:學過概率論的同學有可能會發現這里存一個問題,我們平常所說的高斯分布是一個
概率密度
分布,但在這里,我們卻把它當成了概率分布
。其實這是有一定的合理性的,在此我也不必深究了,要是繼續討論下去,博主都可以出書了。把它當一個概率分布的好處是我們可以直接將已知的值代入上面式子,得到概率值,而不必像概率密度那樣需要求積分。
我們有了上面的概率分布,求\(P(x_1=5.4|c_1)\)那就是小意思了
同理,我們可以計算其它幾個概率\(P(x_2=3.7|c_1)=0.41,\ P(x_3=1.5|c_1)=2.08,\ P(x_4=0.2|c_1)=39.89\),最后我們再計算出\(P(x_1=5.4|c_1)P(x_2=3.7|c_1)P(x_3=1.5|c_1)P(x_4=0.2|c_1)=3.41\)
關於\(P(\boldsymbol{x}|c_2),\ P(\boldsymbol{x}|c_3),\ P(\boldsymbol{x}|c_4)\)計算都是同樣的道理,在此不贅述了。為了方便大家的計算,我將它們的均值和方差列出如下表3.5
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | class_no | |
---|---|---|---|---|---|
mean | 4.860000 | 3.280000 | 1.400000 | 0.200000 | 0 |
std | 0.207364 | 0.258844 | 0.070711 | 0.000000 | 0 |
mean | 6.460000 | 2.920000 | 4.540000 | 1.440000 | 1 |
std | 0.594138 | 0.383406 | 0.336155 | 0.089443 | 1 |
mean | 6.400000 | 2.980000 | 5.680000 | 2.100000 | 2 |
std | 0.469042 | 0.216795 | 0.356371 | 0.273861 | 2 |
最終我們計算出
注:上面的結果,是我把它們歸一化后的結果,即將每個求出的值除以這3個值的和,這樣可以得到它們真實的概率值。在表3.5中還存在一個問題,class_no=0的標准差(方差的平方根)是0,這是不能被除的,因此在計算的時候,我們需要將它變成一個比較小的數,如0.01。
因此我們認為有下如下屬性的花
sepal length (cm) 5.4
sepal width (cm) 3.7
petal length (cm) 1.5
petal width (cm) 0.2
它屬於類別0,即setosa(山尾鳶),我查看它的數據標簽,它確實是setosa(你們可能沒有數據,查不到它的標簽,這里我告訴你就行了,我不會騙你們的),而且我們計算出為類別0的概率值幾乎為1,所以它非常可能是類別0。
我在這里將表3.5的高斯概率分布畫出來,直觀感受一下它們的分布,如圖1.1所示。

圖1.1 鳶尾花各屬性的高斯概率分布
從圖中明顯可以看出來,"petal length"屬性的分布間隔很大,基本上靠這個屬性就能判斷出類別,再加上其它幾個屬性共同判別,可以更加准確地分類出來。
4、貝葉斯之圖像分類
在這節,我們來用Python
語言實現一個貝葉斯圖像分類器。在這個Python大火的時候,不會點Python,都不好意思說自已在搞大數據的。
注:這一節里面有較多的代碼,大部分代碼我在下面基本上都已經寫出來了,但為敘述的流暢性,並未全部寫出,如果大家有需要,可以在下面留言,我會將代碼以百度雲的方式共享給大家
4.1 准備工作
工欲善其事,必先利其器。我們先准備好我們的工作環境:jupyter
+python3.6
,這是我目前用的環境,如果大家沒有用過jupyter
,我建議大家用一下,相信你會愛上它的。關於jupyter的安裝和下載以及使用,我在這里就不說了,聰明的你自會百度或google。其次,我們再准備一下數據集:CIFAR-10圖像數據,我將其放入了我的百度網盤,鏈接: https://pan.baidu.com/s/1yIkiL7xXHsqlXS53gxMkEg 提取碼: wcc4。原始的CIFAR-10圖像是一個用於普世物體識別的數據集,分為airplane、automobile、bird、cat、deer、dog、frog、horse、ship、truck共10類,但是這里為了簡單起見,我用了其中5類。
讀取數據
將數據下載好后,把它放在當前目錄的data
文件夾中。利用如下代碼,讀取數據,並查看一下數據的信息。一般來講,數據都是一個字典類型。
from scipy.io import loadmat
import numpy as np
train_data_mat = loadmat("./data/train_data.mat")
test_data_mat = loadmat("./data/test_data.mat")
labels_name_mat = loadmat("./data/labels_name.mat")
經過上面的步驟,我們讀取到了數據,分為訓練集
,測試集
和標簽名字
。在jupyter的cell
輸入我們的數據變量,可以查一下它信息,讓們知道如何進行一下處理。
train_data_mat
{'__header__': b'MATLAB 5.0 MAT-file, Platform: PCWIN64, Created on: Fri Nov 02 10:09:15 2018',
'__version__': '1.0',
'__globals__': [],
'Data': array([[170, 168, 177, ..., 82, 78, 80],
[159, 150, 153, ..., 14, 17, 19],
[ 50, 51, 42, ..., 166, 166, 162],
...,
[195, 151, 160, ..., 133, 135, 133],
[137, 139, 141, ..., 24, 24, 19],
[177, 174, 182, ..., 119, 127, 136]], dtype=uint8),
'Label': array([[1],
[1],
[1],
...,
[5],
[5],
[5]], dtype=uint8)}
從訓練集輸出的信息來看,它的標簽是從1~5,即有5個類,我們看到了在"Data"里面有一堆數據,但是不知道它的結構,而且這都是一些字典類型的數據,我們需從這些字典中把數據提取出然后看看它們的結構。
# 訓練數據和標簽
train_data = train_data_mat["Data"]
train_data_label = train_data_mat["Label"]
# 測試數據和標簽
test_data = test_data_mat["Data"]
test_data_label = test_data_mat["Label"]
# 標答的實際名字
label_names = labels_name_mat["label_names"]
# 因為原始的標簽名字有誤,我這里把它手動改一下
label_names[:,0]=['automobile','bird','cat','deer','dog']
然后利用shape
屬性查看一下它們大概的數據結構
print(train_data.shape, train_data_label.shape,test_data.shape, test_data_label.shape)
(9968, 3072) (9968, 1) (5000, 3072) (5000, 1)
從上面的的輸出可以看出來,它共有9968個訓練集,5000個測試集,每一個樣本的特征個數為\(3072 = 32 \times 32 \times 3\),這是把一個\(32\times32\)的RGB圖像平鋪成了一個行向量(就是把一個3維數組變成一個1維數組)
從開始的類別中我們知道,這個數據共有5個類,下面我們來看看訓練集中,每個類分別有多少樣本數量。
注:下面代碼中用到了一些Python的庫,如果你運行的時候報錯了,那么你需要先安裝這些包,安裝的方法,還是請你自行百度或google哦。
import pandas as pd
pd.value_counts(train_data_label[:,0])
2 2042
3 2011
4 2009
1 1981
5 1925
dtype: int64
從上面輸出可以看出,這個數據集的中每個類的數量不是相同的。
顯示圖像
下面我選幾幅圖,把測試集和訓練集中的圖片顯示出來給大家看看,直觀感受一下這些數據圖片是不是有點難以分辨。

圖4.1 訓練集部分圖像

圖4.2 測試集部分圖像
看了上面的圖片,不知道你是什么感受。反正我是覺得,有的圖片,人都很難分辨出來。
4.2 開始分類
回顧我們上面所提到的訓練數據集,每一個樣本的特征個數為\(3072 = 32 \times 32 \times 3\),因此它有3072個特征,可以記為\(\boldsymbol{x}=(x_1,x_2,\cdots,x_{3072})\);且數據集分成5個類\(\boldsymbol{c}=(c_1,c_2,c_3,c_4,c_5)\),分別對應'automobile','bird','cat','deer','dog'。因此我們需求出分布
其中\(\mu_{ij}\)表示第\(i\)類的第\(j\)個屬性的均值,\(\sigma_{ij}^2\)表示第\(i\)類的第\(j\)個屬性的方差。因為朴素貝葉斯,各特征間,相互獨立,那么由式(4-1)有
上面計算有一定的復雜性,大量的乘法運算,為了簡化此運算,我們可以將式(4-2)兩邊同時取自然對數
(\(\ln{x},\ \log{x}\))。因為對數運算是一個單調遞增的,所以並不會改變我們對\(P({\boldsymbol{x}}|{c_i}),\ i=1,2,3,4,5\)大小的判定。對式(4-2)兩邊同時取對數,可以得到如下結果
式(4-3)中的常數項同樣不影響我們的判決,所以可去將其去掉,最終我們得到如下一個結果
嗯,看着清爽多了,而且計算量看着也不是很大,我們將式(4-4)稱為我們的判決式
。
注:使用式(4-4)的前的提是\(P(\boldsymbol{x}|c_i)\)是相等的。
根據上面我們辛辛苦苦推導出來的判別式來寫我們的代碼。首先,我將我們的訓練集數據組織成pandas
的DataFrame
數據的格式,這樣子更方便我們后面的操作。
import pandas as pd
col_name_lst = [0]*3072
for i in range(1, 3073):
col_name_lst[i-1] = "x" + str(i)
train_data = pd.DataFrame(train_data,columns=col_name_lst)
train_data_label = pd.DataFrame(train_data_label, columns=['class_no'])
train_dataFrm = train_data.join(train_data_label)
train_data.head()#查看前5行數據
x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | ... | x3067 | x3068 | x3069 | x3070 | x3071 | x3072 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 170 | 168 | 177 | 183 | 181 | 177 | 181 | 184 | 189 | 189 | ... | 83 | 79 | 78 | 82 | 78 | 80 |
1 | 159 | 150 | 153 | 154 | 138 | 184 | 154 | 77 | 61 | 64 | ... | 13 | 16 | 14 | 14 | 17 | 19 |
2 | 50 | 51 | 42 | 62 | 100 | 66 | 29 | 20 | 18 | 11 | ... | 165 | 167 | 165 | 166 | 166 | 162 |
3 | 139 | 144 | 146 | 148 | 145 | 138 | 139 | 136 | 177 | 195 | ... | 60 | 61 | 66 | 80 | 75 | 59 |
4 | 54 | 59 | 56 | 49 | 41 | 50 | 55 | 42 | 38 | 35 | ... | 119 | 110 | 102 | 111 | 106 | 101 |
我們同樣假設每個特征(即每個像素點服從正態分布),因此據式(4-4),我們最關鍵的是要將均值
和方差
求出來。我同樣把均值和方差做成了DataFrame數據的格式。
train_data_mean = pd.DataFrame(dtype=np.float16)
train_data_std = pd.DataFrame(dtype=np.float16)
for i in range(0,5):
mean_temp = train_data[train_dataFrm["class_no"]==i+1].mean()
std_temp = train_data[train_dataFrm["class_no"]==i+1].std()
train_data_mean = train_data_mean.append([mean_temp], ignore_index=True)
train_data_std = train_data_std.append([std_temp], ignore_index=True)
我們在測試集中,隨機取1條數據計算每個類別下的似然概率
(即,式4-3)值。
some_img_index = 10
ftr_data = test_data[some_img_index]
determine_clf = [0]*5
prior_series = pd.value_counts(train_data_label.iloc[:,0])
for i in range(0,5):
ftr_mean = train_data_mean.iloc[i]
ftr_std = train_data_std.iloc[i]
prob_temp = -(np.log(ftr_std)+0.5*((ftr_data-ftr_mean)/ftr_std)**2).sum()
prob_temp = (prob_temp + (-3072*np.log(2*np.pi)/2))*prior_series[i+1]
determine_clf[i]=prob_temp
# 輸出各個類下的似然概率值,下面是歸一化后的結果
print((determine_clf-min(determine_clf))/(max(determine_clf)-min(determine_clf)))
# 輸出測試集的標簽值
print(test_data_label[some_img_index])
[1. 0.35530925 0.50774247 0. 0.86720707]
[1]
從輸出的結果可以看出,最大值在第1個數,因此它最有可能是類別1
,而且輸出的標簽值正好是1,說明我們預測准確了。你可以將上面代碼中的some_img_index
換個值,看看是否還是准確的。
最后一步,計算出所有的測試集的預測值,並與測試集的標簽對比,得出我們所得的模型在測試集上的准確率。
# 算出測試集中的每一條記錄的類別,作出預測
prior_series = pd.value_counts(train_data_label.iloc[:,0])
# 存放預測的標簽值
pred_label = [0]*test_data.shape[0]
for img_index in range(0,test_data.shape[0]):
determine_clf = [0]*5
ftr_data = test_data[img_index]
for i in range(0,5):
ftr_mean = train_data_mean.iloc[i]
ftr_std = train_data_std.iloc[i]
prob_temp = -(np.log(ftr_std)+0.5*((ftr_data-ftr_mean)/ftr_std)**2).sum()
prob_temp = (prob_temp + (-3072*np.log(2*np.pi)/2))#這一行注釋也不會影響結果
determine_clf[i]=prob_temp
pred_label[img_index] = determine_clf.index(max(determine_clf)) + 1
# 統計預測准確率
sum(pred_label == test_data_label[:,0])/test_data.shape[0]
0.4028
上面輸出的准確率為0.4028
,可以看到,這並不高,因為圖像本身也很模糊。
注:此時,你可會覺得,在圖像分類中,貝葉斯的表現並不如人意,相對於
卷積神經網絡來
講,它確實不如,但是我們有提高其准確率的方法,利用LDA
(Linear Discriminant Analysis,線性判別分析)。這里我也不對它多作討論了,這文章寫得有點長了,相信你也看累了,我也累了。
4.3 使用Scikit-Learn的貝葉斯庫
上面我們自己動手用貝葉斯算出來分類准確率並不高,下面我們來看看Scikit-Learn
的庫的貝葉斯函數模型准確率如何。
擬合數據
from sklearn.naive_bayes import GaussianNB
nbbayes_clf = GaussianNB()
#擬合數據
nbbayes_clf.fit(train_data, train_data_label)
利用Predict函數進行預測
print("==Predict result by predict==")
pred_label1 = nbbayes_clf.predict(test_data)
print(pred_label1)
# print "==Predict result by predict_proba=="
# print(clf.predict_proba([[-0.8, -1]]))
# print "==Predict result by predict_log_proba=="
# print(clf.predict_log_proba([[-0.8, -1]]))
sum(pred_label1 == test_data_label[:,0])/test_data.shape[0]
==Predict result by predict==
[3 1 1 ... 3 5 5]
0.403
其輸出的准確率為0.403
,跟我們的結果非常接近。
總結
在理解貝葉斯之前,我本着朴素理解貝葉斯的初心,然后開始了這篇文章,所以我回憶概率論的一些基本知識,接着順便推了一個貝葉斯公式,然后又舉了兩個貝葉斯的例子,最后還把貝葉斯在圖像分類中應用了一下,寫到最后,我才發現我已經忘了初心,實在是對不起大家,文章寫得有點長了,但是自認為已經把貝葉斯這個東西說得很明白了,希望能給大家帶來一些幫助。
最后感謝一下我的女朋友,要不是20多年來她從未出現,我也不會在此雙11之季,奮筆疾書,寫下此篇博客,供諸君品讀。日后若有他人問君貝葉斯分類,請君甩出此文,侃侃而談,而后佛袖離去,深藏功與名。