用 Python 輕松實(shí)現(xiàn)機(jī)器學(xué)習(xí)
用樸素貝葉斯分類器解決現(xiàn)實(shí)世界里的機(jī)器學(xué)習(xí)問題。
樸素貝葉斯是一種分類技術(shù),它是許多分類器建模算法的基礎(chǔ)?;跇闼刎惾~斯的分類器是簡(jiǎn)單、快速和易用的機(jī)器學(xué)習(xí)技術(shù)之一,而且在現(xiàn)實(shí)世界的應(yīng)用中很有效。
樸素貝葉斯是從 貝葉斯定理 發(fā)展來的。貝葉斯定理由 18 世紀(jì)的統(tǒng)計(jì)學(xué)家 托馬斯·貝葉斯 提出,它根據(jù)與一個(gè)事件相關(guān)聯(lián)的其他條件來計(jì)算該事件發(fā)生的概率。比如,帕金森氏病 患者通常嗓音會(huì)發(fā)生變化,因此嗓音變化就是與預(yù)測(cè)帕金森氏病相關(guān)聯(lián)的癥狀。貝葉斯定理提供了計(jì)算目標(biāo)事件發(fā)生概率的方法,而樸素貝葉斯是對(duì)該方法的推廣和簡(jiǎn)化。
解決一個(gè)現(xiàn)實(shí)世界里的問題
這篇文章展示了樸素貝葉斯分類器解決現(xiàn)實(shí)世界問題(相對(duì)于完整的商業(yè)級(jí)應(yīng)用)的能力。我會(huì)假設(shè)你對(duì)機(jī)器學(xué)習(xí)有基本的了解,所以文章里會(huì)跳過一些與機(jī)器學(xué)習(xí)預(yù)測(cè)不大相關(guān)的步驟,比如 數(shù)據(jù)打亂 和 數(shù)據(jù)切片。如果你是機(jī)器學(xué)習(xí)方面的新手或者需要一個(gè)進(jìn)修課程,請(qǐng)查看 《An introduction to machine learning today》 和 《Getting started with open source machine learning》。
樸素貝葉斯分類器是 有監(jiān)督的、屬于 生成模型 的、非線性的、屬于 參數(shù)模型 的和 基于概率的。
在這篇文章里,我會(huì)演示如何用樸素貝葉斯預(yù)測(cè)帕金森氏病。需要用到的數(shù)據(jù)集來自 UCI 機(jī)器學(xué)習(xí)庫。這個(gè)數(shù)據(jù)集包含許多語音信號(hào)的指標(biāo),用于計(jì)算患帕金森氏病的可能性;在這個(gè)例子里我們將使用這些指標(biāo)中的前 8 個(gè):
- MDVP:Fo(Hz):平均聲帶基頻
- MDVP:Fhi(Hz):最高聲帶基頻
- MDVP:Flo(Hz):最低聲帶基頻
- MDVP:Jitter(%)、MDVP:Jitter(Abs)、MDVP:RAP、MDVP:PPQ 和 Jitter:DDP:5 個(gè)衡量聲帶基頻變化的指標(biāo)
這個(gè)例子里用到的數(shù)據(jù)集,可以在我的 GitHub 倉庫 里找到。數(shù)據(jù)集已經(jīng)事先做了打亂和切片。
用 Python 實(shí)現(xiàn)機(jī)器學(xué)習(xí)
接下來我會(huì)用 Python 來解決這個(gè)問題。我用的軟件是:
- Python 3.8.2
- Pandas 1.1.1
- scikit-learn 0.22.2.post1
Python 有多個(gè)樸素貝葉斯分類器的實(shí)現(xiàn),都是開源的,包括:
- NLTK Naïve Bayes:基于標(biāo)準(zhǔn)的樸素貝葉斯算法,用于文本分類
- NLTK Positive Naïve Bayes:NLTK Naïve Bayes 的變體,用于對(duì)只標(biāo)注了一部分的訓(xùn)練集進(jìn)行二分類
- Scikit-learn Gaussian Naïve Bayes:提供了部分?jǐn)M合方法來支持?jǐn)?shù)據(jù)流或很大的數(shù)據(jù)集(LCTT 譯注:它們可能無法一次性導(dǎo)入內(nèi)存,用部分?jǐn)M合可以動(dòng)態(tài)地增加數(shù)據(jù))
- Scikit-learn Multinomial Naïve Bayes:針對(duì)離散型特征、實(shí)例計(jì)數(shù)、頻率等作了優(yōu)化
- Scikit-learn Bernoulli Naïve Bayes:用于各個(gè)特征都是二元變量/布爾特征的情況
在這個(gè)例子里我將使用 sklearn Gaussian Naive Bayes。
我的 Python 實(shí)現(xiàn)在 naive_bayes_parkinsons.py
里,如下所示:
import pandas as pd
# x_rows 是我們所使用的 8 個(gè)特征的列名
x_rows=['MDVP:Fo(Hz)','MDVP:Fhi(Hz)','MDVP:Flo(Hz)',
'MDVP:Jitter(%)','MDVP:Jitter(Abs)','MDVP:RAP','MDVP:PPQ','Jitter:DDP']
y_rows=['status'] # y_rows 是類別的列名,若患病,值為 1,若不患病,值為 0
# 訓(xùn)練
# 讀取訓(xùn)練數(shù)據(jù)
train_data = pd.read_csv('parkinsons/Data_Parkinsons_TRAIN.csv')
train_x = train_data[x_rows]
train_y = train_data[y_rows]
print("train_x:\n", train_x)
print("train_y:\n", train_y)
# 導(dǎo)入 sklearn Gaussian Naive Bayes,然后進(jìn)行對(duì)訓(xùn)練數(shù)據(jù)進(jìn)行擬合
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
gnb.fit(train_x, train_y)
# 對(duì)訓(xùn)練數(shù)據(jù)進(jìn)行預(yù)測(cè)
predict_train = gnb.predict(train_x)
print('Prediction on train data:', predict_train)
# 在訓(xùn)練數(shù)據(jù)上的準(zhǔn)確率
from sklearn.metrics import accuracy_score
accuracy_train = accuracy_score(train_y, predict_train)
print('Accuray score on train data:', accuracy_train)
# 測(cè)試
# 讀取測(cè)試數(shù)據(jù)
test_data = pd.read_csv('parkinsons/Data_Parkinsons_TEST.csv')
test_x = test_data[x_rows]
test_y = test_data[y_rows]
# 對(duì)測(cè)試數(shù)據(jù)進(jìn)行預(yù)測(cè)
predict_test = gnb.predict(test_x)
print('Prediction on test data:', predict_test)
# 在測(cè)試數(shù)據(jù)上的準(zhǔn)確率
accuracy_test = accuracy_score(test_y, predict_test)
print('Accuray score on test data:', accuracy_train)
運(yùn)行這個(gè) Python 腳本:
$ python naive_bayes_parkinsons.py
train_x:
MDVP:Fo(Hz) MDVP:Fhi(Hz) ... MDVP:RAP MDVP:PPQ Jitter:DDP
0 152.125 161.469 ... 0.00191 0.00226 0.00574
1 120.080 139.710 ... 0.00180 0.00220 0.00540
2 122.400 148.650 ... 0.00465 0.00696 0.01394
3 237.323 243.709 ... 0.00173 0.00159 0.00519
.. ... ... ... ... ... ...
155 138.190 203.522 ... 0.00406 0.00398 0.01218
[156 rows x 8 columns]
train_y:
status
0 1
1 1
2 1
3 0
.. ...
155 1
[156 rows x 1 columns]
Prediction on train data: [1 1 1 0 ... 1]
Accuracy score on train data: 0.6666666666666666
Prediction on test data: [1 1 1 1 ... 1
1 1]
Accuracy score on test data: 0.6666666666666666
在訓(xùn)練集和測(cè)試集上的準(zhǔn)確率都是 67%。它的性能還可以進(jìn)一步優(yōu)化。你想嘗試一下嗎?你可以在下面的評(píng)論區(qū)給出你的方法。
背后原理
樸素貝葉斯分類器從貝葉斯定理發(fā)展來的。貝葉斯定理用于計(jì)算條件概率,或者說貝葉斯定理用于計(jì)算當(dāng)與一個(gè)事件相關(guān)聯(lián)的其他事件發(fā)生時(shí),該事件發(fā)生的概率。簡(jiǎn)而言之,它解決了這個(gè)問題:如果我們已經(jīng)知道事件 x 發(fā)生在事件 y 之前的概率,那么當(dāng)事件 x 再次發(fā)生時(shí),事件 y 發(fā)生的概率是多少? 貝葉斯定理用一個(gè)先驗(yàn)的預(yù)測(cè)值來逐漸逼近一個(gè)最終的 后驗(yàn)概率。貝葉斯定理有一個(gè)基本假設(shè),就是所有的參數(shù)重要性相同(LCTT 譯注:即相互獨(dú)立)。
貝葉斯計(jì)算主要包括以下步驟:
- 計(jì)算總的先驗(yàn)概率:
P(患病) 和 P(不患病) - 計(jì)算 8 種指標(biāo)各自是某個(gè)值時(shí)的后驗(yàn)概率 (value1,...,value8 分別是 MDVP:Fo(Hz),...,Jitter:DDP 的取值):
P(value1,\ldots,value8\ |\ 患病)
P(value1,\ldots,value8\ |\ 不患病) - 將第 1 步和第 2 步的結(jié)果相乘,最終得到患病和不患病的后驗(yàn)概率:
P(患病\ |\ value1,\ldots,value8) \propto P(患病) \times P(value1,\ldots,value8\ |\ 患病)
P(不患病\ |\ value1,\ldots,value8) \propto P(不患病) \times P(value1,\ldots,value8\ |\ 不患病)
上面第 2 步的計(jì)算非常復(fù)雜,樸素貝葉斯將它作了簡(jiǎn)化:
- 計(jì)算總的先驗(yàn)概率:
P(患病) 和 P(不患病) - 對(duì) 8 種指標(biāo)里的每個(gè)指標(biāo),計(jì)算其取某個(gè)值時(shí)的后驗(yàn)概率:
P(value1\ |\ 患病),\ldots,P(value8\ |\ 患病)
P(value1\ |\ 不患病),\ldots,P(value8\ |\ 不患病) - 將第 1 步和第 2 步的結(jié)果相乘,最終得到患病和不患病的后驗(yàn)概率:
P(患病\ |\ value1,\ldots,value8) \propto P(患病) \times P(value1\ |\ 患病) \times \ldots \times P(value8\ |\ 患病)
P(不患病\ |\ value1,\ldots,value8) \propto P(不患病) \times P(value1\ |\ 不患病) \times \ldots \times P(value8\ |\ 不患病)
這只是一個(gè)很初步的解釋,還有很多其他因素需要考慮,比如數(shù)據(jù)類型的差異,稀疏數(shù)據(jù),數(shù)據(jù)可能有缺失值等。
超參數(shù)
樸素貝葉斯作為一個(gè)簡(jiǎn)單直接的算法,不需要超參數(shù)。然而,有的版本的樸素貝葉斯實(shí)現(xiàn)可能提供一些高級(jí)特性(比如超參數(shù))。比如,GaussianNB 就有 2 個(gè)超參數(shù):
- priors:先驗(yàn)概率,可以事先指定,這樣就不必讓算法從數(shù)據(jù)中計(jì)算才能得出。
- var_smoothing:考慮數(shù)據(jù)的分布情況,當(dāng)數(shù)據(jù)不滿足標(biāo)準(zhǔn)的高斯分布時(shí),這個(gè)超參數(shù)會(huì)發(fā)揮作用。
損失函數(shù)
為了堅(jiān)持簡(jiǎn)單的原則,樸素貝葉斯使用 0-1 損失函數(shù)。如果預(yù)測(cè)結(jié)果與期望的輸出相匹配,損失值為 0,否則為 1。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):樸素貝葉斯是最簡(jiǎn)單、最快速的算法之一。
優(yōu)點(diǎn):在數(shù)據(jù)量較少時(shí),用樸素貝葉斯仍可作出可靠的預(yù)測(cè)。
缺點(diǎn):樸素貝葉斯的預(yù)測(cè)只是估計(jì)值,并不準(zhǔn)確。它勝在速度而不是準(zhǔn)確度。
缺點(diǎn):樸素貝葉斯有一個(gè)基本假設(shè),就是所有特征相互獨(dú)立,但現(xiàn)實(shí)情況并不總是如此。
從本質(zhì)上說,樸素貝葉斯是貝葉斯定理的推廣。它是最簡(jiǎn)單最快速的機(jī)器學(xué)習(xí)算法之一,用來進(jìn)行簡(jiǎn)單和快速的訓(xùn)練和預(yù)測(cè)。樸素貝葉斯提供了足夠好、比較準(zhǔn)確的預(yù)測(cè)。樸素貝葉斯假設(shè)預(yù)測(cè)特征之間是相互獨(dú)立的。已經(jīng)有許多樸素貝葉斯的開源的實(shí)現(xiàn),它們的特性甚至超過了貝葉斯算法的實(shí)現(xiàn)。