樸素貝葉斯(Naive Bayers)算法是一種基于概率統(tǒng)計(jì)的分類方法懂衩。它在條件獨(dú)立假設(shè)的基礎(chǔ)上脯倒,使用貝葉斯定理構(gòu)建算法撮躁,在文本處理領(lǐng)域有廣泛的應(yīng)用漱病。
1.樸素貝葉斯算法原理
樸素貝葉斯算法,需要從貝葉斯定理說起把曼,它是一個(gè)條件概率公式杨帽。
1.貝葉斯定理
先來看一個(gè)案例。某警察使用一個(gè)假冒偽劣的呼吸測試儀來測試司機(jī)是否醉駕嗤军。假設(shè)這個(gè)儀器有5%的概率會(huì)把一個(gè)正常的司機(jī)判斷為醉駕注盈,但對真正醉駕的司機(jī)其測試結(jié)果是100%準(zhǔn)確的。從過往的統(tǒng)計(jì)得知叙赚,大概有0.1%的司機(jī)為醉駕老客。假設(shè)該警察隨機(jī)攔下一個(gè)司機(jī),讓他做呼吸測試震叮,儀器測試結(jié)果為醉駕胧砰。僅憑這一結(jié)果判斷,這位司機(jī)真的是醉駕的概率有多高苇瓣?
90%尉间?50%?真實(shí)的結(jié)果是不到2%钓简。如果我們沒有通過其他方法(如聞司機(jī)身上的酒味)乌妒,僅憑這個(gè)儀器的測試結(jié)果來判斷,其實(shí)準(zhǔn)確性是非常低的外邓。
假設(shè)我們的樣本里有1000人撤蚊,根據(jù)過往的統(tǒng)計(jì)數(shù)據(jù),這1000位司機(jī)里有0.1%的概率為真正醉駕损话,即有1位是真正醉駕的司機(jī)侦啸,999位是正常的槽唾。這1000位司機(jī)均拿這個(gè)劣質(zhì)呼吸測試儀來測試,則有多少人會(huì)被判斷為醉駕光涂?對這位真正醉駕的司機(jī)庞萍,他無法蒙混過關(guān),而對999位正常的司機(jī)忘闻,有5%的概率會(huì)被誤判钝计,所以總共有1+999*5%=51個(gè)司機(jī)會(huì)被儀器判斷為醉駕。由此可知齐佳,所有被判斷為醉駕的司機(jī)里私恬,真正醉駕的概率是1/(1+51)=1.96%。
實(shí)際上炼吴,貝葉斯定理是計(jì)算這類條件概率問題的絕佳方法本鸣。我們記P(A|B)表示觀察到的事件B發(fā)生時(shí)事件A發(fā)生的概率,則貝葉斯定理的數(shù)學(xué)表達(dá)式為:
回到醉駕的例子硅蹦,我們記事件A為司機(jī)真正醉駕荣德,事件B為儀器顯示司機(jī)醉駕。則例子里要求解的問題即為P(A|B)童芹,即觀察到儀器顯示司機(jī)醉駕(事件B發(fā)生)時(shí)涮瞻,司機(jī)真正醉駕(事件A發(fā)生)的概率是多少。P(A)表示司機(jī)真正醉駕的概率辐脖,這是先驗(yàn)概率饲宛,這里是0.1%皆愉。P(B|A)表示當(dāng)司機(jī)真正醉駕時(shí)(事件A發(fā)生)嗜价,儀器顯示司機(jī)醉駕(事件B發(fā)生)的概率是多少,這里是100%幕庐。P(B)表示儀器顯示司機(jī)醉駕的概率久锥,包含兩部分?jǐn)?shù)據(jù),針對真正醉駕的司機(jī)(0.1%)异剥,儀器能100%檢測出來瑟由,因?yàn)檫@部分的數(shù)值為0.1% x 100%;針對正常的司機(jī)(1-0.1%)冤寿,儀器顯示醉駕的概率為(1-0.1%) x 5%歹苦。代入貝葉斯定理公式得:
2.樸素貝葉斯分類法
假設(shè)有一個(gè)已經(jīng)標(biāo)記的數(shù)據(jù)集,其中
督怜,即數(shù)據(jù)集總共有b個(gè)類別殴瘦;
,即總共有n個(gè)輸入特征号杠。針對一個(gè)新的樣本x蚪腋,我們需要預(yù)測y的值丰歌,即對x進(jìn)行分類。這是個(gè)典型的機(jī)器學(xué)習(xí)里的分類問題屉凯。
對我們要求解的問題立帖,使用統(tǒng)計(jì)學(xué)的語言可以描述為:當(dāng)觀察到輸入樣本是x時(shí),其所屬于的類別的概率悠砚,使用條件概率公式表示為:
其中晓勇,,我們只需要分別求出所有b個(gè)類別的概率灌旧,然后取概率最大的那個(gè)
即是x所屬的類別宵蕉。直接求解上述公式比較困難,可以應(yīng)用貝葉斯定理進(jìn)行一次變換:
對于一個(gè)確定的數(shù)據(jù)集节榜,羡玛,
都是固定的值。因此:
其中宗苍,表示成正比的意思稼稿。因此,我們只需要求解讳窟,針對不同的
的情況下让歼,
的最大值即可知道,x屬于哪個(gè)類別丽啡。根據(jù)聯(lián)合概率公式谋右,可得:
因?yàn)閤是n維向量,即补箍,可得
根據(jù)鏈?zhǔn)椒▌t及條件概率的定義改执,可進(jìn)一步推導(dǎo)公式:
上述推導(dǎo)里,只用了貝葉斯定理坑雅,我們還要再前面加上“樸素”二字辈挂。樸素指的是條件獨(dú)立假設(shè),即事件之間沒有關(guān)聯(lián)關(guān)系裹粤。例如终蒂,擲一個(gè)質(zhì)地均勻的骰子兩次,前后之間出現(xiàn)的數(shù)字是獨(dú)立遥诉、不相關(guān)的拇泣,我們稱這兩個(gè)事件時(shí)條件獨(dú)立的。樸素貝葉斯算法的前提是矮锈,輸入特征需要滿足條件假設(shè)霉翔。即當(dāng)時(shí),
和
是不相關(guān)的愕难,通俗地說就是
事件是否發(fā)生和
沒關(guān)系早龟。根據(jù)條件獨(dú)立的原則:
有了這個(gè)公式惫霸,我們就可以簡化為:
這樣我們的最終推導(dǎo)結(jié)果就是:
其中,是連乘符號(hào)葱弟。
表示每種類別出現(xiàn)的概率壹店,這個(gè)值很容易從數(shù)據(jù)集里統(tǒng)計(jì)出來。
表示當(dāng)類別為
時(shí)芝加,特征
出現(xiàn)的概率硅卢,這個(gè)值也可以從數(shù)據(jù)集中統(tǒng)計(jì)出來。
這就是樸素貝葉斯分類算法的數(shù)學(xué)原理藏杖。
2.一個(gè)簡單的例子
我們先通過一個(gè)簡單的例子将塑,來看怎樣應(yīng)用樸素貝葉斯分類算法。假設(shè)有以下關(guān)于駕齡蝌麸、平均車速和性別的統(tǒng)計(jì)數(shù)據(jù):
現(xiàn)在觀察到一個(gè)駕齡為2年的人点寥,平均車速為80。問:這個(gè)人的性別是什么来吩?
假設(shè)表示女敢辩,
表示男,
表示駕齡弟疆,
表示平均車速戚长。我們先來計(jì)算這個(gè)人為女性的概率相對值。根據(jù)統(tǒng)計(jì)數(shù)據(jù)怠苔,女性司機(jī)的概率
同廉。駕齡為2年的女性司機(jī)的概率即
。平均車速為80的女性司機(jī)的概率
柑司。根據(jù)樸素貝葉斯分類算法的數(shù)學(xué)公式:
接著計(jì)算這個(gè)人為男性的概率相對值迫肖。根據(jù)統(tǒng)計(jì)數(shù)據(jù),不難得出男性司機(jī)的概率帜羊。駕齡為2年的男性司機(jī)的概率
咒程。平均車速為80的男性司機(jī)的概率
鸠天。根據(jù)樸素貝葉斯分類算法的數(shù)學(xué)公式:
從相對概率來看讼育,這個(gè)人是男性的概率是女性的概率的6倍,據(jù)此判斷這個(gè)人是男性稠集。我們也可以從相對概率中算出絕對概率奶段,即這個(gè)人是男性的絕對概率是0.12/(0.12+0.02)=0.857。
3.概率分布
到目前為止剥纷,我們介紹的樸素貝葉斯分類算法是根據(jù)數(shù)據(jù)集里的數(shù)據(jù)痹籍,計(jì)算出絕對概率來進(jìn)行求解。再看一遍樸素貝葉斯分類算法的數(shù)學(xué)公式:
其中晦鞋,表示在類別
里特征
出現(xiàn)的概率蹲缠。這里有個(gè)最大的問題棺克,如果數(shù)據(jù)集太小,那么從數(shù)據(jù)集里計(jì)算出來的概率偏差將非常嚴(yán)重线定。例如娜谊,觀察一個(gè)質(zhì)地均勻的骰子投擲6次的結(jié)果是[1,3,1,5,3,3]。質(zhì)地均勻的骰子每個(gè)點(diǎn)出現(xiàn)的概率都是1/6斤讥,如果根據(jù)觀察到的數(shù)據(jù)集去計(jì)算每個(gè)點(diǎn)的概率纱皆,和真實(shí)的概率相差將是非常大的。
怎么解決這個(gè)問題呢芭商?答案是使用概率分布來計(jì)算概率派草,而不是從數(shù)據(jù)集里計(jì)算概率。
1.概率統(tǒng)計(jì)的基本概念
人的身高是一個(gè)連續(xù)的隨機(jī)變量铛楣,而投擲一個(gè)骰子得到的點(diǎn)數(shù)則是一個(gè)離散的隨機(jī)變量近迁。我們閉著眼睛隨便找一個(gè)人,問這個(gè)人的身高是170cm的可能性是多大呢簸州?如果有一個(gè)函數(shù)钳踊,能描述人類身高的可能性,那么直接把170cm代入即可求出這個(gè)可能性勿侯。這個(gè)函數(shù)就是概率密度函數(shù)拓瞪,也稱為PDF(Probability Density Function)。典型的概率密度函數(shù)是高斯分布函數(shù)助琐,如人類的身高就滿足高斯分布的規(guī)律祭埂。
再例如,投擲一個(gè)質(zhì)地均勻的骰子兵钮,得到6的概率是多少呢蛆橡?大家都知道答案是1/6。假如有一個(gè)函數(shù)f(x)掘譬,能描述骰子出現(xiàn)x點(diǎn)數(shù)()的概率泰演,那么把x代入即可得到概率,這個(gè)函數(shù)稱為概率質(zhì)量函數(shù)葱轩,即PMF(Probability Mass Function)睦焕。那么,為什么還有使用概率質(zhì)量函數(shù)呢靴拱?一是在數(shù)學(xué)上追求統(tǒng)一性垃喊,二是并不是所有的離散隨機(jī)變量的概率分布都像擲一次骰子這個(gè)直觀。例如袜炕,投擲6次質(zhì)地均勻的骰子本谜,得到4個(gè)4的概率是多少?這個(gè)時(shí)候如果有概率質(zhì)量函數(shù)偎窘,就可以輕松求解了乌助。
總結(jié)一下溜在,隨機(jī)變量分成兩種,一種是連續(xù)隨機(jī)變量他托,另外一種是離散隨機(jī)變量炕泳。概率密度函數(shù)描述的是連續(xù)隨機(jī)變量在某個(gè)特定值的可能性,概率質(zhì)量函數(shù)描述的是離散隨機(jī)變量在某個(gè)特定值的可能性上祈。而概率分布則是描述隨機(jī)變量取值的概率規(guī)律培遵。
2.多項(xiàng)式分布
拋一枚硬幣,要么出現(xiàn)正面登刺,要么出現(xiàn)反面(假設(shè)硬幣不會(huì)立起來)籽腕。假如出現(xiàn)正面的概率是p,則出現(xiàn)反面的概率就是1-p纸俭。符合這個(gè)規(guī)律的概率分布皇耗,稱為伯努利分布(Bernoulli Distribution)。其概率質(zhì)量函數(shù)為:
其中揍很,郎楼,p是出現(xiàn)1的概率。例如窒悔,一枚質(zhì)地均勻的硬幣被拋一次呜袁,得到正面的概率為0.5。我們代入上述公式简珠,也可以得到相同的結(jié)果阶界,即f(1;0.5)=0.5。
更一般的情況聋庵,即不止兩種可能性時(shí)膘融,假設(shè)每種可能性是,則滿足
條件的概率分布祭玉,稱為類別分布(Categorical Distribution)氧映。例如,投擲一枚骰子脱货,則會(huì)出現(xiàn)6中可能性岛都,所有的可能性加起來的概率為1。類別分布的概率質(zhì)量函數(shù)為:
其中蹭劈,是連乘符號(hào)疗绣,
是類別的數(shù)量,
是第
種類別的概率铺韧,
當(dāng)且僅當(dāng)類別
為類別
,其值為1缓淹,其他情況其值為0哈打。例如塔逃,對于質(zhì)地均勻的骰子,
的值為6料仗,
的值都為1/6湾盗。問:投擲這個(gè)骰子得到3的概率是多少?答案是1/6立轧。我們代入概率質(zhì)量函數(shù)驗(yàn)算一下:
格粪,針對所有
的情況,
氛改,針對
的情況帐萎,
,所以容易算出
胜卤。
那么疆导,一枚質(zhì)地均勻的硬幣被拋10次,出現(xiàn)3次正面的概率是多少呢葛躏?這是個(gè)典型的二項(xiàng)式分布問題澈段。二項(xiàng)式分布指的是把符號(hào)伯努利分布的實(shí)驗(yàn)做了n次,結(jié)果1出現(xiàn)0次舰攒、1次败富、2次……n次的概率分別是多少,它的概率質(zhì)量函數(shù)為:
其中摩窃,是結(jié)果1出現(xiàn)的次數(shù)囤耳,
,
是實(shí)驗(yàn)的總次數(shù)偶芍。怎么理解這個(gè)公式呢充择,我們總共進(jìn)行了
次實(shí)驗(yàn),那么出現(xiàn)
次結(jié)果1的概率為
匪蟀,剩下的必定是結(jié)果0的次數(shù)椎麦,即出現(xiàn)了
次,其概率是
材彪。公式前面的系數(shù)
表示的是組合观挎,即
次實(shí)驗(yàn)中出現(xiàn)
次1的情況有多少種《位回到最初的問題:一枚質(zhì)地均勻的硬幣被拋10次嘁捷,出現(xiàn)3次正面的概率是多少?代入二項(xiàng)式分布的概率質(zhì)量函數(shù)显熏,得到:
其中雄嚣,0的階乘為1,即0!=1。結(jié)果跟我們預(yù)期的相符缓升。當(dāng)實(shí)驗(yàn)只做一次時(shí)鼓鲁,二項(xiàng)式分布退化為伯努利分布。
多項(xiàng)式分布是指滿足類別分布的實(shí)驗(yàn)港谊,連續(xù)做n次后骇吭,每種類別出現(xiàn)的特定次數(shù)組合的概率分布情況。假設(shè)歧寺,表示類別
出現(xiàn)的次數(shù)燥狰,
表示類別
在單次實(shí)驗(yàn)中出現(xiàn)的概率。當(dāng)滿足前提條件
時(shí)斜筐,由隨機(jī)變量
構(gòu)成的隨機(jī)向量
滿足以下分布函數(shù):
其中龙致,P是由各個(gè)類別的概率構(gòu)成的向量,即奴艾,k表示類別的總數(shù)净当,n表示實(shí)驗(yàn)進(jìn)行的總次數(shù)。理解這個(gè)公式也比較簡單蕴潦,可以把
理解為按照特定順序像啼,所有類別出現(xiàn)的某個(gè)特定次數(shù)組合的概率,如投擲6次骰子潭苞,出現(xiàn)(1,2,3,4,5,6)這樣特定順序組合的概率忽冻。前面的系數(shù)表示組合的個(gè)數(shù),如投擲6次骰子此疹,每個(gè)點(diǎn)數(shù)都出現(xiàn)一次僧诚,可以是(1,2,3,4,5,6),也可以是(1,3,2,4,5,6)蝗碎。
我們看一個(gè)例子湖笨,同時(shí)投擲6個(gè)質(zhì)地均勻的骰子,出現(xiàn)(1,2,3,4,5,6)這種組合的概率是多少蹦骑?我們可以把這個(gè)問題轉(zhuǎn)換成連續(xù)6次投擲質(zhì)地均勻的骰子慈省。這是個(gè)典型的多項(xiàng)式分布問題,其中隨機(jī)向量眠菇,代入多項(xiàng)式分布的概率質(zhì)量函數(shù)可得:
那么边败,將質(zhì)地均勻的骰子投擲6次,得到4個(gè)4的概率是多少捎废?我們需要把這個(gè)問題轉(zhuǎn)換為二項(xiàng)分布問題笑窜。投擲1次骰子時(shí),得到4的概率是1/6登疗,得到其他點(diǎn)數(shù)(非4)的概率是5/6∨沤兀現(xiàn)在需要就算投擲6次骰子得到4個(gè)4的概率,代入二項(xiàng)式分布的概率質(zhì)量函數(shù)可得:
我們再來算一下同時(shí)投擲6個(gè)質(zhì)地均勻的骰子,出現(xiàn)5個(gè)1的概率是多少匾寝?還是轉(zhuǎn)換為二項(xiàng)式分布問題:
簡單總結(jié)一下搬葬,二項(xiàng)式分布描述的是多次伯努利實(shí)驗(yàn)中荷腊,某個(gè)結(jié)果出現(xiàn)次數(shù)的概率艳悔。多項(xiàng)式分布描述的是多次進(jìn)行滿足類別分布的實(shí)驗(yàn)中,所有類別出現(xiàn)的次數(shù)組合的分布女仰。
二項(xiàng)式分布和多項(xiàng)式分布結(jié)合樸素貝葉斯算法猜年,經(jīng)常被用來實(shí)現(xiàn)文章分類算法。例如疾忍,有一個(gè)論壇需要對用戶的評(píng)論進(jìn)行過濾乔外,屏蔽不文明的評(píng)論。首先要有一個(gè)經(jīng)過標(biāo)記的數(shù)據(jù)集一罩,我們稱為語料庫杨幼。假設(shè)使用人工標(biāo)記的方法對評(píng)論進(jìn)行標(biāo)記,1表示不文明的評(píng)論聂渊,0表示正常評(píng)論差购。
假設(shè)我們的詞庫大小為k,則評(píng)論中出現(xiàn)某個(gè)詞可以看成是一次滿足k個(gè)類別的類別分布實(shí)驗(yàn)汉嗽。我們知道欲逃,一篇評(píng)論是由n個(gè)詞組成的刹泄,因此一篇評(píng)論可以看出是進(jìn)行n次類別分布實(shí)驗(yàn)后的產(chǎn)物骚灸。由此得知,一篇評(píng)論服從多項(xiàng)式分布梳虽,它是詞庫里的所有詞語出現(xiàn)的次數(shù)組合構(gòu)成的隨機(jī)向量弓叛。一般情況下彰居,詞庫比較大,評(píng)論只是由少量詞組成撰筷,所以這個(gè)隨機(jī)向量是很稀疏的陈惰,即大部分元素為0。通過分析預(yù)料庫闭专,我們?nèi)菀捉y(tǒng)計(jì)出每個(gè)詞出現(xiàn)在不文明評(píng)論及正常評(píng)論的概率奴潘,即的值。同時(shí)針對待預(yù)測的評(píng)論影钉,我們可以統(tǒng)計(jì)詞庫里的所有詞在這篇評(píng)論里出現(xiàn)的次數(shù)即
的值画髓,及評(píng)論的詞語個(gè)數(shù)。代入多項(xiàng)式分布的概率質(zhì)量函數(shù):
我們可以求出平委,待預(yù)測評(píng)論構(gòu)成的隨機(jī)向量奈虾,其為不文明評(píng)論的相對概率。同理也可以求出其為正常評(píng)論的相對概率。通過比較兩個(gè)相對概率肉微,就可以對這篇評(píng)論輸出一個(gè)預(yù)測值匾鸥。當(dāng)然,實(shí)際應(yīng)用中碉纳,涉及大量的自然語言處理的手段勿负,包括中文分詞技術(shù)、詞的數(shù)學(xué)表示等劳曹,這里不再展開奴愉。
3.高斯分布
在前面的車速和性別預(yù)測的例子里,對于平均車速铁孵,給出的是離散值锭硼,實(shí)際上它是一個(gè)連續(xù)值。這個(gè)時(shí)候怎么使用貝葉斯算法來處理呢蜕劝?答案是檀头,可以用區(qū)間把連續(xù)值轉(zhuǎn)換成離散值。例如岖沛,我們可以把平均車速[0,40]作為一個(gè)級(jí)別暑始,[40-80],等等烫止。這樣就可以把連續(xù)值變成離散值蒋荚,從而使用貝葉斯算法進(jìn)行處理。另外一個(gè)方法馆蠕,是使用連續(xù)隨機(jī)變量的概率密度函數(shù)期升,把數(shù)值轉(zhuǎn)換為一個(gè)相對概率。高斯分布就是這樣一種方法互躬。
高斯分布(Gaussian Distribution)也稱為正態(tài)分布(Normal Distribution)播赁,是最常見的一種分布。例如人的身高滿足高斯分布吼渡,特別高和特別矮的人出現(xiàn)的相對概率都很低容为,大部分人身高都處在中間水平。還有人的智商也符合高斯分布寺酪,特別聰明的天才和特別笨的人出現(xiàn)的相對概率都很低坎背,大部分人的智力都差不多。高斯分布的概率密度函數(shù)為:
其中寄雀,為隨機(jī)變量的值得滤,
為隨機(jī)變量的相對概率,
為樣本的平均值盒犹,其決定了高斯分布曲線的位置懂更,
為標(biāo)準(zhǔn)差眨业,其決定了高斯分布的幅度,
值越大沮协,分布越分散龄捡,
值越小,分布越集中慷暂。典型的高斯分布如下圖所示:
這里需要注意的是:高斯分布的概率密度函數(shù)和支持向量機(jī)里的高斯核函數(shù)的區(qū)別聘殖。二者的核心數(shù)學(xué)模型是相同的,但是目的不同呜呐。
4.連續(xù)值的處理
假設(shè)就斤,有一組身體特征的統(tǒng)計(jì)數(shù)據(jù)如下:
假設(shè)某人身高6英尺悍募,體重130榜蘑辑,腳掌8英寸,請問此人的性別是什么坠宴?
根據(jù)樸素貝葉斯公式:
針對待預(yù)測的這個(gè)人的數(shù)據(jù)洋魂,我們只需要分別求出男性和女性的相對概率:
然后取相對概率較高的性別為預(yù)測值即可。這里的困難在于喜鼓,所有的特征都是連續(xù)變量副砍,無法根據(jù)統(tǒng)計(jì)數(shù)據(jù)計(jì)算概率。當(dāng)然庄岖,這里我們可以使用區(qū)間法豁翎,把連續(xù)變量變?yōu)殡x散變量,然后再計(jì)算概率隅忿。但由于數(shù)據(jù)量較小心剥,這顯然不是一個(gè)好辦法。由于人類身高背桐、體重优烧、腳掌尺寸滿足高斯分布,因此更好的辦法是使用高斯分布的概率密度函數(shù)來求相對概率链峭。
首先畦娄,針對男性和女性,分別求出特征的平均值和方差:
接著利用高斯分布的概率密度函數(shù)弊仪,來求解男性身高為6英尺的相對概率:
這里的關(guān)鍵是把連續(xù)值(身高)作為輸入熙卡,通過高斯分布的概率密度函數(shù)的處理,直接轉(zhuǎn)換為相對概率励饵。注意這里是相對概率驳癌,所以其值大于1并未違反概率論規(guī)則。
使用相同的方法曲横,可以算出以下數(shù)值:
由于喂柒,因此這個(gè)人是男性的相對概率為:
使用相同的方法不瓶,可以算出這個(gè)人為女性的相對概率為。這個(gè)人為女性的概率比男性的概率大灾杰,因此判定這個(gè)人為女性蚊丐。
5.示例:文檔分類
在scikit-learn里,樸素貝葉斯算法在sklearn.naive_bayes包里實(shí)現(xiàn)艳吠,包含本文介紹的幾種典型的概率分布算法麦备。其中GaussianNB實(shí)現(xiàn)了高斯分布的樸素貝葉斯算法,MultinomialNB實(shí)現(xiàn)了多項(xiàng)式分布的樸素貝葉斯算法昭娩,BernoulliNB實(shí)現(xiàn)了伯努利分布的樸素貝葉斯算法凛篙。樸素貝葉斯算法在自然語言處理領(lǐng)域有著廣泛的應(yīng)用,這里我們使用MultinomialNB來實(shí)現(xiàn)文檔的自動(dòng)分類栏渺。
1.獲取數(shù)據(jù)集
這里使用的數(shù)據(jù)集來自mlcomp.org上的20news-18828呛梆,可以直接訪問http://mlcomp.org/datasets/379下載。載完數(shù)據(jù)集后磕诊,解壓到datasets/mlcomp/目錄下填物,解壓后會(huì)在datasets/mlcomp下生成一個(gè)名為379的目錄,其目錄下包含3個(gè)子目錄和一個(gè)名為metadata的介紹文件:
$ cd ~/code/datasets/mlcomp
$ ls 379
metadata raw test train
我們將使用train子目錄下的文檔進(jìn)行模型訓(xùn)練霎终,然后使用test子目錄下的文檔進(jìn)行模型測試滞磺。train子目錄下包含20個(gè)子目錄,每個(gè)子目錄代表一種文檔的類型莱褒,子目錄下的所有文檔都是屬于目錄名稱所標(biāo)識(shí)的文檔類型击困。可以隨意瀏覽數(shù)據(jù)集广凸,以便對數(shù)據(jù)集有一個(gè)感性的認(rèn)識(shí)阅茶。例如,datasets/mlcomp/379/train/rec.autos/6652-103421是一個(gè)討論汽車主題的帖子:
Hahahahahaha. gasp pant Hm, I’m not sure whether the above was just a silly
remark or a serious remark. But in case there are some misconceptions,
I think Henry Robertson hasn’t updated his data file on Korea since…mid 1970s.
Owning a car in Korea is no longer a luxury. Most middle class people
in Korea can afford a car and do have at least one car. The problem in Korea,
especially in Seoul, is that there are just so many privately-owned cars,
as well as taxis and buses, the rush-hour has become a 24 hour phenomenon
and that there is no place to park. Last time I heard, back in January,
the Kim Administration wanted to legislate a law requireing a potential
car owner to provide his or her own parking area, just like they do in Japan.
Also, Henry would be glad to know that Hyundai isn’t the only car manufacturer
in Korea. Daewoo has always manufactured cars and I believe Kia is back in
business as well. Imported cars, such as Mercury Sable are becoming quite
popular as well, though they are still quite expensive.
Finally, please ignore Henry’s posting about Korean politics and bureaucracy.
He’s quite uninformed.
2.文檔的數(shù)學(xué)表達(dá)
怎樣把一個(gè)文檔表達(dá)為計(jì)算機(jī)可以理解并處理的信息炮障,是自然語言處理中的一個(gè)重要課題目派,完整內(nèi)容可以寫成鴻篇巨著。本節(jié)簡單介紹TF-IDF的原理胁赢,以便更好地理解本文介紹的實(shí)例企蹭。
TF-IDF是一種統(tǒng)計(jì)方法,用以評(píng)估一個(gè)詞語對于一份文檔的重要程度智末。TF(Term Frequency)表示詞頻谅摄,對于一份文檔而言,詞頻是指特定詞語在這篇文檔里出現(xiàn)的次數(shù)除以該文檔總詞數(shù)系馆。例如送漠,一篇文檔一共有1000個(gè)詞,其中“樸素貝葉斯”出現(xiàn)了5次由蘑,“的”出現(xiàn)了25次闽寡,“應(yīng)用”出現(xiàn)了12次代兵,那么它們的詞頻分別是0.005,0.025和0.012爷狈。
IDF(Inverse Document Frequency)表示一個(gè)詞的逆向文檔頻率植影,由總文檔數(shù)除以包含該詞的文檔數(shù)的商再取對數(shù)得到。例如:我們的數(shù)據(jù)集一共10000篇文檔涎永,其中“樸素貝葉斯”只出現(xiàn)在10篇文檔中思币,則其IDF=log(10000/10)=3;“的”在所有文檔中都出現(xiàn)過羡微,則其IDF=log(10000/10000)=0谷饿;“應(yīng)用”在1000篇文檔中出現(xiàn)過,則其IDF=log(10000/1000)=1妈倔。
計(jì)算出每個(gè)詞的TF和IDF之后博投,兩者相乘,即可得到這個(gè)詞在文檔中的重要程度启涯。詞語的重要性與它在該文檔中出現(xiàn)的次數(shù)成正比贬堵,與它在語料庫中出現(xiàn)的文檔數(shù)成反比。
有了TF-IDF這個(gè)工具结洼,我們就可以把一篇文檔轉(zhuǎn)換為一個(gè)向量。首先叉跛,可以從數(shù)據(jù)集(在自然語言處理領(lǐng)域也稱corpus松忍,即語料庫)里提取出所有出現(xiàn)的詞,我們稱為詞典筷厘。假設(shè)詞典里總共有10000個(gè)詞語鸣峭,則每個(gè)文檔都可以轉(zhuǎn)化為一個(gè)10000維的向量。其次酥艳,針對我們要轉(zhuǎn)換的文檔里出現(xiàn)的每個(gè)詞語摊溶,都去計(jì)算其TF-IDF,并把這個(gè)值填入文檔向量里這個(gè)詞對應(yīng)的元素上充石。這樣就完成了把一篇文檔轉(zhuǎn)換為一個(gè)向量的過程莫换。一個(gè)文檔往往只會(huì)由詞典里的一小部分詞語構(gòu)成,這就意味著這個(gè)向量里的大部分元素都是0骤铃。
所幸拉岁,上述過程不需要我們自己寫代碼去完成,scikit-learn軟件包里實(shí)現(xiàn)了把文檔轉(zhuǎn)換為向量的過程惰爬。首先喊暖,把訓(xùn)練用的語料庫讀入內(nèi)存:
from time import time
from sklearn.datasets import load_files
print("loading train dataset ...")
t = time()
news_train = load_files('datasets/mlcomp/379/train')
print("summary: {0} documents in {1} categories."
.format(len(news_train.data),len(news_train.target_names)))
print("done in {0} seconds".format(time()-t))
輸出如下:
loading train dataset ...
summary: 13180 documents in 20 categories.
done in 0.8563289642333984 seconds
其中,datasets/mlcomp/379/train目錄下放的就是我們的語料庫撕瞧,其中包含20個(gè)子目錄陵叽,每個(gè)子目錄的名字表示的是文檔的類別狞尔,子目錄下包含這種類別的所有文檔。load_files()函數(shù)會(huì)從這個(gè)目錄里把所有的文檔都讀入內(nèi)存巩掺,并且自動(dòng)根據(jù)所在的子目錄名稱打上標(biāo)簽沪么。其中,news_train.data是一個(gè)數(shù)組锌半,里面包含了所有文檔的文本信息禽车。news_train.target也是一個(gè)數(shù)組,包含了所有文檔所屬的類別刊殉,而news_train.target_names則是類別的名稱殉摔,因此,如果我們想知道第一篇文檔所屬的類別名稱记焊,只需要通過代碼news_train.target_names[news_train.target[0]]即可得到逸月。
該語料庫里總共有13180個(gè)文檔,分成20個(gè)類別遍膜。接著需要把這些文檔全部轉(zhuǎn)換為由TF-IDF表達(dá)的權(quán)重信息構(gòu)成的向量:
from sklearn.feature_extraction.text import TfidfVectorizer
print("vectorizing train dataset ...")
t = time()
vectorizer = TfidfVectorizer(encoding='latin-1')
X_train = vectorizer.fit_transform((d for d in news_train.data))
print("n_samples: %d, n_features: %d" % X_train.shape)
print("number of non-zero features in samples [{0}]:{1}"
.format(news_train.filenames[0],X_train[0].getnnz()))
print("done in {0} seconds".format(time()-t))
輸出如下:
vectorizing train dataset ...
n_samples: 13180, n_features: 130274
number of non-zero features in samples [datasets/mlcomp/379/train\talk.politics.misc\17860-178992]:108
done in 2.6174726486206055 seconds
其中碗硬,TfidfVectorizer類是用來把所有的文檔轉(zhuǎn)換為矩陣,該矩陣每行都代表一個(gè)文檔瓢颅,一行中的每個(gè)元素代表一個(gè)對應(yīng)的詞語的重要性恩尾,詞語的重要性由TF-IDF來表示。其fit_transform()方法是fit()和transform()合并起來挽懦。其中翰意,fit()會(huì)先完成語料庫分析、提取詞典等操作信柿,transform()會(huì)把對每篇文檔轉(zhuǎn)換為向量冀偶,最終構(gòu)成一個(gè)矩陣,保存在X_train變量里渔嚷。
由輸出可以知道进鸠,我們的詞典總共有130274個(gè)詞語,即每篇文檔都可轉(zhuǎn)換為一個(gè)130274維的向量形病。第一篇文檔中客年,只有108個(gè)非零元素,即這篇文檔總共由108個(gè)不重復(fù)的單詞組成窒朋,在這篇文檔中出現(xiàn)的這108個(gè)單詞的TF-IDF值會(huì)被計(jì)算出來搀罢,并保存在向量中的指定位置上。X_train是一個(gè)維度為13180*130274的稀疏矩陣侥猩。
X_train稀疏矩陣由一個(gè)三元組(row,col,score)構(gòu)成:
print(X_train[0])
輸出如下:
(0, 56813) 0.014332663773643272
(0, 45689) 0.08373343949755
(0, 46084) 0.08109733529789522
(0, 125882) 0.0873157704840211
(0, 50150) 0.020654313721609956
(0, 87702) 0.04643235585055511
(0, 33334) 0.1025405658189532
(0, 111805) 0.014332663773643272
: :
(0, 67768) 0.08982314745972582
(0, 41790) 0.09260592033433869
(0, 105800) 0.08713990737243116
(0, 37075) 0.10018566542781165
(0, 23162) 0.08920437523600384
(0, 124699) 0.06257976758779137
(0, 94119) 0.1159317059788844
(0, 56555) 0.06984885482106491
(0, 62776) 0.10474995568339582
3.模型訓(xùn)練
使用MultinomialNB對數(shù)據(jù)集進(jìn)行訓(xùn)練:
from sklearn.naive_bayes import MultinomialNB
print("training models ...")
t = time()
y_train = news_train.target
clf = MultinomialNB(alpha=0.0001)
clf.fit(X_train,y_train)
train_score = clf.score(X_train,y_train)
print("train score: {0}".format(train_score))
print("done in {0} seconds".format(time()-t))
輸出如下:
training models ...
train score: 0.9978755690440061
done in 0.4581763744354248 seconds
其中榔至,alpha表示平滑參數(shù),其值越小欺劳,越容易造成過擬合唧取,值太大铅鲤,容易造成欠擬合。
接著枫弟,我們加載測試數(shù)據(jù)集邢享,并用一篇文檔來預(yù)測其是否準(zhǔn)確。測試數(shù)據(jù)集在datasets/mlcomp/379/test目錄下淡诗,我們用前面介紹的相同的方法先加載數(shù)據(jù)集:
print("loading test dataset ...")
t = time()
news_test = load_files('datasets/mlcomp/379/test')
print("summary: {0} documents in {1} categories."
.format(len(news_test.data),len(news_test.target_names)))
print("done in {0} seconds".format(time()-t))
輸出如下:
loading test dataset ...
summary: 5648 documents in 20 categories.
done in 0.3548290729522705 seconds
測試數(shù)據(jù)集共有5648篇文檔骇塘。接著,我們把文檔向量化:
print("vectorizing test dataset ...")
t = time()
X_test = vectorizer.transform((d for d in news_test.data))
y_test = news_test.target
print("n_samples: %d, n_features: %d" % X_test.shape)
print("number of non-zero features in sample [{0}]: {1}"
.format(news_test.filenames[0],X_test[0].getnnz()))
print("done in {0} seconds".format(time()-t))
輸出如下:
vectorizing test dataset ...
n_samples: 5648, n_features: 130274
number of non-zero features in sample [datasets/mlcomp/379/test\rec.autos\7429-103268]: 61
done in 0.9695498943328857 seconds
注意韩容,vectorizer變量是我們處理訓(xùn)練數(shù)據(jù)集時(shí)用到的向量化的類的實(shí)例款违,此處我們只需要調(diào)用transform()進(jìn)行TF-IDF數(shù)值計(jì)算即可,不需要再調(diào)用fit()進(jìn)行語料庫分析了群凶。
這樣插爹,我們的測試數(shù)據(jù)集也轉(zhuǎn)換為了一個(gè)維度為5648*130274的稀疏矩陣∏肷遥可以取測試數(shù)據(jù)集里的第一篇文檔初步驗(yàn)證一下赠尾,看看訓(xùn)練出來的模型能否正確地預(yù)測這個(gè)文檔所屬的類別:
pred = clf.predict(X_test[0])
print("predict: {0} is in category {1}"
.format(news_test.filenames[0],news_test.target_names[pred[0]]))
print("actually: {0} is in category {1}"
.format(news_test.filenames[0],news_test.target_names[news_test.target[0]]))
輸出如下:
predict: datasets/mlcomp/379/test\rec.autos\7429-103268 is in category rec.autos
actually: datasets/mlcomp/379/test\rec.autos\7429-103268 is in category rec.autos
看來預(yù)測的結(jié)果和實(shí)際結(jié)果是相符的。
4.模型評(píng)價(jià)
雖然通過驗(yàn)證毅弧,說明我們訓(xùn)練的模型是可用的气嫁,但是不能通過一個(gè)樣本的預(yù)測來評(píng)價(jià)模型的準(zhǔn)確性。我們需要對模型有個(gè)全方位的評(píng)價(jià)形真,所幸scikit-learn軟件包提供了全方位的模型評(píng)價(jià)工具杉编。
首先需要對測試數(shù)據(jù)集進(jìn)行預(yù)測:
print("predicting test dataset ...")
t = time()
pred_test = clf.predict(X_test)
print("done in %fs" % (time()-t))
接著使用classification_report()函數(shù)來查看一下針對每個(gè)類別的預(yù)測準(zhǔn)確性:
from sklearn.metrics import classification_report
print("classification report on test set for classifier:")
print(clf)
print(classification_report(y_test,pred_test,target_names=news_test.target_names))
輸出如下:
classification report on test set for classifier:
MultinomialNB(alpha=0.0001, class_prior=None, fit_prior=True)
precision recall f1-score support
alt.atheism 0.90 0.91 0.91 245
comp.graphics 0.80 0.90 0.85 298
comp.os.ms-windows.misc 0.82 0.79 0.80 292
comp.sys.ibm.pc.hardware 0.81 0.80 0.81 301
comp.sys.mac.hardware 0.90 0.91 0.91 256
comp.windows.x 0.88 0.88 0.88 297
misc.forsale 0.87 0.81 0.84 290
rec.autos 0.92 0.93 0.92 324
rec.motorcycles 0.96 0.96 0.96 294
rec.sport.baseball 0.97 0.94 0.96 315
rec.sport.hockey 0.96 0.99 0.98 302
sci.crypt 0.95 0.96 0.95 297
sci.electronics 0.91 0.85 0.88 313
sci.med 0.96 0.96 0.96 277
sci.space 0.94 0.97 0.96 305
soc.religion.christian 0.93 0.96 0.94 293
talk.politics.guns 0.91 0.96 0.93 246
talk.politics.mideast 0.96 0.98 0.97 296
talk.politics.misc 0.90 0.90 0.90 236
talk.religion.misc 0.89 0.78 0.83 171
avg / total 0.91 0.91 0.91 5648
從輸出結(jié)果中可以看出,針對每種類別都統(tǒng)計(jì)了查準(zhǔn)率咆霜、召回率和F1-Score。此外嘶朱,還可以通過confusion_matrix()函數(shù)生成混淆矩陣蛾坯,觀察每種類別被錯(cuò)誤分類的情況。例如疏遏,這些被錯(cuò)誤分類的文檔是被錯(cuò)誤分類到哪些類別里的:
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test,pred_test)
print("confusion matrix:\n")
print(cm)
輸出如下:
confusion matrix:
[[224 0 0 0 0 0 0 0 0 0 0 0 0 0 2 5 0 0 1 13]
[ 1 267 5 5 2 8 1 1 0 0 0 2 3 2 1 0 0 0 0 0]
[ 1 13 230 24 4 10 5 0 0 0 0 1 2 1 0 0 0 0 1 0]
[ 0 9 21 242 7 2 10 1 0 0 1 1 7 0 0 0 0 0 0 0]
[ 0 1 5 5 233 2 2 2 1 0 0 3 1 0 1 0 0 0 0 0]
[ 0 20 6 3 1 260 0 0 0 2 0 1 0 0 2 0 2 0 0 0]
[ 0 2 5 12 3 1 235 10 2 3 1 0 7 0 2 0 2 1 4 0]
[ 0 1 0 0 1 0 8 300 4 1 0 0 1 2 3 0 2 0 1 0]
[ 0 1 0 0 0 2 2 3 283 0 0 0 1 0 0 0 0 0 1 1]
[ 0 1 1 0 1 2 1 2 0 297 8 1 0 1 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 2 2 298 0 0 0 0 0 0 0 0 0]
[ 0 1 2 0 0 1 1 0 0 0 0 284 2 1 0 0 2 1 2 0]
[ 0 11 3 5 4 2 4 5 1 1 0 4 266 1 4 0 1 0 1 0]
[ 1 1 0 1 0 2 1 0 0 0 0 0 1 266 2 1 0 0 1 0]
[ 0 3 0 0 1 1 0 0 0 0 0 1 0 1 296 0 1 0 1 0]
[ 3 1 0 1 0 0 0 0 0 0 1 0 0 2 1 280 0 1 1 2]
[ 1 0 2 0 0 0 0 0 1 0 0 0 0 0 0 0 236 1 4 1]
[ 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 0 290 1 0]
[ 2 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1 10 7 212 0]
[ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 4 1 4 134]]
例如:從第一行可以看出脉课,類別0(alt.atheism)的文檔,有13個(gè)被錯(cuò)誤地分類到類別19(talk.religion.misc)里财异。當(dāng)然倘零,我們還可以把混淆矩陣進(jìn)行數(shù)據(jù)可視化:
# Show confusion matrix
import matplotlib.pyplot as plt
plt.figure(figsize=(8,8),dpi=144)
plt.title('Confusion matrix of the classifier')
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.set_xticklabels([])
ax.set_yticklabels([])
plt.matshow(cm,fignum=1,cmap='gray')
plt.colorbar()
輸出圖形如下:
除對角線外,其他地方顏色越淺戳寸,說明此處錯(cuò)誤越多呈驶。通過這些數(shù)據(jù),我們可以詳細(xì)分析樣本數(shù)據(jù)疫鹊,找出為什么某種類別會(huì)被錯(cuò)誤地分類到另一種類別里袖瞻,從而進(jìn)一步優(yōu)化模型司致。