K近鄰算法詳解

筆者公衆號:技術雜學鋪

筆者網站:mwhitelab.com

筆者最近準備寫一個系列的機器學習算法教程。每篇文章詳細講解一個機器學習的經典算法,旨在讓零基礎的讀者可以快速入門。

 

1. 算法介紹

k近鄰(k-neareast neighbor,kNN)是解決分類與迴歸問題最基本的算法之一。該算法沒有顯式的訓練過程。

k近鄰算法的核心思想用一句話就可以概括:

給定測試樣本,基於某種距離度量找出訓練集中與其最近的k個訓練樣本,然後基於這k個“鄰居”的信息來進行預測。

——《機器學習》第10章

該算法有三個重要要素:距離度量、k值的選擇和分類決策規則。

1.1 距離度量

假設一個人身高175cm,體重爲65公斤,體重正常。我們可以用 X1 = [175, 60] Y1 = “非肥胖” 來表示這個人。同樣,若一個人身高170cm,體重90公斤,體重超標。我們用 X2 = [170, 90] Y2 = “肥胖” 來表示此人。

現在已知一人X = [160,100](即身高160cm,體重100公斤),我們想要知道此人與前兩人的相似程度,從而推斷出此人是肥胖還是非肥胖。

這裏的相似程度可用距離來表示,若兩個人之間的X(特徵)越接近,則認爲這兩個人越相似。

因此,我們需要考慮如何度量兩個樣本之間的距離。

距離的定義不止一種,一種通用的定義爲Lp距離:

該定義爲一種範數。當p=2時,即爲我們常見的L2範數(歐式距離);p=1爲L1範數(曼哈頓距離)

如下圖,歐式距離爲兩點之間直線段長度,曼哈頓距離爲各軸座標差之和。

所選的距離的度量(曼哈頓距離、歐式距離、L3距離……)不同,各個點之間的距離不同,各點之間的最近鄰點也可能不同。

歐式距離

對於上述體重的例子,X=[160,100] 與非肥胖樣本 X1=[175,60] 的歐式距離(L2距離)爲42.72;X=[160,100] 與肥胖樣本X2=[170,90] 的歐式距離爲14.14。

待預測樣本X與肥胖樣本X2更接近,因此我們預測樣本X爲肥胖樣本。

這裏有一個值得注意的細節:若我們把身高的單位變爲毫米,把體重的單位變成噸。則樣本之間的距離也會發生變化。一個人可以是1600mm身高,有0.1噸的體重。此時若是做樣本之間歐式距離的計算,則身高的差距會在歐式距離中佔據主導地位,而體重之間的差距幾乎可忽略不計。

如果我們認爲不同特徵對結果同等重要的話,我們常常會對特徵進行歸一化處理——把樣本數據縮放到一個指定範圍內。

歸一化的方法有很多,比如把分佈在0-220cm的身高線性變換成分佈在-1到1之間的數據、或者縮放成均值爲0,方差爲1的數據等等。

1.2 K值的選擇

之前我們預測體重樣本時,是選擇與待測樣本最近的一個樣本來進行參考。

事實上,我們可以選擇k個與待測樣本最近的樣本,並認爲k各已知樣本中出現次數最多的類別即爲待測樣本的類別。

有的時候,k值的選擇不同,預測的結果也會不同。

k值的選擇決定了綠色小球的類別

上圖中,當k=3時,小球應該爲紅色三角;k=5時,小球應該爲藍色正方形。

 

k值越小,模型越複雜,越容易發生過擬合。k值越大,模型越簡單。當k等於訓練集樣本數的時候,模型只是單純返回訓練集中出現次數最多的類別。

應用中,k值的選擇通常爲人爲設定。一般取值都較小。可用交叉驗證法來確定最優的k值。

1.3 分類決策規則

在分類問題(如判斷人是否肥胖)中,我們通常是在取k個樣本後,採取多數表決:出現次數最多的類別即爲待測樣本的類別。

亦可使用加權投票:與待測樣本越近的樣本權重越大,對各類別進行權重求和後,選擇權重和最大的類別。

而在迴歸問題中(如已知一個人身高、年齡、平均每日攝取卡路里等信息,預測此人體重),我們可取k個樣本後,對其結果Y進行平均或者加權平均

2. 代碼實現

2.1 核心代碼

本段代碼改編自《機器學習實戰》。在技術雜學鋪公衆號內回覆“機器學習”即可下載電子版。

筆者已將《機器學習實戰》第二章kNN全部代碼整合爲jupyter文件,並改版爲python3版本且附帶大量中文註釋。讀者可前往github下載 | jupyter環境安裝

kNN算法

數據歸一化算法

2.2 實戰任務

這裏我們使用sklearn函數庫、Iris數據集。相關介紹可見往期文章

注:本任務需安裝sklearn函數庫。

Iris – 鳶尾花

已知三種品種的鳶尾花(山鳶尾、維吉尼亞鳶尾、變色鳶尾)的花萼長度,花萼寬度,花瓣長度,花瓣寬度4個屬性各50條。數據樣本如下:

花萼長度 花萼寬度 花瓣長度 花瓣寬度 品種
6.4 2.8 5.6 2.2 2
5.0 2.3 3.3 1.0 1
4.9 2.5 4.5 1.7 2
4.9 3.1 1.5 0.1 0
5.7 3.8 1.7 0.3 0

任務:對Iris數據集,自行劃分訓練集與測試集,探究kNN算法對此數據集的效果如何。

鳶尾花數據分佈圖

第一步,導入數據。sklearn函數庫自帶iris數據集。直接導入即可。

共150條數據,X爲樣本特徵,每個樣本有4個數字屬性。Y爲樣本的類別,取值在0、1、2之間。

第二步,劃分訓練集和測試集。

使用sklearn的函數train_test_split可將數據打亂後劃分到訓練集與測試集中。其中設置 test_size=0.2表示測試集佔總數據的20%

第三步,創建kNN模型。

導入KNeighborsClassifier函數,設置其k的取值爲5(n_neighbors)

設置完後我們會看到kNN模型的具體參數:

KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=5, p=2,
           weights='uniform')

我們未設置其距離度量方式。模型默認取p=2,即爲歐式距離。分類決策規則默認爲多數表決。

使用 模型.fit(訓練集特徵,訓練集類別) 可訓練模型(kNN無需訓練模型,這裏只是綁定了訓練集數據。若是其他模型執行fit函數,可能會需要訓練一段時間)

第四步,結果評估。

使用 模型.predict(測試集特徵) 可得到模型對待測樣本的預測結果。在以後的模型應用時,執行此函數即可。

執行 模型.score(測試集特徵,測試集類別) 可得到模型對於測試集預測的準確率,即可知道模型的好壞。

此代碼文件與2.1節核心代碼放在github同一目錄下,讀者可前往github下載

3. 其他

  • k近鄰法一般都是線性掃描訓練集樣本。kd樹可提高其搜索效率,其平均計算複雜度爲O(logN)。具體算法可見《統計學習方法》。
  • kNN雖然算法簡單,但在手寫數字數據集MNIST上,識別錯誤率僅爲5%,最好的kNN變體算法,識別錯誤率可低至0.52%!

kNN在MNIST數據集上的表現
從左至右分別爲 算法名、預處理方法、錯誤率、來源

4. 總結

本文我們討論了機器學習經典算法之一的k近鄰算法。討論其距離度量、k的選擇和決策分類規則,並使用sklearn機器學習庫來解決鳶尾花的分類問題。

k近鄰算法無需提前訓練模型,而是根據與待測樣本最近的k個樣本的信息來預測待測樣本。

4.1 k近鄰算法優點

  • 實現簡單
  • 無需提前訓練模型
  • 對異常值不敏感

4.2 k近鄰算法缺點

  • 訓練集過大時預測速度極慢(計算複雜度高)
  • 預測時需要保留訓練數據集(空間複雜度高)
  • 無法給出數據的基礎結構信息,不知道典型實例樣本有怎樣的特徵

參考資料

  • 《機器學習實戰》第2章 k-近鄰算法
  • 《統計學習方法》第2章 k-近鄰算法
  • 《機器學習》10.1 k近鄰學習
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章