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