Scikit-Learn: KNeighbors 教學 ( Iris dataset 及 Tips dataset)

以下將簡單教大家怎麼使用Python 來玩 Machine Learning (機器學習). Scikit-Learn 是最著名的一個package, 我們將使用其下的 KNeighbors 對 IRIS 和 TIP 這兩個 dataset 做預測.


首先先來了解一下 KNeighbors 的原理, 其實滿簡單的. 它是利用物以類聚的概念, 具有相同性質的物件會聚集在一起, 我們利用這樣的特性來預測新的值可能落在哪一群. 那麼在已經分好群的情況下, 該採取幾個點(k)來預測新值呢?

這是可以討論的, 如下圖所示, 當採用一個點(k=1)來做新點的分群, 可以看到Fig 1左上的星星, 它會尋找最接近它的一個點, 然後它就是屬於最接那一點的群(藍色群).
Fig 1: k=1
但是並不是k=1就是一個準確的預測, 有些時候也會加大k值來評判要取幾個點會比較好, 下圖採用的是三個點(k=3), 當採用三個點時, 左上的星星明顯就不再屬於藍色群了, 變成橘色群. 因為在最接近它的三個點, 有兩個是橘色的. 
Fig 2: k=2
  • Load data

了解了這個基本的概念, 我們就可以利用這個方法來做一些分析. 以下先用 IRIS (鳶尾花) 這個dataset來做說明, Iris可以從 sklearn 直接下載.

接著來先來簡單看一下這個dataset的內容, 可以搭配如下的指令.

1. irir_dataset: 這個指令會把 Iris 所有內容都列出來, 可以看到我們download下來的資料有train需要的花萼/花瓣的長/寬數據, 還有花的品種數據. 等等將使用這些資料來建模型, 並預估新數據花的品種.

2. iris_dataset.target 及 iris_dataset.target_names: 這兩個指令是用來叫出每筆數據的花是屬於哪一種品種的. 總共有三個品種的花, Setosa (0), Versicolor (1), Virginica (2).

3. iris_dataset.data.shape, iris_dataset.target.shape: 這個指令(.shape)是用來查看array的維度, 可以看到整個資料集總共有150筆數據, data (也就是存放花萼/花瓣的長及寬)有四個欄位, target (存放花的品種有一個欄位).

  • Split data

觀察完數據集以後, 接著要知道怎麼建模型, 在建立模型前我們會把原始資料集(original dataset)隨機分成兩個部分, 一個是train set, 另一個是test set. 使用的指令為 train_test_split, 其下有幾個常用的參數; text_size=0.25 (optinal) 選配, 不設定的話, 預設為0.25, 所以150筆的原始數據有75%會被分成train set, 也就是112筆. 剩下的25%會被分成test set, 也就是38筆. random_state=0為seed, 方便重現實驗結果.
  • Data Leakage

在建模之前, 先檢查一下有沒有Leakage的問題, leakage指的是分析的結果被當成資料來建模, 進而影響到模型的準確性. 這種導因為果的方式並不是我們想看到的. 這裡我們使用統計上的相關係數(correlation)來檢查. 可以發現儘管第三和第四項有很高的相關性, 但是並不是1 或 -1 (1或-1表完全相關), 所以可以放心的建立模型.

  • Build Model

接下來導入需要的模組KNeighborsClassifier, 因為等等還需要繪圖, 所以我連 matplotlib 一起導入.
因為把k值分開算太花時間了, 所以直接使用for迴圈撈20個k值來看結果. 從下圖可以發現k=1時, 模型的準確度最高. 所以等等將採用k=1來預測. (實際操作上k值的選擇需要相當謹慎, 因為k值選太小, 則可能會發生overfitting的情況, 一點noise就會影響到模型的精準度; k值選太大, 模型會變得太簡單, 發生 underfitting的情況. 為了講解方便, 所以下例取k=1來做說明.)
有些同學會把 random_state=0 拿掉, 會跑出不同的圖形(如下). 該怎麼選擇一個比較好的 k值, 幾個小技巧提供給大家參考:
1. k值不要挑太小, 也不要挑太大. 
2. Train score 挑高的
3. Train score 和 Test score 的gap選較小的.

如果以上面三個技巧來選擇, 下圖可以選 k=4, 應該會是一個比較好的值.

  • Prediction

在預測前, 先把k值設成1, 並且創造一組新數據X_new, 透過 knn.predict() 預測出這組新數據可能的品種是 Setosa.

以上是KNN簡單的介紹, 另外如果想深入研究, 還可以多寫一個for迴圈去看看在哪一個test_size下可以獲得較高accuracy.

最後, 有些人發現怎麼沒講到TIPS這個dataset, 不要急, 正要開始. Tips這個datasset是在seaborn這個package, 所以要從seaborn下載. 
從上圖可以知道, 這是一個關於小費的資料集, 包含的欄位有 total_bill (帳單總額), tip (小費金額), sex (付款者性別), smoker (有無抽菸), day (用餐日期), time (用餐時段), size (用餐人數). 

利用value_counts()可以窺看個各欄位的一些基本的統計數據. 

有沒有人想過為什麼今天要來介紹TIPS這個dataset呢? 這是因為 KNeighborsClassifier 對於離散型資料很好用, 但是對於連續型資料就不好用了, 如下例要來預測小費, 這種非整數的變數, KNN並不適用, 這時候就要改用 KNeighborsRegressor 迴歸型的KNN分群. 

在建模前, 我們得先處理categorial variable, 畢竟用到迴歸, 所有變數都要是 numerical variable. 以下我採用.map({欲換值: 取代值}) 來轉換變數.

接著把dataset分成data和target. Target即是要預測的目標變, 此例為tip 
導入需要用到的package.
建立模型, 並從中挑選一個合適的k值. (可以發現下圖的值落在0.4多, 並不高; 故KNeighborsRegressor 演算法可能並不適合 TIPS 資料集, 可以考慮換其他的方法做預測.)


以上是對 KNeighborsClassifier 和 KNeighborsRegressor 的簡單介紹及一點心得感想, 如果有疑問, 可以於下面討論. 感謝!

張貼留言

0 留言